Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ config/s0-prod.config.jsonc

# misc
.DS_Store
apps/web/.og-render/

# Alchemy
.alchemy/
Expand Down
3 changes: 2 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"deploy:pre": "nub run --cwd ../../packages/infra deploy:web:pre",
"deploy:prod": "nub run --cwd ../../packages/infra deploy:web:prod",
"typecheck": "tsrs --noEmit",
"render:release": "tsx scripts/render-release-card.ts render-release"
"render:release": "tsx scripts/render-release-card.ts render-release",
"render:og": "tsx scripts/render-og.ts"
},
"dependencies": {
"@cloudflare/kumo": "^2.12.0",
Expand Down
75 changes: 75 additions & 0 deletions apps/web/scripts/render-og.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { dirname, isAbsolute, join, resolve } from "node:path"
import { fileURLToPath } from "node:url"
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem"
import * as Console from "effect/Console"
import * as Effect from "effect/Effect"
import * as Schema from "effect/Schema"
import { ogConcepts, type OgConceptId } from "../src/creative/og/scenes"
import { renderOgConcepts } from "../src/creative/og/node"

const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../..")

class OgCliError extends Schema.TaggedError<OgCliError>()("OgCliError", {
message: Schema.String,
cause: Schema.optionalKey(Schema.Unknown),
}) {}

function option(args: readonly string[], name: string): string | undefined {
const index = args.indexOf(name)

if (index < 0) return undefined

const value = args[index + 1]

if (!value || value.startsWith("--")) throw new Error(`${name} requires a value.`)

return value
}

function conceptId(value: string | undefined): OgConceptId | undefined {
if (!value) return undefined

const match = ogConcepts.find((concept) => concept.id === value)

if (!match) {
throw new Error(
`--concept must be one of ${ogConcepts.map((concept) => concept.id).join(", ")}.`,
)
}

return match.id
}

const main = Effect.fn("og.cli")(function* () {
const args = process.argv.slice(2)

const requested = yield* Effect.try({
try: () => ({
conceptId: conceptId(option(args, "--concept")),
output: option(args, "--output"),
}),
catch: (cause) =>
new OgCliError({
message: cause instanceof Error ? cause.message : "The command arguments are invalid.",
cause,
}),
})

const outputDir = requested.output
? isAbsolute(requested.output)
? requested.output
: resolve(REPO_ROOT, requested.output)
: join(REPO_ROOT, "apps/web/.og-render")

const written = yield* renderOgConcepts({
conceptId: requested.conceptId,
outputDir,
repoRoot: REPO_ROOT,
})

for (const path of written) {
yield* Console.log(path)
}
})

await Effect.runPromise(main().pipe(Effect.provide(NodeFileSystem.layer)))
5 changes: 4 additions & 1 deletion apps/web/src/components/home-agent-sessions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import type { SessionsListQuery } from "@solzero/api"
import { AGENT_RUNTIMES, formatAgentRuntimeLabel, resolveAgentRuntime } from "@solzero/shared"
import { S0Loader, TableCellState } from "@/components/s0-loader"
import { primaryNavLabels } from "@/components/primary-nav-labels"
import {
archiveSession,
formatSessionLabel,
Expand Down Expand Up @@ -487,7 +488,9 @@ export function PreviousSessionsTable() {
<section className="mx-auto w-full max-w-6xl px-6 py-14 sm:px-8">
<div className="mb-6 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div>
<h2 className="text-2xl font-semibold text-kumo-default">Previous sessions</h2>
<h2 className="text-2xl font-semibold text-kumo-default">
{primaryNavLabels.previousSessions}
</h2>
<p className="mt-1 text-sm text-kumo-subtle">
Search, filter, sort, and reopen prior agent sessions.
</p>
Expand Down
11 changes: 11 additions & 0 deletions apps/web/src/components/home-hero-copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const homeComposerPlaceholder = "Chat, build, and automate with project context"

export const homeStartNewAgentLabel = "Start a new Agent"

export function homeWelcomeHeading(brandName: string): string {
return `Welcome to ${brandName}`
}

export function homePreparingAgentLabel(runtimeLabel: string): string {
return `Preparing ${runtimeLabel} agent...`
}
19 changes: 13 additions & 6 deletions apps/web/src/components/home-page/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ import {
} from "@solzero/shared"
import { S0Loader } from "@/components/s0-loader"
import { S0AnimatedIcon } from "@/components/s0-animated-icon"
import {
homeComposerPlaceholder,
homePreparingAgentLabel,
homeStartNewAgentLabel,
homeWelcomeHeading,
} from "@/components/home-hero-copy"
import { primaryNavLabels } from "@/components/primary-nav-labels"
import {
HomeToolbarIconButton,
TOOLS_TOOLBAR_CLASS_NAME,
Expand Down Expand Up @@ -319,7 +326,7 @@ export function HomeContent({
className="pointer-events-auto invisible flex -translate-y-2 items-center gap-2 rounded-full bg-kumo-elevated px-4 py-2 text-sm text-kumo-subtle opacity-0 shadow-lg ring-1 ring-kumo-hairline transition hover:bg-kumo-tint hover:text-kumo-default focus:outline-none focus:ring-2 focus:ring-kumo-brand"
>
<ArrowUp className="h-4 w-4" aria-hidden />
Start a new Agent
{homeStartNewAgentLabel}
</button>
</div>

Expand All @@ -331,7 +338,7 @@ export function HomeContent({
<h1
className={`${manrope.className} mb-2 text-3xl font-normal leading-9 text-kumo-default`}
>
Welcome to {brand.name}
{homeWelcomeHeading(brand.name)}
</h1>
{!isAuthenticated ? (
<p className="text-kumo-subtle">Sign in to start a new agent</p>
Expand Down Expand Up @@ -425,7 +432,7 @@ export function HomeContent({
value={prompt}
onChange={(e) => handlePromptChange(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Chat, build, and automate with project context"
placeholder={homeComposerPlaceholder}
disabled={creating}
className="session-composer-textarea session-composer-textarea--ringed min-h-28 w-full resize-none bg-transparent px-4 py-4 text-kumo-default placeholder:text-kumo-placeholder focus:outline-none disabled:opacity-50"
rows={3}
Expand Down Expand Up @@ -536,7 +543,7 @@ export function HomeContent({
<div className="flex items-center gap-2 flex-shrink-0">
{isCreatingSession && (
<span className="text-xs text-kumo-brand whitespace-nowrap">
Preparing {selectedAgentRuntimeLabel} agent...
{homePreparingAgentLabel(selectedAgentRuntimeLabel)}
</span>
)}
{modelControlLoading ? (
Expand Down Expand Up @@ -591,11 +598,11 @@ export function HomeContent({
<button
ref={scrollCueRef}
type="button"
aria-label="Previous sessions"
aria-label={primaryNavLabels.previousSessions}
onClick={scrollToPreviousSessions}
className="pointer-events-auto flex flex-col items-center gap-1 rounded-full px-4 py-2 text-sm text-kumo-subtle transition hover:text-kumo-default focus:outline-none focus:ring-2 focus:ring-kumo-brand"
>
<span>Previous sessions</span>
<span>{primaryNavLabels.previousSessions}</span>
<ChevronDown className="h-5 w-5" aria-hidden />
</button>
</div>
Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/components/primary-nav-labels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const primaryNavLabels = {
agents: "Agents",
workflows: "Workflows",
bots: "Bots",
previousSessions: "Previous sessions",
} as const
3 changes: 2 additions & 1 deletion apps/web/src/components/s0-animated-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { GemSmoke } from "@paper-design/shaders-react"
import { S0LogoSvg } from "@/components/s0-logo-svg"
import { solzeroGemSmokeColors } from "@/components/s0-gem-smoke"
import { usePaperShaderReady, usePrefersReducedMotion } from "@/hooks/use-paper-shader"
import { useTheme } from "@/lib/theme"

Expand Down Expand Up @@ -41,7 +42,7 @@ export function S0AnimatedIcon({ size = 20, className = "" }: S0AnimatedIconProp
width={size}
height={size}
image="/images/solzero-logo.svg"
colors={["#0078d7", "#4da6e8", "#ffffff"]}
colors={[...solzeroGemSmokeColors]}
colorBack="rgba(0, 0, 0, 0)"
colorInner={isDark ? "#000000" : "#FFFFFF"}
innerDistortion={1}
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/s0-gem-smoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const solzeroGemSmokeColors = ["#0078d7", "#4da6e8", "#ffffff"] as const
36 changes: 36 additions & 0 deletions apps/web/src/components/s0-loader-motion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export const SOLZERO_OUTER_PATH =
"M376 188V377C376 480.76 291.76 565 188 565C84.24 565 0 480.76 0 377V188C0 84.24 84.24 0 188 0C291.76 0 376 84.24 376 188Z"

export const SOLZERO_STAR_PATH =
"M188 94.5C188 197.634 272.866 282.5 376 282.5C272.866 282.5 188 367.366 188 470.5C188 367.366 103.134 282.5 0 282.5C103.134 282.5 188 197.634 188 94.5Z"

export const SOLZERO_MARK_PATH = `${SOLZERO_OUTER_PATH}${SOLZERO_STAR_PATH}`

export const LOADER_CYCLE_SECONDS = 2.8

export const LOADER_ASSEMBLY_START = LOADER_CYCLE_SECONDS * 0.12

export const LOADER_ASSEMBLY_DURATION = LOADER_CYCLE_SECONDS * 0.28

export const LOADER_DEPARTURE_START = LOADER_CYCLE_SECONDS * 0.64

export const LOADER_DEPARTURE_DURATION = LOADER_CYCLE_SECONDS * 0.14

export const LOADER_PIECE_STAGGER = LOADER_CYCLE_SECONDS * 0.06

export const LOADER_PIECES = [
{ name: "north-west", height: 282.5, offsetX: -34, offsetY: -26, width: 188, x: 0, y: 0 },
{ name: "north-east", height: 282.5, offsetX: 34, offsetY: -26, width: 188, x: 188, y: 0 },
{
name: "south-east",
height: 282.5,
offsetX: 34,
offsetY: 26,
width: 188,
x: 188,
y: 282.5,
},
{ name: "south-west", height: 282.5, offsetX: -34, offsetY: 26, width: 188, x: 0, y: 282.5 },
] as const

export type LoaderPiece = (typeof LOADER_PIECES)[number]
50 changes: 15 additions & 35 deletions apps/web/src/components/s0-loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,22 @@ import { useEffect, useId, useRef, type ReactNode } from "react"
import { hasCustomS0Logo } from "@solzero/shared"
import { getS0Brand } from "@/lib/brand"
import { S0LogoSvg } from "@/components/s0-logo-svg"
import {
LOADER_ASSEMBLY_DURATION,
LOADER_ASSEMBLY_START,
LOADER_CYCLE_SECONDS,
LOADER_DEPARTURE_DURATION,
LOADER_DEPARTURE_START,
LOADER_PIECE_STAGGER,
LOADER_PIECES,
SOLZERO_MARK_PATH,
} from "@/components/s0-loader-motion"

interface S0LoaderProps {
size?: number
className?: string
}

const SOLZERO_OUTER_PATH =
"M376 188V377C376 480.76 291.76 565 188 565C84.24 565 0 480.76 0 377V188C0 84.24 84.24 0 188 0C291.76 0 376 84.24 376 188Z"

const SOLZERO_STAR_PATH =
"M188 94.5C188 197.634 272.866 282.5 376 282.5C272.866 282.5 188 367.366 188 470.5C188 367.366 103.134 282.5 0 282.5C103.134 282.5 188 197.634 188 94.5Z"

const SOLZERO_MARK_PATH = `${SOLZERO_OUTER_PATH}${SOLZERO_STAR_PATH}`

const CYCLE_DURATION = 2.8
const ASSEMBLY_START = CYCLE_DURATION * 0.12
const ASSEMBLY_DURATION = CYCLE_DURATION * 0.28
const DEPARTURE_START = CYCLE_DURATION * 0.64
const DEPARTURE_DURATION = CYCLE_DURATION * 0.14
const PIECE_STAGGER = CYCLE_DURATION * 0.06

const LOADER_PIECES = [
{ name: "north-west", height: 282.5, offsetX: -34, offsetY: -26, width: 188, x: 0, y: 0 },
{ name: "north-east", height: 282.5, offsetX: 34, offsetY: -26, width: 188, x: 188, y: 0 },
{
name: "south-east",
height: 282.5,
offsetX: 34,
offsetY: 26,
width: 188,
x: 188,
y: 282.5,
},
{ name: "south-west", height: 282.5, offsetX: -34, offsetY: 26, width: 188, x: 0, y: 282.5 },
] as const

function sanitizeSvgId(id: string): string {
return id.replace(/[^a-zA-Z0-9_-]/g, "")
}
Expand Down Expand Up @@ -120,30 +100,30 @@ function SolZeroLoader({ size = 64, className = "" }: S0LoaderProps) {
.to(
piece,
{
duration: ASSEMBLY_DURATION,
duration: LOADER_ASSEMBLY_DURATION,
ease: "power2.out",
opacity: 1,
scale: 1,
x: 0,
y: 0,
},
ASSEMBLY_START + index * PIECE_STAGGER,
LOADER_ASSEMBLY_START + index * LOADER_PIECE_STAGGER,
)
.to(
piece,
{
duration: DEPARTURE_DURATION,
duration: LOADER_DEPARTURE_DURATION,
ease: "power2.in",
opacity: 0,
scale: 0.94,
x: motion.offsetX,
y: motion.offsetY,
},
DEPARTURE_START + index * PIECE_STAGGER,
LOADER_DEPARTURE_START + index * LOADER_PIECE_STAGGER,
)
})

timeline.set({}, {}, CYCLE_DURATION)
timeline.set({}, {}, LOADER_CYCLE_SECONDS)
})
}, containerRef)

Expand Down
9 changes: 5 additions & 4 deletions apps/web/src/components/session-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from "lucide-react"
import { useState } from "react"
import { S0LogoSvg } from "@/components/s0-logo-svg"
import { primaryNavLabels } from "@/components/primary-nav-labels"
import {
ADMIN_SIDEBAR_ITEMS,
getAdminViewFromLocation,
Expand Down Expand Up @@ -139,7 +140,7 @@ export function SessionSidebar({ content, isOpen }: SessionSidebarProps) {
}`}
>
<MessageSquare className="h-4 w-4 flex-shrink-0" aria-hidden />
Agents
{primaryNavLabels.agents}
</Link>
<button
type="button"
Expand Down Expand Up @@ -171,7 +172,7 @@ export function SessionSidebar({ content, isOpen }: SessionSidebarProps) {
className={settingsSubLinkClass(isPreviousSessionsActive)}
>
<History className="h-4 w-4 flex-shrink-0" aria-hidden />
Previous sessions
{primaryNavLabels.previousSessions}
</Link>
</div>
) : null}
Expand All @@ -182,15 +183,15 @@ export function SessionSidebar({ content, isOpen }: SessionSidebarProps) {
className={navLinkClass(activeNav === "workflows")}
>
<GitBranch className="h-4 w-4 flex-shrink-0" aria-hidden />
Workflows
{primaryNavLabels.workflows}
</Link>
<Link
to="/bots"
aria-current={activeNav === "bots" ? "page" : undefined}
className={navLinkClass(activeNav === "bots")}
>
<BotIcon className="h-4 w-4 flex-shrink-0" aria-hidden />
Bots
{primaryNavLabels.bots}
</Link>
<span
aria-disabled="true"
Expand Down
Loading