Skip to content
Merged
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
34 changes: 34 additions & 0 deletions claude-notes/plans/2026-02-25-merge-react-renderer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Merge new-react-renderer-stuff into main

## Overview
Merge the React renderer and slide functionality from `new-react-renderer-stuff` branch into `main`, preserving both old and new functionality where conflicts exist.

## Branch Comparison
- 15 commits ahead in new-react-renderer-stuff
- Main changes: React rendering pipeline, slides support, thumbnails, cursor sync
- 7 new files (mostly React components/hooks)
- 11 modified files (Rust WASM, React components, styles)

## Work Items

### Phase 1: Analysis
- [ ] Review modified Rust files for conflicts
- [ ] Review modified TypeScript/React files for conflicts
- [ ] Identify areas needing feature flags

### Phase 2: Merge Strategy
- [ ] Attempt git merge to see automatic conflict resolution
- [ ] Handle conflicts in Rust files (pipeline.rs, WASM entry points)
- [ ] Handle conflicts in React files (Editor.tsx, etc.)
- [ ] Add feature flags where old/new functionality differs

### Phase 3: Testing & Validation
- [ ] Build Rust workspace (`cargo build --workspace`)
- [ ] Build WASM module
- [ ] Build hub-client
- [ ] Test basic functionality
- [ ] Verify both rendering paths work

### Phase 4: Cleanup
- [ ] Update changelog if needed
- [ ] Commit merge results
34 changes: 32 additions & 2 deletions crates/pampa/src/wasm_entry_points/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,38 @@ pub fn qmd_to_pandoc(
}

pub fn parse_qmd(input: &[u8], include_resolved_locations: bool) -> String {
let (pandoc, context) = qmd_to_pandoc(input).unwrap();
pandoc_to_json(&pandoc, &context, include_resolved_locations).unwrap()
match qmd_to_pandoc(input) {
Ok((pandoc, context)) => {
match pandoc_to_json(&pandoc, &context, include_resolved_locations) {
Ok(json) => {
// Return success response with AST
serde_json::json!({
"success": true,
"ast": json
})
.to_string()
}
Err(e) => {
// JSON serialization error
serde_json::json!({
"success": false,
"error": e,
"diagnostics": []
})
.to_string()
}
}
}
Err(diagnostic_strings) => {
// Parse errors with diagnostics
serde_json::json!({
"success": false,
"error": "Failed to parse QMD content",
"diagnostics": diagnostic_strings.iter().map(|d| serde_json::json!({"message": d})).collect::<Vec<_>>()
})
.to_string()
}
}
}

/// Render a parsed document using a template bundle.
Expand Down
Binary file added crates/quarto-core/.DS_Store
Binary file not shown.
142 changes: 97 additions & 45 deletions crates/quarto-core/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use std::sync::Arc;

use quarto_doctemplate::Template;
use quarto_error_reporting::DiagnosticMessage;
use quarto_pandoc_types::Pandoc;
use quarto_source_map::SourceContext;

use crate::Result;
Expand Down Expand Up @@ -110,6 +111,15 @@ pub struct RenderOutput {
pub source_context: SourceContext,
}

pub struct AstOutput {
/// The AST serialized as JSON.
pub ast: Pandoc,
/// Non-fatal warnings collected during rendering.
pub warnings: Vec<DiagnosticMessage>,
/// Source context for mapping locations in diagnostics.
pub source_context: SourceContext,
}

/// Build the standard HTML pipeline stages.
///
/// Returns the stages as a vector, allowing callers to customize before
Expand Down Expand Up @@ -219,6 +229,89 @@ pub fn build_html_pipeline_with_stages(
Pipeline::new(stages)
}

pub async fn run_pipeline(
content: &[u8],
source_name: &str,
ctx: &mut RenderContext<'_>,
runtime: Arc<dyn quarto_system_runtime::SystemRuntime>,
stages: Vec<Box<dyn PipelineStage>>,
) -> Result<(PipelineData, Vec<DiagnosticMessage>)> {
// Create StageContext from RenderContext data
let mut stage_ctx = StageContext::new(
runtime,
ctx.format.clone(),
ctx.project.clone(),
ctx.document.clone(),
)
.map_err(|e| crate::error::QuartoError::Other(e.to_string()))?;

// Transfer artifacts from RenderContext to StageContext
stage_ctx.artifacts = std::mem::take(&mut ctx.artifacts);

// Create input from content
let input = PipelineData::LoadedSource(LoadedSource::new(
PathBuf::from(source_name),
content.to_vec(),
));

let pipeline = Pipeline::new(stages).expect("Pipeline stages should be compatible");

let result = pipeline.run(input, &mut stage_ctx).await;

// Transfer artifacts back to RenderContext
ctx.artifacts = stage_ctx.artifacts;

result
.map_err(|e| match e {
crate::stage::PipelineError::StageError { diagnostics, .. }
if !diagnostics.is_empty() =>
{
// Create a SourceContext for the parse error
let mut source_context = SourceContext::new();
let content_str = String::from_utf8_lossy(content).to_string();
source_context.add_file(source_name.to_string(), Some(content_str));
crate::error::QuartoError::Parse(crate::error::ParseError::new(
diagnostics,
source_context,
))
}
other => crate::error::QuartoError::Other(other.to_string()),
})
.map(|d| (d, stage_ctx.diagnostics))
}

pub async fn parse_qmd_to_ast(
content: &[u8],
source_name: &str,
ctx: &mut RenderContext<'_>,
runtime: Arc<dyn quarto_system_runtime::SystemRuntime>,
) -> Result<AstOutput> {
// Build pipeline based on config
// If custom CSS or template is specified, use a customized ApplyTemplateStage
let stages: Vec<Box<dyn PipelineStage>> = vec![
Box::new(ParseDocumentStage::new()),
Box::new(EngineExecutionStage::new()),
Box::new(AstTransformsStage::new()),
];

let (output, warnings) = run_pipeline(content, source_name, ctx, runtime, stages).await?;
// Extract the rendered output
let ast = output.into_document_ast().ok_or_else(|| {
crate::error::QuartoError::Other("Pipeline did not produce ast".to_string())
})?;

// Create source context for the output
let mut source_context = SourceContext::new();
let content_str = String::from_utf8_lossy(content).to_string();
source_context.add_file(source_name.to_string(), Some(content_str));

Ok(AstOutput {
ast: ast.ast,
warnings,
source_context,
})
}

/// Render QMD content to HTML.
///
/// This is the unified async render pipeline used by both CLI and WASM. It:
Expand Down Expand Up @@ -263,27 +356,9 @@ pub async fn render_qmd_to_html(
config: &HtmlRenderConfig<'_>,
runtime: Arc<dyn quarto_system_runtime::SystemRuntime>,
) -> Result<RenderOutput> {
// Create StageContext from RenderContext data
let mut stage_ctx = StageContext::new(
runtime,
ctx.format.clone(),
ctx.project.clone(),
ctx.document.clone(),
)
.map_err(|e| crate::error::QuartoError::Other(e.to_string()))?;

// Transfer artifacts from RenderContext to StageContext
stage_ctx.artifacts = std::mem::take(&mut ctx.artifacts);

// Create input from content
let input = PipelineData::LoadedSource(LoadedSource::new(
PathBuf::from(source_name),
content.to_vec(),
));

// Build pipeline based on config
// If custom CSS or template is specified, use a customized ApplyTemplateStage
let pipeline = if config.template.is_some() || !config.css_paths.is_empty() {
let stages = if config.template.is_some() || !config.css_paths.is_empty() {
let apply_config = ApplyTemplateConfig::new().with_css_paths(config.css_paths.to_vec());
// If custom template is provided, we'd need to pass it too
// For now, css_paths is the main customization needed
Expand All @@ -295,40 +370,17 @@ pub async fn render_qmd_to_html(
Box::new(RenderHtmlBodyStage::new()),
Box::new(ApplyTemplateStage::with_config(apply_config)),
];
Pipeline::new(stages).expect("HTML pipeline stages should be compatible")
stages
} else {
build_html_pipeline()
build_html_pipeline_stages()
};

// Run the async pipeline
let result = pipeline.run(input, &mut stage_ctx).await;

// Transfer artifacts back to RenderContext
ctx.artifacts = stage_ctx.artifacts;

// Handle result
let output = result.map_err(|e| match e {
crate::stage::PipelineError::StageError { diagnostics, .. } if !diagnostics.is_empty() => {
// Create a SourceContext for the parse error
let mut source_context = SourceContext::new();
let content_str = String::from_utf8_lossy(content).to_string();
source_context.add_file(source_name.to_string(), Some(content_str));
crate::error::QuartoError::Parse(crate::error::ParseError::new(
diagnostics,
source_context,
))
}
other => crate::error::QuartoError::Other(other.to_string()),
})?;

let (output, diagnostics) = run_pipeline(content, source_name, ctx, runtime, stages).await?;
// Extract the rendered output
let rendered = output.into_rendered_output().ok_or_else(|| {
crate::error::QuartoError::Other("Pipeline did not produce RenderedOutput".to_string())
})?;

// Collect diagnostics from the pipeline
let diagnostics = stage_ctx.diagnostics;

// Create source context for the output
let mut source_context = SourceContext::new();
let content_str = String::from_utf8_lossy(content).to_string();
Expand Down
6 changes: 3 additions & 3 deletions crates/quarto-core/src/render_to_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ use tracing::{debug, warn};

use quarto_system_runtime::SystemRuntime;

use crate::Result;
use crate::error::QuartoError;
use crate::format::{extract_format_metadata, Format};
use crate::pipeline::{render_qmd_to_html, HtmlRenderConfig, RenderOutput};
use crate::format::{Format, extract_format_metadata};
use crate::pipeline::{HtmlRenderConfig, RenderOutput, render_qmd_to_html};
use crate::project::{DocumentInfo, ProjectContext};
use crate::render::{BinaryDependencies, RenderContext};
use crate::resources::{self, HtmlResourcePaths};
use crate::Result;

/// Options for rendering a document to a file.
#[derive(Debug, Clone, Default)]
Expand Down
4 changes: 2 additions & 2 deletions crates/quarto-test/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use anyhow::{Context, Result};
use serde_yaml::Value;

use crate::assertions::{LogLevel, LogMessage, VerifyContext};
use crate::spec::{parse_test_specs, TestSpec};
use crate::spec::{TestSpec, parse_test_specs};

/// Result of running tests on a single file.
#[derive(Debug)]
Expand Down Expand Up @@ -216,7 +216,7 @@ fn run_format_tests(input_path: &Path, spec: &TestSpec) -> Result<Vec<FailureDet
fn render_document(input_path: &Path, format: &str) -> RenderOutput {
use std::sync::Arc;

use quarto_core::render_to_file::{render_to_file, RenderToFileOptions};
use quarto_core::render_to_file::{RenderToFileOptions, render_to_file};
use quarto_system_runtime::NativeRuntime;

let mut messages = Vec::new();
Expand Down
4 changes: 2 additions & 2 deletions crates/quarto/src/commands/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use anyhow::{Context, Result};
use tracing::info;

use quarto_core::{
render_document_to_file, Format, FormatIdentifier, ProjectContext, QuartoError,
RenderToFileOptions,
Format, FormatIdentifier, ProjectContext, QuartoError, RenderToFileOptions,
render_document_to_file,
};
use quarto_system_runtime::{NativeRuntime, SystemRuntime};

Expand Down
2 changes: 1 addition & 1 deletion crates/quarto/tests/smoke_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use std::path::Path;

use quarto_test::{run_test_file, TestResult};
use quarto_test::{TestResult, run_test_file};
use walkdir::WalkDir;

/// Run all smoke-all tests by discovering .qmd files in the smoke-all directory.
Expand Down
1 change: 0 additions & 1 deletion crates/wasm-quarto-hub-client/Cargo.lock

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

Loading