feat(documents): surface filesystem work-products in Document Center
The Document Center only read the `documents` table, which is prose
content POSTed via /api/documents. Everything the agents wrote via
`bash` / `write` / `edit` — the bulk of their output on a real
project — landed in `work_products` and never appeared on the page.
Operators saw "0 items" after a full night's run.
Lift work_products into the same UI:
- `listWorkProductsForCompany` returns a newest-first slice with a
500-row cap so the snapshot stays bounded.
- `ProfileSnapshot.workProducts` flows through `useCompanyData` as
`companyWorkProducts`, filtered to the current company.
- `DocumentCenter` renders two labelled sections in order — prose
documents (editable) on top, files & deliverables below. Each
work-product row opens the underlying URL or filesystem path
when clickable, so the agent's bash-generated markdown, PRs,
and artifacts are one click away.
`WorkProductRecord` lifts into `@shared/types/records-extended` so
the renderer, snapshot, and database shells all reference the same
shape. The store-test fixture learns about the new field.
This commit is contained in:
1 parent
734a2b04e0
commit
22ba7a9004
15 files changed
+890
-158
No files matched your search
@@ -4,35 +4,234 @@ impl AppRoot {
|
||||
pub(super) fn render_onboarding(&self, cx: &mut Context<Self>) -> impl IntoElement + '_ {
|
||||
div()
|
||||
.size_full()
|
||||
.p_8()
|
||||
.flex()
|
||||
.gap_8()
|
||||
.bg(theme::canvas())
|
||||
.bg(theme::window_bg())
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.gap_4()
|
||||
.child(div().text_3xl().text_color(theme::text_primary()).child("Start a native AgentCompany profile"))
|
||||
.child(div().text_sm().text_color(theme::text_muted()).child("Create the first company, point it at a workspace, and keep the SQLite profile local."))
|
||||
.child(self.company_name_input.clone())
|
||||
.child(self.mission_input.clone())
|
||||
.child(self.workspace_input.clone())
|
||||
.child(primary_button("Create company", cx, |this, cx| this.create_company(cx))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w(px(380.0))
|
||||
.p_5()
|
||||
.gap_3()
|
||||
.border_1()
|
||||
.rounded_md()
|
||||
.border_color(theme::panel_border())
|
||||
.bg(theme::panel())
|
||||
.child(div().text_lg().child("What lands in SQLite"))
|
||||
.child(div().text_sm().text_color(theme::text_muted()).child("Company"))
|
||||
.child(div().text_sm().text_color(theme::text_muted()).child("Primary workspace"))
|
||||
.child(div().text_sm().text_color(theme::text_muted()).child("Connector catalog"))
|
||||
.child(div().text_sm().text_color(theme::text_muted()).child("Shared profile settings")),
|
||||
.size_full()
|
||||
.flex()
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.p_8()
|
||||
.gap_8()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.child(traffic_light(theme::danger()))
|
||||
.child(traffic_light(rgb_hex(0xfebc2e)))
|
||||
.child(traffic_light(rgb_hex(0x28c840))),
|
||||
)
|
||||
.child(div().h(px(80.0)))
|
||||
.child(eyebrow("AGENTCOMPANY DESKTOP"))
|
||||
.child(
|
||||
div()
|
||||
.text_3xl()
|
||||
.text_color(theme::text_primary())
|
||||
.child("Start a real local-first")
|
||||
.child(div().child("agent company.")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_lg()
|
||||
.text_color(theme::text_muted())
|
||||
.child("Set up your company, point it at a workspace, and launch")
|
||||
.child(div().child("the first native Rust control center.")),
|
||||
)
|
||||
.child(onboarding_steps())
|
||||
.child(
|
||||
div()
|
||||
.w(px(520.0))
|
||||
.p_5()
|
||||
.gap_4()
|
||||
.border_1()
|
||||
.rounded_lg()
|
||||
.border_color(theme::panel_border())
|
||||
.bg(theme::panel())
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(theme::text_muted())
|
||||
.child("COMPANY SETUP"),
|
||||
)
|
||||
.child(self.company_name_input.clone())
|
||||
.child(self.mission_input.clone())
|
||||
.child(self.workspace_input.clone())
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(theme::text_muted())
|
||||
.child("The native shell writes directly into SQLite with no cloud fallback."),
|
||||
),
|
||||
)
|
||||
.child(readiness_panel())
|
||||
.child(div().flex_1())
|
||||
.child(eyebrow("BY ELYDORA")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w(px(14.0))
|
||||
.bg(theme::split_divider()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w(px(520.0))
|
||||
.p_8()
|
||||
.child(
|
||||
div()
|
||||
.mt(px(220.0))
|
||||
.p_8()
|
||||
.gap_5()
|
||||
.border_1()
|
||||
.rounded_lg()
|
||||
.border_color(theme::panel_border())
|
||||
.bg(theme::panel())
|
||||
.child(success_badge())
|
||||
.child(
|
||||
div()
|
||||
.text_2xl()
|
||||
.text_color(theme::text_primary())
|
||||
.child("Native shell ready."),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_lg()
|
||||
.text_color(theme::text_muted())
|
||||
.child("Create the company record, register the workspace, and")
|
||||
.child(div().child("open the GPUI control center.")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.child(launch_tile("CONNECTOR", "SQLite local profile"))
|
||||
.child(launch_tile("LEAD AGENT", "CEO shell created")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.child(launch_tile("PRIMARY WORKSPACE", "Registered on create"))
|
||||
.child(launch_tile("KICKOFF PROJECT", "Created from first task")),
|
||||
)
|
||||
.child(launch_tile("INITIAL RUN", "Queued once company state lands"))
|
||||
.child(primary_button("Create company", cx, |this, cx| {
|
||||
this.create_company(cx)
|
||||
})),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn eyebrow(label: &'static str) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.items_center()
|
||||
.child(div().w(px(48.0)).h(px(1.0)).bg(theme::text_subtle()))
|
||||
.child(div().text_xs().text_color(theme::text_subtle()).child(label))
|
||||
}
|
||||
|
||||
fn onboarding_steps() -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.items_center()
|
||||
.child(step_chip("Company", true))
|
||||
.child(div().w(px(32.0)).h(px(1.0)).bg(theme::text_subtle()))
|
||||
.child(step_chip("Mission", true))
|
||||
.child(div().w(px(32.0)).h(px(1.0)).bg(theme::text_subtle()))
|
||||
.child(step_chip("Launch", false))
|
||||
}
|
||||
|
||||
fn step_chip(label: &'static str, complete: bool) -> impl IntoElement {
|
||||
div()
|
||||
.px_4()
|
||||
.py_2()
|
||||
.rounded_full()
|
||||
.bg(if complete {
|
||||
theme::success_bg()
|
||||
} else {
|
||||
theme::panel_muted()
|
||||
})
|
||||
.text_sm()
|
||||
.text_color(if complete {
|
||||
theme::accent_teal()
|
||||
} else {
|
||||
theme::text_secondary()
|
||||
})
|
||||
.child(label)
|
||||
}
|
||||
|
||||
fn readiness_panel() -> impl IntoElement {
|
||||
div()
|
||||
.w(px(520.0))
|
||||
.gap_3()
|
||||
.child(div().text_xs().text_color(theme::text_muted()).child("CONNECTOR READINESS"))
|
||||
.child(readiness_row("Claude Code", "ready", true))
|
||||
.child(readiness_row("Codex", "ready", true))
|
||||
.child(readiness_row("Gemini CLI", "auth required", false))
|
||||
.child(
|
||||
div()
|
||||
.pl(px(28.0))
|
||||
.text_xs()
|
||||
.text_color(theme::text_subtle())
|
||||
.child("Gemini CLI requires authentication before the runtime can use it."),
|
||||
)
|
||||
}
|
||||
|
||||
fn readiness_row(label: &'static str, state: &'static str, ready: bool) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.w(px(16.0))
|
||||
.h(px(16.0))
|
||||
.rounded_full()
|
||||
.bg(if ready {
|
||||
theme::accent_teal()
|
||||
} else {
|
||||
theme::accent_orange()
|
||||
}),
|
||||
)
|
||||
.child(div().text_sm().text_color(theme::text_primary()).child(label))
|
||||
.child(div().text_sm().text_color(theme::text_muted()).child(state))
|
||||
}
|
||||
|
||||
fn success_badge() -> impl IntoElement {
|
||||
div()
|
||||
.w(px(72.0))
|
||||
.h(px(72.0))
|
||||
.rounded_full()
|
||||
.bg(theme::success_bg())
|
||||
.child(
|
||||
div()
|
||||
.m(px(20.0))
|
||||
.w(px(32.0))
|
||||
.h(px(32.0))
|
||||
.rounded_full()
|
||||
.bg(theme::accent_teal()),
|
||||
)
|
||||
}
|
||||
|
||||
fn launch_tile(label: &'static str, value: &'static str) -> impl IntoElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.p_4()
|
||||
.gap_2()
|
||||
.rounded_lg()
|
||||
.bg(theme::panel_muted())
|
||||
.child(div().text_xs().text_color(theme::text_muted()).child(label))
|
||||
.child(div().text_sm().text_color(theme::text_primary()).child(value))
|
||||
}
|
||||
|
||||
fn rgb_hex(hex: u32) -> gpui::Hsla {
|
||||
gpui::rgb(hex).into()
|
||||
}
|
||||
|
||||
fn traffic_light(color: gpui::Hsla) -> impl IntoElement {
|
||||
div().w(px(12.0)).h(px(12.0)).rounded_full().bg(color)
|
||||
}
|
||||
Reference in new issue
Block a user