mirror of
https://github.com/varkor/quiver.git
synced 2024-10-05 18:07:27 +03:00
Initial version of quiver
A graphical editor for commutative diagrams that exports to tikzcd. This version contains the following features: - 0-cell (object), 1-cell (morphism) and 2-cell (natural transformation) placement. - Repositioning cells. - Connecting cells. - MathJax labels. - Smart label alignment. - tikzcd output.
This commit is contained in:
commit
985df2c6fd
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "src/MathJax"]
|
||||
path = src/MathJax
|
||||
url = https://github.com/mathjax/MathJax.git
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 varkor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# quiver
|
||||
A graphical editor for commutative diagrams that exports to tikzcd.
|
||||
|
||||
## Features
|
||||
- An intuitive graphical interface for creating and modifying commutative diagrams.
|
||||
- Support for objects, morphisms and natural transformations.
|
||||
- tikzcd (LaTeX) export.
|
||||
- Smart label alignment.
|
1
src/MathJax
Submodule
1
src/MathJax
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 419b0a6eee7eefc0f85e47f7d4f8227ec28b8e57
|
13
src/index.html
Normal file
13
src/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>quiver</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
|
||||
<script type="text/javascript" src="ui.js"></script>
|
||||
<script type="text/javascript" async src="MathJax/MathJax.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
280
src/main.css
Normal file
280
src/main.css
Normal file
@ -0,0 +1,280 @@
|
||||
/* Root styles */
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Cell attributes. */
|
||||
--cell-size: 64px;
|
||||
/* Cell states. */
|
||||
--cell-hover: hsla(0, 0%, 0%, 0.1);
|
||||
--cell-selected: hsla(0, 0%, 0%, 0.2);
|
||||
--cell-source: hsla(0, 0%, 0%, 0.2);
|
||||
--cell-target: hsla(0, 0%, 0%, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
width: 100%; height: 100%;
|
||||
margin: 0;
|
||||
|
||||
background-color: white;
|
||||
background-size: var(--cell-size) var(--cell-size);
|
||||
}
|
||||
|
||||
/* Special elements */
|
||||
|
||||
.MathJax_SVG {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Grid interaction */
|
||||
|
||||
.insertion-point {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: var(--cell-size); height: var(--cell-size);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
background: hsla(0, 0%, 0%, 0);
|
||||
|
||||
text-align: center;
|
||||
font: 16px sans-serif;
|
||||
line-height: var(--cell-size);
|
||||
color: hsla(0, 0%, 0%, 0.4);
|
||||
}
|
||||
|
||||
.insertion-point.revealed {
|
||||
background: hsla(0, 0%, 0%, 0.1);
|
||||
}
|
||||
|
||||
.insertion-point::before {
|
||||
content: "Add vertex";
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.insertion-point.revealed::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Vertices */
|
||||
|
||||
.vertex {
|
||||
position: absolute;
|
||||
width: var(--cell-size); height: var(--cell-size);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.ui:not(.connect) .vertex {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.vertex .content {
|
||||
position: absolute;
|
||||
width: calc(var(--cell-size) / 2); height: calc(var(--cell-size) / 2);
|
||||
left: calc(var(--cell-size) / 2); top: calc(var(--cell-size) / 2);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
border-radius: 100%;
|
||||
|
||||
line-height: calc(var(--cell-size) / 2);
|
||||
text-align: center;
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* This is so explicit because of the CSS specificity rules. */
|
||||
.ui:not(.connect):not(.move) .vertex:not(.selected):not(.source):not(.target) .content:hover {
|
||||
background: var(--cell-hover);
|
||||
}
|
||||
|
||||
.vertex.source .content {
|
||||
background: var(--cell-source);
|
||||
}
|
||||
|
||||
.vertex.target .content {
|
||||
background: var(--cell-target);
|
||||
}
|
||||
|
||||
.vertex.selected .content {
|
||||
background: var(--cell-selected);
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%; top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
text-align: center;
|
||||
font-size: 26px;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.label.buffer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Edges */
|
||||
|
||||
.edge {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.edge > svg {
|
||||
position: absolute;
|
||||
left: 50%; top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* The overlay edge drawn while connecting cells. */
|
||||
.overlay {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* This is so explicit because of the CSS specificity rules. */
|
||||
.ui:not(.connect):not(.move) .edge:hover:not(.selected):not(.source):not(.target) {
|
||||
background: var(--cell-hover);
|
||||
}
|
||||
|
||||
.edge.source {
|
||||
background: var(--cell-source);
|
||||
}
|
||||
|
||||
.edge.target {
|
||||
background: var(--cell-target);
|
||||
}
|
||||
|
||||
.edge.selected {
|
||||
background: var(--cell-selected);
|
||||
}
|
||||
|
||||
.edge .label {
|
||||
font-size: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* The side panel */
|
||||
|
||||
.panel {
|
||||
position: fixed;
|
||||
width: 20%; height: 100%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
padding: 24px 16px;
|
||||
|
||||
background: hsl(0, 0%, 20%);
|
||||
|
||||
font: 14px sans-serif;
|
||||
color: hsl(0, 0%, 80%);
|
||||
}
|
||||
|
||||
.panel input[type="text"] {
|
||||
padding: 2px 4px;
|
||||
|
||||
background: hsl(0, 0%, 16%);
|
||||
border: hsl(0, 0%, 28%) solid 1px;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
|
||||
font-size: inherit;
|
||||
font-family: monospace;
|
||||
color: hsl(0, 0%, 96%);
|
||||
}
|
||||
|
||||
.panel input[type="text"]:hover {
|
||||
background: hsl(0, 0%, 18%);
|
||||
}
|
||||
|
||||
.panel input[type="text"]:focus {
|
||||
background: hsl(0, 0%, 96%);
|
||||
border-color: hsl(200, 100%, 40%);
|
||||
|
||||
color: hsl(0, 0%, 16%);
|
||||
}
|
||||
|
||||
.panel input[type="text"]:disabled {
|
||||
background: hsl(0, 0%, 20%);
|
||||
}
|
||||
|
||||
.panel .options {
|
||||
margin: 8px 0;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.panel input[type="radio"] {
|
||||
-webkit-appearance: none;
|
||||
display: inline-block;
|
||||
width: 48px; height: 48px;
|
||||
|
||||
background-color: hsl(0, 0%, 16%);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
/* We use stacked backgrounds for the background image, */
|
||||
/* to allow us to change the image directly via CSS. */
|
||||
background-size: 0%, 100%;
|
||||
border: hsl(0, 0%, 28%) solid 1px;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.panel input[type="radio"]:hover {
|
||||
background-color: hsl(0, 0%, 18%);
|
||||
}
|
||||
|
||||
.panel input[type="radio"]:checked {
|
||||
background-color: hsl(0, 0%, 96%);
|
||||
background-size: 100%, 0%;
|
||||
border-color: hsl(200, 100%, 40%);
|
||||
}
|
||||
|
||||
.panel input[type="radio"]:disabled {
|
||||
background-size: 0%, 100%;
|
||||
background-color: hsl(0, 0%, 20%);
|
||||
border-color: hsl(0, 0%, 28%);
|
||||
}
|
||||
|
||||
.panel button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: calc(100% - 8px * 2); height: 30px;
|
||||
left: 8px; bottom: 8px;
|
||||
|
||||
background: transparent;
|
||||
border: transparent solid 1px;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
|
||||
font: inherit;
|
||||
color: hsl(0, 0%, 96%);
|
||||
}
|
||||
|
||||
.panel button:hover {
|
||||
background: hsl(0, 0%, 24%);
|
||||
border-color: hsl(0, 0%, 36%);
|
||||
}
|
||||
|
||||
.panel button:active {
|
||||
background: hsl(0, 0%, 16%);
|
||||
border-color: hsl(0, 0%, 36%);
|
||||
}
|
||||
|
||||
.export {
|
||||
position: fixed;
|
||||
width: 80%; height: 100%;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
padding: 20px 24px;
|
||||
|
||||
background: hsla(0, 0%, 10%, 0.8);
|
||||
white-space: pre-wrap;
|
||||
tab-size: 4;
|
||||
|
||||
font: 16px monospace;
|
||||
color: white;
|
||||
}
|
Loading…
Reference in New Issue
Block a user