-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
0 parents
commit 194a455
Showing
4 changed files
with
1,796 additions
and
0 deletions.
There are no files selected for viewing
Submodule MathJax
added at
419b0a
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.