Skip to content
Closed
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
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "AI-powered development tool",
"private": true,
"type": "module",
"packageManager": "bun@1.3.5",
"packageManager": "bun@1.3.6",
"scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"typecheck": "bun turbo typecheck",
Expand Down Expand Up @@ -98,4 +98,4 @@
"patchedDependencies": {
"ghostty-web@0.3.0": "patches/ghostty-web@0.3.0.patch"
}
}
}
18 changes: 18 additions & 0 deletions packages/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { Suspense } from "solid-js"

const Home = lazy(() => import("@/pages/home"))
const Session = lazy(() => import("@/pages/session"))
const Brand = lazy(() => import("@/pages/brand"))
const Commerce = lazy(() => import("@/pages/commerce"))
const Loading = () => <div class="size-full flex items-center justify-center text-text-weak">Loading...</div>

declare global {
Expand Down Expand Up @@ -103,6 +105,22 @@ export function AppInterface(props: { defaultUrl?: string }) {
/>
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={() => <Navigate href="session" />} />
<Route
path="/brand"
component={() => (
<Suspense fallback={<Loading />}>
<Brand />
</Suspense>
)}
/>
<Route
path="/commerce"
component={() => (
<Suspense fallback={<Loading />}>
<Commerce />
</Suspense>
)}
/>
<Route
path="/session/:id?"
component={() => (
Expand Down
58 changes: 58 additions & 0 deletions packages/app/src/components/agent-activity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Show, createSignal, createEffect } from "solid-js"
import { Icon } from "@opencode-ai/ui/icon"

export interface AgentActivityProps {
active: boolean
title?: string
detail?: string
type?: "thinking" | "writing" | "reading" | "success"
}

export function AgentActivity(props: AgentActivityProps) {

return (
<Show when={props.active}>
<div class="fixed bottom-8 left-1/2 -translate-x-1/2 z-[100] pointer-events-none">
<div class="flex flex-col items-center gap-1 animate-in slide-in-from-bottom-4 fade-in duration-300">

{/* The "Island" */}
<div class="flex items-center gap-4 bg-[#1a1a1a] text-white px-5 py-3.5 rounded-full shadow-[0_8px_32px_rgba(0,0,0,0.12)] border border-white/10 backdrop-blur-xl min-w-[300px] max-w-[400px]">

{/* Status Icon / Spinner */}
<div class="shrink-0 flex items-center justify-center size-5">
<Show when={props.type === 'success'}>
<Icon name="check-small" class="text-green-400" />
</Show>
<Show when={props.type !== 'success'}>
<div class="relative size-2.5">
<div class="absolute inset-0 bg-white rounded-full animate-ping opacity-75"></div>
<div class="relative size-2.5 bg-white rounded-full"></div>
</div>
</Show>
</div>

{/* Content */}
<div class="flex flex-col gap-0.5 min-w-0 flex-1">
<div class="flex items-center justify-between gap-4">
<span class="text-sm font-medium tracking-wide text-white/95 truncate">
{props.title || "Thinking"}
</span>
<span class="text-[10px] font-mono text-white/50 bg-white/10 px-1.5 py-0.5 rounded uppercase tracking-wider shrink-0">
AGENT
</span>
</div>
<Show when={props.detail}>
<span class="text-xs text-white/60 font-mono truncate animate-pulse">
{props.detail}
</span>
</Show>
</div>
</div>

{/* Reflection/Glow below */}
<div class="w-[80%] h-4 bg-black/20 blur-lg rounded-full -mt-2 opacity-50"></div>
</div>
</div>
</Show>
)
}
103 changes: 103 additions & 0 deletions packages/app/src/components/attention-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Icon } from "@opencode-ai/ui/icon"
import { Button } from "@opencode-ai/ui/button"

interface AttentionItemProps {
title: string
impact: string
action: string
urgency: "high" | "medium"
}

// Helper function to highlight numbers and currency in text
function highlightNumbers(text: string) {
const parts: Array<{ text: string; isNumber: boolean; isNegative?: boolean }> = []

// Regex to match numbers, percentages, and currency
const regex = /(\+?\-?\₹?\d+(?:,\d+)*(?:\.\d+)?%?|\d+\s+days?)/g
let lastIndex = 0
let match

while ((match = regex.exec(text)) !== null) {
// Add text before the number
if (match.index > lastIndex) {
parts.push({ text: text.slice(lastIndex, match.index), isNumber: false })
}

// Determine if negative indicator
const numText = match[0]
const isNegative = numText.includes('-') ||
(text.toLowerCase().includes('drop') ||
text.toLowerCase().includes('decreased') ||
text.toLowerCase().includes('undercutting'))

parts.push({ text: numText, isNumber: true, isNegative })
lastIndex = regex.lastIndex
}

// Add remaining text
if (lastIndex < text.length) {
parts.push({ text: text.slice(lastIndex), isNumber: false })
}

return parts.length > 0 ? parts : [{ text, isNumber: false }]
}

export function AttentionCard(props: AttentionItemProps) {
const iconName = props.urgency === 'high' ? 'alert-triangle' : 'bubble-5'
const impactParts = highlightNumbers(props.impact)

return (
<div class={`group relative flex flex-col md:flex-row md:items-center justify-between p-5 bg-white rounded-xl shadow-md hover:shadow-2xl transition-all duration-300 overflow-hidden animate-in slide-in-from-left-4 fade-in ${props.urgency === 'high'
? 'border-2 border-orange-400 bg-gradient-to-r from-orange-50 to-red-50 hover:scale-[1.02]'
: 'border-2 border-amber-400 bg-gradient-to-r from-amber-50 to-yellow-50 hover:scale-[1.02]'
}`}>

{/* Animated pulse ring for high urgency */}
{props.urgency === 'high' && (
<div class="absolute inset-0 rounded-xl border-2 border-red-500 animate-ping opacity-20" />
)}

<div class="flex items-start gap-4 flex-1">
{/* Vibrant icon container with animation */}
<div class={`mt-0.5 w-12 h-12 rounded-xl flex items-center justify-center shrink-0 shadow-lg animate-bounce ${props.urgency === 'high'
? 'bg-gradient-to-br from-red-500 to-orange-600 text-white shadow-red-500/40'
: 'bg-gradient-to-br from-amber-500 to-yellow-600 text-white shadow-amber-500/40'
}`}>
<Icon name={iconName as any} size="small" />
</div>

<div class="flex flex-col gap-2 flex-1">
<h3 class="text-lg font-bold leading-snug" style={{ color: "#000000" }}>
{props.title}
</h3>
<p class="text-sm font-medium leading-relaxed flex flex-wrap gap-1 items-center">
{impactParts.map((part, i) =>
part.isNumber ? (
<span
key={i}
class={`inline-flex items-center px-2.5 py-1 rounded-lg font-bold text-sm shadow-md animate-pulse ${part.isNegative
? 'bg-gradient-to-r from-red-500 to-red-600 text-white'
: 'bg-gradient-to-r from-blue-500 to-indigo-600 text-white'
}`}
>
{part.text}
</span>
) : (
<span key={i} style={{ color: "#3f3f46" }}>{part.text}</span>
)
)}
</p>
</div>
</div>

<div class="mt-4 md:mt-0 flex items-center pl-16 md:pl-4">
<Button
variant="secondary"
class="font-bold text-sm bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700 border-0 transition-all shadow-lg hover:shadow-xl hover:scale-110 px-5 py-2.5 rounded-xl"
>
{props.action}
</Button>
</div>
</div>
)
}
46 changes: 46 additions & 0 deletions packages/app/src/components/business-pulse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Icon } from "@opencode-ai/ui/icon"
import { Show } from "solid-js"

interface PulseProps {
summary: string
detail: string
trend?: "up" | "down" | "neutral"
}

export function BusinessPulse(props: PulseProps) {
return (
<div class="flex flex-col gap-4 py-8 relative group">
{/* Vibrant left accent */}
<div class="absolute left-0 top-8 bottom-8 w-1.5 bg-gradient-to-b from-emerald-400 to-blue-400 rounded-full shadow-sm" />

<div class="pl-6 flex flex-col gap-3">
<div class="flex items-center gap-2.5">
<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-emerald-500 to-emerald-600 flex items-center justify-center text-white shadow-md">
<Icon name="arrow-up" size="small" />
</div>
<h2 class="text-xs font-bold tracking-widest uppercase" style={{ color: "#18181b" }}>Daily Brief</h2>
</div>

<div class="flex flex-col gap-2">
<div class="text-3xl md:text-4xl font-serif font-bold tracking-tight leading-tight" style={{ color: "#000000" }}>
{props.summary}
</div>

<div class="flex items-center gap-2 text-base md:text-lg mt-1">
<span class="font-bold" style={{ color: "#000000" }}>{props.detail}</span>
<Show when={props.trend === 'down'}>
<span class="inline-flex items-center gap-1 text-white bg-gradient-to-r from-red-500 to-red-600 px-3 py-1 rounded-full text-xs font-bold shadow-md">
<Icon name="arrow-up" size="small" style={{ transform: "rotate(180deg)" }} /> 3%
</span>
</Show>
<Show when={props.trend === 'up'}>
<span class="inline-flex items-center gap-1 text-white bg-gradient-to-r from-emerald-500 to-emerald-600 px-3 py-1 rounded-full text-xs font-bold shadow-md">
<Icon name="arrow-up" size="small" /> 12%
</span>
</Show>
</div>
</div>
</div>
</div>
)
}
36 changes: 36 additions & 0 deletions packages/app/src/components/decision-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Icon } from "@opencode-ai/ui/icon"

interface DecisionProps {
question: string
impact?: string
}

export function DecisionItem(props: DecisionProps) {
return (
<button class="w-full group text-left flex items-center justify-between gap-4 p-5 -mx-4 rounded-xl hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 transition-all duration-200 relative border-l-4 border-transparent hover:border-l-blue-500 hover:shadow-md">
{/* Strategic decision icon */}
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-500 to-indigo-500 flex items-center justify-center shrink-0 text-white group-hover:scale-110 transition-transform shadow-md">
<Icon name="brain" size="small" />
</div>

<div class="flex flex-col gap-1.5 flex-1">
<span class="text-base font-bold leading-snug group-hover:text-blue-700 transition-colors" style={{ color: "#000000" }}>
{props.question}
</span>
{props.impact && (
<span class="text-sm font-medium leading-relaxed" style={{ color: "#52525b" }}>
{props.impact}
</span>
)}
</div>

<div class="flex items-center gap-2 shrink-0">
<Icon
name="chevron-right"
class="text-zinc-400 group-hover:text-blue-600 group-hover:translate-x-1 transition-all duration-200"
size="small"
/>
</div>
</button>
)
}
Loading