Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
56246d8
feat(dashboards): add table-backed dashboard resources behind rollout…
TheodoreSpeaks Sep 25, 2026
90bbd59
refactor(files): separate discovery from storage context
TheodoreSpeaks Sep 25, 2026
a6e0941
fix(charts): keep pie labels readable and separate neutral colors
TheodoreSpeaks Sep 25, 2026
f894b9e
feat(dashboards): compute percentages from row conditions
TheodoreSpeaks Sep 25, 2026
743b0e3
refactor(dashboards): store dashboards as workspace files
TheodoreSpeaks Sep 26, 2026
0dc3b9d
Merge origin/staging into codex/research-sim-dashboards
TheodoreSpeaks Sep 26, 2026
26a9dd2
fix(dashboards): address review findings on chart layout and precision
TheodoreSpeaks Sep 26, 2026
6c58c01
fix(charts): size grouped bar rows and keep the row highlight through…
TheodoreSpeaks Sep 26, 2026
3d630db
Merge remote-tracking branch 'origin/staging' into codex/research-sim…
TheodoreSpeaks Sep 26, 2026
af21484
fix(charts): keep authored tooltip arrays and drop the v2 chat mode o…
TheodoreSpeaks Sep 26, 2026
bf3243c
fix(charts): reserve the authored bar gap between grouped bars
TheodoreSpeaks Sep 26, 2026
4f1b0e0
fix(charts): format index-encoded pie tooltips and test dashboard ref…
TheodoreSpeaks Sep 26, 2026
d736526
Merge remote-tracking branch 'origin/staging' into codex/research-sim…
TheodoreSpeaks Sep 26, 2026
8e34901
fix(charts): format bar tooltip values with each series' own value axis
TheodoreSpeaks Sep 26, 2026
bb59bdf
test(dashboards): drop mock-call and rendered-text assertions
TheodoreSpeaks Sep 26, 2026
dd4b207
Merge remote-tracking branch 'origin/staging' into codex/research-sim…
TheodoreSpeaks Sep 26, 2026
0958aab
test(dashboards): drop redundant mock resets and default environment …
TheodoreSpeaks Sep 26, 2026
a3e287e
refactor(dashboards): move authoring guidance to Mothership like Sim …
TheodoreSpeaks Sep 27, 2026
c2207cf
Merge remote-tracking branch 'origin/staging' into codex/research-sim…
TheodoreSpeaks Sep 27, 2026
54dfd1b
refactor(dashboards): give DashboardFeatureGate a props interface
TheodoreSpeaks Sep 27, 2026
4cc80af
feat(dashboards): report dashboard parse errors on file writes
TheodoreSpeaks Sep 27, 2026
5d0e7e4
fix(dashboards): split diagnostics, switch panel query modes, return …
TheodoreSpeaks Sep 27, 2026
68e8448
fix(dashboards): drop inherited ordering on query-mode switches and c…
TheodoreSpeaks Sep 27, 2026
470b450
feat(playground): org-level project view prototype
TheodoreSpeaks Sep 29, 2026
301c682
feat(playground): Supabase-style full view, settings in the sidebar, …
TheodoreSpeaks Sep 29, 2026
e4cd049
feat(playground): minimal black-and-white issues list, underline tabs…
TheodoreSpeaks Sep 30, 2026
6ab84c0
feat(playground): chat with the resource panel on the right; new tab …
TheodoreSpeaks Sep 30, 2026
11af0da
feat(playground): resource header is the full path, actions fold into…
TheodoreSpeaks Sep 30, 2026
8dc50fa
feat(resource): compact header keeps parent + current crumb so a narr…
TheodoreSpeaks Sep 30, 2026
ab81f6a
feat(resource): compact header is the current crumb alone, path on hover
TheodoreSpeaks Sep 30, 2026
912f0b2
feat(playground): + opens a New tab; browsing from it turns it into t…
TheodoreSpeaks Sep 30, 2026
2c9efe8
feat(playground): resource actions trail the tab strip; browser and t…
TheodoreSpeaks Sep 30, 2026
e342e50
feat(playground): chats are not scoped to projects
TheodoreSpeaks Sep 30, 2026
95dc82d
feat(playground): one project page with every section as a tab; full …
TheodoreSpeaks Sep 30, 2026
4e7607b
feat(playground): Resources tab lists every other kind under the proj…
TheodoreSpeaks Sep 30, 2026
092bde3
feat(playground): Chat about this opens a new chat with the thing in …
TheodoreSpeaks Sep 30, 2026
00a48d8
feat(playground): chat docks on the left of any project page from the…
TheodoreSpeaks Sep 30, 2026
ab38337
feat(playground): floating chat button starts a real chat with the pa…
TheodoreSpeaks Sep 30, 2026
eb297db
feat(playground): chat trigger is an icon at the top right of the pro…
TheodoreSpeaks Sep 30, 2026
828d23c
feat(playground): chats on the left, the workspace on the right
TheodoreSpeaks Sep 30, 2026
ffc8eef
feat(playground): home lists the projects under the composer; one cli…
TheodoreSpeaks Sep 30, 2026
f214bbf
feat(playground): Projects back in the sidebar; a project opens in th…
TheodoreSpeaks Oct 1, 2026
b225cbd
feat(playground): a project link goes Home with that project open; th…
TheodoreSpeaks Oct 1, 2026
6b4cf4a
feat(playground): home suggests what to do next under the composer
TheodoreSpeaks Oct 1, 2026
4d1df5d
feat(playground): a new tab starts on the pane's project; the browser…
TheodoreSpeaks Oct 1, 2026
5aa7077
feat(playground): Home and a second click on the active project desel…
TheodoreSpeaks Oct 1, 2026
edace95
feat(playground): Resources opens a browsing tab; bare workspace ref …
TheodoreSpeaks Oct 1, 2026
e8d3f69
feat(playground): browsing tab gets a search bar over the project's r…
TheodoreSpeaks Oct 1, 2026
9063f80
feat(playground): project rows in the browsing tab carry their tile
TheodoreSpeaks Oct 1, 2026
e0f0024
feat(playground): the search bar sits under the Browse section's label
TheodoreSpeaks Oct 1, 2026
f944167
feat(playground): Resources is the in-tab directory with real lists a…
TheodoreSpeaks Oct 1, 2026
e202859
feat(playground): the resource header bar stays on the Resources dire…
TheodoreSpeaks Oct 1, 2026
a09dc35
feat(playground): the project is a black profile chip at the head of …
TheodoreSpeaks Oct 1, 2026
e044127
feat(playground): quieter workspace tab
TheodoreSpeaks Oct 1, 2026
9280653
feat(playground): outlined project chip, title block back, no header …
TheodoreSpeaks Oct 1, 2026
cc58ff8
feat(playground): the dashboard is only the workspace tab's main view…
TheodoreSpeaks Oct 1, 2026
a614f67
feat(playground): the project tile in the strip is the switcher
TheodoreSpeaks Oct 1, 2026
7f8d6d5
feat(playground): the project switcher is one tile, filled edge to ed…
TheodoreSpeaks Oct 1, 2026
764c146
feat(playground): split profile control at the strip's right end
TheodoreSpeaks Oct 1, 2026
ef69716
feat(playground): resource actions sit in the resource's own header, …
TheodoreSpeaks Oct 1, 2026
4c016c0
feat(playground): expand toggle hides the chat; no divider on the pre…
TheodoreSpeaks Oct 1, 2026
685e1ce
feat(identity-tile): md size, the 24px toolbar mark
TheodoreSpeaks Oct 1, 2026
00383a1
feat(playground): new tab leads with Browse and search; Mentioned fol…
TheodoreSpeaks Oct 1, 2026
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
Prev Previous commit
Next Next commit
feat(playground): Supabase-style full view, settings in the sidebar, …
…side chat

The project main view shows Dashboard, Changelog, and Issues; "Open full view"
switches the sidebar to the project's sections, and Settings swaps it again to
the settings list. Chats open in a left side panel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DeA8zbNdZMuB1MSNbcH6p6
  • Loading branch information
TheodoreSpeaks and claude committed Sep 29, 2026
commit 301c682e2614ba3db7fc284889d62e4ed7739def
69 changes: 69 additions & 0 deletions apps/sim/app/playground/org/components/issues-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { chipHoverSurfaceClass, cn, OverflowText } from '@sim/emcn'
import Link from 'next/link'
import { DelegateAvatar } from '@/app/playground/org/components/delegate-avatar'
import { AgentStateIcon, PriorityIcon, StatusIcon } from '@/app/playground/org/components/glyphs'
import {
ISSUES,
type Issue,
STATUS_LABELS,
STATUS_ORDER,
type Workspace,
} from '@/app/playground/org/lib/mock-data'
import { protoRoutes } from '@/app/playground/org/lib/routes'

/** Sim issues for a project, grouped by status. Tracker links and chats live on the issue page. */
export function IssuesList({ workspace }: { workspace: Workspace }) {
const issues = ISSUES.filter((issue) => issue.workspaceId === workspace.id)
if (!issues.length)
return <p className='px-6 py-10 text-[var(--text-muted)] text-small'>No issues yet.</p>
return (
<div className='h-full overflow-y-auto px-3 pt-3 pb-8'>
{STATUS_ORDER.map((status) => {
const inStatus = [...issues]
.filter((issue) => issue.status === status)
.sort((a, b) => (a.priority || 5) - (b.priority || 5))
if (!inStatus.length) return null
return (
<section key={status} className='mb-3'>
<div className='flex h-8 items-center gap-2 px-3 text-small'>
<StatusIcon status={status} />
<span className='text-[var(--text-body)]'>{STATUS_LABELS[status]}</span>
<span className='text-[var(--text-muted)]'>{inStatus.length}</span>
</div>
<div className='flex flex-col gap-[1px]'>
{inStatus.map((issue) => (
<IssueRow key={issue.key} issue={issue} />
))}
</div>
</section>
)
})}
</div>
)
}

function IssueRow({ issue }: { issue: Issue }) {
return (
<Link
href={protoRoutes.issue(issue.workspaceId, issue.key)}
className={cn(
'flex h-9 items-center gap-3 rounded-lg px-3 text-small',
chipHoverSurfaceClass
)}
>
<PriorityIcon priority={issue.priority} />
<span className='w-[64px] shrink-0 text-[var(--text-muted)] tabular-nums'>{issue.key}</span>
<StatusIcon status={issue.status} />
<OverflowText label={issue.title} className='min-w-0 flex-1 text-[var(--text-body)]' />
<span className='flex w-[200px] shrink-0 items-center gap-1.5 whitespace-nowrap text-[var(--text-muted)]'>
{issue.agent && (
<>
<AgentStateIcon state={issue.agent.state} />
<span className='truncate'>{issue.agent.label}</span>
</>
)}
</span>
<DelegateAvatar owner={issue.owner} delegate={issue.delegate} />
</Link>
)
}
111 changes: 25 additions & 86 deletions apps/sim/app/playground/org/components/project-settings.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,39 @@
'use client'

import type { ComponentType, ReactNode } from 'react'
import { Avatar, Chip, ChipTag, chipVariants, cn } from '@sim/emcn'
import { Key, Plus, SlidersHorizontal } from '@sim/emcn/icons'
import { useQueryStates } from 'nuqs'
import type { ReactNode } from 'react'
import { Avatar, Chip, ChipTag } from '@sim/emcn'
import { Key, Plus } from '@sim/emcn/icons'
import { JiraIcon, LinearIcon, SlackMonoIcon } from '@/components/icons'
import { WORKSPACE_SETTINGS_ITEMS } from '@/components/settings/navigation'
import { PEOPLE, type Workspace } from '@/app/playground/org/lib/mock-data'
import { protoParsers } from '@/app/playground/org/lib/search-params'
import { SETTINGS_NAV } from '@/app/playground/org/lib/settings-nav'
import { SettingsResourceRow } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row'

type Icon = ComponentType<{ className?: string }>

interface NavItem {
id: string
label: string
description: string
icon: Icon
group: string
}

const GROUP_TITLES: Record<string, string> = {
project: 'Project',
workspace: 'Workspace',
tools: 'Tools',
system: 'System',
enterprise: 'Enterprise',
interface ProjectSettingsProps {
workspace: Workspace
sectionId: string
}

/** Prod's workspace settings catalog, plus a Project group for the tracker and feedback loop. */
const NAV: NavItem[] = [
{
id: 'project',
label: 'General',
description: 'Tracker, feedback sources, and how Sim triages and replies.',
icon: SlidersHorizontal,
group: 'project',
},
...WORKSPACE_SETTINGS_ITEMS.filter((item) => item.id !== 'self-host').map((item) => ({
id: item.id,
label: item.label,
description: item.description,
icon: item.icon,
group: item.group,
})),
]
const GROUPS = [...new Set(NAV.map((item) => item.group))]

/** Settings for one project: the prod workspace sections, filled with mock data. */
export function ProjectSettings({ workspace }: { workspace: Workspace }) {
const [{ settings }, setParams] = useQueryStates(protoParsers)
const active = NAV.find((item) => item.id === settings) ?? NAV[0]
/** One settings section; the settings list itself lives in the sidebar. */
export function ProjectSettings({ workspace, sectionId }: ProjectSettingsProps) {
const active = SETTINGS_NAV.find((item) => item.id === sectionId)
if (!active) throw new Error(`Unknown settings section ${sectionId}`)
const ActiveIcon = active.icon
return (
<div className='flex h-full min-h-0'>
<nav className='flex w-[220px] shrink-0 flex-col gap-4 overflow-y-auto border-[var(--border)] border-r px-2 py-4'>
{GROUPS.map((group) => (
<div key={group} className='flex flex-col gap-[1px]'>
<span className='px-2 pb-1 text-[var(--text-muted)] text-caption'>
{GROUP_TITLES[group] ?? group}
</span>
{NAV.filter((item) => item.group === group).map((item) => {
const ItemIcon = item.icon
return (
<button
key={item.id}
type='button'
onClick={() => void setParams({ settings: item.id })}
className={cn(chipVariants({ active: item.id === active.id, fullWidth: true }))}
>
<ItemIcon className='size-[14px] shrink-0 text-[var(--text-icon)]' />
<span className='truncate text-[var(--text-body)]'>{item.label}</span>
</button>
)
})}
<div className='h-full overflow-y-auto'>
<div className='mx-auto flex max-w-[760px] flex-col gap-6 px-8 py-8'>
<header className='flex items-start gap-3'>
<ActiveIcon className='mt-1 size-[18px] shrink-0 text-[var(--text-icon)]' />
<div className='flex min-w-0 flex-1 flex-col gap-1'>
<h2 className='text-[20px] text-[var(--text-primary)] leading-tight'>{active.label}</h2>
<p className='text-[var(--text-muted)] text-small'>{active.description}</p>
</div>
))}
</nav>
<div className='min-w-0 flex-1 overflow-y-auto'>
<div className='mx-auto flex max-w-[760px] flex-col gap-6 px-8 py-8'>
<header className='flex items-start gap-3'>
<ActiveIcon className='mt-1 size-[18px] shrink-0 text-[var(--text-icon)]' />
<div className='flex min-w-0 flex-1 flex-col gap-1'>
<h2 className='text-[20px] text-[var(--text-primary)] leading-tight'>
{active.label}
</h2>
<p className='text-[var(--text-muted)] text-small'>{active.description}</p>
</div>
{PRIMARY_ACTION[active.id] && (
<Chip variant='primary' leftIcon={Plus}>
{PRIMARY_ACTION[active.id]}
</Chip>
)}
</header>
<SectionContent id={active.id} workspace={workspace} />
</div>
{PRIMARY_ACTION[active.id] && (
<Chip variant='primary' leftIcon={Plus}>
{PRIMARY_ACTION[active.id]}
</Chip>
)}
</header>
<SectionContent id={active.id} workspace={workspace} />
</div>
</div>
)
Expand Down
114 changes: 46 additions & 68 deletions apps/sim/app/playground/org/components/project-shell.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client'

import { type ReactNode, useState } from 'react'
import type { ReactNode } from 'react'
import { Avatar, cn } from '@sim/emcn'
import { ChevronDown, MessageSquareText, X } from '@sim/emcn/icons'
import { X } from '@sim/emcn/icons'
import { useQueryStates } from 'nuqs'
import { AgentRun } from '@/app/playground/org/components/agent-run'
import { ChatThread } from '@/app/playground/org/components/chat-thread'
Expand All @@ -17,7 +17,7 @@ interface ProjectShellProps {
children: ReactNode
}

/** Project page with the chat you opened floating at the bottom; the page stays usable under it. */
/** Project page with the chat you opened in a side panel on the left; the page stays usable beside it. */
export function ProjectShell({ workspace, children }: ProjectShellProps) {
const [{ chat: chatId }, setParams] = useQueryStates(protoParsers)
const chat: Chat | undefined =
Expand All @@ -27,88 +27,66 @@ export function ProjectShell({ workspace, children }: ProjectShellProps) {
? CHATS.find((c) => c.id === chatId && c.workspaceId === workspace.id)
: undefined
return (
<div className='relative h-full min-h-0'>
{children}
{chat && (
<FloatingChat
key={chat.id}
chat={chat}
workspace={workspace}
onClose={() => void setParams({ chat: null }, { history: 'replace' })}
/>
)}
<div className='flex h-full min-h-0'>
<div
className={cn(
'shrink-0 overflow-hidden border-[var(--border)] transition-[width] duration-200 ease-out',
chat ? 'w-[440px] border-r' : 'w-0'
)}
>
{chat && (
<SideChat
key={chat.id}
chat={chat}
workspace={workspace}
onClose={() => void setParams({ chat: null }, { history: 'replace' })}
/>
)}
</div>
<div className='min-w-0 flex-1'>{children}</div>
</div>
)
}

interface FloatingChatProps {
interface SideChatProps {
chat: Chat
workspace: Workspace
onClose: () => void
}

function FloatingChat({ chat, workspace, onClose }: FloatingChatProps) {
const [minimized, setMinimized] = useState(false)
function SideChat({ chat, workspace, onClose }: SideChatProps) {
const runs =
DRAFTS.find((draft) => draft.workspaceId === workspace.id)?.running.filter(
(work) => work.chat.id === chat.id
) ?? []
const running = runs.length > 0

return (
<div className='pointer-events-none absolute inset-x-0 bottom-5 z-30 flex justify-center px-6'>
{minimized ? (
<button
type='button'
onClick={() => setMinimized(false)}
className='pointer-events-auto flex max-w-[420px] items-center gap-2 rounded-full border border-[var(--border)] bg-[var(--bg)] px-4 py-2 text-small shadow-[var(--shadow-overlay)] hover-hover:bg-[var(--surface-hover)]'
>
{running ? (
<RunningDot />
) : (
<MessageSquareText className='size-[14px] shrink-0 text-[var(--text-icon)]' />
)}
<span className='truncate text-[var(--text-body)]'>{chat.title}</span>
</button>
) : (
<div className='pointer-events-auto flex max-h-[min(560px,70vh)] w-full max-w-[760px] flex-col overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--bg)] shadow-[var(--shadow-overlay)]'>
<header className='flex shrink-0 items-center gap-2 border-[var(--border)] border-b py-1.5 pr-1.5 pl-4'>
{running && <RunningDot />}
<span className='min-w-0 flex-1 truncate text-[var(--text-body)] text-small'>
{chat.title}
</span>
{chat.owner && (
<span className='flex shrink-0 items-center gap-1.5 text-[var(--text-muted)] text-small'>
<Avatar size='xs' name={chat.owner} />
{chat.owner.split(' ')[0]}
</span>
)}
<HeaderButton label='Minimize' onClick={() => setMinimized(true)}>
<ChevronDown className='size-[14px] text-[var(--text-icon)]' />
</HeaderButton>
<HeaderButton label='Close chat' onClick={onClose}>
<X className='size-[14px] text-[var(--text-icon)]' />
</HeaderButton>
</header>
{running ? (
<div className='min-h-0 flex-1 overflow-y-auto px-4 py-4'>
<div className='flex flex-col gap-8'>
{runs.map((work) => (
<AgentRun key={work.issue} work={work} workspaceId={workspace.id} />
))}
</div>
</div>
) : chat.id === 'new' ? (
<div className='px-3 py-3'>
<MockComposer placeholder={`Ask Sim about ${workspace.name}…`} />
</div>
) : (
<ChatThread
placeholder={`Ask Sim about ${workspace.name}…`}
className={cn('h-[420px]')}
/>
)}
<div className='flex h-full w-[440px] flex-col bg-[var(--bg)]'>
<header className='flex h-12 shrink-0 items-center gap-2 border-[var(--border)] border-b pr-2 pl-4'>
{running && <RunningDot />}
<span className='min-w-0 flex-1 truncate text-[var(--text-body)] text-small'>
{chat.title}
</span>
{chat.owner && <Avatar size='xs' name={chat.owner} />}
<HeaderButton label='Close chat' onClick={onClose}>
<X className='size-[14px] text-[var(--text-icon)]' />
</HeaderButton>
</header>
{running ? (
<div className='min-h-0 flex-1 overflow-y-auto px-4 py-5'>
<div className='flex flex-col gap-8'>
{runs.map((work) => (
<AgentRun key={work.issue} work={work} workspaceId={workspace.id} />
))}
</div>
</div>
) : chat.id === 'new' ? (
<div className='flex min-h-0 flex-1 flex-col justify-end px-3 pb-3'>
<MockComposer placeholder={`Ask Sim about ${workspace.name}…`} />
</div>
) : (
<ChatThread placeholder={`Ask Sim about ${workspace.name}…`} />
)}
</div>
)
Expand Down
7 changes: 6 additions & 1 deletion apps/sim/app/playground/org/components/proto-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ export function ProtoPage({ slug }: { slug?: string[] }) {
if (!workspace) notFound()
return (
<ProjectShell workspace={workspace}>
<WorkspaceView workspace={workspace} section={route.section} />
<WorkspaceView
workspace={workspace}
section={route.section}
full={route.full}
settingsSection={route.settingsSection}
/>
</ProjectShell>
)
}
Expand Down
Loading