diff --git a/src/canvas.rs b/src/canvas.rs
new file mode 100644
index 0000000..4284f10
--- /dev/null
+++ b/src/canvas.rs
@@ -0,0 +1,115 @@
+// Stiletto
+// Copyright (C) 2020 Stiletto Authors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use im::Vector;
+
+use serde::de::{Deserializer, SeqAccess, Visitor};
+use serde::ser::Serializer;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, druid::Data)]
+pub struct Path {
+ pub kurbo_path: druid::kurbo::BezPath,
+}
+
+pub type Canvas = Vector;
+
+#[derive(Debug, Clone, druid::Data, Serialize, Deserialize)]
+pub enum CanvasElement {
+ Freehand { path: Path, thickness: f64 },
+}
+
+impl CanvasElement {
+ pub fn bounding_box(&self) -> druid::Rect {
+ match self {
+ CanvasElement::Freehand { path, thickness } => {
+ use druid::kurbo::Shape;
+ path.kurbo_path
+ .bounding_box()
+ .inflate(*thickness, *thickness)
+ }
+ }
+ }
+ pub fn draw(&self, ctx: &mut druid::PaintCtx) {
+ match self {
+ CanvasElement::Freehand { path, thickness } => {
+ use druid::RenderContext;
+ let stroke_color = druid::Color::rgb8(0, 128, 0);
+ ctx.stroke(&path.kurbo_path, &stroke_color, *thickness);
+ }
+ }
+ }
+
+ pub fn get_path_mut(&mut self) -> Option<&mut Path> {
+ match self {
+ CanvasElement::Freehand { path, .. } => Some(path),
+ }
+ }
+}
+
+impl<'de> Deserialize<'de> for Path {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_seq(PathDeserializer)
+ }
+}
+
+impl Serialize for Path {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ use druid::kurbo::PathEl;
+
+ serializer.collect_seq(self.kurbo_path.iter().filter_map(|path_el| match path_el {
+ PathEl::MoveTo(pt) => Some(Into::<(f64, f64)>::into(pt)),
+ PathEl::LineTo(pt) => Some(Into::<(f64, f64)>::into(pt)),
+ _ => None,
+ }))
+ }
+}
+
+struct PathDeserializer;
+
+impl<'de> Visitor<'de> for PathDeserializer {
+ type Value = Path;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(formatter, "A sequence of 2D points")
+ }
+
+ fn visit_seq(self, mut seq: A) -> Result
+ where
+ A: SeqAccess<'de>,
+ {
+ use druid::kurbo::BezPath;
+
+ let mut kurbo_path = BezPath::new();
+ let mut first_element = true;
+ while let Some(point) = seq.next_element::<(f64, f64)>()? {
+ if first_element {
+ kurbo_path.move_to(point);
+ first_element = false;
+ } else {
+ kurbo_path.line_to(point);
+ }
+ }
+
+ Ok(Path { kurbo_path })
+ }
+}
diff --git a/src/history.rs b/src/history.rs
new file mode 100644
index 0000000..44ab433
--- /dev/null
+++ b/src/history.rs
@@ -0,0 +1,92 @@
+// Stiletto
+// Copyright (C) 2020 Stiletto Authors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use super::canvas::Canvas;
+use im::Vector;
+
+#[derive(Clone, druid::Data)]
+pub struct VersionedCanvas {
+ // We internally guarantee that this vector
+ // is never empty
+ versions: Vector