Skip to content
Open
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
1 change: 1 addition & 0 deletions crates/koharu-app/bin/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ async fn run() -> Result<()> {
text_node_ids: None,
reading_order: None,
region: None,
merge_with_previous_op: false,
},
};

Expand Down
20 changes: 20 additions & 0 deletions crates/koharu-app/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ impl History {
Ok(self.epoch)
}

/// Does the same as apply, but merges the op with the previous undo entry so they undo together.
pub fn apply_merge_up(&mut self, scene: &mut Scene, mut op: Op) -> Result<u64> {
op.apply(scene).context("apply op to scene")?;
self.epoch += 1;
self.write_frame(&op)?;

if let Some(back) = self.undo_stack.pop_back() {
self.push_undo(Op::Batch {
ops: vec![back, op],
label: "Merged undo entry".into(),
});
} else {
self.push_undo(op);
}

self.redo_stack.clear();
Ok(self.epoch)
}

/// Undo the most recent op. Applies its inverse, records the inverse in
/// the log, and moves the original onto the redo stack. Returns the new
/// epoch + the inverse op that was just applied (so the RPC layer can
Expand All @@ -94,6 +113,7 @@ impl History {
let Some(original) = self.undo_stack.pop_back() else {
return Ok(None);
};

let mut inverse = original.inverse();
inverse.apply(scene).context("apply inverse op")?;
self.epoch += 1;
Expand Down
3 changes: 3 additions & 0 deletions crates/koharu-app/src/pipeline/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ pub struct PipelineRunOptions {
/// and process just that one block. Other engines ignore it.
pub region: Option<Region>,
pub reading_order: Option<ReadingOrder>,
/// If enabled, merge any ops generated by this with the entry above it, ensuring that they undo as a batch.
/// Used for implementing auto-render so that the render doesn't get a separate undo entry.
pub merge_with_previous_op: bool,
}

// ---------------------------------------------------------------------------
Expand Down
9 changes: 8 additions & 1 deletion crates/koharu-app/src/pipeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,14 @@ pub async fn run(
ops,
label: format!("{}: page {}", info.id, page_id),
};
if let Err(err) = session.apply(batch) {

let apply_res = if spec.options.merge_with_previous_op {
session.apply_merge_up(batch)
} else {
session.apply(batch)
};

if let Err(err) = apply_res {
report_step_failure(
info.id,
page_id,
Expand Down
7 changes: 7 additions & 0 deletions crates/koharu-app/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ impl ProjectSession {
history.apply(&mut scene, op)
}

/// Apply an op, merges with the op immediately before if possible.
pub fn apply_merge_up(&self, op: Op) -> Result<u64> {
let mut history = self.history.lock();
let mut scene = self.scene.write();
history.apply_merge_up(&mut scene, op)
}

pub fn undo(&self) -> Result<Option<(u64, Op)>> {
let mut history = self.history.lock();
let mut scene = self.scene.write();
Expand Down
2 changes: 2 additions & 0 deletions crates/koharu-rpc/src/mcp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub struct StartPipelineInput {
pub system_prompt: Option<String>,
pub default_font: Option<String>,
pub reading_order: Option<ReadingOrder>,
pub merge_with_previous_op: bool,
}

#[derive(Debug, Clone, Serialize, schemars::JsonSchema)]
Expand Down Expand Up @@ -192,6 +193,7 @@ impl KoharuServer {
text_node_ids: input.text_node_ids,
reading_order: input.reading_order,
region: None,
merge_with_previous_op: input.merge_with_previous_op,
},
};
let job_id = Uuid::new_v4().to_string();
Expand Down
5 changes: 5 additions & 0 deletions crates/koharu-rpc/src/routes/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub struct StartPipelineRequest {
pub default_font: Option<String>,
#[serde(default)]
pub reading_order: Option<ReadingOrder>,
/// If enabled, merge any ops generated by this with the entry above it, ensuring that they undo as a batch.
/// Used for implementing auto-render so that the render doesn't get a separate undo entry.
#[serde(default)]
pub merge_with_previous_op: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
Expand Down Expand Up @@ -88,6 +92,7 @@ async fn start_pipeline(
text_node_ids: req.text_node_ids,
region: req.region,
reading_order: req.reading_order,
merge_with_previous_op: req.merge_with_previous_op,
},
};

Expand Down
44 changes: 24 additions & 20 deletions ui/lib/api/schemas/startPipelineRequest.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
/**
* Generated by orval v8.8.1 🍺
* Generated by orval v8.19.0 🍺
* Do not edit manually.
* OpenAPI spec version: 0.0.1
*/
import type { NodeId } from './nodeId'
import type { PageId } from './pageId'
import type { ReadingOrder } from './readingOrder'
import type { Region } from './region'
import type { NodeId } from './nodeId';
import type { PageId } from './pageId';
import type { ReadingOrder } from './readingOrder';
import type { Region } from './region';

export interface StartPipelineRequest {
/** @nullable */
defaultFont?: string | null
defaultFont?: string | null;
/**
* `None` → whole project, `Some(pages)` → just those pages.
* @nullable
*/
pages?: PageId[] | null
readingOrder?: null | ReadingOrder
region?: null | Region
* If enabled, merge any ops generated by this with the entry above it, ensuring that they undo as a batch.
* Used for implementing auto-render so that the render doesn't get a separate undo entry.
*/
mergeWithPreviousOp?: boolean;
/**
* `None` → whole project, `Some(pages)` → just those pages.
* @nullable
*/
pages?: PageId[] | null;
readingOrder?: null | ReadingOrder;
region?: null | Region;
/** Engine ids (`inventory::submit!` ids) to run in order. */
steps: string[]
steps: string[];
/** @nullable */
systemPrompt?: string | null
systemPrompt?: string | null;
/** @nullable */
targetLanguage?: string | null
targetLanguage?: string | null;
/**
* Optional text-node ids for engines that can operate on individual blocks.
* @nullable
*/
textNodeIds?: NodeId[] | null
* Optional text-node ids for engines that can operate on individual blocks.
* @nullable
*/
textNodeIds?: NodeId[] | null;
}
Loading
Loading