mirror of
https://github.com/simstudioai/sim.git
synced 2026-10-31 03:47:12 +08:00
fix(landing): refresh sidebars across landing surface (#7102)
* fix(landing): refresh sidebars across landing surface * fix(landing): remove inline preview scaling
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
'use client'
|
||||
|
||||
import Image from 'next/image'
|
||||
import {
|
||||
PREVIEW_SIDEBAR_CHATS,
|
||||
PREVIEW_SIDEBAR_WORKFLOWS,
|
||||
} from '@/app/(landing)/components/shared/sidebar-preview-content'
|
||||
import {
|
||||
EnterpriseSidebar,
|
||||
type EnterpriseSidebarProps,
|
||||
} from '@/app/(landing)/enterprise/components/enterprise-platform-loop/enterprise-sidebar'
|
||||
|
||||
interface CapturedPlatformSurfaceProps {
|
||||
src: string
|
||||
sizes: string
|
||||
activeItem: NonNullable<EnterpriseSidebarProps['activeItem']>
|
||||
}
|
||||
|
||||
/**
|
||||
* A captured product surface with the current landing sidebar rendered over
|
||||
* the capture's legacy sidebar pixels. Keeping the sidebar live gives every
|
||||
* landing callout one source of truth while preserving the detailed product
|
||||
* content in the capture beside it.
|
||||
*/
|
||||
export function CapturedPlatformSurface({ src, sizes, activeItem }: CapturedPlatformSurfaceProps) {
|
||||
return (
|
||||
<div className='absolute inset-0 overflow-hidden'>
|
||||
<Image src={src} alt='' fill sizes={sizes} className='object-cover' />
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
className='pointer-events-none absolute inset-0 size-full overflow-hidden'
|
||||
viewBox='0 0 1280 735'
|
||||
preserveAspectRatio='xMinYMin meet'
|
||||
>
|
||||
<foreignObject width='1280' height='735'>
|
||||
<div className='flex h-full w-[249px] border-[var(--border)] border-r bg-[var(--surface-1)]'>
|
||||
<EnterpriseSidebar
|
||||
chats={PREVIEW_SIDEBAR_CHATS}
|
||||
workflows={PREVIEW_SIDEBAR_WORKFLOWS}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
<div className='min-w-0 flex-1 bg-[var(--surface-1)]' />
|
||||
</div>
|
||||
</foreignObject>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+6
-12
@@ -1,14 +1,10 @@
|
||||
import Image from 'next/image'
|
||||
import { CalloutFrame } from '@/app/(landing)/components/features/components/callout-frame'
|
||||
import { CapturedPlatformSurface } from '@/app/(landing)/components/features/components/captured-platform-surface'
|
||||
|
||||
/**
|
||||
* The Integrate beat's callout - the REAL platform Integrations page as one
|
||||
* floating window: a full capture of the workspace UI (sidebar + Integrations
|
||||
* tab with the showcase mosaic, search, and Featured sections) taken by
|
||||
* `exports/readme-banner/capture-integrations-ui.mjs` at the hero shot's card
|
||||
* geometry (1280x735 @2x), framed by the shared {@link CalloutFrame} so it
|
||||
* wears the hero platform window's exact chrome (10px radius + layered
|
||||
* shadow).
|
||||
* The Integrate beat's callout - the platform Integrations page as one
|
||||
* floating window. The detailed page capture is paired with the shared live
|
||||
* landing sidebar so its navigation and footer stay aligned with the product.
|
||||
*
|
||||
* The window is oversized (125% of the media stage, ~82% of the capture's
|
||||
* native scale) and anchored with visually EQUAL top and left insets
|
||||
@@ -37,12 +33,10 @@ export function IntegrationsCallout() {
|
||||
className='absolute top-[14.4%] left-[9.6%] w-[125%]'
|
||||
bodyClassName='aspect-[1280/735]'
|
||||
>
|
||||
<Image
|
||||
<CapturedPlatformSurface
|
||||
src='/landing/feature-integrate-ui.png'
|
||||
alt=''
|
||||
fill
|
||||
sizes='(max-width: 1023px) 114vw, (max-width: 1460px) 109vw, 1053px'
|
||||
className='object-cover'
|
||||
activeItem='Integrations'
|
||||
/>
|
||||
</CalloutFrame>
|
||||
</div>
|
||||
|
||||
+6
-12
@@ -1,14 +1,10 @@
|
||||
import Image from 'next/image'
|
||||
import { CalloutFrame } from '@/app/(landing)/components/features/components/callout-frame'
|
||||
import { CapturedPlatformSurface } from '@/app/(landing)/components/features/components/captured-platform-surface'
|
||||
|
||||
/**
|
||||
* The Context beat's callout - the REAL platform Knowledge base page as one
|
||||
* floating window: a full capture of the workspace UI (sidebar with Knowledge
|
||||
* base selected + the knowledge list) taken by
|
||||
* `exports/readme-banner/capture-knowledge-ui.mjs` at the hero shot's card
|
||||
* geometry (1280x735 @2x), framed by the shared {@link CalloutFrame} so it
|
||||
* wears the hero platform window's exact chrome (10px radius + layered
|
||||
* shadow).
|
||||
* The Context beat's callout - the platform Knowledge bases page as one
|
||||
* floating window. The detailed page capture is paired with the shared live
|
||||
* landing sidebar so its navigation and footer stay aligned with the product.
|
||||
*
|
||||
* Same oversized treatment as the Integrate card: 125% of the media stage
|
||||
* with EQUAL top and left insets (96px), so the top-left corner floats free
|
||||
@@ -22,12 +18,10 @@ export function KnowledgeCallout() {
|
||||
className='absolute top-[14.4%] left-[9.6%] w-[125%]'
|
||||
bodyClassName='aspect-[1280/735]'
|
||||
>
|
||||
<Image
|
||||
<CapturedPlatformSurface
|
||||
src='/landing/feature-context-ui.png'
|
||||
alt=''
|
||||
fill
|
||||
sizes='1050px'
|
||||
className='object-cover'
|
||||
activeItem='Knowledge bases'
|
||||
/>
|
||||
</CalloutFrame>
|
||||
</div>
|
||||
|
||||
+6
-12
@@ -1,14 +1,10 @@
|
||||
import Image from 'next/image'
|
||||
import { CalloutFrame } from '@/app/(landing)/components/features/components/callout-frame'
|
||||
import { CapturedPlatformSurface } from '@/app/(landing)/components/features/components/captured-platform-surface'
|
||||
|
||||
/**
|
||||
* The Monitor beat's callout - the REAL platform Logs page as one floating
|
||||
* window: a full capture of the workspace UI (sidebar with Logs selected +
|
||||
* the seeded run table) taken by
|
||||
* `exports/readme-banner/capture-logs-ui.mjs` at the hero shot's card
|
||||
* geometry (1280x735 @2x), framed by the shared {@link CalloutFrame} so it
|
||||
* wears the hero platform window's exact chrome (10px radius + layered
|
||||
* shadow).
|
||||
* The Monitor beat's callout - the platform Logs page as one floating window.
|
||||
* The detailed page capture is paired with the shared live landing sidebar so
|
||||
* its navigation and footer stay aligned with the product.
|
||||
*
|
||||
* Same oversized treatment as the Context card: 125% of the media stage with
|
||||
* EQUAL top and left insets (96px), so the top-left corner floats free over
|
||||
@@ -22,12 +18,10 @@ export function LogsCallout() {
|
||||
className='absolute top-[14.4%] left-[9.6%] w-[125%]'
|
||||
bodyClassName='aspect-[1280/735]'
|
||||
>
|
||||
<Image
|
||||
<CapturedPlatformSurface
|
||||
src='/landing/feature-monitor-ui.png'
|
||||
alt=''
|
||||
fill
|
||||
sizes='1050px'
|
||||
className='object-cover'
|
||||
activeItem='Logs'
|
||||
/>
|
||||
</CalloutFrame>
|
||||
</div>
|
||||
|
||||
+24
-64
@@ -1,14 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import {
|
||||
HeroChatLoop,
|
||||
type HeroChatPhase,
|
||||
} from '@/app/(landing)/components/hero/components/hero-chat-loop'
|
||||
import { HeroWorkflowStage } from '@/app/(landing)/components/hero/components/hero-platform-loop/hero-workflow-stage'
|
||||
import { SidebarHotspots } from '@/app/(landing)/components/hero/components/hero-platform-loop/sidebar-hotspots'
|
||||
import { STAGE_BLOCKS } from '@/app/(landing)/components/hero/components/hero-platform-loop/stage-data'
|
||||
import { HeroLoopShell } from '@/app/(landing)/components/shared/hero-loop-shell'
|
||||
import {
|
||||
PREVIEW_SIDEBAR_CHATS,
|
||||
PREVIEW_SIDEBAR_WORKFLOWS,
|
||||
} from '@/app/(landing)/components/shared/sidebar-preview-content'
|
||||
|
||||
/**
|
||||
* One pass of the synced loop, matching the REAL platform behavior: the chat
|
||||
@@ -29,35 +33,14 @@ const TOTAL_MS = 12_500
|
||||
const RESET_FADE_MS = 260
|
||||
|
||||
/**
|
||||
* The workspace container's interior in the capture's design space (the shot
|
||||
* is 2560x1470 at 2x, i.e. a 1280x735 CSS layout - oversized vs the 1080x620
|
||||
* window so the whole UI displays at 84.4%, landing the app's native type at
|
||||
* cursor.com's ~12.7px demo scale): x 249-1272, y 7-727. The live layer lays
|
||||
* out at this FIXED size and scales down with the window (`transform: scale`),
|
||||
* so its text and controls shrink in lockstep with the baked sidebar pixels -
|
||||
* the "mini app" reading - instead of rendering at 1:1 CSS sizes and looking
|
||||
* oversized next to the scaled screenshot.
|
||||
*/
|
||||
const CHROME_INTERIOR = { width: 1024, height: 721 } as const
|
||||
|
||||
/**
|
||||
* The hero window's live layer - one flex region replaying the REAL Home
|
||||
* two-pane over the static screenshot. The region covers the workspace
|
||||
* container's interior (inset a hair inside the shot's baked chrome: the
|
||||
* horizontal rules 6px from the card top/bottom (the chrome's `p-[8px]` gap is
|
||||
* tightened to 6px at capture time), the container's left border at ~19.4%,
|
||||
* and its right border at ~99.4%; the container renders at `6px` radius
|
||||
* (overridden at capture time from the chrome's 8px so it DISPLAYS at the
|
||||
* concentric ~4.9px after the 84.4% shot-to-window factor), so the region
|
||||
* clips itself `rounded-[4px]` to hug the baked corner curves without
|
||||
* covering them) so every visible outline is the real UI's pixels.
|
||||
*
|
||||
* Inside, the layout mirrors `Home`: the {@link HeroChatLoop} is a flex-1
|
||||
* The homepage's live platform preview, rendered through the same shared shell
|
||||
* as every product and solutions hero so the sidebar never drifts between
|
||||
* landing surfaces. Inside, the layout mirrors `Home`: the
|
||||
* {@link HeroChatLoop} is a flex-1
|
||||
* `--bg` column; the {@link HeroWorkflowStage} pane animates `w-0 ↔ w-1/2`
|
||||
* with the real `MothershipView` width transition (200ms,
|
||||
* `cubic-bezier(0.25,0.1,0.25,1)`, `border-l` only while open) - the baked
|
||||
* chat|stage divider is covered by this region, so the divider users see is
|
||||
* the live `border-l`, appearing exactly as it does in the product.
|
||||
* `cubic-bezier(0.25,0.1,0.25,1)`, `border-l` only while open), matching the
|
||||
* divider users see in the product.
|
||||
*
|
||||
* Both panes stay `pointer-events-none` (decorative, matching the hero's
|
||||
* `aria-hidden` frame) - blocks are static. Remounting the stage per cycle
|
||||
@@ -67,28 +50,11 @@ const CHROME_INTERIOR = { width: 1024, height: 721 } as const
|
||||
* open stage, and fully-built workflow render statically.
|
||||
*/
|
||||
export function HeroPlatformLoop() {
|
||||
const regionRef = useRef<HTMLDivElement>(null)
|
||||
const [phase, setPhase] = useState<HeroChatPhase>('idle')
|
||||
const [stageOpen, setStageOpen] = useState(false)
|
||||
const [builtCount, setBuiltCount] = useState(0)
|
||||
const [fading, setFading] = useState(false)
|
||||
const [cycleId, setCycleId] = useState(0)
|
||||
const [scale, setScale] = useState(1)
|
||||
|
||||
// Track the rendered region width and scale the design-space layer to fill
|
||||
// it, keeping the live layer's proportions locked to the screenshot's.
|
||||
useLayoutEffect(() => {
|
||||
const el = regionRef.current
|
||||
if (!el) return
|
||||
const measure = () => {
|
||||
const w = el.getBoundingClientRect().width
|
||||
if (w > 40) setScale(w / CHROME_INTERIOR.width)
|
||||
}
|
||||
measure()
|
||||
const ro = new ResizeObserver(measure)
|
||||
ro.observe(el)
|
||||
return () => ro.disconnect()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia('(prefers-reduced-motion: reduce)')
|
||||
@@ -144,33 +110,27 @@ export function HeroPlatformLoop() {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarHotspots />
|
||||
<div
|
||||
ref={regionRef}
|
||||
className='absolute top-[0.95%] right-[0.55%] bottom-[0.95%] left-[19.45%] overflow-hidden rounded-[4px]'
|
||||
>
|
||||
<HeroLoopShell chats={PREVIEW_SIDEBAR_CHATS} workflows={PREVIEW_SIDEBAR_WORKFLOWS}>
|
||||
<div className='flex h-full w-full overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--bg)]'>
|
||||
<div className='relative h-full min-w-0 flex-1'>
|
||||
<HeroChatLoop phase={phase} fading={fading} />
|
||||
</div>
|
||||
<div
|
||||
className='flex origin-top-left'
|
||||
style={{
|
||||
width: CHROME_INTERIOR.width,
|
||||
height: CHROME_INTERIOR.height,
|
||||
transform: `scale(${scale})`,
|
||||
}}
|
||||
className={cn(
|
||||
'h-full shrink-0 overflow-hidden border-[var(--border)] bg-[var(--bg)] transition-[width,min-width,border-width] duration-200 [transition-timing-function:cubic-bezier(0.25,0.1,0.25,1)]',
|
||||
stageOpen ? 'w-1/2 border-l' : 'w-0 min-w-0 border-l-0'
|
||||
)}
|
||||
>
|
||||
<div className='pointer-events-none relative h-full min-w-0 flex-1'>
|
||||
<HeroChatLoop phase={phase} fading={fading} />
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'h-full shrink-0 overflow-hidden border-[var(--border)] bg-[var(--bg)] transition-[width,min-width,border-width] duration-200 [transition-timing-function:cubic-bezier(0.25,0.1,0.25,1)]',
|
||||
stageOpen ? 'w-1/2 border-l' : 'w-0 min-w-0 border-l-0'
|
||||
'h-full w-full transition-opacity duration-300 ease-out',
|
||||
fading ? 'opacity-0' : 'opacity-100'
|
||||
)}
|
||||
>
|
||||
<HeroWorkflowStage key={cycleId} builtCount={builtCount} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</HeroLoopShell>
|
||||
)
|
||||
}
|
||||
|
||||
+1
-88
@@ -1,89 +1,2 @@
|
||||
'use client'
|
||||
|
||||
import { Tooltip } from '@sim/emcn'
|
||||
|
||||
/**
|
||||
* Cursor-to-bubble gap for tooltips over the mini platform UI - tighter than
|
||||
* the product-standard 16px so the bubble hugs the cursor proportionately to
|
||||
* the scaled-down preview.
|
||||
*/
|
||||
/** Tight tooltip spacing for the scaled-down platform preview. */
|
||||
export const HERO_TOOLTIP_OFFSET = 8
|
||||
|
||||
/** Percent-of-image bounds shared by both hotspot kinds. */
|
||||
interface HotspotBounds {
|
||||
left: string
|
||||
top: string
|
||||
width: string
|
||||
height: string
|
||||
}
|
||||
|
||||
/** An icon control that shows a tooltip on hover, like the real product. */
|
||||
interface TooltipHotspot extends HotspotBounds {
|
||||
label: string
|
||||
}
|
||||
|
||||
/** A sidebar nav row that shows the real row-hover highlight on hover. */
|
||||
interface RowHotspot extends HotspotBounds {
|
||||
name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon controls measured from the capture (2560x1470): the collapse-sidebar
|
||||
* toggle (x428-468, y36-72) and the Workflows section's "More actions"
|
||||
* ellipsis (x376-412) and "Create workflow" plus (x424-460) at y920-952.
|
||||
* Copy matches the real product's tooltips.
|
||||
*/
|
||||
const TOOLTIP_HOTSPOTS: TooltipHotspot[] = [
|
||||
{ label: 'Collapse sidebar', left: '16.72%', top: '2.45%', width: '1.56%', height: '2.45%' },
|
||||
{ label: 'More actions', left: '14.69%', top: '62.59%', width: '1.4%', height: '2.18%' },
|
||||
{ label: 'Create workflow', left: '16.56%', top: '62.59%', width: '1.4%', height: '2.18%' },
|
||||
]
|
||||
|
||||
/**
|
||||
* Workspace nav rows (Tables / Files / Knowledge base), boxed like the real
|
||||
* sidebar row (`h-[30px] rounded-lg`, text rows measured at y598-616, 662-680,
|
||||
* 726-750): a 60px-tall (at 2x) highlight box centered on each row's text,
|
||||
* starting left of the row ICON (icons measured at x37-39, the box carries the
|
||||
* real row's 8px icon gutter) so the icon sits inside the highlight.
|
||||
*/
|
||||
const ROW_HOTSPOTS: RowHotspot[] = [
|
||||
{ name: 'Tables', left: '0.82%', top: '39.25%', width: '17.15%', height: '4.08%' },
|
||||
{ name: 'Files', left: '0.82%', top: '43.61%', width: '17.15%', height: '4.08%' },
|
||||
{ name: 'Knowledge base', left: '0.82%', top: '48.16%', width: '17.15%', height: '4.08%' },
|
||||
]
|
||||
|
||||
/**
|
||||
* Hover layers over the BAKED sidebar pixels. Icon controls (collapse toggle,
|
||||
* Workflows more/create) get transparent targets wired to the emcn
|
||||
* {@link Tooltip} with the product's real copy ({@link HERO_TOOLTIP_OFFSET}
|
||||
* keeps the bubble close over the mini UI). The Workspace nav rows instead
|
||||
* reproduce the REAL sidebar row-hover state - `--surface-active` on the
|
||||
* row's rounded box - via `mix-blend-multiply`, which paints over the baked
|
||||
* white/text pixels to exactly the result of a background behind the text.
|
||||
*/
|
||||
export function SidebarHotspots() {
|
||||
return (
|
||||
<div aria-hidden='true' className='pointer-events-none absolute inset-0'>
|
||||
{TOOLTIP_HOTSPOTS.map((spot) => (
|
||||
<Tooltip.Root key={spot.label}>
|
||||
<Tooltip.Trigger asChild>
|
||||
<span
|
||||
aria-label={spot.label}
|
||||
className='pointer-events-auto absolute block'
|
||||
style={{ left: spot.left, top: spot.top, width: spot.width, height: spot.height }}
|
||||
/>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content offset={HERO_TOOLTIP_OFFSET}>{spot.label}</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
))}
|
||||
{ROW_HOTSPOTS.map((row) => (
|
||||
<span
|
||||
key={row.name}
|
||||
aria-label={row.name}
|
||||
className='pointer-events-auto absolute block cursor-pointer rounded-[6px] mix-blend-multiply transition-colors duration-100 hover:bg-[var(--surface-active)]'
|
||||
style={{ left: row.left, top: row.top, width: row.width, height: row.height }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,23 +31,11 @@ import { TrustedBy } from '@/app/(landing)/components/trusted-by'
|
||||
* LCP element) behind a white window (a soft three-part shadow stack:
|
||||
* `0 0 0 1px rgba(0,0,0,0.08)` ring in place of a CSS border, plus
|
||||
* `0 2px 6px rgba(0,0,0,0.05)` contact and `0 4px 42px rgba(0,0,0,0.06)`
|
||||
* ambient shadows; no browser toolbar) filled edge to edge by the REAL
|
||||
* platform UI - a 2x
|
||||
* screenshot (`hero-platform-ui.png`, 2560x1470: a 1280x735 layout shown in
|
||||
* the 1080x620 window, so the UI reads at 84.4% - the "mini app" type scale
|
||||
* cursor.com's demo window uses) of the chat-everywhere two-pane (seeded
|
||||
* Mothership chat left, staged workflow right) captured from the
|
||||
* `readme-tour-capture` route via
|
||||
* `exports/readme-banner/capture-hero-platform.mjs`. The window is
|
||||
* `rounded-[10px]` - matching cursor.com's demo window - and the shot's
|
||||
* workspace container renders at the concentric inner radius `4px` (outer
|
||||
* 10px - 6px gap; overridden at capture time from the chrome's 8px). Only the
|
||||
* SIDEBAR
|
||||
* remains visible from the shot: the {@link HeroPlatformLoop} island overlays
|
||||
* the container interior (full-width chat that stages the workflow pane in,
|
||||
* replaying the conversation with the goo ThinkingLoader), inset a hair INSIDE
|
||||
* the shot's own baked outlines so the visible chrome is the real UI's pixels
|
||||
* - never re-drawn.
|
||||
* ambient shadows; no browser toolbar) filled edge to edge by the live
|
||||
* {@link HeroPlatformLoop}. The shared landing sidebar and animated Home
|
||||
* workspace render together in a fixed 1280x735 design space, so the homepage
|
||||
* stays aligned with every product and solutions hero without a baked UI
|
||||
* capture drifting behind it.
|
||||
* The frame is `1300/720` and the window `1080/620` at `83.08%` width, centered
|
||||
* - matching cursor.com's hero media proportions, with backdrop showing on all
|
||||
* four sides. Decorative, `aria-hidden`; the `--surface-3` fill remains as the
|
||||
@@ -109,14 +97,6 @@ export function Hero() {
|
||||
/>
|
||||
<div className='-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2 flex aspect-[1080/620] w-[83.08%] flex-col overflow-hidden rounded-[10px] bg-[var(--surface-1)] shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_2px_6px_0_rgba(0,0,0,0.05),0_4px_42px_0_rgba(0,0,0,0.06)]'>
|
||||
<div className='relative flex-1'>
|
||||
<Image
|
||||
src='/landing/hero-platform-ui.png'
|
||||
alt=''
|
||||
fill
|
||||
priority
|
||||
sizes='(max-width: 1460px) 83vw, 1080px'
|
||||
className='object-cover object-left-top'
|
||||
/>
|
||||
<HeroPlatformLoop />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,8 +32,8 @@ export interface EditorLoopContent {
|
||||
canvas: { width: number; height: number }
|
||||
/** The block the "editing" beat selects once the flow is assembled. */
|
||||
selectedBlockId: string
|
||||
/** Workspace-nav row to highlight in the sidebar; unset keeps New chat active. */
|
||||
activeNav?: EnterpriseSidebarProps['activeNav']
|
||||
/** Sidebar row to highlight; unset keeps New chat active. */
|
||||
activeItem?: EnterpriseSidebarProps['activeItem']
|
||||
}
|
||||
|
||||
interface EditorLoopProps {
|
||||
@@ -96,7 +96,7 @@ export function EditorLoop({ content }: EditorLoopProps) {
|
||||
<HeroLoopShell
|
||||
chats={content.sidebarChats}
|
||||
workflows={content.sidebarWorkflows}
|
||||
activeNav={content.activeNav}
|
||||
activeItem={content.activeItem}
|
||||
>
|
||||
<div className='h-full w-full overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--bg)]'>
|
||||
<div
|
||||
|
||||
@@ -16,8 +16,8 @@ interface HeroLoopShellProps {
|
||||
chats: readonly string[]
|
||||
/** Deployed-workflow entries in the sidebar - five fill the design height. */
|
||||
workflows: readonly string[]
|
||||
/** Workspace-nav row to highlight; unset keeps New chat active. */
|
||||
activeNav?: EnterpriseSidebarProps['activeNav']
|
||||
/** Sidebar row to highlight; unset keeps New chat active. */
|
||||
activeItem?: EnterpriseSidebarProps['activeItem']
|
||||
/** The workspace pane's contents, rendered inside the inset pane gutter. */
|
||||
children: ReactNode
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export function HeroLoopShell({
|
||||
profileName = 'Morgan',
|
||||
chats,
|
||||
workflows,
|
||||
activeNav,
|
||||
activeItem,
|
||||
children,
|
||||
}: HeroLoopShellProps) {
|
||||
const { regionRef, scale } = useDesignScale()
|
||||
@@ -55,7 +55,7 @@ export function HeroLoopShell({
|
||||
profileName={profileName}
|
||||
chats={chats}
|
||||
workflows={workflows}
|
||||
activeNav={activeNav}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
<div className='h-full min-w-0 flex-1 py-[7px] pr-[8px]'>{children}</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export const PREVIEW_SIDEBAR_CHATS = ['Enrich new signups', 'Post deal alerts to #sales'] as const
|
||||
|
||||
export const PREVIEW_SIDEBAR_WORKFLOWS = [
|
||||
'Lead enrichment',
|
||||
'Inbound lead routing',
|
||||
'Weekly pipeline report',
|
||||
] as const
|
||||
+10
-13
@@ -32,6 +32,8 @@ const WORKSPACE_NAV = [
|
||||
{ label: 'Logs', icon: Library },
|
||||
] as const
|
||||
|
||||
export type SidebarItem = 'New chat' | 'Integrations' | (typeof WORKSPACE_NAV)[number]['label']
|
||||
|
||||
interface IconRowProps {
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
label: string
|
||||
@@ -81,18 +83,13 @@ export interface EnterpriseSidebarProps {
|
||||
chats?: readonly string[]
|
||||
/** Deployed-workflow entries - five fill the design height. Defaults enterprise. */
|
||||
workflows?: readonly string[]
|
||||
/**
|
||||
* Workspace-nav row to render active (e.g. `'Tables'`) - the platform pages
|
||||
* highlight their own module instead of New chat. Unset keeps the enterprise
|
||||
* default (New chat active).
|
||||
*/
|
||||
activeNav?: (typeof WORKSPACE_NAV)[number]['label']
|
||||
/** Sidebar row to render active. Defaults to New chat. */
|
||||
activeItem?: SidebarItem
|
||||
}
|
||||
|
||||
/**
|
||||
* The Brightwave workspace sidebar, rendered live (the homepage loop keeps its
|
||||
* baked-screenshot sidebar; the enterprise loop draws its own so the content
|
||||
* can read like a large tenured deployment): the workspace header, New chat /
|
||||
* The Brightwave workspace sidebar, rendered live across landing previews so
|
||||
* every surface stays aligned with the product: the workspace header, New chat /
|
||||
* Integrations, a filled-out Chats history, the Workspace nav, a full
|
||||
* Workflows section, and the profile / Help footer. Purely decorative -
|
||||
* hover/click behavior is owned by the parent's `pointer-events-none` frame.
|
||||
@@ -107,7 +104,7 @@ export const EnterpriseSidebar = memo(function EnterpriseSidebar({
|
||||
profileName = 'Morgan',
|
||||
chats = SIDEBAR_CHATS,
|
||||
workflows = SIDEBAR_WORKFLOWS,
|
||||
activeNav,
|
||||
activeItem = 'New chat',
|
||||
}: EnterpriseSidebarProps = {}) {
|
||||
return (
|
||||
<div className='flex h-full w-[238px] flex-shrink-0 flex-col bg-[var(--surface-1)] pt-3'>
|
||||
@@ -137,8 +134,8 @@ export const EnterpriseSidebar = memo(function EnterpriseSidebar({
|
||||
</div>
|
||||
|
||||
<div className='mt-4 flex flex-shrink-0 flex-col gap-[1px] px-2'>
|
||||
<IconRow icon={Home} label='New chat' active={!activeNav} />
|
||||
<IconRow icon={Integration} label='Integrations' />
|
||||
<IconRow icon={Home} label='New chat' active={activeItem === 'New chat'} />
|
||||
<IconRow icon={Integration} label='Integrations' active={activeItem === 'Integrations'} />
|
||||
</div>
|
||||
|
||||
<div className='mt-4 flex flex-shrink-0 flex-col'>
|
||||
@@ -158,7 +155,7 @@ export const EnterpriseSidebar = memo(function EnterpriseSidebar({
|
||||
key={item.label}
|
||||
icon={item.icon}
|
||||
label={item.label}
|
||||
active={item.label === activeNav}
|
||||
active={item.label === activeItem}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -235,7 +235,7 @@ export function FilesHeroLoop() {
|
||||
})
|
||||
|
||||
return (
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeNav='Files'>
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeItem='Files'>
|
||||
<div className='h-full w-full overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--bg)]'>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -162,7 +162,7 @@ export function KnowledgeHeroLoop() {
|
||||
})
|
||||
|
||||
return (
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeNav='Knowledge bases'>
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeItem='Knowledge bases'>
|
||||
<div className='h-full w-full overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--bg)]'>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -274,7 +274,7 @@ export function LogsHeroLoop() {
|
||||
const liveCompleted = liveState === 'completed'
|
||||
|
||||
return (
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeNav='Logs'>
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeItem='Logs'>
|
||||
<div className='h-full w-full overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--bg)]'>
|
||||
<div
|
||||
key={cycleId}
|
||||
|
||||
@@ -390,7 +390,7 @@ export function TablesHeroLoop() {
|
||||
})
|
||||
|
||||
return (
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeNav='Tables'>
|
||||
<HeroLoopShell chats={SIDEBAR_CHATS} workflows={SIDEBAR_WORKFLOWS} activeItem='Tables'>
|
||||
<div className='h-full w-full overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--bg)]'>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
Reference in New Issue
Block a user