commit 8153da2091572f7b07ec9202b9264e6f759a7c20
Author: Michael Sippel <micha@fragmental.art>
Date:   Sat Feb 11 15:36:05 2023 +0100

    initial commit

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..da2c95d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+Cargo.lock
+/target
+*~
+\#*\#
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..750471d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+authors = ["Michael Sippel <micha@fragmental.art>"]
+edition = "2018"
+name = "r3vi"
+version = "0.1.0"
+
+[dependencies]
+cgmath = { version = "0.18.0", features = ["serde"] }
+serde = { version = "1.0", features = ["derive"] }
+bincode = "1.3.3"
+serde_json = "*"
+
+[dependencies.async-std]
+version = "1.9.0"
+features = ["unstable", "attributes"]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..14d778e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,43 @@
+# r3vi
+
+Rust Runtime for Reactive View-Projections
+
+Using **r3vi** you can define *Projections*, i.e. transformations on *Views* that are
+updated *reactively* on changes in the source-view.
+These updates are performed incrementally using fine-granuar diffs.
+
+*Views* are abstract accessor-interfaces that also define the update protocol (the diff).
+*Observers* can register to observe a *View* and are notified with the according diff-message
+whenever the view changes.
+*Projections* are transformations from one view into antoher.
+They are made of the target view and an observer that observes the source view.
+
+R3vi provides basic data-structures and projections to build projectional pipelines
+with an interface similar to native rust iterators.
+
+
+## Examples
+
+```rust
+use r3vi::buffer::vec::*;
+
+let mut buffer = VecBuffer::<i32>::new();
+buffer.push(3);
+
+let projected_port = buffer.get_port()
+                      .to_sequence()     // make SequenceView from Vec
+                      .map(|x| x + 10)
+                      .filter(|x| x > 10);
+
+let projected_view = projected_port.get_view();
+
+assert_eq!(projected_view.get(&0), Some(13));
+
+buffer.push(5);   // maps to 15
+buffer.push(-9);  // maps to 1, is eliminated by filter
+buffer.push(1);   // maps to 11
+
+assert_eq!(projected_view.get(&1), Some(15));
+assert_eq!(projected_view.get(&2), Some(11));
+
+```
diff --git a/src/buffer/grid_hashmap.rs b/src/buffer/grid_hashmap.rs
new file mode 100644
index 0000000..613c858
--- /dev/null
+++ b/src/buffer/grid_hashmap.rs
@@ -0,0 +1,90 @@
+
+use {
+    std::{
+        sync::Arc,
+        collections::HashMap,
+        hash::Hash
+    },
+    std::sync::RwLock,
+    crate::{
+        core::{
+            Observer,
+            ObserverBroadcast,
+            View,
+            InnerViewPort
+        },
+        index::{IndexArea, IndexView}
+    }
+};
+
+
+struct GridBuffer<Item> {
+    data: HashMap<Point2<i16>, Item>,
+    limit: Point2<i16>
+}
+
+impl<Item> View for GridBuffer<Item>
+where Item: Clone + Send + Sync + 'static
+{
+    type Msg = IndexArea<Point2<i16>>;
+}
+
+impl<Item> IndexView<Point2<i16>> for GridBufferView<Item>
+where Item: Clone + Send + Sync + 'static
+{
+    type Item = Item;
+
+    fn get(&self, key: &Point2<i16>) -> Option<Self::Item> {
+        self.data.get(key).cloned()
+    }
+
+    fn area(&self) -> IndexArea<Point2<i16>> {
+        IndexArea::Range(
+            Point2::new(0, 0)
+                ..= self.limit
+        )
+    }
+}
+
+pub struct GridBufferController<Item>
+where Item: Clone + Send + Sync + 'static
+{
+    data: Arc<RwLock<HashMap<Point2<i16>, Item>>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Point2<i16>, Item = Item>>>>
+}
+
+impl<Key, Item> GridBuffer<Key, Item>
+where Key: Clone + Hash + Eq + Send + Sync + 'static,
+      Item: Clone + Send + Sync + 'static
+{
+    pub fn new(port: InnerViewPort<dyn IndexView<Point2<i16>, Item = Item>>) -> Self {
+        let data = Arc::new(RwLock::new(HashMap::<Point2<i16>, Item>::new()));
+        port.set_view(Some(Arc::new(GridBufferView(data.clone()))));
+
+        GridBuffer {
+            data,
+            cast: port.get_broadcast()
+        }
+    }
+
+    pub fn insert(&mut self, key: Point2<i16>, item: Item) {
+        self.data.write().unwrap().insert(key.clone(), item);
+
+        if
+        
+        self.cast.notify(&IndexArea::Set(vec![ key ]));
+    }
+
+    pub fn insert_iter<T>(&mut self, iter: T)
+    where T: IntoIterator<Item = (Point2<i16>, Item)> {
+        for (key, item) in iter {
+            self.insert(key, item);
+        }
+    }
+
+    pub fn remove(&mut self, key: Point2<i16>) {
+        self.data.write().unwrap().remove(&key);
+        self.cast.notify(&IndexArea::Set(vec![ key ]));
+    }
+}
+
diff --git a/src/buffer/index_hashmap.rs b/src/buffer/index_hashmap.rs
new file mode 100644
index 0000000..0593865
--- /dev/null
+++ b/src/buffer/index_hashmap.rs
@@ -0,0 +1,158 @@
+use {
+    crate::{
+        view::{
+            InnerViewPort, OuterViewPort, ViewPort, View,
+            index::{IndexArea, IndexView},
+        },
+    },
+    std::sync::RwLock,
+    std::{collections::HashMap, hash::Hash, sync::Arc, ops::{Deref, DerefMut}},
+};
+
+pub struct IndexBufferView<Key, Item>(Arc<RwLock<HashMap<Key, Item>>>)
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static;
+
+impl<Key, Item> View for IndexBufferView<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    type Msg = IndexArea<Key>;
+}
+
+impl<Key, Item> IndexView<Key> for IndexBufferView<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    type Item = Item;
+
+    fn get(&self, key: &Key) -> Option<Self::Item> {
+        self.0.read().unwrap().get(key).cloned()
+    }
+
+    fn area(&self) -> IndexArea<Key> {
+        IndexArea::Set(self.0.read().unwrap().keys().cloned().collect())
+    }
+}
+
+#[derive(Clone)]
+pub struct IndexBuffer<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    data: Arc<RwLock<HashMap<Key, Item>>>,
+    port: InnerViewPort<dyn IndexView<Key, Item = Item>>,
+}
+
+impl<Key, Item> IndexBuffer<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    pub fn with_port(port: InnerViewPort<dyn IndexView<Key, Item = Item>>) -> Self {
+        let data = Arc::new(RwLock::new(HashMap::<Key, Item>::new()));
+        port.set_view(Some(Arc::new(IndexBufferView(data.clone()))));
+
+        IndexBuffer {
+            data,
+            port
+        }
+    }
+
+    pub fn new() -> Self {
+        IndexBuffer::with_port(ViewPort::new().into_inner())
+    }
+
+    pub fn get_port(&self) -> OuterViewPort<dyn IndexView<Key, Item = Item>> {
+        self.port.0.outer()
+    }
+
+    pub fn get(&self, key: &Key) -> Option<Item> {
+        self.data.read().unwrap().get(key).cloned()
+    }
+
+    pub fn get_mut(&mut self, key: &Key) -> MutableIndexAccess<Key, Item> {
+        MutableIndexAccess {
+            buf: self.clone(),
+            key: key.clone(),
+            val: self.get(key)
+        }
+    }
+
+    pub fn update(&mut self, key: Key, item: Option<Item>) {
+        if let Some(item) = item {
+            self.data.write().unwrap().insert(key.clone(), item);
+        } else {
+            self.data.write().unwrap().remove(&key);
+        }
+        self.port.notify(&IndexArea::Set(vec![key]));        
+    }
+    
+    pub fn insert(&mut self, key: Key, item: Item) {
+        self.data.write().unwrap().insert(key.clone(), item);
+        self.port.notify(&IndexArea::Set(vec![key]));
+    }
+
+    pub fn insert_iter<T>(&mut self, iter: T)
+    where
+        T: IntoIterator<Item = (Key, Item)>,
+    {
+        for (key, item) in iter {
+            self.insert(key, item);
+        }
+    }
+
+    pub fn remove(&mut self, key: Key) {
+        self.data.write().unwrap().remove(&key);
+        self.port.notify(&IndexArea::Set(vec![key]));
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct MutableIndexAccess<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    buf: IndexBuffer<Key, Item>,
+    key: Key,
+    val: Option<Item>,
+}
+
+impl<Key, Item> Deref for MutableIndexAccess<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    type Target = Option<Item>;
+
+    fn deref(&self) -> &Option<Item> {
+        &self.val
+    }
+}
+
+impl<Key, Item> DerefMut for MutableIndexAccess<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.val
+    }
+}
+
+impl<Key, Item> Drop for MutableIndexAccess<Key, Item>
+where
+    Key: Clone + Hash + Eq + Send + Sync + 'static,
+    Item: Clone + Send + Sync + 'static,
+{
+    fn drop(&mut self) {
+        self.buf.update(self.key.clone(), self.val.clone());
+    }
+}
+
diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs
new file mode 100644
index 0000000..f387915
--- /dev/null
+++ b/src/buffer/mod.rs
@@ -0,0 +1,5 @@
+
+pub mod singleton;
+pub mod vec;
+pub mod index_hashmap;
+
diff --git a/src/buffer/singleton.rs b/src/buffer/singleton.rs
new file mode 100644
index 0000000..c62d289
--- /dev/null
+++ b/src/buffer/singleton.rs
@@ -0,0 +1,147 @@
+use {
+    crate::{
+        view::{
+            InnerViewPort, OuterViewPort, View, ViewPort,
+            singleton::SingletonView
+        },
+    },
+    std::sync::RwLock,
+    std::{
+        ops::{Deref, DerefMut},
+        sync::Arc,
+    },
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct SingletonBufferView<T: Clone + Send + Sync + 'static>(Arc<RwLock<T>>);
+
+impl<T> View for SingletonBufferView<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Msg = ();
+}
+
+impl<T> SingletonView for SingletonBufferView<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Item = T;
+
+    fn get(&self) -> Self::Item {
+        self.0.read().unwrap().clone()
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[derive(Clone)]
+pub struct SingletonBuffer<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    value: Arc<RwLock<T>>,
+    port: InnerViewPort<dyn SingletonView<Item = T>>
+}
+
+impl<T> SingletonBuffer<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    pub fn with_port(value: T, port: InnerViewPort<dyn SingletonView<Item = T>>) -> Self {
+        let value = Arc::new(RwLock::new(value));
+        port.set_view(Some(Arc::new(SingletonBufferView(value.clone()))));
+
+        SingletonBuffer {
+            value,
+            port
+        }
+    }
+
+    pub fn new(value: T) -> Self {
+        SingletonBuffer::with_port(value, ViewPort::new().into_inner())
+    }
+
+    pub fn get_port(&self) -> OuterViewPort<dyn SingletonView<Item = T>> {
+        self.port.0.outer()
+    }
+
+    pub fn get(&self) -> T {
+        self.value.read().unwrap().clone()
+    }
+
+    pub fn get_mut(&self) -> MutableSingletonAccess<T> {
+        MutableSingletonAccess {
+            buf: self.clone(),
+            val: self.get(),
+        }
+    }
+
+    pub fn set(&mut self, new_value: T) {
+        let mut v = self.value.write().unwrap();
+        *v = new_value;
+        drop(v);
+        self.port.notify(&());
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct MutableSingletonAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    buf: SingletonBuffer<T>,
+    val: T,
+}
+
+impl<T> Deref for MutableSingletonAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &self.val
+    }
+}
+
+impl<T> DerefMut for MutableSingletonAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.val
+    }
+}
+
+impl<T> Drop for MutableSingletonAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    fn drop(&mut self) {
+        self.buf.set(self.val.clone());
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::buffer::singleton::*;
+    
+    #[test]
+    fn singleton_buffer1() {
+        let buffer = SingletonBuffer::<char>::new('a');
+        let port = buffer.get_port();
+
+        assert_eq!(buffer.get(), 'a');
+        assert_eq!(port.get_view().get(), 'a');
+
+        *buffer.get_mut() = 'b';
+        assert_eq!(buffer.get(), 'b');
+        assert_eq!(port.get_view().get(), 'b');
+    }
+}
+
diff --git a/src/buffer/vec.rs b/src/buffer/vec.rs
new file mode 100644
index 0000000..de42a27
--- /dev/null
+++ b/src/buffer/vec.rs
@@ -0,0 +1,193 @@
+use {
+    crate::{
+        view::{InnerViewPort, OuterViewPort, View, ViewPort},
+    },
+    std::sync::RwLock,
+    std::{
+        ops::{Deref, DerefMut},
+        sync::Arc,
+    },
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Serialize, Deserialize)]
+pub enum VecDiff<T> {
+    Clear,
+    Push(T),
+    Remove(usize),
+    Insert { idx: usize, val: T },
+    Update { idx: usize, val: T },
+}
+
+impl<T> View for Vec<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Msg = VecDiff<T>;
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[derive(Clone)]
+pub struct VecBuffer<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    data: Arc<RwLock<Vec<T>>>,
+    port: InnerViewPort<RwLock<Vec<T>>>
+}
+
+impl<T> VecBuffer<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    pub fn with_data_port(data: Vec<T>, port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
+        let data = Arc::new(RwLock::new(data));
+        port.set_view(Some(data.clone()));
+
+        for x in data.read().unwrap().iter().cloned() {
+            port.notify(&VecDiff::Push(x));
+        }
+            
+        VecBuffer {
+            data,
+            port
+        }
+    }
+
+    pub fn with_data(data: Vec<T>) -> Self {
+        VecBuffer::with_data_port(data, ViewPort::new().into_inner())
+    }
+    
+    pub fn with_port(port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
+        VecBuffer::with_data_port(vec![], port)
+    }
+
+    pub fn new() -> Self {
+        VecBuffer::with_port(ViewPort::new().into_inner())
+    }
+
+    pub fn get_port(&self) -> OuterViewPort<RwLock<Vec<T>>> {
+        self.port.0.outer()
+    }
+
+    pub fn apply_diff(&mut self, diff: VecDiff<T>) {
+        let mut data = self.data.write().unwrap();
+        match &diff {
+            VecDiff::Clear => {
+                data.clear();
+            }
+            VecDiff::Push(val) => {
+                data.push(val.clone());
+            }
+            VecDiff::Remove(idx) => {
+                data.remove(*idx);
+            }
+            VecDiff::Insert { idx, val } => {
+                data.insert(*idx, val.clone());
+            }
+            VecDiff::Update { idx, val } => {
+                data[*idx] = val.clone();
+            }
+        }
+        drop(data);
+
+        self.port.notify(&diff);
+    }
+
+    pub fn len(&self) -> usize {
+        self.data.read().unwrap().len()
+    }
+
+    pub fn get(&self, idx: usize) -> T {
+        self.data.read().unwrap()[idx].clone()
+    }
+
+    pub fn clear(&mut self) {
+        self.apply_diff(VecDiff::Clear);
+    }
+
+    pub fn push(&mut self, val: T) {
+        self.apply_diff(VecDiff::Push(val));
+    }
+
+    pub fn remove(&mut self, idx: usize) {
+        self.apply_diff(VecDiff::Remove(idx));
+    }
+
+    pub fn insert(&mut self, idx: usize, val: T) {
+        self.apply_diff(VecDiff::Insert { idx, val });
+    }
+
+    pub fn update(&mut self, idx: usize, val: T) {
+        self.apply_diff(VecDiff::Update { idx, val });
+    }
+
+    pub fn get_mut(&mut self, idx: usize) -> MutableVecAccess<T> {
+        MutableVecAccess {
+            buf: self.clone(),
+            idx,
+            val: self.get(idx),
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct MutableVecAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    buf: VecBuffer<T>,
+    idx: usize,
+    val: T,
+}
+
+impl<T> Deref for MutableVecAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &self.val
+    }
+}
+
+impl<T> DerefMut for MutableVecAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.val
+    }
+}
+
+impl<T> Drop for MutableVecAccess<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    fn drop(&mut self) {
+        self.buf.update(self.idx, self.val.clone());
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::buffer::vec::*;
+
+    #[test]
+    fn vec_buffer1() {
+        let mut buffer = VecBuffer::new();
+        
+        buffer.push('a');
+        buffer.push('b');
+        buffer.push('c');
+    }
+}
+
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..9783281
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,48 @@
+#![feature(trait_alias)]
+
+//! Rust Runtime for Reactive View-Projections
+//!
+//! Using **r3vi** you can define *Projections*, i.e. transformations on *Views* that are
+//! updated *reactively* on changes in the source-view.
+//! These updates are performed incrementally using fine-granuar diffs.
+//!
+//! *Views* are abstract accessor-interfaces that also define the update protocol (the diff).
+//! *Observers* can register to observe a *View* and are notified with the according diff-message
+//! whenever the view changes.
+//! *Projections* are transformations from one view into antoher.
+//! They are made of the target view and an observer that observes the source view.
+//!
+//! R3vi provides basic data-structures and projections to build projectional pipelines
+//! with an interface similar to native rust iterators.
+//!
+//!
+//!# Examples
+//!
+//! ```
+//! use r3vi::buffer::vec::*;
+//!
+//! let mut buffer = VecBuffer::<i32>::new();
+//! buffer.push(3);
+//!
+//! let projected_port = buffer.get_port()
+//!                       .to_sequence()     // make SequenceView from Vec
+//!                       .map(|x| x + 10)
+//!                       .filter(|x| x > 10);
+//!
+//! let projected_view = projected_port.get_view();
+//!
+//! assert_eq!(projected_view.get(&0), Some(13));
+//!
+//! buffer.push(5);   // maps to 15
+//! buffer.push(-9);  // maps to 1, is eliminated by filter
+//! buffer.push(1);   // maps to 11
+//!
+//! assert_eq!(projected_view.get(&1), Some(15));
+//! assert_eq!(projected_view.get(&2), Some(11));
+//!
+//! ```
+
+pub mod view;
+pub mod buffer;
+pub mod projection;
+
diff --git a/src/projection/decorate_sequence.rs b/src/projection/decorate_sequence.rs
new file mode 100644
index 0000000..0eb9cba
--- /dev/null
+++ b/src/projection/decorate_sequence.rs
@@ -0,0 +1,171 @@
+use {
+    crate::{
+        view::{
+            View, OuterViewPort, Observer, ViewPort, ObserverBroadcast,
+            sequence::*
+        },
+        projection::projection_helper::ProjectionHelper,
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+//                   Wrap
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+    
+pub struct Wrapped<T>
+where T: Send + Sync + 'static
+{
+    pub(super) opening: T,
+    pub(super) closing: T,
+    pub(super) items: Arc<dyn SequenceView<Item = T>>,
+
+    pub(super) cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
+    pub(super) proj_helper: ProjectionHelper<(), Self>,
+}
+
+impl<T> View for Wrapped<T>
+where T: Clone + Send + Sync + 'static
+{
+    type Msg = usize;
+}
+
+impl<T> SequenceView for Wrapped<T>
+where T: Clone + Send + Sync + 'static
+{
+    type Item = T;
+
+    fn len(&self) -> Option<usize> {
+        Some(self.items.len()? + 2)
+    }
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        let l = self.items.len().unwrap_or((-2 as i32) as usize);
+        if *idx < l+2 {
+        Some(
+            if *idx == 0 {
+                self.opening.clone()
+            } else if *idx < l+1 {
+                self.items.get(&(*idx - 1))?
+            } else {
+                self.closing.clone()
+            })
+        } else {
+            None
+        }
+    }        
+}
+
+
+pub trait Wrap<T> {
+    fn wrap(&self, opening: T, closing: T) -> OuterViewPort<dyn SequenceView<Item = T>>;
+}
+
+impl<T> Wrap<T> for OuterViewPort<dyn SequenceView<Item = T>>
+where T: Clone + Send + Sync + 'static
+{
+    fn wrap(&self, opening: T, closing: T) -> OuterViewPort<dyn SequenceView<Item = T>> {
+        let port = ViewPort::new();
+
+        let mut proj_helper = ProjectionHelper::new(port.update_hooks.clone());
+        let w = Arc::new(RwLock::new(Wrapped {
+            opening,
+            closing,
+            items: proj_helper.new_sequence_arg((), self.clone(), |s: &mut Wrapped<T>, item_idx| {
+                s.cast.notify(&(*item_idx + 1));
+                s.cast.notify(&(*item_idx + 2));
+            }),
+            cast: port.get_cast(),
+            proj_helper,
+        }));
+
+        w.write().unwrap().proj_helper.set_proj(&w);
+        port.set_view(Some(w.clone()));
+        port.into_outer()
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+//                   Separate
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct Separated<T>
+where T: Send + Sync + 'static
+{
+    pub(super) delimiter: T,
+    pub(super) items: Arc<dyn SequenceView<Item = T>>,
+
+    pub(super) cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
+    pub(super) proj_helper: ProjectionHelper<(), Self>,
+}
+
+impl<T> View for Separated<T>
+where T: Clone + Send + Sync + 'static    
+{
+    type Msg = usize;
+}
+
+impl<T> SequenceView for Separated<T>
+where T: Clone + Send + Sync + 'static
+{
+    type Item = T;
+
+    fn len(&self) -> Option<usize> {
+        let l = self.items.len()?;
+        if l == 0 {
+            Some(0)
+        } else if l == 1 {
+            Some(1)
+        } else {
+            Some(l*2 - 1)
+        }
+    }
+
+    fn get(&self, idx: &usize) -> Option<T> {
+        let l = self.items.len().unwrap_or(usize::MAX);
+        if *idx+1 < l*2 {
+            if *idx % 2 == 0 {
+                self.items.get(&(*idx / 2))
+            } else {
+                Some(self.delimiter.clone())
+            }
+        } else {
+            None
+        }
+    }
+}
+
+pub trait Separate<T> {
+    fn separate(&self, delimiter: T) -> OuterViewPort<dyn SequenceView<Item = T>>;
+}
+
+impl<T> Separate<T> for OuterViewPort<dyn SequenceView<Item = T>>
+where T: Clone + Send + Sync + 'static
+{
+    fn separate(&self, delimiter: T) -> OuterViewPort<dyn SequenceView<Item = T>> {
+        let port = ViewPort::new();
+
+        let mut proj_helper = ProjectionHelper::new(port.update_hooks.clone());
+        let w = Arc::new(RwLock::new(Separated {
+            delimiter,
+            items: proj_helper.new_sequence_arg(
+                (),
+                self.clone(),
+                |s: &mut Separated<T>, item_idx| {
+                    s.cast.notify(&(*item_idx * 2));
+                    if *item_idx > 0 {
+                        s.cast.notify(&(*item_idx * 2 - 1));
+                    }
+                }),
+                    
+            cast: port.get_cast(),
+            proj_helper,
+        }));
+
+        w.write().unwrap().proj_helper.set_proj(&w);
+        port.set_view(Some(w.clone()));
+        port.into_outer()
+    }
+}
+
diff --git a/src/projection/enumerate_sequence.rs b/src/projection/enumerate_sequence.rs
new file mode 100644
index 0000000..3da91eb
--- /dev/null
+++ b/src/projection/enumerate_sequence.rs
@@ -0,0 +1,119 @@
+use {
+    crate::{
+        view::{
+            Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
+            sequence::SequenceView,
+        }
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
+    pub fn enumerate(&self) -> OuterViewPort<dyn SequenceView<Item = (usize, Item)>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let view = Arc::new(RwLock::new(EnumerateSequence {
+            src_view: None,
+            cast: port.inner().get_broadcast(),
+        }));
+
+        self.add_observer(view.clone());
+        port.inner().set_view(Some(view));
+        port.into_outer()
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct EnumerateSequence<SrcView>
+where
+    SrcView: SequenceView + ?Sized,
+{
+    src_view: Option<Arc<SrcView>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = (usize, SrcView::Item)>>>>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView> View for EnumerateSequence<SrcView>
+where
+    SrcView: SequenceView + ?Sized,
+{
+    type Msg = usize;
+}
+
+impl<SrcView> SequenceView for EnumerateSequence<SrcView>
+where
+    SrcView: SequenceView + ?Sized
+{
+    type Item = (usize, SrcView::Item);
+
+    fn len(&self) -> Option<usize> {
+        self.src_view.len()
+    }
+
+    fn get(&self, idx: &usize) -> Option<(usize, SrcView::Item)> {
+        self.src_view.get(idx).map(|item| (*idx, item))
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView> Observer<SrcView> for EnumerateSequence<SrcView>
+where
+    SrcView: SequenceView + ?Sized
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        let old_len = self.len();
+        self.src_view = view;
+        let new_len = self.len();
+
+        if let Some(len) = old_len {
+            self.cast.notify_each(0..len);
+        }
+        if let Some(len) = new_len {
+            self.cast.notify_each(0..len);
+        }
+    }
+
+    fn notify(&mut self, msg: &usize) {
+        self.cast.notify(msg);
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::buffer::vec::*;
+    use crate::projection::enumerate_sequence::*;
+
+    use crate::view::port::UpdateTask;
+    
+    #[test]
+    fn map_seq1() {
+        let mut buffer = VecBuffer::new();
+
+        let target_port = buffer.get_port().to_sequence().enumerate();
+        let target_view = target_port.get_view();
+
+        buffer.push(0);
+        buffer.push(7);
+        buffer.push(9);
+
+        target_port.0.update();
+
+        assert_eq!(target_view.len(), Some(3));
+
+        assert_eq!(target_view.get(&0), Some((0, 0)));
+        assert_eq!(target_view.get(&1), Some((1, 7)));
+        assert_eq!(target_view.get(&2), Some((2, 9)));
+        assert_eq!(target_view.get(&3), None);
+    }
+}
+
+
diff --git a/src/projection/filter_map_sequence.rs b/src/projection/filter_map_sequence.rs
new file mode 100644
index 0000000..3c90bb0
--- /dev/null
+++ b/src/projection/filter_map_sequence.rs
@@ -0,0 +1,25 @@
+use {
+    crate::{
+        view::{
+            OuterViewPort,
+            sequence::SequenceView,
+        },
+    }
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
+    pub fn filter_map<
+        DstItem: Clone + 'static,
+        F: Fn(&Item) -> Option<DstItem> + Send + Sync + 'static,
+    >(
+        &self,
+        f: F,
+    ) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
+        self.map(f)
+            .filter(|x| x.is_some())
+            .map(|x| x.clone().unwrap())
+    }
+}
+
diff --git a/src/projection/filter_sequence.rs b/src/projection/filter_sequence.rs
new file mode 100644
index 0000000..a6e2b45
--- /dev/null
+++ b/src/projection/filter_sequence.rs
@@ -0,0 +1,213 @@
+use {
+    crate::{
+        view::{
+            Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
+            sequence::SequenceView,
+        },
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<V: SequenceView + ?Sized + 'static> OuterViewPort<V> {
+    pub fn filter<P: Fn(&V::Item) -> bool + Send + Sync + 'static>(
+        &self,
+        pred: P,
+    ) -> OuterViewPort<dyn SequenceView<Item = V::Item>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let filter = Arc::new(RwLock::new(Filter {
+            src_view: None,
+            pred,
+            old_preds: RwLock::new(Vec::new()),
+            cast: port.inner().get_broadcast(),
+        }));
+
+        self.add_observer(filter.clone());
+        port.inner().set_view(Some(filter));
+        port.into_outer()
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+struct Filter<SrcView, P>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+    P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
+{
+    src_view: Option<Arc<SrcView>>,
+    pred: P,
+    old_preds: RwLock<Vec<bool>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = SrcView::Item>>>>,
+}
+
+impl<SrcView, P> Filter<SrcView, P>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+    P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
+{
+    fn get_offset(&self, idx: usize) -> usize {
+        if let Some(v) = self.src_view.clone() {
+            let mut i = 0;
+            let mut j = 0;
+            let mut offset = 0;
+
+            while let (Some(x), true) = (v.get(&i), j <= idx) {
+                if (self.pred)(&x) {
+                    j += 1;
+                } else {
+                    offset += 1;
+                }
+                i += 1;
+            }
+
+            offset
+        } else {
+            0
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView, P> View for Filter<SrcView, P>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+    P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
+{
+    type Msg = usize;
+}
+
+impl<SrcView, P> SequenceView for Filter<SrcView, P>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+    P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
+{
+    type Item = SrcView::Item;
+
+    fn len(&self) -> Option<usize> {
+        if let Some(src_len) = self.src_view.len() {
+            Some(src_len - self.get_offset(src_len))
+        } else {
+            None
+        }
+    }
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        self.src_view.get(&(idx + self.get_offset(*idx)))
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView, P> Observer<SrcView> for Filter<SrcView, P>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+    P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
+{
+    fn reset(&mut self, new_src: Option<Arc<SrcView>>) {
+        let old_len = self.len();
+        self.src_view = new_src;
+        self.old_preds = RwLock::new(Vec::new());
+        let new_len = self.len();
+
+        if let Some(len) = old_len {
+            self.cast.notify_each(0..len);
+        }
+        if let Some(len) = new_len {
+            self.cast.notify_each(0..len);
+        }
+    }
+
+    fn notify(&mut self, idx: &usize) {
+        let l = self.len().unwrap_or(0) + 1;
+        let np = if let Some(x) = self.src_view.get(idx) {
+            (self.pred)(&x)
+        } else {
+            false
+        };
+
+        let mut opds = self.old_preds.write().unwrap();
+
+        opds.resize_with(1 + *idx, || false);
+        let op = opds.get(*idx).cloned().unwrap_or(false);
+        *opds.get_mut(*idx).unwrap() = np;
+
+        drop(opds);
+
+        let i = (0..*idx)
+            .map(|j| {
+                if let Some(x) = self.src_view.get(&j) {
+                    if (self.pred)(&x) {
+                        1
+                    } else {
+                        0
+                    }
+                } else {
+                    0
+                }
+            })
+            .sum();
+
+        if np != op {
+            self.cast.notify_each(i..l);
+        } else {
+            self.cast.notify(&i);
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::buffer::vec::*;
+    use crate::projection::filter_sequence::*;
+
+    use crate::view::port::UpdateTask;
+    
+    #[test]
+    fn filter_seq1() {
+        let mut buffer = VecBuffer::new();
+
+        let target_port = buffer.get_port()
+            .to_sequence()
+            .filter(
+                |x| *x > 10
+            );
+
+        let target_view = target_port.get_view();
+
+        buffer.push(0);
+        buffer.push(7);
+        buffer.push(9);
+
+        target_port.0.update();
+        assert_eq!(target_view.len(), Some(0));
+        assert_eq!(target_view.get(&0), None);
+
+        buffer.push(11);
+
+        target_port.0.update();
+        assert_eq!(target_view.len(), Some(1));
+        assert_eq!(target_view.get(&0), Some(11));
+        assert_eq!(target_view.get(&1), None);
+
+        buffer.push(13);
+        buffer.push(1);
+        buffer.push(5);
+        buffer.push(19);
+
+        target_port.0.update();
+        assert_eq!(target_view.len(), Some(3));
+        assert_eq!(target_view.get(&0), Some(11));
+        assert_eq!(target_view.get(&1), Some(13));
+        assert_eq!(target_view.get(&2), Some(19));
+    }
+}
+
+
diff --git a/src/projection/flatten_grid.rs b/src/projection/flatten_grid.rs
new file mode 100644
index 0000000..c24c264
--- /dev/null
+++ b/src/projection/flatten_grid.rs
@@ -0,0 +1,246 @@
+use {
+    crate::{
+        view::{
+            InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
+            grid::*,
+            index::*,
+        },
+        projection::projection_helper::ProjectionHelper,
+    },
+    cgmath::{Point2, Vector2},
+    std::sync::RwLock,
+    std::{cmp::max, collections::HashMap, sync::Arc},
+};
+
+impl<Item> OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>
+where
+    Item: 'static,
+{
+    pub fn flatten(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+        Flatten::new(self.clone(), port.inner());
+        port.into_outer()
+    }
+}
+
+pub struct Chunk<Item>
+where
+    Item: 'static,
+{
+    offset: Vector2<i16>,
+    limit: Point2<i16>,
+    view: Arc<dyn GridView<Item = Item>>,
+}
+
+pub struct Flatten<Item>
+where
+    Item: 'static,
+{
+    limit: Point2<i16>,
+    top: Arc<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
+    chunks: HashMap<Point2<i16>, Chunk<Item>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = Item>>>>,
+    proj_helper: ProjectionHelper<Point2<i16>, Self>,
+}
+
+impl<Item> View for Flatten<Item>
+where
+    Item: 'static,
+{
+    type Msg = IndexArea<Point2<i16>>;
+}
+
+impl<Item> IndexView<Point2<i16>> for Flatten<Item>
+where
+    Item: 'static,
+{
+    type Item = Item;
+
+    fn get(&self, idx: &Point2<i16>) -> Option<Self::Item> {
+        let chunk_idx = self.get_chunk_idx(*idx)?;
+        let chunk = self.chunks.get(&chunk_idx)?;
+        chunk.view.get(&(*idx - chunk.offset))
+    }
+
+    fn area(&self) -> IndexArea<Point2<i16>> {
+        IndexArea::Range(Point2::new(0, 0)..=self.limit)
+    }
+}
+
+/* TODO: remove unused projection args (bot-views) if they get replaced by a new viewport  */
+impl<Item> Flatten<Item>
+where
+    Item: 'static,
+{
+    pub fn new(
+        top_port: OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
+        out_port: InnerViewPort<dyn GridView<Item = Item>>,
+    ) -> Arc<RwLock<Self>> {
+        let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
+
+        let flat = Arc::new(RwLock::new(Flatten {
+            limit: Point2::new(0, 0),
+            top: proj_helper.new_index_arg(
+                Point2::new(-1, -1),
+                top_port,
+                |s: &mut Self, chunk_area| {
+                    for chunk_idx in chunk_area.iter() {
+                        s.update_chunk(chunk_idx);
+                    }
+                },
+            ),
+            chunks: HashMap::new(),
+            cast: out_port.get_broadcast(),
+            proj_helper,
+        }));
+
+        flat.write().unwrap().proj_helper.set_proj(&flat);
+        out_port.set_view(Some(flat.clone()));
+        flat
+    }
+
+    /// the top-sequence has changed the item at chunk_idx,
+    /// create a new observer for the contained sub sequence
+    fn update_chunk(&mut self, chunk_idx: Point2<i16>) {
+        if let Some(chunk_port) = self.top.get(&chunk_idx) {
+            let view = self.proj_helper.new_index_arg(
+                chunk_idx,
+                chunk_port.clone(),
+                move |s: &mut Self, area| {
+                    if let Some(chunk) = s.chunks.get(&chunk_idx) {
+                        if chunk.limit != *chunk.view.area().range().end() {
+                            s.update_all_offsets();
+                        }
+                    }
+
+                    if let Some(chunk) = s.chunks.get(&chunk_idx) {
+                        s.cast.notify(&area.map(|pt| pt + chunk.offset));                    
+                    }
+                },
+            );
+
+            if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
+                chunk.view = view;
+
+                let old_limit = chunk.limit;
+                let new_limit = *chunk.view.area().range().end();
+
+                self.cast.notify(
+                    &IndexArea::Range(
+                        Point2::new(chunk.offset.x, chunk.offset.y) ..= Point2::new(chunk.offset.x + max(old_limit.x, new_limit.x), chunk.offset.y + max(old_limit.y, new_limit.y) )));
+
+            } else {
+                self.chunks.insert(
+                    chunk_idx,
+                    Chunk {
+                        offset: Vector2::new(-1, -1),
+                        limit: Point2::new(-1, -1),
+                        view,
+                    },
+                );
+            }
+            
+            self.update_all_offsets();
+        } else {
+            self.proj_helper.remove_arg(&chunk_idx);
+
+            if let Some(_chunk) = self.chunks.remove(&chunk_idx) {
+                self.update_all_offsets();
+            }
+        }
+    }
+
+    /// recalculate all chunk offsets
+    /// and update size of flattened grid
+    fn update_all_offsets(&mut self) {
+        let top_range = self.top.area().range();
+        let mut col_widths = vec![0 as i16; (top_range.end().x + 1) as usize];
+        let mut row_heights = vec![0 as i16; (top_range.end().y + 1) as usize];
+
+        for chunk_idx in GridWindowIterator::from(top_range.clone()) {
+            if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
+                let chunk_range = chunk.view.area().range();
+                let lim = *chunk_range.end();
+
+                col_widths[chunk_idx.x as usize] = max(
+                    col_widths[chunk_idx.x as usize],
+                    if lim.x < 0 { 0 } else { lim.x + 1 },
+                );
+                row_heights[chunk_idx.y as usize] = max(
+                    row_heights[chunk_idx.y as usize],
+                    if lim.y < 0 { 0 } else { lim.y + 1 },
+                );
+            }
+        }
+
+        for chunk_idx in GridWindowIterator::from(top_range.clone()) {
+            if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
+                let _old_offset = chunk.offset;
+                let _old_limit = chunk.limit;
+
+                //chunk.limit = Point2::new( col_widths[chunk_idx.x as usize]-1, row_heights[chunk_idx.y as usize]-1 );
+                chunk.limit = *chunk.view.area().range().end();
+
+                chunk.offset = Vector2::new(
+                    (0..chunk_idx.x as usize).map(|x| col_widths[x]).sum(),
+                    (0..chunk_idx.y as usize).map(|y| row_heights[y]).sum(),
+                );
+/*
+
+                                if old_offset != chunk.offset {
+                                    self.cast.notify(
+                                        &IndexArea::Range(
+                                            Point2::new(
+                                                std::cmp::min(old_offset.x, chunk.offset.x),
+                                                std::cmp::min(old_offset.y, chunk.offset.y)
+                                            )
+                                                ..= Point2::new(
+                                                    std::cmp::max(old_offset.x + old_limit.x, chunk.offset.x + chunk.limit.x),
+                                                    std::cmp::max(old_offset.y + old_limit.y, chunk.offset.y + chunk.limit.y)
+                                                )
+                                        )
+                                    );
+                                }
+*/
+            }
+        }
+
+        let old_limit = self.limit;
+        self.limit = Point2::new(
+            (0..=top_range.end().x)
+                .map(|x| col_widths.get(x as usize).unwrap_or(&0))
+                .sum::<i16>()
+                - 1,
+            (0..=top_range.end().y)
+                .map(|y| row_heights.get(y as usize).unwrap_or(&0))
+                .sum::<i16>()
+                - 1,
+        );
+
+        self.cast.notify(&IndexArea::Range(
+            Point2::new(0, 0)
+                ..=Point2::new(
+                    max(self.limit.x, old_limit.x),
+                    max(self.limit.y, old_limit.y),
+                ),
+        ));
+
+    }
+
+    /// given an index in the flattened sequence,
+    /// which sub-sequence does it belong to?
+    fn get_chunk_idx(&self, glob_pos: Point2<i16>) -> Option<Point2<i16>> {
+        for chunk_idx in GridWindowIterator::from(self.top.area().range()) {
+            if let Some(chunk) = self.chunks.get(&chunk_idx) {
+                let end = chunk.limit + chunk.offset;
+
+                if glob_pos.x <= end.x && glob_pos.y <= end.y {
+                    return Some(chunk_idx);
+                }
+            }
+        }
+
+        None
+    }
+}
diff --git a/src/projection/flatten_sequence.rs b/src/projection/flatten_sequence.rs
new file mode 100644
index 0000000..da6fe82
--- /dev/null
+++ b/src/projection/flatten_sequence.rs
@@ -0,0 +1,227 @@
+use {
+    crate::{
+        view::{
+            port::UpdateTask, InnerViewPort, Observer, ObserverBroadcast, ObserverExt,
+            OuterViewPort, View, ViewPort,
+            sequence::SequenceView,
+        },
+        projection::projection_helper::ProjectionHelper,
+    },
+    std::sync::RwLock,
+    std::{collections::BTreeMap, sync::Arc},
+};
+
+impl<Item> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>
+where
+    Item: 'static,
+{
+    pub fn flatten(&self) -> OuterViewPort<dyn SequenceView<Item = Item>> {
+        let port = ViewPort::new();
+        Flatten::new(self.clone(), port.inner());
+        port.into_outer()
+    }
+}
+
+pub struct Chunk<Item>
+where
+    Item: 'static,
+{
+    offset: usize,
+    len: usize,
+    view: Arc<dyn SequenceView<Item = Item>>,
+}
+
+pub struct Flatten<Item>
+where
+    Item: 'static,
+{
+    length: usize,
+    top: Arc<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>,
+    chunks: BTreeMap<usize, Chunk<Item>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = Item>>>>,
+    proj_helper: ProjectionHelper<usize, Self>,
+}
+
+impl<Item> View for Flatten<Item>
+where
+    Item: 'static,
+{
+    type Msg = usize;
+}
+
+impl<Item> SequenceView for Flatten<Item>
+where
+    Item: 'static,
+{
+    type Item = Item;
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        let chunk = self.chunks.get(&self.get_chunk_idx(*idx)?)?;
+        chunk.view.get(&(*idx - chunk.offset))
+    }
+
+    fn len(&self) -> Option<usize> {
+        Some(self.length)
+    }
+}
+
+impl<Item> Flatten<Item>
+where
+    Item: 'static,
+{
+    pub fn new(
+        top_port: OuterViewPort<
+            dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>,
+        >,
+        out_port: InnerViewPort<dyn SequenceView<Item = Item>>,
+    ) -> Arc<RwLock<Self>> {
+        let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
+
+        let flat = Arc::new(RwLock::new(Flatten {
+            length: 0,
+            top: proj_helper.new_sequence_arg(usize::MAX, top_port, |s: &mut Self, chunk_idx| {
+                s.update_chunk(*chunk_idx);
+            }),
+            chunks: BTreeMap::new(),
+            cast: out_port.get_broadcast(),
+            proj_helper,
+        }));
+
+        flat.write().unwrap().proj_helper.set_proj(&flat);
+        out_port.set_view(Some(flat.clone()));
+        flat
+    }
+
+    /// the top-sequence has changed the item at chunk_idx,
+    /// create a new observer for the contained sub sequence
+    fn update_chunk(&mut self, chunk_idx: usize) {
+        if let Some(chunk_port) = self.top.get(&chunk_idx) {
+            self.chunks.insert(
+                chunk_idx,
+                Chunk {
+                    offset: 0, // will be adjusted by update_offsets() later
+                    len: 0,
+                    view: self.proj_helper.new_sequence_arg(
+                        chunk_idx,
+                        chunk_port.clone(),
+                        move |s: &mut Self, idx| {
+                            if let Some(chunk) = s.chunks.get(&chunk_idx) {
+                                let chunk_offset = chunk.offset;
+                                let chunk_len = chunk.view.len().unwrap_or(0);
+
+                                let mut dirty_idx = Vec::new();
+                                if chunk.len != chunk_len {
+                                    dirty_idx = s.update_all_offsets();
+                                }
+
+                                s.cast.notify(&(idx + chunk_offset));
+                                s.cast.notify_each(dirty_idx);
+                            } else {
+                                let dirty_idx = s.update_all_offsets();
+                                s.cast.notify_each(dirty_idx);
+                            }
+                        },
+                    ),
+                },
+            );
+
+            chunk_port.0.update();
+            let dirty_idx = self.update_all_offsets();
+            self.cast.notify_each(dirty_idx);
+        } else {
+            // todo:
+            self.proj_helper.remove_arg(&chunk_idx);
+
+            self.chunks.remove(&chunk_idx);
+
+            let dirty_idx = self.update_all_offsets();
+            self.cast.notify_each(dirty_idx);
+        }
+    }
+
+    /// recalculate all chunk offsets beginning at start_idx
+    /// and update length of flattened sequence
+    fn update_all_offsets(&mut self) -> Vec<usize> {
+        let mut dirty_idx = Vec::new();
+        let mut cur_offset = 0;
+
+        for (_chunk_idx, chunk) in self.chunks.iter_mut() {
+            let old_offset = chunk.offset;
+            chunk.offset = cur_offset;
+            chunk.len = chunk.view.len().unwrap_or(0);
+
+            if old_offset != cur_offset {
+                dirty_idx.extend(
+                    std::cmp::min(old_offset, cur_offset)
+                        ..std::cmp::max(old_offset, cur_offset) + chunk.len,
+                );
+            }
+
+            cur_offset += chunk.len;
+        }
+
+        let old_length = self.length;
+        self.length = cur_offset;
+
+        dirty_idx.extend(self.length..old_length);
+
+        dirty_idx
+    }
+
+    /// given an index in the flattened sequence,
+    /// which sub-sequence does it belong to?
+    fn get_chunk_idx(&self, glob_idx: usize) -> Option<usize> {
+        let mut offset = 0;
+        for (chunk_idx, chunk) in self.chunks.iter() {
+            offset += chunk.view.len().unwrap_or(0);
+            if glob_idx < offset {
+                return Some(*chunk_idx);
+            }
+        }
+        None
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::buffer::vec::*;
+    use crate::projection::flatten_sequence::*;
+
+    use crate::view::port::UpdateTask;
+    
+    #[test]
+    fn flatten1() {
+        let mut buffer = VecBuffer::new();
+
+        let target_port = buffer.get_port().to_sequence().flatten();
+        let target_view = target_port.get_view();
+
+        let b1 = VecBuffer::with_data(
+            vec!['h', 'a', 'l', 'l', 'o']
+        );
+        let b2 = VecBuffer::with_data(
+            vec!['w', 'e', 'l', 't']
+        );
+        let b3 = VecBuffer::with_data(
+            vec!['!']
+        );
+
+        buffer.push( b1.get_port().to_sequence() );
+        buffer.push( b2.get_port().to_sequence() );
+        buffer.push( b3.get_port().to_sequence() );
+
+        target_port.0.update();
+
+        assert_eq!(target_view.len(), Some(10));
+
+        assert_eq!(target_view.get(&0), Some('h'));
+        assert_eq!(target_view.get(&5), Some('w'));
+        assert_eq!(target_view.get(&6), Some('e'));
+        assert_eq!(target_view.get(&9), Some('!'));
+        assert_eq!(target_view.get(&10), None);
+    }
+}
+
+
diff --git a/src/projection/flatten_singleton.rs b/src/projection/flatten_singleton.rs
new file mode 100644
index 0000000..c55fb94
--- /dev/null
+++ b/src/projection/flatten_singleton.rs
@@ -0,0 +1,88 @@
+use {
+    crate::{
+        view::{
+            InnerViewPort, Observer, ObserverBroadcast,
+            OuterViewPort, View, ViewPort,
+            singleton::SingletonView,
+        },
+        projection::projection_helper::ProjectionHelper,
+    },
+    std::sync::RwLock,
+    std::sync::Arc,
+};
+
+impl<Item> OuterViewPort<dyn SingletonView<Item = OuterViewPort<dyn SingletonView<Item = Item>>>>
+where
+    Item: 'static + Default,
+{
+    pub fn flatten(&self) -> OuterViewPort<dyn SingletonView<Item = Item>> {
+        let port = ViewPort::new();
+        Flatten::new(self.clone(), port.inner());
+        port.into_outer()
+    }
+}
+
+pub struct Flatten<Item>
+where
+    Item: 'static + Default,
+{
+    outer: Arc<dyn SingletonView<Item = OuterViewPort<dyn SingletonView<Item = Item>>>>,
+    inner: OuterViewPort<dyn SingletonView<Item = Item>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = Item>>>>,
+    proj: ProjectionHelper<usize, Self>
+}
+
+impl<Item> View for Flatten<Item>
+where
+    Item: 'static + Default,
+{
+    type Msg = ();
+}
+
+impl<Item> SingletonView for Flatten<Item>
+where
+    Item: 'static + Default,
+{
+    type Item = Item;
+
+    fn get(&self) -> Self::Item {
+        if let Some(i) = self.inner.get_view() {
+            i.get()
+        } else {
+            Item::default()
+        }
+    }
+}
+
+impl<Item> Flatten<Item>
+where
+    Item: 'static + Default,
+{
+    pub fn new(
+        top_port: OuterViewPort<
+            dyn SingletonView<Item = OuterViewPort<dyn SingletonView<Item = Item>>>,
+        >,
+        out_port: InnerViewPort<dyn SingletonView<Item = Item>>,
+    ) -> Arc<RwLock<Self>> {
+        let mut proj = ProjectionHelper::new(out_port.0.update_hooks.clone());
+
+        let flat = Arc::new(RwLock::new(Flatten {
+            outer: proj.new_singleton_arg(0, top_port, |s: &mut Self, _msg| {
+                s.inner = s.outer.get();
+                s.proj.new_singleton_arg(1, s.inner.clone(), |s: &mut Self, _msg| {
+                    s.cast.notify(&());
+                });
+                //s.inner.0.update();
+            }),
+            inner: OuterViewPort::default(),
+            cast: out_port.get_broadcast(),
+            proj,
+        }));
+
+        flat.write().unwrap().proj.set_proj(&flat);
+        out_port.set_view(Some(flat.clone()));
+        flat
+    }
+}
+
+
diff --git a/src/projection/grid_offset.rs b/src/projection/grid_offset.rs
new file mode 100644
index 0000000..574e250
--- /dev/null
+++ b/src/projection/grid_offset.rs
@@ -0,0 +1,21 @@
+use {
+    crate::{
+        view::{
+            OuterViewPort,
+            grid::GridView,
+        }
+    },
+    cgmath::Vector2,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item> OuterViewPort<dyn GridView<Item = Item>>
+where
+    Item: 'static,
+{
+    pub fn offset(&self, offset: Vector2<i16>) -> OuterViewPort<dyn GridView<Item = Item>> {
+        self.map_key(move |pt| pt + offset, move |pt| Some(pt - offset))
+    }
+}
+
diff --git a/src/projection/map_index_item.rs b/src/projection/map_index_item.rs
new file mode 100644
index 0000000..9baf9bd
--- /dev/null
+++ b/src/projection/map_index_item.rs
@@ -0,0 +1,107 @@
+pub use {
+    crate::{
+        view::{
+            InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
+            index::{IndexArea, IndexView},
+        },
+    },
+    std::sync::RwLock,
+    std::{boxed::Box, sync::Arc},
+};
+
+impl<Key, Item> OuterViewPort<dyn IndexView<Key, Item = Item>>
+where
+    Key: Clone + Send + Sync + 'static,
+    Item: Send + Sync + 'static,
+{
+    pub fn map_item<DstItem: 'static, F: Fn(&Key, &Item) -> DstItem + Send + Sync + 'static>(
+        &self,
+        f: F,
+    ) -> OuterViewPort<dyn IndexView<Key, Item = DstItem>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let map = MapIndexItem::new(port.inner(), f);
+        self.add_observer(map.clone());
+        port.into_outer()
+    }
+}
+
+pub struct MapIndexItem<Key, DstItem, SrcView, F>
+where
+    Key: Clone + Send + Sync,
+    SrcView: IndexView<Key> + ?Sized,
+    F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
+{
+    src_view: Option<Arc<SrcView>>,
+    f: F,
+    cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Key, Item = DstItem>>>>,
+}
+
+impl<Key, DstItem, SrcView, F> MapIndexItem<Key, DstItem, SrcView, F>
+where
+    Key: Clone + Send + Sync + 'static,
+    DstItem: 'static,
+    SrcView: IndexView<Key> + ?Sized + 'static,
+    F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync + 'static,
+{
+    fn new(port: InnerViewPort<dyn IndexView<Key, Item = DstItem>>, f: F) -> Arc<RwLock<Self>> {
+        let map = Arc::new(RwLock::new(MapIndexItem {
+            src_view: None,
+            f,
+            cast: port.get_broadcast(),
+        }));
+
+        port.set_view(Some(map.clone()));
+        map
+    }
+}
+
+impl<Key, DstItem, SrcView, F> View for MapIndexItem<Key, DstItem, SrcView, F>
+where
+    Key: Clone + Send + Sync,
+    SrcView: IndexView<Key> + ?Sized,
+    F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
+{
+    type Msg = IndexArea<Key>;
+}
+
+impl<Key, DstItem, SrcView, F> IndexView<Key> for MapIndexItem<Key, DstItem, SrcView, F>
+where
+    Key: Clone + Send + Sync,
+    SrcView: IndexView<Key> + ?Sized,
+    F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
+{
+    type Item = DstItem;
+
+    fn get(&self, key: &Key) -> Option<Self::Item> {
+        self.src_view
+            .get(key)
+            .as_ref()
+            .map(|item| (self.f)(key, item))
+    }
+
+    fn area(&self) -> IndexArea<Key> {
+        self.src_view.area()
+    }
+}
+
+impl<Key, DstItem, SrcView, F> Observer<SrcView> for MapIndexItem<Key, DstItem, SrcView, F>
+where
+    Key: Clone + Send + Sync,
+    SrcView: IndexView<Key> + ?Sized,
+    F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        let old_area = self.area();
+
+        self.src_view = view;
+
+        self.cast.notify(&old_area);
+        self.cast.notify(&self.src_view.area())
+    }
+
+    fn notify(&mut self, area: &IndexArea<Key>) {
+        self.cast.notify(area);
+    }
+}
diff --git a/src/projection/map_index_key.rs b/src/projection/map_index_key.rs
new file mode 100644
index 0000000..91313be
--- /dev/null
+++ b/src/projection/map_index_key.rs
@@ -0,0 +1,145 @@
+pub use {
+    crate::{
+        view::{
+            InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
+            grid::GridView,
+            index::{IndexArea, IndexView},
+        },
+    },
+    std::sync::RwLock,
+    std::{boxed::Box, sync::Arc},
+};
+
+impl<SrcKey, Item> OuterViewPort<dyn IndexView<SrcKey, Item = Item>>
+where
+    SrcKey: Clone + Send + Sync + 'static,
+    Item: 'static,
+{
+    pub fn map_key<
+        DstKey: Clone + Send + Sync + 'static,
+        F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
+        F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
+    >(
+        &self,
+        f1: F1,
+        f2: F2,
+    ) -> OuterViewPort<dyn IndexView<DstKey, Item = Item>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let map = MapIndexKey::new(port.inner(), f1, f2);
+        self.add_observer(map.clone());
+        port.into_outer()
+    }
+}
+
+impl<Item> OuterViewPort<dyn IndexView<usize, Item = Item>>
+where
+    Item: 'static,
+{
+    pub fn to_grid_horizontal(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
+        self.map_key(
+            |idx| cgmath::Point2::new(*idx as i16, 0),
+            |pt| if pt.y == 0 { Some(pt.x as usize) } else { None },
+        )
+    }
+
+    pub fn to_grid_vertical(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
+        self.map_key(
+            |idx| cgmath::Point2::new(0, *idx as i16),
+            |pt| if pt.x == 0 { Some(pt.y as usize) } else { None },
+        )
+    }
+}
+
+pub struct MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
+where
+    DstKey: Clone + Send + Sync,
+    SrcKey: Clone + Send + Sync,
+    SrcView: IndexView<SrcKey> + ?Sized,
+    F1: Fn(&SrcKey) -> DstKey + Send + Sync,
+    F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
+{
+    src_view: Option<Arc<SrcView>>,
+    f1: F1,
+    f2: F2,
+    cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<DstKey, Item = SrcView::Item>>>>,
+}
+
+impl<DstKey, SrcKey, SrcView, F1, F2> MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
+where
+    DstKey: Clone + Send + Sync + 'static,
+    SrcKey: Clone + Send + Sync + 'static,
+    SrcView: IndexView<SrcKey> + ?Sized + 'static,
+    SrcView::Item: 'static,
+    F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
+    F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
+{
+    fn new(
+        port: InnerViewPort<dyn IndexView<DstKey, Item = SrcView::Item>>,
+        f1: F1,
+        f2: F2,
+    ) -> Arc<RwLock<Self>> {
+        let map = Arc::new(RwLock::new(MapIndexKey {
+            src_view: None,
+            f1,
+            f2,
+            cast: port.get_broadcast(),
+        }));
+
+        port.set_view(Some(map.clone()));
+        map
+    }
+}
+
+impl<DstKey, SrcKey, SrcView, F1, F2> View for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
+where
+    DstKey: Clone + Send + Sync,
+    SrcKey: Clone + Send + Sync,
+    SrcView: IndexView<SrcKey> + ?Sized,
+    F1: Fn(&SrcKey) -> DstKey + Send + Sync,
+    F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
+{
+    type Msg = IndexArea<DstKey>;
+}
+
+impl<DstKey, SrcKey, SrcView, F1, F2> IndexView<DstKey>
+    for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
+where
+    DstKey: Clone + Send + Sync,
+    SrcKey: Clone + Send + Sync,
+    SrcView: IndexView<SrcKey> + ?Sized,
+    F1: Fn(&SrcKey) -> DstKey + Send + Sync,
+    F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
+{
+    type Item = SrcView::Item;
+
+    fn get(&self, key: &DstKey) -> Option<Self::Item> {
+        self.src_view.get(&(self.f2)(key)?)
+    }
+
+    fn area(&self) -> IndexArea<DstKey> {
+        self.src_view.area().map(&self.f1)
+    }
+}
+
+impl<DstKey, SrcKey, SrcView, F1, F2> Observer<SrcView>
+    for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
+where
+    DstKey: Clone + Send + Sync,
+    SrcKey: Clone + Send + Sync,
+    SrcView: IndexView<SrcKey> + ?Sized,
+    F1: Fn(&SrcKey) -> DstKey + Send + Sync,
+    F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        let old_area = self.area();
+        self.src_view = view;
+        self.cast.notify(&old_area);
+        self.cast.notify(&self.area());
+    }
+
+    fn notify(&mut self, msg: &IndexArea<SrcKey>) {
+        self.cast.notify(&msg.map(&self.f1));
+    }
+}
diff --git a/src/projection/map_sequence.rs b/src/projection/map_sequence.rs
new file mode 100644
index 0000000..c745f1e
--- /dev/null
+++ b/src/projection/map_sequence.rs
@@ -0,0 +1,127 @@
+use {
+    crate::{
+        view::{
+            Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
+            sequence::SequenceView,
+        },
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
+    pub fn map<DstItem: 'static, F: Fn(&Item) -> DstItem + Send + Sync + 'static>(
+        &self,
+        f: F,
+    ) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let map = Arc::new(RwLock::new(MapSequenceItem {
+            src_view: None,
+            f,
+            cast: port.inner().get_broadcast(),
+        }));
+
+        self.add_observer(map.clone());
+        port.inner().set_view(Some(map));
+        port.into_outer()
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct MapSequenceItem<DstItem, SrcView, F>
+where
+    SrcView: SequenceView + ?Sized,
+    F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
+{
+    src_view: Option<Arc<SrcView>>,
+    f: F,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = DstItem>>>>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<DstItem, SrcView, F> View for MapSequenceItem<DstItem, SrcView, F>
+where
+    SrcView: SequenceView + ?Sized,
+    F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
+{
+    type Msg = usize;
+}
+
+impl<DstItem, SrcView, F> SequenceView for MapSequenceItem<DstItem, SrcView, F>
+where
+    SrcView: SequenceView + ?Sized,
+    F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
+{
+    type Item = DstItem;
+
+    fn len(&self) -> Option<usize> {
+        self.src_view.len()
+    }
+
+    fn get(&self, idx: &usize) -> Option<DstItem> {
+        self.src_view.get(idx).as_ref().map(|item| (self.f)(item))
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<DstItem, SrcView, F> Observer<SrcView> for MapSequenceItem<DstItem, SrcView, F>
+where
+    SrcView: SequenceView + ?Sized,
+    F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        let old_len = self.len();
+        self.src_view = view;
+        let new_len = self.len();
+
+        if let Some(len) = old_len {
+            self.cast.notify_each(0..len);
+        }
+        if let Some(len) = new_len {
+            self.cast.notify_each(0..len);
+        }
+    }
+
+    fn notify(&mut self, msg: &usize) {
+        self.cast.notify(msg);
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::buffer::vec::*;
+    use crate::projection::map_sequence::*;
+
+    use crate::view::port::UpdateTask;
+    
+    #[test]
+    fn map_seq1() {
+        let mut buffer = VecBuffer::new();
+
+        let target_port = buffer.get_port().to_sequence().map(|x| x + 10);
+        let target_view = target_port.get_view();
+
+        buffer.push(0);
+        buffer.push(7);
+        buffer.push(9);
+
+        target_port.0.update();
+
+        assert_eq!(target_view.len(), Some(3));
+
+        assert_eq!(target_view.get(&0), Some(10));
+        assert_eq!(target_view.get(&1), Some(17));
+        assert_eq!(target_view.get(&2), Some(19));
+        assert_eq!(target_view.get(&3), None);
+    }
+}
+
diff --git a/src/projection/map_singleton.rs b/src/projection/map_singleton.rs
new file mode 100644
index 0000000..0f8135f
--- /dev/null
+++ b/src/projection/map_singleton.rs
@@ -0,0 +1,109 @@
+use {
+    crate::{
+        view::{
+            Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
+            singleton::SingletonView,
+        }
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
+    pub fn map<DstItem: 'static, F: Fn(Item) -> DstItem + Send + Sync + 'static>(
+        &self,
+        f: F,
+    ) -> OuterViewPort<dyn SingletonView<Item = DstItem>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let map = Arc::new(RwLock::new(MapSingleton {
+            src_view: None,
+            f,
+            cast: port.inner().get_broadcast(),
+        }));
+
+        self.add_observer(map.clone());
+        port.inner().set_view(Some(map));
+        port.into_outer()
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct MapSingleton<DstItem, SrcView, F>
+where
+    SrcView: SingletonView + ?Sized,
+    F: Fn(SrcView::Item) -> DstItem + Send + Sync,
+{
+    src_view: Option<Arc<SrcView>>,
+    f: F,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = DstItem>>>>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<DstItem, SrcView, F> View for MapSingleton<DstItem, SrcView, F>
+where
+    SrcView: SingletonView + ?Sized,
+    F: Fn(SrcView::Item) -> DstItem + Send + Sync,
+{
+    type Msg = ();
+}
+
+impl<DstItem, SrcView, F> SingletonView for MapSingleton<DstItem, SrcView, F>
+where
+    SrcView: SingletonView + ?Sized,
+    F: Fn(SrcView::Item) -> DstItem + Send + Sync,
+{
+    type Item = DstItem;
+
+    fn get(&self) -> DstItem {
+        (self.f)(self.src_view.as_ref().unwrap().get())
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<DstItem, SrcView, F> Observer<SrcView> for MapSingleton<DstItem, SrcView, F>
+where
+    SrcView: SingletonView + ?Sized,
+    F: Fn(SrcView::Item) -> DstItem + Send + Sync,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        self.src_view = view;
+        self.cast.notify(&());
+    }
+
+    fn notify(&mut self, msg: &()) {
+        self.cast.notify(msg);
+    }
+}
+
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        buffer::singleton::*,
+        projection::map_singleton::*
+    };
+
+    #[test]
+    fn singleton_map1() {
+        let mut buffer = SingletonBuffer::new(0);
+
+        let src_port = buffer.get_port();
+        let dst_port = src_port.map(|x| x + 10);
+
+        let dst_view = dst_port.get_view();
+
+        assert_eq!(dst_view.get(), 10);
+        buffer.set(5);
+        assert_eq!(dst_view.get(), 15);
+    }
+}
+
diff --git a/src/projection/mod.rs b/src/projection/mod.rs
new file mode 100644
index 0000000..7c26a8b
--- /dev/null
+++ b/src/projection/mod.rs
@@ -0,0 +1,22 @@
+
+pub mod projection_helper;
+
+pub mod sgl2idx;
+pub mod sgl2seq;
+pub mod vec2seq;
+pub mod vec2bin;
+pub mod vec2json;
+pub mod seq2idx;
+pub mod enumerate_sequence;
+pub mod filter_sequence;
+pub mod filter_map_sequence;
+pub mod flatten_singleton;
+pub mod flatten_sequence;
+pub mod flatten_grid;
+pub mod map_singleton;
+pub mod map_sequence;
+pub mod map_index_item;
+pub mod map_index_key;
+pub mod grid_offset;
+pub mod decorate_sequence;
+
diff --git a/src/projection/projection_helper.rs b/src/projection/projection_helper.rs
new file mode 100644
index 0000000..ac533b6
--- /dev/null
+++ b/src/projection/projection_helper.rs
@@ -0,0 +1,239 @@
+use {
+    crate::{
+        view::{
+            channel::{queue_channel, set_channel, ChannelData, ChannelReceiver, ChannelSender},
+            port::UpdateTask,
+            Observer, ObserverExt, OuterViewPort, View,
+            index::{IndexArea, IndexView},
+            sequence::SequenceView,
+            singleton::SingletonView,
+        },
+    },
+    std::sync::RwLock,
+    std::{
+        any::Any,
+        cmp::max,
+        collections::HashMap,
+        hash::Hash,
+        sync::{Arc, Weak},
+    },
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct ProjectionHelper<ArgKey, P>
+where
+    ArgKey: Clone + Hash + Eq,
+    P: Send + Sync + 'static,
+{
+    keepalive: HashMap<ArgKey, (usize, Arc<dyn Any + Send + Sync>)>,
+    proj: Arc<RwLock<Weak<RwLock<P>>>>,
+    update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
+}
+
+impl<ArgKey, P> ProjectionHelper<ArgKey, P>
+where
+    ArgKey: Clone + Hash + Eq,
+    P: Send + Sync + 'static,
+{
+    pub fn new(update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>) -> Self {
+        ProjectionHelper {
+            keepalive: HashMap::new(),
+            proj: Arc::new(RwLock::new(Weak::new())),
+            update_hooks,
+        }
+    }
+
+    pub fn set_proj(&mut self, proj: &Arc<RwLock<P>>) {
+        *self.proj.write().unwrap() = Arc::downgrade(proj);
+    }
+
+    // todo: make this functions generic over the View
+    // this does currently not work because Observer<V> is not implemented for ProjectionArg for *all* V.
+
+    pub fn new_singleton_arg<Item: 'static>(
+        &mut self,
+        arg_key: ArgKey,
+        port: OuterViewPort<dyn SingletonView<Item = Item>>,
+        notify: impl Fn(&mut P, &()) + Send + Sync + 'static,
+    ) -> Arc<RwLock<Option<Arc<dyn SingletonView<Item = Item>>>>> {
+        port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel()));
+        port.get_view_arc()
+    }
+
+    pub fn new_sequence_arg<Item: 'static>(
+        &mut self,
+        arg_key: ArgKey,
+        port: OuterViewPort<dyn SequenceView<Item = Item>>,
+        notify: impl Fn(&mut P, &usize) + Send + Sync + 'static,
+    ) -> Arc<RwLock<Option<Arc<dyn SequenceView<Item = Item>>>>> {
+        port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel()));
+        port.get_view_arc()
+    }
+
+    pub fn new_index_arg<Key: Clone + Send + Sync + 'static, Item: 'static>(
+        &mut self,
+        arg_key: ArgKey,
+        port: OuterViewPort<dyn IndexView<Key, Item = Item>>,
+        notify: impl Fn(&mut P, &IndexArea<Key>) + Send + Sync + 'static,
+    ) -> Arc<RwLock<Option<Arc<dyn IndexView<Key, Item = Item>>>>> {
+        port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, queue_channel()));
+        port.get_view_arc()
+    }
+
+    pub fn new_arg<V: View + ?Sized + 'static, D: ChannelData<Item = V::Msg> + 'static>(
+        &mut self,
+        arg_key: ArgKey,
+        src_update: Arc<dyn UpdateTask>,
+        notify: impl Fn(&mut P, &V::Msg) + Send + Sync + 'static,
+        (tx, rx): (ChannelSender<D>, ChannelReceiver<D>),
+    ) -> Arc<RwLock<ProjectionArg<P, V, D>>>
+    where
+        V::Msg: Send + Sync,
+        D::IntoIter: Send + Sync + 'static,
+    {
+        self.remove_arg(&arg_key);
+
+        let arg = Arc::new(RwLock::new(ProjectionArg {
+            src: None,
+            notify: Box::new(notify),
+            proj: self.proj.clone(),
+            rx,
+            tx,
+        }));
+
+        let mut hooks = self.update_hooks.write().unwrap();
+        let idx = hooks.len();
+        hooks.push(src_update);
+        hooks.push(arg.clone());
+        self.keepalive.insert(arg_key, (idx, arg.clone()));
+
+        arg
+    }
+
+    pub fn remove_arg(&mut self, arg_key: &ArgKey) {
+        let mut hooks = self.update_hooks.write().unwrap();
+        if let Some((idx, _arg)) = self.keepalive.remove(arg_key) {
+            hooks.remove(idx);
+            hooks.remove(idx);
+            for (_, (j, _)) in self.keepalive.iter_mut() {
+                if *j > idx {
+                    *j -= 2;
+                }
+            }
+        }
+    }
+}
+
+/// Special Observer which can access the state of the projection on notify
+/// also handles the reset()
+pub struct ProjectionArg<P, V, D>
+where
+    P: Send + Sync + 'static,
+    V: View + ?Sized,
+    D: ChannelData<Item = V::Msg>,
+    D::IntoIter: Send + Sync,
+{
+    src: Option<Arc<V>>,
+    notify: Box<dyn Fn(&mut P, &V::Msg) + Send + Sync + 'static>,
+    proj: Arc<RwLock<Weak<RwLock<P>>>>,
+    rx: ChannelReceiver<D>,
+    tx: ChannelSender<D>,
+}
+
+impl<P, V, D> UpdateTask for ProjectionArg<P, V, D>
+where
+    P: Send + Sync + 'static,
+    V: View + ?Sized,
+    D: ChannelData<Item = V::Msg>,
+    D::IntoIter: Send + Sync,
+{
+    fn update(&self) {
+        if let Some(p) = self.proj.read().unwrap().upgrade() {
+            if let Some(data) = self.rx.try_recv() {
+                for msg in data {
+                    //eprintln!("proj update {:?}", msg);
+                    (self.notify)(&mut *p.write().unwrap(), &msg);
+                }
+            }
+        } else {
+            //eprintln!("proj update: upgrade fail");
+        }
+    }
+}
+
+impl<P, V, D> UpdateTask for RwLock<ProjectionArg<P, V, D>>
+where
+    P: Send + Sync + 'static,
+    V: View + ?Sized,
+    D: ChannelData<Item = V::Msg>,
+    D::IntoIter: Send + Sync,
+{
+    fn update(&self) {
+        self.read().unwrap().update();
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<P, Item, D> Observer<dyn SingletonView<Item = Item>>
+    for ProjectionArg<P, dyn SingletonView<Item = Item>, D>
+where
+    P: Send + Sync + 'static,
+    D: ChannelData<Item = ()>,
+    D::IntoIter: Send + Sync,
+{
+    fn reset(&mut self, new_src: Option<Arc<dyn SingletonView<Item = Item>>>) {
+        self.src = new_src;
+        self.notify(&());
+    }
+
+    fn notify(&mut self, msg: &()) {
+        self.tx.send(msg.clone());
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<P, Item, D> Observer<dyn SequenceView<Item = Item>>
+    for ProjectionArg<P, dyn SequenceView<Item = Item>, D>
+where
+    P: Send + Sync + 'static,
+    D: ChannelData<Item = usize>,
+    D::IntoIter: Send + Sync,
+{
+    fn reset(&mut self, new_src: Option<Arc<dyn SequenceView<Item = Item>>>) {
+        let old_len = self.src.len().unwrap_or(0);
+        self.src = new_src;
+        let new_len = self.src.len().unwrap_or(0);
+
+        self.notify_each(0..max(old_len, new_len));
+    }
+
+    fn notify(&mut self, msg: &usize) {
+        self.tx.send(*msg);
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<P, Key, Item, D> Observer<dyn IndexView<Key, Item = Item>>
+    for ProjectionArg<P, dyn IndexView<Key, Item = Item>, D>
+where
+    P: Send + Sync + 'static,
+    Key: Clone + Send + Sync,
+    D: ChannelData<Item = IndexArea<Key>>,
+    D::IntoIter: Send + Sync,
+{
+    fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
+        let old_area = self.src.area();
+        self.src = new_src;
+
+        self.notify(&old_area);
+        self.notify(&self.src.area())
+    }
+
+    fn notify(&mut self, msg: &IndexArea<Key>) {
+        self.tx.send(msg.clone());
+    }
+}
diff --git a/src/projection/seq2idx.rs b/src/projection/seq2idx.rs
new file mode 100644
index 0000000..22cc00c
--- /dev/null
+++ b/src/projection/seq2idx.rs
@@ -0,0 +1,101 @@
+use {
+    crate::{
+        view::{
+            InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
+            grid::GridView,
+            index::{IndexArea, IndexView},
+            sequence::SequenceView,
+        }
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+/// Transforms a SequenceView into IndexView<usize>
+pub struct Sequence2Index<SrcView>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+{
+    src_view: Option<Arc<SrcView>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<usize, Item = SrcView::Item>>>>,
+}
+
+impl<SrcView> Sequence2Index<SrcView>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+{
+    pub fn new(
+        port: InnerViewPort<dyn IndexView<usize, Item = SrcView::Item>>,
+    ) -> Arc<RwLock<Self>> {
+        let s2i = Arc::new(RwLock::new(Sequence2Index {
+            src_view: None,
+            cast: port.get_broadcast(),
+        }));
+        port.set_view(Some(s2i.clone()));
+        s2i
+    }
+}
+
+impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
+    pub fn to_index(&self) -> OuterViewPort<dyn IndexView<usize, Item = Item>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+        self.add_observer(Sequence2Index::new(port.inner()));
+        port.into_outer()
+    }
+
+    pub fn to_grid_horizontal(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
+        self.to_index().to_grid_horizontal()
+    }
+
+    pub fn to_grid_vertical(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
+        self.to_index().to_grid_vertical()
+    }
+}
+
+impl<SrcView> View for Sequence2Index<SrcView>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+{
+    type Msg = IndexArea<usize>;
+}
+
+impl<SrcView> IndexView<usize> for Sequence2Index<SrcView>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+{
+    type Item = SrcView::Item;
+
+    fn get(&self, key: &usize) -> Option<Self::Item> {
+        self.src_view.get(key)
+    }
+
+    fn area(&self) -> IndexArea<usize> {
+        if let Some(len) = self.src_view.len() {
+            if len > 0 {
+                IndexArea::Range(0..=len - 1)
+            } else {
+                IndexArea::Empty
+            }
+        } else {
+            IndexArea::Full
+        }
+    }
+}
+
+impl<SrcView> Observer<SrcView> for Sequence2Index<SrcView>
+where
+    SrcView: SequenceView + ?Sized + 'static,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        let old_area = self.area();
+        self.src_view = view;
+
+        self.cast.notify(&old_area);
+        self.cast.notify(&self.area());
+    }
+
+    fn notify(&mut self, idx: &usize) {
+        self.cast.notify(&IndexArea::Set(vec![*idx]));
+    }
+}
diff --git a/src/projection/sgl2idx.rs b/src/projection/sgl2idx.rs
new file mode 100644
index 0000000..715b268
--- /dev/null
+++ b/src/projection/sgl2idx.rs
@@ -0,0 +1,93 @@
+use {
+    crate::{
+        view::{
+            Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
+            grid::GridView,
+            index::{IndexArea, IndexView},
+            singleton::SingletonView,
+        }
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
+    pub fn to_index(&self) -> OuterViewPort<dyn IndexView<(), Item = Item>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let map = Arc::new(RwLock::new(Singleton2Index {
+            src_view: None,
+            cast: port.inner().get_broadcast(),
+        }));
+
+        self.add_observer(map.clone());
+        port.inner().set_view(Some(map));
+        port.into_outer()
+    }
+
+    pub fn to_grid(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
+        self.to_index().map_key(
+            |_msg: &()| cgmath::Point2::new(0, 0),
+            |pt| {
+                if pt.x == 0 && pt.y == 0 {
+                    Some(())
+                } else {
+                    None
+                }
+            },
+        )
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct Singleton2Index<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    src_view: Option<Arc<SrcView>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<(), Item = SrcView::Item>>>>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView> View for Singleton2Index<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    type Msg = IndexArea<()>;
+}
+
+impl<SrcView> IndexView<()> for Singleton2Index<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    type Item = SrcView::Item;
+
+    fn area(&self) -> IndexArea<()> {
+        IndexArea::Set(vec![ () ])
+    }
+
+    fn get(&self, _msg: &()) -> Option<Self::Item> {
+        Some(self.src_view.as_ref().unwrap().get())
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView> Observer<SrcView> for Singleton2Index<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        self.src_view = view;
+        self.cast.notify(&IndexArea::Set(vec![ () ]));
+    }
+
+    fn notify(&mut self, _: &()) {
+        self.cast.notify(&IndexArea::Set(vec![ () ]));
+    }
+}
diff --git a/src/projection/sgl2seq.rs b/src/projection/sgl2seq.rs
new file mode 100644
index 0000000..be87680
--- /dev/null
+++ b/src/projection/sgl2seq.rs
@@ -0,0 +1,83 @@
+use {
+    crate::{
+        view::{
+            Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
+            sequence::{SequenceView},
+            singleton::SingletonView,
+        },
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
+    pub fn to_sequence(&self) -> OuterViewPort<dyn SequenceView<Item = Item>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let map = Arc::new(RwLock::new(Singleton2Sequence {
+            src_view: None,
+            cast: port.inner().get_broadcast(),
+        }));
+
+        self.add_observer(map.clone());
+        port.inner().set_view(Some(map));
+        port.into_outer()
+    }    
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct Singleton2Sequence<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    src_view: Option<Arc<SrcView>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = SrcView::Item>>>>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView> View for Singleton2Sequence<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    type Msg = usize;
+}
+
+impl<SrcView> SequenceView for Singleton2Sequence<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    type Item = SrcView::Item;
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        if *idx == 0 {
+            Some(self.src_view.as_ref()?.get())
+        } else {
+            None
+        }
+    }
+
+    fn len(&self) -> Option<usize> {
+        Some(if self.src_view.is_some() { 1 } else { 0 })
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<SrcView> Observer<SrcView> for Singleton2Sequence<SrcView>
+where
+    SrcView: SingletonView + ?Sized,
+{
+    fn reset(&mut self, view: Option<Arc<SrcView>>) {
+        self.src_view = view;
+        self.cast.notify(&0);
+    }
+
+    fn notify(&mut self, _: &()) {
+        self.cast.notify(&0);
+    }
+}
diff --git a/src/projection/vec2bin.rs b/src/projection/vec2bin.rs
new file mode 100644
index 0000000..d578cd3
--- /dev/null
+++ b/src/projection/vec2bin.rs
@@ -0,0 +1,85 @@
+use {
+    crate::{
+        view::{
+            Observer, OuterViewPort,
+        },
+        buffer::{
+            vec::VecDiff,
+        }
+    },
+    serde::Serialize,
+    std::sync::RwLock,
+    std::{io::Write, sync::Arc},
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+/// Serialization Observer for `Vec`
+pub struct VecBinWriter<T, W>
+where
+    T: Clone + Send + Sync + 'static,
+    W: Write + Send + Sync,
+{
+    data: Option<Arc<RwLock<Vec<T>>>>,
+    out: RwLock<W>,
+}
+
+impl<T> OuterViewPort<RwLock<Vec<T>>>
+where
+    T: Clone + Serialize + Send + Sync + 'static,
+{
+    pub fn serialize_bin<W: Write + Send + Sync + 'static>(
+        &self,
+        out: W,
+    ) -> Arc<RwLock<VecBinWriter<T, W>>> {
+        let writer = Arc::new(RwLock::new(VecBinWriter {
+            data: None,
+            out: RwLock::new(out),
+        }));
+        self.add_observer(writer.clone());
+        writer
+    }
+}
+
+impl<T, W> Observer<RwLock<Vec<T>>> for VecBinWriter<T, W>
+where
+    T: Clone + Serialize + Send + Sync + 'static,
+    W: Write + Send + Sync,
+{
+    fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
+        self.data = view;
+        let mut out = self.out.write().unwrap();
+
+        out.write(
+            &bincode::serialized_size(&VecDiff::<T>::Clear)
+                .unwrap()
+                .to_le_bytes(),
+        )
+        .expect("");
+        out.write(&bincode::serialize(&VecDiff::<T>::Clear).unwrap())
+            .expect("");
+
+        if let Some(data) = self.data.as_ref() {
+            for x in data.read().unwrap().iter() {
+                out.write(
+                    &bincode::serialized_size(&VecDiff::Push(x))
+                        .unwrap()
+                        .to_le_bytes(),
+                )
+                .expect("");
+                out.write(&bincode::serialize(&VecDiff::Push(x)).unwrap())
+                    .expect("");
+            }
+        }
+
+        out.flush().expect("");
+    }
+
+    fn notify(&mut self, diff: &VecDiff<T>) {
+        let mut out = self.out.write().unwrap();
+        out.write(&bincode::serialized_size(diff).unwrap().to_le_bytes())
+            .expect("");
+        out.write(&bincode::serialize(diff).unwrap()).expect("");
+        out.flush().expect("");
+    }
+}
diff --git a/src/projection/vec2json.rs b/src/projection/vec2json.rs
new file mode 100644
index 0000000..b3fb130
--- /dev/null
+++ b/src/projection/vec2json.rs
@@ -0,0 +1,110 @@
+use {
+    crate::{
+        view::{Observer, OuterViewPort},
+        buffer::vec::{VecBuffer, VecDiff},
+    },
+    async_std::{
+        io::{Read, ReadExt},
+        stream::StreamExt,
+    },
+    serde::{de::DeserializeOwned, Serialize},
+    std::sync::RwLock,
+    std::{io::Write, sync::Arc},
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct VecJsonWriter<T, W>
+where
+    T: Clone + Send + Sync + 'static,
+    W: Write + Send + Sync,
+{
+    data: Option<Arc<RwLock<Vec<T>>>>,
+    out: RwLock<W>,
+}
+
+impl<T> OuterViewPort<RwLock<Vec<T>>>
+where
+    T: Clone + Serialize + Send + Sync + 'static,
+{
+    pub fn serialize_json<W: Write + Send + Sync + 'static>(
+        &self,
+        out: W,
+    ) -> Arc<RwLock<VecJsonWriter<T, W>>> {
+        let writer = Arc::new(RwLock::new(VecJsonWriter {
+            data: None,
+            out: RwLock::new(out),
+        }));
+        self.add_observer(writer.clone());
+        writer
+    }
+}
+
+impl<T, W> Observer<RwLock<Vec<T>>> for VecJsonWriter<T, W>
+where
+    T: Clone + Serialize + Send + Sync + 'static,
+    W: Write + Send + Sync,
+{
+    fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
+        self.data = view;
+
+        self.out
+            .write()
+            .unwrap()
+            .write(
+                &serde_json::to_string(&VecDiff::<T>::Clear)
+                    .unwrap()
+                    .as_bytes(),
+            )
+            .expect("");
+        self.out.write().unwrap().write(b"\n").expect("");
+
+        if let Some(data) = self.data.as_ref() {
+            for x in data.read().unwrap().iter() {
+                self.out
+                    .write()
+                    .unwrap()
+                    .write(&serde_json::to_string(&VecDiff::Push(x)).unwrap().as_bytes())
+                    .expect("");
+                self.out.write().unwrap().write(b"\n").expect("");
+            }
+        }
+
+        self.out.write().unwrap().flush().expect("");
+    }
+
+    fn notify(&mut self, diff: &VecDiff<T>) {
+        self.out
+            .write()
+            .unwrap()
+            .write(serde_json::to_string(diff).unwrap().as_bytes())
+            .expect("");
+        self.out.write().unwrap().write(b"\n").expect("");
+        self.out.write().unwrap().flush().expect("");
+    }
+}
+
+impl<T> VecBuffer<T>
+where
+    T: DeserializeOwned + Clone + Send + Sync + 'static,
+{
+    pub async fn from_json<R: Read + async_std::io::Read + Unpin>(&mut self, read: R) {
+        let mut bytes = read.bytes();
+        let mut s = String::new();
+        while let Some(Ok(b)) = bytes.next().await {
+            match b {
+                b'\n' => {
+                    if s.len() > 0 {
+                        let diff =
+                            serde_json::from_str::<VecDiff<T>>(&s).expect("error parsing json");
+                        self.apply_diff(diff);
+                        s.clear();
+                    }
+                }
+                c => {
+                    s.push(c as char);
+                }
+            }
+        }
+    }
+}
diff --git a/src/projection/vec2seq.rs b/src/projection/vec2seq.rs
new file mode 100644
index 0000000..ca11e53
--- /dev/null
+++ b/src/projection/vec2seq.rs
@@ -0,0 +1,118 @@
+use {
+    crate::{
+        view::{
+            InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
+            sequence::SequenceView,
+        },
+        buffer::vec::VecDiff,
+    },
+    std::sync::Arc,
+    std::sync::RwLock,
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+/// Adapter View implementing `Sequence` for `Vec`
+pub struct VecSequence<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    cur_len: usize,
+    data: Option<Arc<RwLock<Vec<T>>>>,
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
+}
+
+impl<T> VecSequence<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    pub fn new(port: InnerViewPort<dyn SequenceView<Item = T>>) -> Arc<RwLock<Self>> {
+        let seq = Arc::new(RwLock::new(VecSequence {
+            cur_len: 0,
+            data: None,
+            cast: port.get_broadcast(),
+        }));
+        port.set_view(Some(seq.clone()));
+        seq
+    }
+}
+
+impl<T> Observer<RwLock<Vec<T>>> for VecSequence<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
+        let old_len = self.cur_len;
+        self.data = view;
+        let new_len = if let Some(data) = self.data.as_ref() {
+            data.read().unwrap().len()
+        } else {
+            0
+        };
+
+        self.cur_len = new_len;
+        self.cast.notify_each(0..std::cmp::max(old_len, new_len));
+    }
+
+    fn notify(&mut self, diff: &VecDiff<T>) {
+        match diff {
+            VecDiff::Clear => {
+                self.cast.notify_each(0..self.cur_len);
+                self.cur_len = 0
+            }
+            VecDiff::Push(_) => {
+                self.cast.notify(&self.cur_len);
+                self.cur_len += 1;
+            }
+            VecDiff::Remove(idx) => {
+                self.cast.notify_each(*idx..self.cur_len);
+                self.cur_len -= 1;
+            }
+            VecDiff::Insert { idx, val: _ } => {
+                self.cur_len += 1;
+                self.cast.notify_each(*idx..self.cur_len);
+            }
+            VecDiff::Update { idx, val: _ } => {
+                self.cast.notify(&idx);
+            }
+        }
+    }
+}
+
+impl<T> View for VecSequence<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Msg = usize;
+}
+
+impl<T> SequenceView for VecSequence<T>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    type Item = T;
+
+    fn get(&self, idx: &usize) -> Option<T> {
+        self.data.as_ref()?.read().unwrap().get(*idx).cloned()
+    }
+
+    fn len(&self) -> Option<usize> {
+        Some(self.cur_len)
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<T> OuterViewPort<RwLock<Vec<T>>>
+where
+    T: Clone + Send + Sync + 'static,
+{
+    pub fn to_sequence(&self) -> OuterViewPort<dyn SequenceView<Item = T>> {
+        let port = ViewPort::new();
+        port.add_update_hook(Arc::new(self.0.clone()));
+
+        let vec_seq = VecSequence::new(port.inner());
+        self.add_observer(vec_seq.clone());
+        port.into_outer()
+    }
+}
diff --git a/src/view/channel.rs b/src/view/channel.rs
new file mode 100644
index 0000000..9253bf4
--- /dev/null
+++ b/src/view/channel.rs
@@ -0,0 +1,222 @@
+use {
+    crate::view::{View, Observer},
+    async_std::stream::Stream,
+    core::{
+        pin::Pin,
+        task::{Context, Poll, Waker},
+    },
+    std::{
+        collections::HashSet,
+        hash::Hash,
+        sync::{Arc, Mutex},
+    },
+};
+
+                    /*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                  Traits
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+pub trait ChannelData: Default + IntoIterator + Send + Sync {
+    fn channel_insert(&mut self, x: Self::Item);
+}
+
+                    /*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+               Queue Channel
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+impl<T> ChannelData for Vec<T>
+where
+    T: Send + Sync,
+{
+    fn channel_insert(&mut self, x: T) {
+        self.push(x);
+    }
+}
+
+/*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                 Set Channel
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+impl<T> ChannelData for HashSet<T>
+where
+    T: Eq + Hash + Send + Sync,
+{
+    fn channel_insert(&mut self, x: T) {
+        self.insert(x);
+    }
+}
+
+/*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+             Singleton Channel
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+impl<T> ChannelData for Option<T>
+where
+    T: Send + Sync,
+{
+    fn channel_insert(&mut self, x: T) {
+        *self = Some(x);
+    }
+}
+
+/*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                  Channel
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+struct ChannelState<Data: ChannelData> {
+    send_buf: Option<Data>,
+    recv_iter: Option<Data::IntoIter>,
+    num_senders: usize,
+    waker: Option<Waker>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct ChannelSender<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
+pub struct ChannelReceiver<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Data: ChannelData> ChannelSender<Data>
+where
+    Data::IntoIter: Send + Sync,
+{
+    pub fn send(&self, msg: Data::Item) {
+        let mut state = self.0.lock().unwrap();
+
+        if state.send_buf.is_none() {
+            state.send_buf = Some(Data::default());
+        }
+
+        state.send_buf.as_mut().unwrap().channel_insert(msg);
+
+        if let Some(waker) = state.waker.take() {
+            waker.wake();
+        }
+    }
+}
+
+impl<V: View + ?Sized, Data: ChannelData<Item = V::Msg>> Observer<V> for ChannelSender<Data>
+where
+    V::Msg: Clone,
+    Data::IntoIter: Send + Sync,
+{
+    fn notify(&mut self, msg: &V::Msg) {
+        self.send(msg.clone());
+    }
+}
+
+impl<Data: ChannelData> Clone for ChannelSender<Data> {
+    fn clone(&self) -> Self {
+        self.0.lock().unwrap().num_senders += 1;
+        ChannelSender(self.0.clone())
+    }
+}
+
+impl<Data: ChannelData> Drop for ChannelSender<Data> {
+    fn drop(&mut self) {
+        let mut state = self.0.lock().unwrap();
+        state.num_senders -= 1;
+        if let Some(waker) = state.waker.take() {
+            waker.wake();
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Data: ChannelData> ChannelReceiver<Data> {
+    pub async fn recv(&self) -> Option<Data> {
+        ChannelRead(self.0.clone()).await
+    }
+
+    pub fn try_recv(&self) -> Option<Data> {
+        let mut state = self.0.lock().unwrap();
+        if let Some(buf) = state.send_buf.take() {
+            Some(buf)
+        } else {
+            None
+        }
+    }
+}
+
+struct ChannelRead<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
+impl<Data: ChannelData> std::future::Future for ChannelRead<Data> {
+    type Output = Option<Data>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        let mut state = self.0.lock().unwrap();
+        if let Some(buf) = state.send_buf.take() {
+            Poll::Ready(Some(buf))
+        } else if state.num_senders == 0 {
+            Poll::Ready(None)
+        } else {
+            state.waker = Some(cx.waker().clone());
+            Poll::Pending
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Data: ChannelData> Stream for ChannelReceiver<Data> {
+    type Item = Data::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut state = self.0.lock().unwrap();
+
+        if let Some(recv_iter) = state.recv_iter.as_mut() {
+            if let Some(val) = recv_iter.next() {
+                return Poll::Ready(Some(val));
+            } else {
+                state.recv_iter = None
+            }
+        }
+
+        if let Some(send_buf) = state.send_buf.take() {
+            state.recv_iter = Some(send_buf.into_iter());
+            // recv_iter.next() is guaranteed to be Some(x)
+            Poll::Ready(state.recv_iter.as_mut().unwrap().next())
+        } else if state.num_senders == 0 {
+            Poll::Ready(None)
+        } else {
+            state.waker = Some(cx.waker().clone());
+            Poll::Pending
+        }
+    }
+}
+
+/*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+             Factory Functions
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+pub fn channel<Data: ChannelData>() -> (ChannelSender<Data>, ChannelReceiver<Data>) {
+    let state = Arc::new(Mutex::new(ChannelState {
+        send_buf: None,
+        recv_iter: None,
+        num_senders: 1,
+        waker: None,
+    }));
+
+    (ChannelSender(state.clone()), ChannelReceiver(state))
+}
+
+pub fn set_channel<T: Eq + Hash + Send + Sync>(
+) -> (ChannelSender<HashSet<T>>, ChannelReceiver<HashSet<T>>) {
+    channel::<HashSet<T>>()
+}
+
+pub fn queue_channel<T: Send + Sync>() -> (ChannelSender<Vec<T>>, ChannelReceiver<Vec<T>>) {
+    channel::<Vec<T>>()
+}
+
+pub fn singleton_channel<T: Send + Sync>() -> (ChannelSender<Option<T>>, ChannelReceiver<Option<T>>)
+{
+    channel::<Option<T>>()
+}
diff --git a/src/view/grid/mod.rs b/src/view/grid/mod.rs
new file mode 100644
index 0000000..b64f8b1
--- /dev/null
+++ b/src/view/grid/mod.rs
@@ -0,0 +1,71 @@
+use {
+    crate::view::index::{IndexArea, IndexView},
+    cgmath::Point2,
+    std::{
+        cmp::{max, min},
+        ops::RangeInclusive,
+    },
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub trait GridView = IndexView<Point2<i16>>;
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub mod window_iterator;
+pub use window_iterator::GridWindowIterator;
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl IndexArea<Point2<i16>> {
+    // todo: this is not perfect (e.g. diagonals are inefficient)
+    pub fn iter(&self) -> GridWindowIterator {
+        GridWindowIterator::from(self.range())
+    }
+
+    pub fn range(&self) -> RangeInclusive<Point2<i16>> {
+        match self {
+            IndexArea::Empty => Point2::new(0, 0)..=Point2::new(-1, -1),
+            IndexArea::Full => panic!("range from full grid area"),
+            IndexArea::Set(v) => {
+                Point2::new(
+                    v.iter().map(|p| p.x).min().unwrap_or(i16::MAX),
+                    v.iter().map(|p| p.y).min().unwrap_or(i16::MAX),
+                )
+                    ..=Point2::new(
+                        v.iter().map(|p| p.x).max().unwrap_or(i16::MIN),
+                        v.iter().map(|p| p.y).max().unwrap_or(i16::MIN),
+                    )
+            }
+            IndexArea::Range(r) => r.clone(),
+        }
+    }
+
+    pub fn union(self, other: IndexArea<Point2<i16>>) -> IndexArea<Point2<i16>> {
+        match (self, other) {
+            (IndexArea::Empty, a) | (a, IndexArea::Empty) => a,
+
+            (IndexArea::Full, _) | (_, IndexArea::Full) => IndexArea::Full,
+
+            (IndexArea::Set(mut va), IndexArea::Set(vb)) => {
+                va.extend(vb.into_iter());
+                IndexArea::Set(va)
+            }
+
+            (IndexArea::Range(r), IndexArea::Set(mut v))
+            | (IndexArea::Set(mut v), IndexArea::Range(r)) => {
+                v.extend(GridWindowIterator::from(r));
+                IndexArea::Set(v)
+            }
+
+            (IndexArea::Range(ra), IndexArea::Range(rb)) => IndexArea::Range(
+                Point2::new(
+                    min(ra.start().x, rb.start().x),
+                    min(ra.start().y, rb.start().y),
+                )
+                    ..=Point2::new(max(ra.end().x, rb.end().x), max(ra.end().y, rb.end().y)),
+            ),
+        }
+    }
+}
diff --git a/src/view/grid/window_iterator.rs b/src/view/grid/window_iterator.rs
new file mode 100644
index 0000000..831456d
--- /dev/null
+++ b/src/view/grid/window_iterator.rs
@@ -0,0 +1,58 @@
+use {
+    cgmath::Point2,
+    std::ops::{Range, RangeInclusive},
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct GridWindowIterator {
+    next: Point2<i16>,
+    range: Range<Point2<i16>>,
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl From<Range<Point2<i16>>> for GridWindowIterator {
+    fn from(range: Range<Point2<i16>>) -> Self {
+        GridWindowIterator {
+            next: range.start,
+            range,
+        }
+    }
+}
+
+impl From<RangeInclusive<Point2<i16>>> for GridWindowIterator {
+    fn from(range: RangeInclusive<Point2<i16>>) -> Self {
+        GridWindowIterator {
+            next: *range.start(),
+            range: *range.start()..Point2::new(range.end().x + 1, range.end().y + 1),
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl Iterator for GridWindowIterator {
+    type Item = Point2<i16>;
+
+    fn next(&mut self) -> Option<Point2<i16>> {
+        if self.next.y < self.range.end.y {
+            if self.next.x < self.range.end.x {
+                let next = self.next;
+
+                if self.next.x + 1 < self.range.end.x {
+                    self.next.x += 1;
+                } else {
+                    self.next.x = self.range.start.x;
+                    self.next.y += 1;
+                }
+
+                Some(next)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    }
+}
diff --git a/src/view/index/mod.rs b/src/view/index/mod.rs
new file mode 100644
index 0000000..95f0acc
--- /dev/null
+++ b/src/view/index/mod.rs
@@ -0,0 +1,126 @@
+use {
+    crate::view::View,
+    std::sync::RwLock,
+    std::{
+        ops::{Deref, RangeInclusive},
+        sync::Arc,
+    },
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[derive(Clone)]
+pub enum IndexArea<Key> {
+    Empty,
+    Full,
+    Set(Vec<Key>),
+    Range(RangeInclusive<Key>),
+    //Procedural(Arc<dyn Fn() -> Box<dyn Iterator<Item = Key>>>)
+}
+
+impl<Key> IndexArea<Key> {
+    pub fn map<T>(&self, f: impl Fn(&Key) -> T) -> IndexArea<T> {
+        match self {
+            IndexArea::Empty => IndexArea::Empty,
+            IndexArea::Full => IndexArea::Full,
+            IndexArea::Set(v) => IndexArea::Set(v.iter().map(&f).collect()),
+            IndexArea::Range(r) => IndexArea::Range(f(&r.start())..=f(&r.end())),
+        }
+    }
+}
+
+pub trait IndexView<Key>: View<Msg = IndexArea<Key>>
+where
+    Key: Send + Sync,
+{
+    type Item;
+
+    fn get(&self, key: &Key) -> Option<Self::Item>;
+
+    fn area(&self) -> IndexArea<Key> {
+        IndexArea::Full
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<Key, V> IndexView<Key> for RwLock<V>
+where
+    Key: Send + Sync,
+    V: IndexView<Key> + ?Sized,
+{
+    type Item = V::Item;
+
+    fn get(&self, key: &Key) -> Option<Self::Item> {
+        self.read().unwrap().get(key)
+    }
+
+    fn area(&self) -> IndexArea<Key> {
+        self.read().unwrap().area()
+    }
+}
+
+impl<Key, V> IndexView<Key> for Arc<V>
+where
+    Key: Send + Sync,
+    V: IndexView<Key> + ?Sized,
+{
+    type Item = V::Item;
+
+    fn get(&self, key: &Key) -> Option<Self::Item> {
+        self.deref().get(key)
+    }
+
+    fn area(&self) -> IndexArea<Key> {
+        self.deref().area()
+    }
+}
+
+impl<Key, V> IndexView<Key> for Option<V>
+where
+    Key: Send + Sync,
+    V: IndexView<Key>,
+{
+    type Item = V::Item;
+
+    fn get(&self, key: &Key) -> Option<Self::Item> {
+        self.as_ref()?.get(key)
+    }
+
+    fn area(&self) -> IndexArea<Key> {
+        if let Some(v) = self.as_ref() {
+            v.area()
+        } else {
+            IndexArea::Empty
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+/*
+pub trait ImplIndexView : Send + Sync {
+    type Key : Send + Sync;
+    type Value;
+
+    fn get(&self, key: &Self::Key) -> Option<Self::Value>;
+    fn area(&self) -> Option<Vec<Self::Key>> {
+        None
+    }
+}
+
+impl<V: ImplIndexView> View for V {
+    type Msg = V::Key;
+}
+
+impl<V: ImplIndexView> IndexView<V::Key> for V {
+    type Item = V::Value;
+
+    fn get(&self, key: &V::Key) -> Option<Self::Item> {
+        (self as &V).get(key)
+    }
+
+    fn area(&self) -> Option<Vec<V::Key>> {
+        (self as &V).area()
+    }
+}
+*/
diff --git a/src/view/mod.rs b/src/view/mod.rs
new file mode 100644
index 0000000..445bbbd
--- /dev/null
+++ b/src/view/mod.rs
@@ -0,0 +1,46 @@
+
+                    /*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                   View
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+pub trait View: Send + Sync {
+    /// Notification message for the observers
+    type Msg: Send + Sync;
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+use std::sync::{Arc, RwLock};
+
+impl<V: View + ?Sized> View for RwLock<V> {
+    type Msg = V::Msg;
+}
+
+impl<V: View + ?Sized> View for Arc<V> {
+    type Msg = V::Msg;
+}
+
+impl<V: View> View for Option<V> {
+    type Msg = V::Msg;
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub mod channel;
+pub mod observer;
+pub mod port;
+
+pub use {
+    channel::{queue_channel, set_channel, singleton_channel, ChannelReceiver, ChannelSender},
+    observer::{NotifyFnObserver, Observer, ObserverBroadcast, ObserverExt, ResetFnObserver},
+    port::{AnyInnerViewPort, AnyOuterViewPort, AnyViewPort, InnerViewPort, OuterViewPort, ViewPort}
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub mod singleton;
+pub mod sequence;
+pub mod index;
+pub mod grid;
+
diff --git a/src/view/observer.rs b/src/view/observer.rs
new file mode 100644
index 0000000..9e157d1
--- /dev/null
+++ b/src/view/observer.rs
@@ -0,0 +1,179 @@
+use {
+    crate::view::{
+        channel::{channel, ChannelReceiver, ChannelSender},
+        View,
+    },
+    std::sync::RwLock,
+    std::sync::{Arc, Weak},
+};
+
+                    /*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                 Observer
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+pub trait Observer<V: View + ?Sized>: Send + Sync {
+    fn reset(&mut self, _view: Option<Arc<V>>) {}
+    fn notify(&mut self, msg: &V::Msg);
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<V: View + ?Sized, O: Observer<V>> Observer<V> for Arc<RwLock<O>> {
+    fn reset(&mut self, view: Option<Arc<V>>) {
+        self.write().unwrap().reset(view);
+    }
+
+    fn notify(&mut self, msg: &V::Msg) {
+        self.write().unwrap().notify(msg);
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub trait ObserverExt<V: View + ?Sized>: Observer<V> {
+    fn notify_each(&mut self, it: impl IntoIterator<Item = V::Msg>);
+}
+
+impl<V: View + ?Sized, T: Observer<V>> ObserverExt<V> for T {
+    fn notify_each(&mut self, it: impl IntoIterator<Item = V::Msg>) {
+        for msg in it {
+            self.notify(&msg);
+        }
+    }
+}
+
+                    /*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                 Broadcast
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+pub struct ObserverBroadcast<V: View + ?Sized>
+where
+    V::Msg: Send + Sync,
+{
+    rx: ChannelReceiver<Vec<V::Msg>>,
+    tx: ChannelSender<Vec<V::Msg>>,
+    observers: Vec<Weak<RwLock<dyn Observer<V>>>>,
+}
+
+impl<V: View + ?Sized> ObserverBroadcast<V>
+where
+    V::Msg: Clone + Send + Sync,
+{
+    pub fn new() -> Self {
+        let (tx, rx) = channel::<Vec<V::Msg>>();
+        ObserverBroadcast {
+            rx,
+            tx,
+            observers: Vec::new(),
+        }
+    }
+
+    pub fn add_observer(&mut self, obs: Weak<RwLock<dyn Observer<V>>>) {
+        self.cleanup();
+        self.observers.push(obs);
+    }
+
+    fn cleanup(&mut self) {
+        self.observers.retain(|o| o.strong_count() > 0);
+    }
+
+    fn iter(&self) -> impl Iterator<Item = Arc<RwLock<dyn Observer<V>>>> + '_ {
+        self.observers.iter().filter_map(|o| o.upgrade())
+    }
+
+    pub fn update(&self) {
+        if let Some(msg_vec) = self.rx.try_recv() {
+            for msg in msg_vec {
+                for o in self.iter() {
+                    o.write().unwrap().notify(&msg);
+                }
+            }
+        }
+    }
+}
+
+impl<V: View + ?Sized> Observer<V> for ObserverBroadcast<V>
+where
+    V::Msg: Clone,
+{
+    fn reset(&mut self, view: Option<Arc<V>>) {
+        for o in self.iter() {
+            o.write().unwrap().reset(view.clone());
+        }
+    }
+
+    fn notify(&mut self, msg: &V::Msg) {
+        self.tx.send(msg.clone());
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct NotifyFnObserver<V, F>
+where
+    V: View + ?Sized,
+    F: Fn(&V::Msg) + Send + Sync,
+{
+    f: F,
+    _phantom: std::marker::PhantomData<V>,
+}
+
+impl<V, F> NotifyFnObserver<V, F>
+where
+    V: View + ?Sized,
+    F: Fn(&V::Msg) + Send + Sync,
+{
+    pub fn new(f: F) -> Self {
+        NotifyFnObserver {
+            f,
+            _phantom: std::marker::PhantomData,
+        }
+    }
+}
+
+impl<V, F> Observer<V> for NotifyFnObserver<V, F>
+where
+    V: View + ?Sized,
+    F: Fn(&V::Msg) + Send + Sync,
+{
+    fn notify(&mut self, msg: &V::Msg) {
+        (self.f)(msg);
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct ResetFnObserver<V, F>
+where
+    V: View + ?Sized,
+    F: Fn(Option<Arc<V>>) + Send + Sync,
+{
+    f: F,
+    _phantom: std::marker::PhantomData<V>,
+}
+
+impl<V, F> ResetFnObserver<V, F>
+where
+    V: View + ?Sized,
+    F: Fn(Option<Arc<V>>) + Send + Sync,
+{
+    pub fn new(f: F) -> Self {
+        ResetFnObserver {
+            f,
+            _phantom: std::marker::PhantomData,
+        }
+    }
+}
+
+impl<V, F> Observer<V> for ResetFnObserver<V, F>
+where
+    V: View + ?Sized,
+    F: Fn(Option<Arc<V>>) + Send + Sync,
+{
+    fn notify(&mut self, _msg: &V::Msg) {}
+    fn reset(&mut self, view: Option<Arc<V>>) {
+        (self.f)(view);
+    }
+}
diff --git a/src/view/port.rs b/src/view/port.rs
new file mode 100644
index 0000000..932be30
--- /dev/null
+++ b/src/view/port.rs
@@ -0,0 +1,340 @@
+use {
+    crate::view::{NotifyFnObserver, Observer, ObserverBroadcast, ResetFnObserver, View},
+    std::any::Any,
+    std::sync::{Arc, RwLock}
+};
+
+pub trait UpdateTask: Send + Sync {
+    fn update(&self);
+}
+
+                    /*\
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                 View Port
+<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+                    \*/
+pub struct ViewPort<V: View + ?Sized> {
+    view: Arc<RwLock<Option<Arc<V>>>>,
+    cast: Arc<RwLock<ObserverBroadcast<V>>>,
+    pub update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
+}
+
+impl<V: View + ?Sized> ViewPort<V>
+where
+    V::Msg: Clone,
+{
+    pub fn new() -> Self {
+        ViewPort {
+            view: Arc::new(RwLock::new(None)),
+            cast: Arc::new(RwLock::new(ObserverBroadcast::new())),
+            update_hooks: Arc::new(RwLock::new(Vec::new())),
+        }
+    }
+
+    pub fn with_view(view: Arc<V>) -> Self {
+        let port = ViewPort::new();
+        port.set_view(Some(view));
+        port
+    }
+
+    pub fn set_view(&self, view: Option<Arc<V>>) {
+        self.update();
+        *self.view.write().unwrap() = view.clone();
+        self.cast.write().unwrap().reset(view);
+    }
+
+    pub fn get_cast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
+        self.cast.clone()
+    }
+
+    pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) {
+        self.update();
+        self.cast
+            .write()
+            .unwrap()
+            .add_observer(Arc::downgrade(&observer));
+
+        observer
+            .write()
+            .unwrap()
+            .reset(self.view.read().unwrap().clone());
+    }
+
+    pub fn add_update_hook(&self, hook_cast: Arc<dyn UpdateTask>) {
+        self.update_hooks.write().unwrap().push(hook_cast);
+    }
+
+    pub fn inner(&self) -> InnerViewPort<V> {
+        InnerViewPort(ViewPort {
+            view: self.view.clone(),
+            cast: self.cast.clone(),
+            update_hooks: self.update_hooks.clone(),
+        })
+    }
+
+    pub fn outer(&self) -> OuterViewPort<V> {
+        OuterViewPort(ViewPort {
+            view: self.view.clone(),
+            cast: self.cast.clone(),
+            update_hooks: self.update_hooks.clone(),
+        })
+    }
+
+    pub fn into_inner(self) -> InnerViewPort<V> {
+        InnerViewPort(ViewPort {
+            view: self.view,
+            cast: self.cast,
+            update_hooks: self.update_hooks,
+        })
+    }
+
+    pub fn into_outer(self) -> OuterViewPort<V> {
+        OuterViewPort(ViewPort {
+            view: self.view,
+            cast: self.cast,
+            update_hooks: self.update_hooks,
+        })
+    }
+}
+
+impl<V: View + ?Sized> UpdateTask for ViewPort<V>
+where
+    V::Msg: Clone + Send + Sync,
+{
+    fn update(&self) {
+        let v = {
+            let t = self.update_hooks.read().unwrap();
+            t.iter().cloned().collect::<Vec<_>>()
+        };
+
+        for hook in v {
+            hook.update();
+        }
+
+        self.cast.read().unwrap().update();
+    }
+}
+
+impl<V: View + ?Sized> Clone for ViewPort<V>
+where
+    V::Msg: Clone,
+{
+    fn clone(&self) -> Self {
+        ViewPort {
+            view: self.view.clone(),
+            cast: self.cast.clone(),
+            update_hooks: self.update_hooks.clone(),
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct InnerViewPort<V: View + ?Sized>(pub ViewPort<V>)
+where
+    V::Msg: Clone;
+pub struct OuterViewPort<V: View + ?Sized>(pub ViewPort<V>)
+where
+    V::Msg: Clone;
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<V: View + ?Sized> InnerViewPort<V>
+where
+    V::Msg: Clone,
+{
+    pub fn get_broadcast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
+        self.0.cast.clone()
+    }
+
+    pub fn set_view(&self, view: Option<Arc<V>>) -> Arc<RwLock<ObserverBroadcast<V>>> {
+        self.0.set_view(view);
+        self.get_broadcast()
+    }
+
+    pub fn get_view(&self) -> Option<Arc<V>> {
+        self.0.view.read().unwrap().clone()
+    }
+
+    pub fn notify(&self, msg: &V::Msg) {
+        self.0.cast.write().unwrap().notify(msg);
+    }
+}
+
+impl<V: View + ?Sized> Clone for InnerViewPort<V>
+where
+    V::Msg: Clone,
+{
+    fn clone(&self) -> Self {
+        InnerViewPort(self.0.clone())
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<V: View + ?Sized + 'static> OuterViewPort<V>
+where
+    V::Msg: Clone,
+{
+    pub fn get_view(&self) -> Option<Arc<V>> {
+        self.0.view.read().unwrap().clone()
+    }
+
+    pub fn get_view_arc(&self) -> Arc<RwLock<Option<Arc<V>>>> {
+        self.0.view.clone()
+    }
+
+    pub fn add_observer(
+        &self,
+        observer: Arc<RwLock<dyn Observer<V>>>,
+    ) -> Arc<RwLock<Option<Arc<V>>>> {
+        self.0.add_observer(observer);
+        self.get_view_arc()
+    }
+
+    pub fn add_reset_fn<F: Fn(Option<Arc<V>>) + Send + Sync + 'static>(
+        &self,
+        reset: F,
+    ) -> Arc<RwLock<ResetFnObserver<V, F>>> {
+        let obs = Arc::new(RwLock::new(ResetFnObserver::new(reset)));
+        self.add_observer(obs.clone());
+        obs
+    }
+
+    pub fn add_notify_fn<F: Fn(&V::Msg) + Send + Sync + 'static>(
+        &self,
+        notify: F,
+    ) -> Arc<RwLock<NotifyFnObserver<V, F>>> {
+        let obs = Arc::new(RwLock::new(NotifyFnObserver::new(notify)));
+        self.add_observer(obs.clone());
+        obs
+    }
+}
+
+impl<V: View + ?Sized> Clone for OuterViewPort<V>
+where
+    V::Msg: Clone,
+{
+    fn clone(&self) -> Self {
+        OuterViewPort(self.0.clone())
+    }
+}
+
+impl<V: View + ?Sized> Default for OuterViewPort<V>
+where V::Msg: Clone
+{
+    fn default() -> Self {
+        ViewPort::new().into_outer()
+    }
+}
+
+/*
+impl<V: View + ?Sized + 'static> OuterViewPort<V>
+where V::Msg: Clone {
+    pub fn into_stream<Data>(
+        self,
+        reset: impl Fn(Option<Arc<V>>, ChannelSender<Data>) + Send + Sync + 'static
+    ) -> ChannelReceiver<Data>
+    where Data: ChannelData<Item = V::Msg> + 'static,
+          Data::IntoIter: Send + Sync + 'static
+    {
+        let (s, r) = crate::core::channel::channel::<Data>();
+        self.add_observer(Arc::new(s.clone()));
+        self.add_reset_fn(
+            move |view| { reset(view, s.clone()); }
+        );
+        r
+    }
+}
+*/
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[derive(Clone)]
+pub struct AnyViewPort {
+    view: Arc<dyn Any + Send + Sync + 'static>,
+    cast: Arc<dyn Any + Send + Sync + 'static>,
+    update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
+}
+
+impl AnyViewPort {
+    pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<ViewPort<V>, AnyViewPort> {
+        match (
+            self.view.clone().downcast::<RwLock<Option<Arc<V>>>>(),
+            self.cast.clone().downcast::<RwLock<ObserverBroadcast<V>>>(),
+            self.update_hooks.clone(),
+        ) {
+            (Ok(view), Ok(cast), update_hooks) => Ok(ViewPort {
+                view,
+                cast,
+                update_hooks,
+            }),
+            _ => Err(self),
+        }
+    }
+}
+
+impl<V: View + ?Sized + 'static> From<ViewPort<V>> for AnyViewPort {
+    fn from(port: ViewPort<V>) -> Self {
+        AnyViewPort {
+            view: port.view as Arc<dyn Any + Send + Sync + 'static>,
+            cast: port.cast as Arc<dyn Any + Send + Sync + 'static>,
+            update_hooks: port.update_hooks,
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+#[derive(Clone)]
+pub struct AnyOuterViewPort(AnyViewPort);
+
+#[derive(Clone)]
+pub struct AnyInnerViewPort(AnyViewPort);
+
+impl AnyOuterViewPort {
+    pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<OuterViewPort<V>, AnyViewPort>
+    where
+        V::Msg: Clone,
+    {
+        Ok(OuterViewPort(self.0.downcast::<V>()?))
+    }
+}
+
+impl<V: View + ?Sized + 'static> From<OuterViewPort<V>> for AnyOuterViewPort
+where
+    V::Msg: Clone,
+{
+    fn from(port: OuterViewPort<V>) -> Self {
+        AnyOuterViewPort(AnyViewPort {
+            view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
+            cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
+            update_hooks: port.0.update_hooks,
+        })
+    }
+}
+
+impl AnyInnerViewPort {
+    pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<InnerViewPort<V>, AnyViewPort>
+    where
+        V::Msg: Clone,
+    {
+        Ok(InnerViewPort(self.0.downcast::<V>()?))
+    }
+}
+
+impl<V: View + ?Sized + 'static> From<InnerViewPort<V>> for AnyInnerViewPort
+where
+    V::Msg: Clone,
+{
+    fn from(port: InnerViewPort<V>) -> Self {
+        AnyInnerViewPort(AnyViewPort {
+            view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
+            cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
+            update_hooks: port.0.update_hooks,
+        })
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
diff --git a/src/view/sequence/mod.rs b/src/view/sequence/mod.rs
new file mode 100644
index 0000000..a92be84
--- /dev/null
+++ b/src/view/sequence/mod.rs
@@ -0,0 +1,87 @@
+
+use crate::view::View;
+
+pub trait SequenceView: View<Msg = usize> {
+    type Item;
+
+    fn get(&self, idx: &usize) -> Option<Self::Item>;
+    fn len(&self) -> Option<usize>;
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub trait SequenceViewExt: SequenceView {
+    fn iter<'a>(&'a self) -> SequenceViewIter<'a, Self> {
+        SequenceViewIter { view: self, cur: 0 }
+    }
+}
+
+impl<V: SequenceView + ?Sized> SequenceViewExt for V {}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+pub struct SequenceViewIter<'a, V>
+where
+    V: SequenceView + ?Sized,
+{
+    view: &'a V,
+    cur: usize,
+}
+
+impl<'a, V> Iterator for SequenceViewIter<'a, V>
+where
+    V: SequenceView + ?Sized,
+{
+    type Item = V::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let i = self.cur;
+        self.cur += 1;
+        self.view.get(&i)
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+use std::sync::RwLock;
+use std::{ops::Deref, sync::Arc};
+
+impl<V: SequenceView + ?Sized> SequenceView for RwLock<V> {
+    type Item = V::Item;
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        self.read().unwrap().get(idx)
+    }
+
+    fn len(&self) -> Option<usize> {
+        self.read().unwrap().len()
+    }
+}
+
+impl<V: SequenceView + ?Sized> SequenceView for Arc<V> {
+    type Item = V::Item;
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        self.deref().get(idx)
+    }
+
+    fn len(&self) -> Option<usize> {
+        self.deref().len()
+    }
+}
+
+impl<V: SequenceView> SequenceView for Option<V> {
+    type Item = V::Item;
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        (self.as_ref()? as &V).get(idx)
+    }
+
+    fn len(&self) -> Option<usize> {
+        if let Some(v) = self.as_ref() {
+            v.len()
+        } else {
+            Some(0)
+        }
+    }
+}
diff --git a/src/view/singleton/mod.rs b/src/view/singleton/mod.rs
new file mode 100644
index 0000000..f64398d
--- /dev/null
+++ b/src/view/singleton/mod.rs
@@ -0,0 +1,57 @@
+use {
+    crate::{view::View},
+    std::{ops::Deref, sync::{Arc, RwLock}},
+};
+
+// TODO: #[ImplForArc, ImplForRwLock]
+pub trait SingletonView: View<Msg = ()> {
+    type Item;
+
+    fn get(&self) -> Self::Item;
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+
+impl<V: SingletonView + ?Sized> SingletonView for RwLock<V> {
+    type Item = V::Item;
+
+    fn get(&self) -> Self::Item {
+        self.read().unwrap().get()
+    }
+}
+
+impl<V: SingletonView + ?Sized> SingletonView for Arc<V> {
+    type Item = V::Item;
+
+    fn get(&self) -> Self::Item {
+        self.deref().get()
+    }
+}
+
+impl<V: SingletonView> SingletonView for Option<V>
+where
+    V::Item: Default,
+{
+    type Item = V::Item;
+
+    fn get(&self) -> Self::Item {
+        if let Some(s) = self.as_ref() {
+            s.get()
+        } else {
+            V::Item::default()
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
+/*
+impl<T> OuterViewPort<dyn SingletonView<Item = T>> {
+    pub fn get(&self) -> T {
+        self.get_view().unrwap().read().unwrap().get();
+    }
+
+    pub fn map<U: Send + Sync + 'static>(&self, f: impl Fn(T) -> U) -> OuterViewPort<dyn SingletonView<Item = U>> {
+
+    }
+}
+ */
diff --git a/src/view/view.rs b/src/view/view.rs
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/view/view.rs
@@ -0,0 +1 @@
+