diff --git a/src/airframe/airframe.js b/src/airframe/airframe.js
new file mode 100644
index 0000000..bcddabf
--- /dev/null
+++ b/src/airframe/airframe.js
@@ -0,0 +1,119 @@
+import easing from "./easing";
+/* eslint-disable */
+function mergeResults(results) {
+ const firstResult = results[0];
+ if (results.length < 2) {
+ return firstResult;
+ }
+ if (Array.isArray(firstResult)) {
+ // console.log("merge", results);
+ return firstResult.map((_, i) => {
+ return mergeResults(results.map(result => result[i]));
+ });
+ } else {
+ return Object.assign({}, ...results);
+ }
+}
+
+const airframe = {
+ parallel: ({ children: fns }) => {
+ return (t, ...args) => {
+ const styles = fns.map(fn => fn(t, ...args));
+ const result = mergeResults(styles);
+ return result;
+ };
+ },
+ chain: ({ children: fns, durations }) => {
+ return (t, ...args) => {
+ let style = fns[0](0, ...args);
+ let lowerDuration = 0;
+ for (let i = 0; i < fns.length; i++) {
+ const fn = fns[i];
+ const thisDuration = durations[i];
+ const upperDuration = lowerDuration + thisDuration;
+ if (lowerDuration <= t && t <= upperDuration) {
+ const innerT = (t - lowerDuration) / thisDuration;
+ style = mergeResults([style, fn(innerT, ...args)]);
+ } else if (upperDuration < t) {
+ // merge the end of previous animation
+ style = mergeResults([style, fn(1, ...args)]);
+ } else if (t < lowerDuration) {
+ // merge the start of future animation
+ style = mergeResults([fn(0, ...args), style]);
+ }
+ lowerDuration = upperDuration;
+ }
+ return style;
+ };
+ },
+ delay: () => () => ({}),
+ tween: ({ from, to, ease = easing.linear }) => (t, targets) => {
+ const style = {};
+ Object.keys(from).forEach(key => {
+ const value = from[key] + (to[key] - from[key]) * ease(t);
+ if (key === "x") {
+ style["transform"] = `translateX(${value}px)`;
+ } else {
+ style[key] = value;
+ }
+ });
+ return style;
+ }
+};
+
+/* @jsx createAnimation */
+export const Stagger = props => (t, targets) => {
+ const filter = target => !props.filter || props.filter(target);
+ const interval =
+ targets.filter(filter).length < 2
+ ? 0
+ : props.interval / (targets.filter(filter).length - 1);
+ let i = 0;
+ return targets.map(target => {
+ // console.log(target, props.filter(target));
+ if (!filter(target)) {
+ return {};
+ }
+ const animation = (
+
+
+
+ {props.children[0]}
+
+
+ );
+ i++;
+ const result = animation(t, target);
+ // console.log("Stagger Result", t, result);
+ return result;
+ });
+};
+
+export function createAnimation(type, props, ...children) {
+ const allProps = Object.assign({ children }, props);
+ if (typeof type === "string") {
+ if (window.LOG === "verbose") {
+ return (t, ...args) => {
+ console.groupCollapsed(type, t);
+ const result = airframe[type](allProps)(t, ...args);
+ console.log(result);
+ console.groupEnd();
+ return result;
+ };
+ } else {
+ return airframe[type](allProps);
+ }
+ } else {
+ if (window.LOG === "verbose") {
+ return (t, ...args) => {
+ console.groupCollapsed(type.name, t);
+ const result = type(allProps)(t, ...args);
+ console.log(result);
+ console.groupEnd();
+ return result;
+ };
+ } else {
+ return type(allProps);
+ }
+ }
+}
diff --git a/src/airframe/easing.js b/src/airframe/easing.js
new file mode 100644
index 0000000..fafdca9
--- /dev/null
+++ b/src/airframe/easing.js
@@ -0,0 +1,54 @@
+export default {
+ // no easing, no acceleration
+ linear: function(t) {
+ return t;
+ },
+ // accelerating from zero velocity
+ easeInQuad: function(t) {
+ return t * t;
+ },
+ // decelerating to zero velocity
+ easeOutQuad: function(t) {
+ return t * (2 - t);
+ },
+ // acceleration until halfway, then deceleration
+ easeInOutQuad: function(t) {
+ return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
+ },
+ // accelerating from zero velocity
+ easeInCubic: function(t) {
+ return t * t * t;
+ },
+ // decelerating to zero velocity
+ easeOutCubic: function(t) {
+ return --t * t * t + 1;
+ },
+ // acceleration until halfway, then deceleration
+ easeInOutCubic: function(t) {
+ return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
+ },
+ // accelerating from zero velocity
+ easeInQuart: function(t) {
+ return t * t * t * t;
+ },
+ // decelerating to zero velocity
+ easeOutQuart: function(t) {
+ return 1 - --t * t * t * t;
+ },
+ // acceleration until halfway, then deceleration
+ easeInOutQuart: function(t) {
+ return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
+ },
+ // accelerating from zero velocity
+ easeInQuint: function(t) {
+ return t * t * t * t * t;
+ },
+ // decelerating to zero velocity
+ easeOutQuint: function(t) {
+ return 1 + --t * t * t * t * t;
+ },
+ // acceleration until halfway, then deceleration
+ easeInOutQuint: function(t) {
+ return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
+ }
+};
diff --git a/src/animation.js b/src/animation.js
new file mode 100644
index 0000000..f173867
--- /dev/null
+++ b/src/animation.js
@@ -0,0 +1,78 @@
+import { createAnimation, Stagger } from "./airframe/airframe";
+import easing from "./airframe/easing";
+
+/* eslint-disable */
+
+const dx = 250;
+
+/* @jsx createAnimation */
+
+// window.LOG = "verbose";
+
+const SlideToLeft = () => (
+
+);
+
+function ShrinkHeight() {
+ return (
+
+ );
+}
+
+const SlideFromRight = () => (
+
+);
+function GrowHeight() {
+ return (
+
+ );
+}
+
+function SwitchLines({ filterExit, filterEnter }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default (
+
+ line.left && !line.middle}
+ filterEnter={line => !line.left && line.middle}
+ />
+ line.middle && !line.right}
+ filterEnter={line => !line.middle && line.right}
+ />
+
+);
diff --git a/src/differ.js b/src/differ.js
index fb74815..ff6e6a0 100644
--- a/src/differ.js
+++ b/src/differ.js
@@ -83,61 +83,3 @@ export function getSlides(codes) {
.filter(line => line.middle || line.left || line.right);
});
}
-
-//
-
-function getLines(change) {
- return change.value
- .trimRight(newlineRe)
- .split(newlineRe)
- .map(content => ({
- content
- }));
-}
-
-export function tripleDiff(left, middle, right) {
- const leftDiff = diff.diffLines(left, middle);
- const rightDiff = diff.diffLines(middle, right);
-
- let x = leftDiff
- .map(change =>
- change.value
- .trimRight(newlineRe)
- .split(newlineRe)
- .map(content => ({
- content,
- left: !change.added,
- middle: !change.removed
- }))
- )
- .flat();
- // console.log(JSON.stringify(leftDiff, null, 2));
- // console.log(JSON.stringify(x, null, 2));
-
- let i = 0;
- // console.log(rightDiff);
- rightDiff.forEach(change => {
- let mx = x.filter(l => l.middle || l.right);
- if (change.added) {
- const lines = change.value
- .trimRight(newlineRe)
- .split(newlineRe)
- .map(content => ({
- content,
- left: false,
- middle: false,
- right: true
- }));
- insert(x, i, lines);
- } else if (!change.removed) {
- // console.log(change);
- for (let j = 0; j < change.count; j++) {
- mx[i + j].right = true;
- }
- }
- i += change.count;
- });
- // console.log(JSON.stringify(rightDiff, null, 2));
- // console.log(JSON.stringify(x, null, 2));
- return x;
-}
diff --git a/src/history.js b/src/history.js
index 803ed1e..92d01d7 100644
--- a/src/history.js
+++ b/src/history.js
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react";
import { getSlides } from "./differ";
import { useSpring } from "react-use";
+import Slide from "./slide";
export default function History({ commits, language }) {
const codes = commits.map(commit => commit.content);
@@ -22,7 +23,11 @@ export default function History({ commits, language }) {
}
};
});
- return
{codes[index]}
;
+ return (
+
+
+
+ );
}
function useSliderSpring(initial) {
const [target, setTarget] = useState(initial);
diff --git a/src/index.css b/src/index.css
index cee5f34..c67b788 100755
--- a/src/index.css
+++ b/src/index.css
@@ -6,6 +6,8 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+ background-color: rgb(1, 22, 39);
+ color: rgb(214, 222, 235);
}
code {
diff --git a/src/nightOwl.js b/src/nightOwl.js
new file mode 100644
index 0000000..4d61fe7
--- /dev/null
+++ b/src/nightOwl.js
@@ -0,0 +1,107 @@
+const theme = {
+ plain: {
+ color: "#d6deeb",
+ backgroundColor: "#011627"
+ },
+ styles: [
+ {
+ types: ["changed"],
+ style: {
+ color: "rgb(162, 191, 252)",
+ fontStyle: "italic"
+ }
+ },
+ {
+ types: ["deleted"],
+ style: {
+ color: "rgba(239, 83, 80, 0.56)",
+ fontStyle: "italic"
+ }
+ },
+ {
+ types: ["inserted", "attr-name"],
+ style: {
+ color: "rgb(173, 219, 103)",
+ fontStyle: "italic"
+ }
+ },
+ {
+ types: ["comment"],
+ style: {
+ color: "rgb(99, 119, 119)",
+ fontStyle: "italic"
+ }
+ },
+ {
+ types: ["string", "url"],
+ style: {
+ color: "rgb(173, 219, 103)"
+ }
+ },
+ {
+ types: ["variable"],
+ style: {
+ color: "rgb(214, 222, 235)"
+ }
+ },
+ {
+ types: ["number"],
+ style: {
+ color: "rgb(247, 140, 108)"
+ }
+ },
+ {
+ types: ["builtin", "char", "constant", "function"],
+ style: {
+ color: "rgb(130, 170, 255)"
+ }
+ },
+ {
+ // This was manually added after the auto-generation
+ // so that punctuations are not italicised
+ types: ["punctuation"],
+ style: {
+ color: "rgb(199, 146, 234)"
+ }
+ },
+ {
+ types: ["selector", "doctype"],
+ style: {
+ color: "rgb(199, 146, 234)",
+ fontStyle: "italic"
+ }
+ },
+ {
+ types: ["class-name"],
+ style: {
+ color: "rgb(255, 203, 139)"
+ }
+ },
+ {
+ types: ["tag", "operator", "keyword"],
+ style: {
+ color: "rgb(127, 219, 202)"
+ }
+ },
+ {
+ types: ["boolean"],
+ style: {
+ color: "rgb(255, 88, 116)"
+ }
+ },
+ {
+ types: ["property"],
+ style: {
+ color: "rgb(128, 203, 196)"
+ }
+ },
+ {
+ types: ["namespace"],
+ style: {
+ color: "rgb(178, 204, 214)"
+ }
+ }
+ ]
+};
+
+module.exports = theme;
diff --git a/src/slide.js b/src/slide.js
new file mode 100644
index 0000000..4863d5c
--- /dev/null
+++ b/src/slide.js
@@ -0,0 +1,34 @@
+import React from "react";
+import animation from "./animation";
+import theme from "./nightOwl";
+
+export default function Slide({ time, lines }) {
+ const styles = animation((time + 1) / 2, lines);
+ return (
+
+ {lines.map((line, i) => (
+
+ {line.tokens.map(token => {
+ const props = theme.styles.find(s => s.types.includes(token.type));
+ return {token.content};
+ })}
+
+ ))}
+
+ );
+}