feat: add markdown view component
This commit is contained in:
@@ -43,6 +43,7 @@ use gpui_native_data::tree::{Tree, TreeItem};
|
||||
use gpui_native_editor::code_view::CodeViewState;
|
||||
use gpui_native_editor::diff_view::{DiffRowKind, DiffViewState};
|
||||
use gpui_native_editor::highlight::Language;
|
||||
use gpui_native_editor::markdown::{MarkdownBlock, MarkdownViewState};
|
||||
use gpui_native_icons::{IconAssets, IconRole, icon};
|
||||
use gpui_native_layout::dock::{Dock, DockSidebar, DockState, PanelDescriptor, SplitDirection};
|
||||
use gpui_native_layout::resize_handle::ResizeHandle;
|
||||
@@ -87,7 +88,11 @@ const CATALOG: &[(&str, &[(&str, &str)])] = &[
|
||||
),
|
||||
(
|
||||
"Editor",
|
||||
&[("code-view", "Code view"), ("diff-view", "Diff view")],
|
||||
&[
|
||||
("code-view", "Code view"),
|
||||
("diff-view", "Diff view"),
|
||||
("markdown-view", "Markdown view"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Feedback",
|
||||
@@ -642,6 +647,12 @@ impl Gallery {
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
dock.register_panel(
|
||||
PanelDescriptor::new("markdown-view", "Markdown view", |_, cx| {
|
||||
cx.new(MarkdownViewDemo::new).into()
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
dock.register_panel(
|
||||
PanelDescriptor::new("sidebar", "Sidebar", |_, cx| {
|
||||
cx.new(|_| SidebarDemo { selected: None }).into()
|
||||
@@ -3102,6 +3113,161 @@ impl Render for DiffViewDemo {
|
||||
}
|
||||
}
|
||||
|
||||
/// The markdown view demo's sample document.
|
||||
const MARKDOWN_SAMPLE: &str = r###"# Release notes
|
||||
|
||||
Everything shipped this week, with **bold claims**, *quiet fixes*, and ~~regressions~~.
|
||||
|
||||
## Highlights
|
||||
|
||||
- The relay pool now [retries automatically](https://example.com/relay) on stale sockets
|
||||
- Inline `code spans` and keyboard chords read fine
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] Ship the diff view
|
||||
- [x] Ship the markdown view
|
||||
- [ ] Wire it into signed
|
||||
- [ ] Profile the large file
|
||||
|
||||
1. Cut the branch
|
||||
2. Run the gallery
|
||||
1. Open this panel
|
||||
2. Scroll the large file
|
||||
|
||||
> Notes from the field: **measure first**, then optimize.
|
||||
|
||||
```rust
|
||||
fn bind_all(&self) -> Result<(), String> {
|
||||
for port in &self.ports {
|
||||
if port > &1024 {
|
||||
continue;
|
||||
}
|
||||
return Err(format!("port {} is privileged", port));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
| Area | Status | Owner |
|
||||
|------|--------|-------|
|
||||
| Editor | shipping | reya |
|
||||
| Layout | staged | team |
|
||||
|
||||
---
|
||||
|
||||
That is the news.
|
||||
"###;
|
||||
|
||||
/// The section count of the large markdown sample.
|
||||
const LARGE_MARKDOWN_SECTIONS: usize = 1000;
|
||||
|
||||
/// Generates a long markdown document, so scrolling the view can be
|
||||
/// exercised on realistic content.
|
||||
fn large_markdown(sections: usize) -> String {
|
||||
let mut source = String::new();
|
||||
for section in 0..sections {
|
||||
source.push_str(&format!("## Section {section}\n\n"));
|
||||
source.push_str(&format!(
|
||||
"Handler {section} retries stale sockets and logs every outcome.\n\n"
|
||||
));
|
||||
source.push_str(&format!(
|
||||
"- [ ] Audit handler {section}\n- See [issue {section}](https://example.com/issues/{section})\n\n"
|
||||
));
|
||||
source.push_str("```rust\nfn handle() -> bool { true }\n```\n\n");
|
||||
}
|
||||
source
|
||||
}
|
||||
|
||||
struct MarkdownViewDemo {
|
||||
view: Entity<MarkdownViewState>,
|
||||
sample: MarkdownSample,
|
||||
large_source: SharedString,
|
||||
_subscription: gpui::Subscription,
|
||||
}
|
||||
|
||||
/// The markdown view demo's sample choice.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum MarkdownSample {
|
||||
#[default]
|
||||
Guide,
|
||||
/// A generated document with thousands of blocks, to test scroll
|
||||
/// performance.
|
||||
Large,
|
||||
}
|
||||
|
||||
impl MarkdownViewDemo {
|
||||
fn new(cx: &mut Context<Self>) -> Self {
|
||||
let view = cx.new(|_| MarkdownViewState::new("markdown-view-demo", MARKDOWN_SAMPLE));
|
||||
// The panel is a cached view in the dock: it re-renders only when
|
||||
// the markdown state it renders is notified.
|
||||
let subscription = cx.observe(&view, |_, _, cx| cx.notify());
|
||||
Self {
|
||||
view,
|
||||
sample: MarkdownSample::Guide,
|
||||
large_source: large_markdown(LARGE_MARKDOWN_SECTIONS).into(),
|
||||
_subscription: subscription,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for MarkdownViewDemo {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let colors = cx.theme().colors;
|
||||
let radius = cx.theme().radius;
|
||||
let view = cx.entity();
|
||||
let blocks = self.view.read(cx).blocks();
|
||||
let block_count = blocks.len();
|
||||
let heading_count = blocks
|
||||
.iter()
|
||||
.filter(|block| matches!(block, MarkdownBlock::Heading { .. }))
|
||||
.count();
|
||||
|
||||
ScrollArea::new("markdown-view-demo").size_full().p_4().child(
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.child(demo_section(
|
||||
"document",
|
||||
colors,
|
||||
Segmented::new("markdown-view-demo-sample", self.sample)
|
||||
.options(vec![
|
||||
(MarkdownSample::Guide, "Release notes"),
|
||||
(MarkdownSample::Large, "Large file"),
|
||||
])
|
||||
.on_change(move |next, _, cx| {
|
||||
view.update(cx, |demo, cx| {
|
||||
demo.sample = next;
|
||||
let source = match next {
|
||||
MarkdownSample::Guide => MARKDOWN_SAMPLE.into(),
|
||||
MarkdownSample::Large => demo.large_source.clone(),
|
||||
};
|
||||
demo.view.update(cx, |view, cx| view.set_source(source, cx));
|
||||
});
|
||||
}),
|
||||
))
|
||||
.child(demo_section(
|
||||
"rendered, virtualized, links clickable",
|
||||
colors,
|
||||
div()
|
||||
.h(px(480.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.overflow_hidden()
|
||||
.child(self.view.clone()),
|
||||
))
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(format!(
|
||||
"{block_count} blocks, {heading_count} headings \u{2014} scroll to test performance"
|
||||
)),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct SidebarDemo {
|
||||
selected: Option<usize>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user