Restore native document center grammar

This commit is contained in:
ZacharyZhang-NY committed 2026-04-19 15:59:10 -04:00
1 parent bd8c420718
commit 95c539380b
3 files changed
+170 -82

No files matched your search

@@ -7,6 +7,8 @@ impl AppRoot {
.selected_document_id
.as_ref()
.and_then(|id| self.snapshot.documents.iter().find(|item| &item.id == id));
let work_products = self.snapshot.artifacts.iter().take(12).collect::<Vec<_>>();
let total_count = self.snapshot.documents.len() + work_products.len();
div()
.flex()
@@ -16,7 +18,7 @@ impl AppRoot {
div()
.flex()
.justify_between()
.items_start()
.items_center()
.child(
div()
.child(
@@ -30,102 +32,188 @@ impl AppRoot {
.mt(px(6.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!("{} items", self.snapshot.documents.len())),
.child(format!("{} items", total_count)),
),
)
.child(primary_button("Save draft", cx, |this, cx| {
this.create_document(cx)
})),
)
.child(div().h(px(1.0)).bg(theme::panel_border()))
.child(document_section_label("DOCUMENTS PROSE"))
.child(self.render_document_prose_list(cx, selected))
.when(!work_products.is_empty(), |container| {
container
.child(document_section_label("WORK PRODUCTS"))
.child(render_work_products(work_products.as_slice()))
})
.child(self.render_document_editor(selected))
}
fn render_document_prose_list(
&self,
cx: &mut Context<Self>,
selected: Option<&domain::Document>,
) -> impl IntoElement + '_ {
div()
.rounded_lg()
.border_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.when(self.snapshot.documents.is_empty(), |container| {
container.child(
div()
.py_8()
.text_base()
.min_h(px(180.0))
.flex()
.items_center()
.justify_center()
.text_color(theme::text_muted())
.child("No documents have been created yet."),
)
})
.when(!self.snapshot.documents.is_empty(), |container| {
container.child(div().id("documents-list").children(
self.snapshot.documents.iter().map(|item| {
let active =
self.state.selected_document_id.as_deref() == Some(item.id.as_str());
let document_id = item.id.clone();
div()
.id(SharedString::from(format!("document-{}", item.id)))
.flex()
.justify_between()
.items_center()
.px_6()
.py_5()
.border_b_1()
.border_color(theme::panel_border())
.cursor_pointer()
.bg(if active {
theme::selection_fill()
} else {
theme::window_bg()
})
.hover(|style| style.bg(theme::panel_hover()))
.on_click(cx.listener(move |this, _, _, cx| {
this.state.selected_document_id = Some(document_id.clone());
cx.notify();
}))
.child(
div()
.flex_1()
.child(
div()
.text_base()
.text_color(theme::text_primary())
.truncate()
.child(item.title.clone()),
)
.child(
div()
.mt(px(8.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!(
"{} · v{} · {}",
item.document_type.replace('_', " "),
item.version,
short_date(item.updated_at.as_str())
)),
),
)
.child(document_status(item.status.as_str()))
}),
))
})
.child(div().h(px(1.0)).bg(theme::panel_border()))
.child(
div()
.gap_4()
.child(
div().text_lg().text_color(theme::text_primary()).child(
selected
.map(|item| item.title.clone())
.unwrap_or_else(|| "Quick draft".to_string()),
),
)
.child(self.document_title_input.clone())
.child(self.document_body_input.clone())
.when_some(selected, |container, item| {
container.child(
container.children(self.snapshot.documents.iter().map(|document| {
let active =
selected.map(|item| item.id.as_str()) == Some(document.id.as_str());
let document_id = document.id.clone();
div()
.id(SharedString::from(format!("document-{}", document.id)))
.cursor_pointer()
.hover(|style| style.bg(theme::panel_hover()))
.bg(if active {
theme::selection_fill()
} else {
theme::panel()
})
.on_click(cx.listener(move |this, _, _, cx| {
this.state.selected_document_id = Some(document_id.clone());
cx.notify();
}))
.px_5()
.py_4()
.border_b_1()
.border_color(theme::panel_border())
.child(
div()
.p_4()
.rounded_lg()
.border_1()
.border_color(theme::panel_inset_border())
.bg(theme::panel_inset())
.child(item.content.clone()),
.flex()
.justify_between()
.items_center()
.child(
div()
.flex_1()
.child(
div()
.text_base()
.text_color(theme::text_primary())
.child(document.title.clone()),
)
.child(
div()
.mt(px(4.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!(
"{} · v{} · {}",
document.document_type.replace('_', " "),
document.version,
short_date(document.updated_at.as_str())
)),
),
)
.child(document_status(document.status.as_str())),
)
}),
)
}))
})
}
fn render_document_editor(&self, selected: Option<&domain::Document>) -> impl IntoElement + '_ {
div()
.rounded_lg()
.border_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.p_5()
.child(
div().text_lg().text_color(theme::text_primary()).child(
selected
.map(|item| item.title.clone())
.unwrap_or_else(|| "New document".to_string()),
),
)
.child(self.document_title_input.clone())
.child(self.document_body_input.clone())
.when_some(selected, |container, document| {
container.child(
div()
.mt(px(12.0))
.rounded_lg()
.border_1()
.border_color(theme::panel_inset_border())
.bg(theme::panel_inset())
.p_4()
.text_sm()
.line_height(px(22.0))
.text_color(theme::text_secondary())
.child(document.content.clone()),
)
})
}
}
fn document_section_label(label: &'static str) -> impl IntoElement {
div().text_xs().text_color(theme::text_muted()).child(label)
}
fn render_work_products<'a>(products: &'a [&'a domain::ArtifactRecord]) -> impl IntoElement + 'a {
div()
.rounded_lg()
.border_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.children(products.iter().map(|artifact| {
div()
.px_5()
.py_4()
.border_b_1()
.border_color(theme::panel_border())
.child(
div()
.flex()
.justify_between()
.items_center()
.child(
div()
.flex_1()
.child(
div()
.text_base()
.text_color(theme::text_primary())
.child(artifact.title.clone()),
)
.child(
div()
.mt(px(4.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!(
"{} · {} · {}",
artifact.provider,
artifact.external_id,
short_date(artifact.created_at.as_str())
)),
),
)
.child(
div()
.px_3()
.py_2()
.rounded_full()
.bg(theme::accent_blue_soft())
.text_xs()
.text_color(theme::accent_blue())
.child("ARTIFACT"),
),
)
}))
}
fn document_status(status: &str) -> impl IntoElement {
+1 -1
View File
@@ -183,7 +183,7 @@
],
"priority": 13,
"passes": false,
"notes": "Audit found current native documents surface lacks work-product grouping, modal/editor grammar, and action hierarchy from the original source."
"notes": "In progress: native Document Center now restores a two-section grammar with documents prose plus work products (sourced from persisted artifacts), instead of the earlier flat list plus quick draft shell. Remaining gap is finer editor/message parity and final source-to-native audit."
},
{
"id": "US-014",
+1 -1
View File
@@ -14,4 +14,4 @@ Started: 2026-04-19
- Added runtime_contract_snapshot_e2e.rs to verify runtime event and artifact persistence across reopen.
- Runtime contract integration advanced: RuntimeEvent ingestion now persists message/tool/artifact/run-finished events into storage and exposes them in the runs inspector.
- Wired a live native runtime session into the GPUI Runs surface. Selected runs now launch Pi Mono through env-configured stdio sidecar settings, runtime tool calls create approval rows, approval decisions send ToolApproval back into the live session, runtime messages project into agent_messages, and run lifecycle still persists into run_events/artifacts/runs.\n- Closed the final native verification matrix. The workspace now has explicit storage-backed e2e coverage for onboarding bootstrap, approvals/documents/runs visibility, settings/workspace mutations, live runtime contract flows, and inbox attention derivation.\n- Reopened Ralph parity work after a direct source audit. The current native app has real runtime/data flows, but several surfaces still diverge from Electron page grammar: onboarding is too collapsed, dashboard misses banner/attention/chart structure, inbox filters are not real, run detail is dominated by native control slabs not present in source, approvals flatten board/user/manager semantics, and documents still lack work-product/editor hierarchy. Added US-010 through US-014 to close these parity gaps honestly.
- Rebuilt native onboarding away from the old static split shell. It now has a real multi-stage company/mission/launch flow, screenshot-aligned narrative/summary composition, real connector readiness derived from snapshot data, and a storage bootstrap transaction that creates company, workspace, goal, CEO, kickoff project, kickoff task, and queued run. Remaining US-010 work is the finer original wizard-tab parity.\n- Rebuilt the native dashboard away from the placeholder metrics/activity shell. It now has real recovery and budget banners, attention rows, a performance section, active work, and recent activity, all driven by persisted local data. Remaining US-011 work is the inbox-side parity and finer dashboard detail matching.\n- Restored more source grammar across inbox, approvals, and runs. Inbox now has real interactive filters driving a unified attention stream; approvals again distinguish board/user/manager review groupings; and run detail no longer lets live runtime controls dominate the main header region.\n
- Rebuilt native onboarding away from the old static split shell. It now has a real multi-stage company/mission/launch flow, screenshot-aligned narrative/summary composition, real connector readiness derived from snapshot data, and a storage bootstrap transaction that creates company, workspace, goal, CEO, kickoff project, kickoff task, and queued run. Remaining US-010 work is the finer original wizard-tab parity.\n- Rebuilt the native dashboard away from the placeholder metrics/activity shell. It now has real recovery and budget banners, attention rows, a performance section, active work, and recent activity, all driven by persisted local data. Remaining US-011 work is the inbox-side parity and finer dashboard detail matching.\n- Restored more source grammar across inbox, approvals, and runs. Inbox now has real interactive filters driving a unified attention stream; approvals again distinguish board/user/manager review groupings; and run detail no longer lets live runtime controls dominate the main header region.\n- Restored document-center grammar toward the source surface: the native page now separates documents prose from work products and uses persisted artifacts as the work-product list instead of hiding them behind the runs inspector only.\n