summaryrefslogtreecommitdiffstats
path: root/web_src/css/features
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web_src/css/features/codeeditor.css48
-rw-r--r--web_src/css/features/colorpicker.css47
-rw-r--r--web_src/css/features/console.css338
-rw-r--r--web_src/css/features/dropzone.css59
-rw-r--r--web_src/css/features/gitgraph.css309
-rw-r--r--web_src/css/features/heatmap.css58
-rw-r--r--web_src/css/features/imagediff.css114
-rw-r--r--web_src/css/features/projects.css108
-rw-r--r--web_src/css/features/tribute.css42
9 files changed, 1123 insertions, 0 deletions
diff --git a/web_src/css/features/codeeditor.css b/web_src/css/features/codeeditor.css
new file mode 100644
index 0000000..34a104c
--- /dev/null
+++ b/web_src/css/features/codeeditor.css
@@ -0,0 +1,48 @@
+.monaco-editor-container,
+.editor-loading.is-loading {
+ width: 100%;
+ min-height: 200px;
+ height: 90vh;
+}
+
+.edit.githook .monaco-editor-container {
+ border: 1px solid var(--color-secondary);
+ height: 70vh;
+}
+
+/* overwrite conflicting styles from fomantic */
+.monaco-editor-container .inputarea {
+ min-height: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ resize: none !important;
+ border: none !important;
+ color: transparent !important;
+ background-color: transparent !important;
+}
+
+/* these seem unthemeable */
+.monaco-scrollable-element > .scrollbar > .slider {
+ background: var(--color-primary) !important;
+}
+.monaco-scrollable-element > .scrollbar > .slider:hover {
+ background: var(--color-primary-dark-1) !important;
+}
+.monaco-scrollable-element > .scrollbar > .slider:active {
+ background: var(--color-primary-dark-2) !important;
+}
+
+/* fomantic styles destroy this element only visible on IOS, restore it */
+.monaco-editor .iPadShowKeyboard {
+ border: none !important;
+ width: 58px !important;
+ min-width: 0 !important;
+ height: 36px !important;
+ min-height: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ position: absolute !important;
+ resize: none !important;
+ overflow: hidden !important;
+ border-radius: var(--border-radius-medium) !important;
+}
diff --git a/web_src/css/features/colorpicker.css b/web_src/css/features/colorpicker.css
new file mode 100644
index 0000000..b743678
--- /dev/null
+++ b/web_src/css/features/colorpicker.css
@@ -0,0 +1,47 @@
+.js-color-picker-input {
+ display: flex;
+ position: relative;
+}
+
+.js-color-picker-input input {
+ padding-top: 8px !important;
+ padding-bottom: 8px !important;
+ padding-left: 32px !important;
+}
+
+.js-color-picker-input .preview-square {
+ position: absolute;
+ aspect-ratio: 1;
+ height: 16px;
+ left: 10px;
+ top: 50%;
+ transform: translateY(-50%);
+ border-radius: 2px;
+ background: repeating-linear-gradient(45deg, #aaa 25%, transparent 25%, transparent 75%, #aaa 75%, #aaa), repeating-linear-gradient(45deg, #aaa 25%, #fff 25%, #fff 75%, #aaa 75%, #aaa); /* stylelint-disable-line scale-unlimited/declaration-strict-value */
+ background-position: 0 0, 4px 4px;
+ background-size: 8px 8px;
+}
+
+.js-color-picker-input .preview-square::after {
+ content: "";
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border-radius: inherit;
+ background-color: currentcolor;
+}
+
+hex-color-picker {
+ width: 180px;
+ height: 120px;
+}
+
+hex-color-picker::part(hue-pointer),
+hex-color-picker::part(saturation-pointer) {
+ width: 22px;
+ height: 22px;
+}
+
+hex-color-picker::part(hue) {
+ flex-basis: 16px;
+}
diff --git a/web_src/css/features/console.css b/web_src/css/features/console.css
new file mode 100644
index 0000000..e2d3327
--- /dev/null
+++ b/web_src/css/features/console.css
@@ -0,0 +1,338 @@
+/* Based on https://github.com/buildkite/terminal-to-html/blob/697ff23bd8dc48b9d23f11f259f5256dae2455f0/assets/terminal.css */
+
+.console {
+ background: var(--color-console-bg);
+ color: var(--color-console-fg);
+ font-family: var(--fonts-monospace);
+ border-radius: var(--border-radius);
+ overflow-wrap: anywhere;
+}
+
+.console img { max-width: 100%; }
+
+.console a {
+ color: inherit;
+ text-decoration: underline;
+ text-decoration-style: dashed;
+}
+.console a:hover { color: var(--color-primary); }
+
+@keyframes blink-animation {
+ to {
+ visibility: hidden;
+ }
+}
+
+/* ansi_up colors used in actions */
+
+.ansi-black-fg { color: var(--color-ansi-black); }
+.ansi-red-fg { color: var(--color-ansi-red); }
+.ansi-green-fg { color: var(--color-ansi-green); }
+.ansi-yellow-fg { color: var(--color-ansi-yellow); }
+.ansi-blue-fg { color: var(--color-ansi-blue); }
+.ansi-magenta-fg { color: var(--color-ansi-magenta); }
+.ansi-cyan-fg { color: var(--color-ansi-cyan); }
+.ansi-white-fg { color: var(--color-ansi-white); }
+
+.ansi-bright-black-fg { color: var(--color-ansi-bright-black); }
+.ansi-bright-red-fg { color: var(--color-ansi-bright-red); }
+.ansi-bright-green-fg { color: var(--color-ansi-bright-green); }
+.ansi-bright-yellow-fg { color: var(--color-ansi-bright-yellow); }
+.ansi-bright-blue-fg { color: var(--color-ansi-bright-blue); }
+.ansi-bright-magenta-fg { color: var(--color-ansi-bright-magenta); }
+.ansi-bright-cyan-fg { color: var(--color-ansi-bright-cyan); }
+.ansi-bright-white-fg { color: var(--color-ansi-bright-white); }
+
+.ansi-black-bg { background-color: var(--color-ansi-black); }
+.ansi-red-bg { background-color: var(--color-ansi-red); }
+.ansi-green-bg { background-color: var(--color-ansi-green); }
+.ansi-yellow-bg { background-color: var(--color-ansi-yellow); }
+.ansi-blue-bg { background-color: var(--color-ansi-blue); }
+.ansi-magenta-bg { background-color: var(--color-ansi-magenta); }
+.ansi-cyan-bg { background-color: var(--color-ansi-cyan); }
+.ansi-white-bg { background-color: var(--color-ansi-white); }
+
+.ansi-bright-black-bg { background-color: var(--color-ansi-bright-black); }
+.ansi-bright-red-bg { background-color: var(--color-ansi-bright-red); }
+.ansi-bright-green-bg { background-color: var(--color-ansi-bright-green); }
+.ansi-bright-yellow-bg { background-color: var(--color-ansi-bright-yellow); }
+.ansi-bright-blue-bg { background-color: var(--color-ansi-bright-blue); }
+.ansi-bright-magenta-bg { background-color: var(--color-ansi-bright-magenta); }
+.ansi-bright-cyan-bg { background-color: var(--color-ansi-bright-cyan); }
+.ansi-bright-white-bg { background-color: var(--color-ansi-bright-white); }
+
+/* term colors used in console rendering */
+
+.term-fg2 { color: var(--color-ansi-bright-black); } /* faint (decreased intensity) - same as gray really */
+.term-fg3 { font-style: italic; } /* italic */
+.term-fg4 { text-decoration: underline; } /* underline */
+.term-fg5 { animation: blink-animation 1s steps(3, start) infinite; } /* blink */
+.term-fg9 { text-decoration: line-through; } /* crossed-out */
+
+.term-fg30 { color: var(--color-ansi-black); } /* black (but we can't use black, so a diff color) */
+.term-fg31 { color: var(--color-ansi-red); } /* red */
+.term-fg32 { color: var(--color-ansi-green); } /* green */
+.term-fg33 { color: var(--color-ansi-yellow); } /* yellow */
+.term-fg34 { color: var(--color-ansi-blue); } /* blue */
+.term-fg35 { color: var(--color-ansi-magenta); } /* magenta */
+.term-fg36 { color: var(--color-ansi-cyan); } /* cyan */
+
+/* high intense colors */
+.term-fgi1 { color: var(--color-ansi-bright-green); }
+.term-fgi90 { color: var(--color-ansi-bright-black); } /* grey */
+.term-fgi91 { color: var(--color-ansi-bright-red); } /* red */
+.term-fgi92 { color: var(--color-ansi-bright-green); } /* green */
+.term-fgi93 { color: var(--color-ansi-bright-yellow); } /* yellow */
+.term-fgi94 { color: var(--color-ansi-bright-blue); } /* blue */
+.term-fgi95 { color: var(--color-ansi-bright-magenta); } /* magenta */
+.term-fgi96 { color: var(--color-ansi-bright-cyan); } /* cyan */
+
+/* background colors */
+.term-bg40 { background: var(--color-ansi-bright-black); } /* grey */
+.term-bg41 { background: var(--color-ansi-red); } /* red */
+.term-bg42 { background: var(--color-ansi-green); } /* green */
+
+/* custom foreground/background combos for readability */
+.term-fg31.term-bg40 { color: var(--color-ansi-bright-red); }
+
+/* xterm colors */
+.term-fgx16 { color: #000000; }
+.term-fgx17 { color: #00005f; }
+.term-fgx18 { color: #000087; }
+.term-fgx19 { color: #0000af; }
+.term-fgx20 { color: #0000d7; }
+.term-fgx21 { color: #0000ff; }
+.term-fgx22 { color: #005f00; }
+.term-fgx23 { color: #005f5f; }
+.term-fgx24 { color: #005f87; }
+.term-fgx25 { color: #005faf; }
+.term-fgx26 { color: #005fd7; }
+.term-fgx27 { color: #005fff; }
+.term-fgx28 { color: #008700; }
+.term-fgx29 { color: #00875f; }
+.term-fgx30 { color: #008787; }
+.term-fgx31 { color: #0087af; }
+.term-fgx32 { color: #0087d7; }
+.term-fgx33 { color: #0087ff; }
+.term-fgx34 { color: #00af00; }
+.term-fgx35 { color: #00af5f; }
+.term-fgx36 { color: #00af87; }
+.term-fgx37 { color: #00afaf; }
+.term-fgx38 { color: #00afd7; }
+.term-fgx39 { color: #00afff; }
+.term-fgx40 { color: #00d700; }
+.term-fgx41 { color: #00d75f; }
+.term-fgx42 { color: #00d787; }
+.term-fgx43 { color: #00d7af; }
+.term-fgx44 { color: #00d7d7; }
+.term-fgx45 { color: #00d7ff; }
+.term-fgx46 { color: #00ff00; }
+.term-fgx47 { color: #00ff5f; }
+.term-fgx48 { color: #00ff87; }
+.term-fgx49 { color: #00ffaf; }
+.term-fgx50 { color: #00ffd7; }
+.term-fgx51 { color: #00ffff; }
+.term-fgx52 { color: #5f0000; }
+.term-fgx53 { color: #5f005f; }
+.term-fgx54 { color: #5f0087; }
+.term-fgx55 { color: #5f00af; }
+.term-fgx56 { color: #5f00d7; }
+.term-fgx57 { color: #5f00ff; }
+.term-fgx58 { color: #5f5f00; }
+.term-fgx59 { color: #5f5f5f; }
+.term-fgx60 { color: #5f5f87; }
+.term-fgx61 { color: #5f5faf; }
+.term-fgx62 { color: #5f5fd7; }
+.term-fgx63 { color: #5f5fff; }
+.term-fgx64 { color: #5f8700; }
+.term-fgx65 { color: #5f875f; }
+.term-fgx66 { color: #5f8787; }
+.term-fgx67 { color: #5f87af; }
+.term-fgx68 { color: #5f87d7; }
+.term-fgx69 { color: #5f87ff; }
+.term-fgx70 { color: #5faf00; }
+.term-fgx71 { color: #5faf5f; }
+.term-fgx72 { color: #5faf87; }
+.term-fgx73 { color: #5fafaf; }
+.term-fgx74 { color: #5fafd7; }
+.term-fgx75 { color: #5fafff; }
+.term-fgx76 { color: #5fd700; }
+.term-fgx77 { color: #5fd75f; }
+.term-fgx78 { color: #5fd787; }
+.term-fgx79 { color: #5fd7af; }
+.term-fgx80 { color: #5fd7d7; }
+.term-fgx81 { color: #5fd7ff; }
+.term-fgx82 { color: #5fff00; }
+.term-fgx83 { color: #5fff5f; }
+.term-fgx84 { color: #5fff87; }
+.term-fgx85 { color: #5fffaf; }
+.term-fgx86 { color: #5fffd7; }
+.term-fgx87 { color: #5fffff; }
+.term-fgx88 { color: #870000; }
+.term-fgx89 { color: #87005f; }
+.term-fgx90 { color: #870087; }
+.term-fgx91 { color: #8700af; }
+.term-fgx92 { color: #8700d7; }
+.term-fgx93 { color: #8700ff; }
+.term-fgx94 { color: #875f00; }
+.term-fgx95 { color: #875f5f; }
+.term-fgx96 { color: #875f87; }
+.term-fgx97 { color: #875faf; }
+.term-fgx98 { color: #875fd7; }
+.term-fgx99 { color: #875fff; }
+.term-fgx100 { color: #878700; }
+.term-fgx101 { color: #87875f; }
+.term-fgx102 { color: #878787; }
+.term-fgx103 { color: #8787af; }
+.term-fgx104 { color: #8787d7; }
+.term-fgx105 { color: #8787ff; }
+.term-fgx106 { color: #87af00; }
+.term-fgx107 { color: #87af5f; }
+.term-fgx108 { color: #87af87; }
+.term-fgx109 { color: #87afaf; }
+.term-fgx110 { color: #87afd7; }
+.term-fgx111 { color: #87afff; }
+.term-fgx112 { color: #87d700; }
+.term-fgx113 { color: #87d75f; }
+.term-fgx114 { color: #87d787; }
+.term-fgx115 { color: #87d7af; }
+.term-fgx116 { color: #87d7d7; }
+.term-fgx117 { color: #87d7ff; }
+.term-fgx118 { color: #87ff00; }
+.term-fgx119 { color: #87ff5f; }
+.term-fgx120 { color: #87ff87; }
+.term-fgx121 { color: #87ffaf; }
+.term-fgx122 { color: #87ffd7; }
+.term-fgx123 { color: #87ffff; }
+.term-fgx124 { color: #af0000; }
+.term-fgx125 { color: #af005f; }
+.term-fgx126 { color: #af0087; }
+.term-fgx127 { color: #af00af; }
+.term-fgx128 { color: #af00d7; }
+.term-fgx129 { color: #af00ff; }
+.term-fgx130 { color: #af5f00; }
+.term-fgx131 { color: #af5f5f; }
+.term-fgx132 { color: #af5f87; }
+.term-fgx133 { color: #af5faf; }
+.term-fgx134 { color: #af5fd7; }
+.term-fgx135 { color: #af5fff; }
+.term-fgx136 { color: #af8700; }
+.term-fgx137 { color: #af875f; }
+.term-fgx138 { color: #af8787; }
+.term-fgx139 { color: #af87af; }
+.term-fgx140 { color: #af87d7; }
+.term-fgx141 { color: #af87ff; }
+.term-fgx142 { color: #afaf00; }
+.term-fgx143 { color: #afaf5f; }
+.term-fgx144 { color: #afaf87; }
+.term-fgx145 { color: #afafaf; }
+.term-fgx146 { color: #afafd7; }
+.term-fgx147 { color: #afafff; }
+.term-fgx148 { color: #afd700; }
+.term-fgx149 { color: #afd75f; }
+.term-fgx150 { color: #afd787; }
+.term-fgx151 { color: #afd7af; }
+.term-fgx152 { color: #afd7d7; }
+.term-fgx153 { color: #afd7ff; }
+.term-fgx154 { color: #afff00; }
+.term-fgx155 { color: #afff5f; }
+.term-fgx156 { color: #afff87; }
+.term-fgx157 { color: #afffaf; }
+.term-fgx158 { color: #afffd7; }
+.term-fgx159 { color: #afffff; }
+.term-fgx160 { color: #d70000; }
+.term-fgx161 { color: #d7005f; }
+.term-fgx162 { color: #d70087; }
+.term-fgx163 { color: #d700af; }
+.term-fgx164 { color: #d700d7; }
+.term-fgx165 { color: #d700ff; }
+.term-fgx166 { color: #d75f00; }
+.term-fgx167 { color: #d75f5f; }
+.term-fgx168 { color: #d75f87; }
+.term-fgx169 { color: #d75faf; }
+.term-fgx170 { color: #d75fd7; }
+.term-fgx171 { color: #d75fff; }
+.term-fgx172 { color: #d78700; }
+.term-fgx173 { color: #d7875f; }
+.term-fgx174 { color: #d78787; }
+.term-fgx175 { color: #d787af; }
+.term-fgx176 { color: #d787d7; }
+.term-fgx177 { color: #d787ff; }
+.term-fgx178 { color: #d7af00; }
+.term-fgx179 { color: #d7af5f; }
+.term-fgx180 { color: #d7af87; }
+.term-fgx181 { color: #d7afaf; }
+.term-fgx182 { color: #d7afd7; }
+.term-fgx183 { color: #d7afff; }
+.term-fgx184 { color: #d7d700; }
+.term-fgx185 { color: #d7d75f; }
+.term-fgx186 { color: #d7d787; }
+.term-fgx187 { color: #d7d7af; }
+.term-fgx188 { color: #d7d7d7; }
+.term-fgx189 { color: #d7d7ff; }
+.term-fgx190 { color: #d7ff00; }
+.term-fgx191 { color: #d7ff5f; }
+.term-fgx192 { color: #d7ff87; }
+.term-fgx193 { color: #d7ffaf; }
+.term-fgx194 { color: #d7ffd7; }
+.term-fgx195 { color: #d7ffff; }
+.term-fgx196 { color: #ff0000; }
+.term-fgx197 { color: #ff005f; }
+.term-fgx198 { color: #ff0087; }
+.term-fgx199 { color: #ff00af; }
+.term-fgx200 { color: #ff00d7; }
+.term-fgx201 { color: #ff00ff; }
+.term-fgx202 { color: #ff5f00; }
+.term-fgx203 { color: #ff5f5f; }
+.term-fgx204 { color: #ff5f87; }
+.term-fgx205 { color: #ff5faf; }
+.term-fgx206 { color: #ff5fd7; }
+.term-fgx207 { color: #ff5fff; }
+.term-fgx208 { color: #ff8700; }
+.term-fgx209 { color: #ff875f; }
+.term-fgx210 { color: #ff8787; }
+.term-fgx211 { color: #ff87af; }
+.term-fgx212 { color: #ff87d7; }
+.term-fgx213 { color: #ff87ff; }
+.term-fgx214 { color: #ffaf00; }
+.term-fgx215 { color: #ffaf5f; }
+.term-fgx216 { color: #ffaf87; }
+.term-fgx217 { color: #ffafaf; }
+.term-fgx218 { color: #ffafd7; }
+.term-fgx219 { color: #ffafff; }
+.term-fgx220 { color: #ffd700; }
+.term-fgx221 { color: #ffd75f; }
+.term-fgx222 { color: #ffd787; }
+.term-fgx223 { color: #ffd7af; }
+.term-fgx224 { color: #ffd7d7; }
+.term-fgx225 { color: #ffd7ff; }
+.term-fgx226 { color: #ffff00; }
+.term-fgx227 { color: #ffff5f; }
+.term-fgx228 { color: #ffff87; }
+.term-fgx229 { color: #ffffaf; }
+.term-fgx230 { color: #ffffd7; }
+.term-fgx231 { color: #ffffff; }
+.term-fgx232 { color: #080808; }
+.term-fgx233 { color: #121212; }
+.term-fgx234 { color: #1c1c1c; }
+.term-fgx235 { color: #262626; }
+.term-fgx236 { color: #303030; }
+.term-fgx237 { color: #3a3a3a; }
+.term-fgx238 { color: #444444; }
+.term-fgx239 { color: #4e4e4e; }
+.term-fgx240 { color: #585858; }
+.term-fgx241 { color: #626262; }
+.term-fgx242 { color: #6c6c6c; }
+.term-fgx243 { color: #767676; }
+.term-fgx244 { color: #808080; }
+.term-fgx245 { color: #8a8a8a; }
+.term-fgx246 { color: #949494; }
+.term-fgx247 { color: #9e9e9e; }
+.term-fgx248 { color: #a8a8a8; }
+.term-fgx249 { color: #b2b2b2; }
+.term-fgx250 { color: #bcbcbc; }
+.term-fgx251 { color: #c6c6c6; }
+.term-fgx252 { color: #d0d0d0; }
+.term-fgx253 { color: #dadada; }
+.term-fgx254 { color: #e4e4e4; }
+.term-fgx255 { color: #eeeeee; }
diff --git a/web_src/css/features/dropzone.css b/web_src/css/features/dropzone.css
new file mode 100644
index 0000000..cbc32df
--- /dev/null
+++ b/web_src/css/features/dropzone.css
@@ -0,0 +1,59 @@
+.ui .field .dropzone {
+ border: 2px dashed var(--color-secondary);
+ background: none;
+ box-shadow: none;
+ padding: 0;
+ border-radius: var(--border-radius-medium);
+ min-height: 0;
+}
+
+.ui .field .dropzone .dz-message {
+ margin: 10px 0;
+}
+
+.dropzone .dz-button {
+ color: var(--color-text-light) !important;
+}
+
+.dropzone:hover .dz-button {
+ color: var(--color-text) !important;
+}
+
+.dropzone .dz-error-message {
+ top: 145px !important;
+}
+
+.dropzone .dz-image {
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ border-radius: 0 !important;
+}
+
+.dropzone .dz-image img {
+ max-width: 100% !important;
+ max-height: 100% !important;
+ object-fit: contain !important;
+}
+
+.dropzone .dz-preview.dz-image-preview,
+.dropzone-attachments .thumbnails img {
+ background: transparent !important;
+}
+
+.dropzone-attachments .thumbnails img {
+ height: 120px !important;
+ width: 120px !important;
+ object-fit: contain !important;
+ margin-bottom: 0 !important;
+}
+
+.dropzone .dz-preview:hover .dz-image img {
+ filter: opacity(0.5) !important;
+}
+
+.ui .field .dropzone .dz-preview .dz-progress {
+ /* by default the progress-bar is vertically centered (top: 50%), it's better to put it after the "details (size, filename)",
+ then the layout from top to bottom is: size, filename, progress */
+ top: 7em;
+}
diff --git a/web_src/css/features/gitgraph.css b/web_src/css/features/gitgraph.css
new file mode 100644
index 0000000..4da871d
--- /dev/null
+++ b/web_src/css/features/gitgraph.css
@@ -0,0 +1,309 @@
+#git-graph-content {
+ overflow-x: auto;
+ width: 100%;
+ min-height: 250px;
+}
+
+#git-graph-heading {
+ display: flex;
+ justify-content: space-between;
+ padding-bottom: 20px;
+}
+
+#git-graph-heading-left {
+ display: flex;
+ gap: 1rem;
+}
+
+#git-graph-heading h2 {
+ margin: 0;
+}
+
+@media (min-width: 767.98px) {
+ #git-graph-heading {
+ align-items: center;
+ }
+}
+
+@media (max-width: 767.98px) {
+ #git-graph-heading,
+ #git-graph-heading-left {
+ flex-direction: column;
+ }
+
+ #git-graph-heading-left {
+ margin-bottom: 1rem;
+ }
+
+ h2,
+ #flow-select-refs-dropdown {
+ max-width: 100%;
+ }
+}
+
+#git-graph-container #flow-select-refs-dropdown {
+ min-width: 250px;
+}
+
+#git-graph-container #flow-select-refs-dropdown .ui.label {
+ max-width: 180px;
+ display: inline-flex !important;
+ align-items: center;
+}
+
+#git-graph-container #flow-select-refs-dropdown .default.text {
+ padding-top: 4px;
+ padding-bottom: 4px;
+}
+
+#git-graph-container #flow-select-refs-dropdown input.search {
+ position: relative;
+ top: 1px;
+}
+
+#git-graph-container li {
+ list-style-type: none;
+ height: 24px;
+ line-height: 24px;
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+}
+
+#git-graph-container li .node-relation {
+ font-family: var(--fonts-monospace);
+}
+
+#git-graph-container li .author {
+ color: var(--color-text-light);
+}
+
+#git-graph-container li .time {
+ color: var(--color-text-light-3);
+}
+
+#git-graph-container li a:not(.ui):hover {
+ text-decoration: underline;
+}
+
+#git-graph-container li a em {
+ color: var(--color-red);
+ border-bottom: 1px dotted var(--color-secondary);
+ text-decoration: none;
+ font-style: normal;
+}
+
+#git-graph-container #rel-container {
+ max-width: 30%;
+ overflow-x: auto;
+ float: left;
+}
+
+#git-graph-container #rev-list {
+ margin: 0;
+ padding: 0;
+}
+
+#git-graph-container #rev-list li.highlight.hover {
+ background-color: var(--color-secondary-alpha-30);
+}
+
+#git-graph-container #rev-list .commit-refs .button {
+ padding: 2px 4px;
+ margin-right: 0.25em;
+ display: inline-block;
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#git-graph-container #rev-list .sha.label {
+ padding-top: 5px;
+ padding-bottom: 3px;
+}
+
+#git-graph-container #rev-list .sha.label .ui.detail.icon.button {
+ padding-top: 3px;
+ margin-top: -5px;
+ padding-bottom: 1px;
+}
+
+#git-graph-container #rev-list .author img.ui.avatar {
+ width: auto;
+ height: 18px;
+ max-width: none;
+}
+
+#git-graph-container #graph-raw-list {
+ margin: 0;
+}
+
+#git-graph-container.monochrome #rel-container .flow-group {
+ stroke: var(--color-secondary-dark-5);
+ fill: var(--color-secondary-dark-5);
+}
+
+#git-graph-container.monochrome #rel-container .flow-group.highlight {
+ stroke: var(--color-secondary-dark-12);
+ fill: var(--color-secondary-dark-12);
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-1 {
+ stroke: #499a37;
+ fill: #499a37;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-2 {
+ stroke: #ce4751;
+ fill: #ce4751;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-3 {
+ stroke: #8f9121;
+ fill: #8f9121;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-4 {
+ stroke: #ac32a6;
+ fill: #ac32a6;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-5 {
+ stroke: #7445e9;
+ fill: #7445e9;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-6 {
+ stroke: #c67d28;
+ fill: #c67d28;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-7 {
+ stroke: #4db392;
+ fill: #4db392;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-8 {
+ stroke: #aa4d30;
+ fill: #aa4d30;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-9 {
+ stroke: #2a6f84;
+ fill: #2a6f84;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-10 {
+ stroke: #c45327;
+ fill: #c45327;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-11 {
+ stroke: #3d965c;
+ fill: #3d965c;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-12 {
+ stroke: #792a93;
+ fill: #792a93;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-13 {
+ stroke: #439d73;
+ fill: #439d73;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-14 {
+ stroke: #103aad;
+ fill: #103aad;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-15 {
+ stroke: #982e85;
+ fill: #982e85;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-0 {
+ stroke: #7db233;
+ fill: #7db233;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-1 {
+ stroke: #5ac144;
+ fill: #5ac144;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-2 {
+ stroke: #ed5a8b;
+ fill: #ed5a8b;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-3 {
+ stroke: #ced049;
+ fill: #ced048;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-4 {
+ stroke: #db61d7;
+ fill: #db62d6;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-5 {
+ stroke: #8455f9;
+ fill: #8455f9;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-6 {
+ stroke: #e6a151;
+ fill: #e6a151;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-7 {
+ stroke: #44daaa;
+ fill: #44daaa;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-8 {
+ stroke: #dd7a5c;
+ fill: #dd7a5c;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-9 {
+ stroke: #38859c;
+ fill: #38859c;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-10 {
+ stroke: #d95520;
+ fill: #d95520;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-11 {
+ stroke: #42ae68;
+ fill: #42ae68;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-12 {
+ stroke: #9126b5;
+ fill: #9126b5;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-13 {
+ stroke: #4ab080;
+ fill: #4ab080;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-14 {
+ stroke: #284fb8;
+ fill: #284fb8;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-15 {
+ stroke: #971c80;
+ fill: #971c80;
+}
+
+#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight.flow-color-16-0 {
+ stroke: #87ca28;
+ fill: #87ca28;
+}
diff --git a/web_src/css/features/heatmap.css b/web_src/css/features/heatmap.css
new file mode 100644
index 0000000..c064590
--- /dev/null
+++ b/web_src/css/features/heatmap.css
@@ -0,0 +1,58 @@
+#user-heatmap {
+ width: 100%;
+ font-size: 9px;
+ position: relative;
+}
+
+/* before the Vue component is mounted, show a loading indicator with dummy size */
+/* the ratio is guesswork, see https://github.com/razorness/vue3-calendar-heatmap/issues/26 */
+#user-heatmap.is-loading {
+ aspect-ratio: 5.415; /* the size is about 790 x 145 */
+}
+.user.profile #user-heatmap.is-loading {
+ aspect-ratio: 5.645; /* the size is about 953 x 169 */
+}
+
+#user-heatmap text {
+ fill: currentcolor !important;
+}
+
+/* for the "Less" and "More" legend */
+#user-heatmap .vch__legend .vch__legend {
+ display: flex;
+ font-size: 11px;
+ align-items: center;
+ justify-content: right;
+}
+
+#user-heatmap .vch__legend .vch__legend div:first-child,
+#user-heatmap .vch__legend .vch__legend div:last-child {
+ display: inline-block;
+ padding: 0 5px;
+}
+
+#user-heatmap .vch__day__square:hover {
+ outline: 1.5px solid var(--color-text);
+}
+
+/* move the "? contributions in the last ? months" text from top to bottom */
+#user-heatmap .total-contributions {
+ font-size: 11px;
+ position: absolute;
+ bottom: 0;
+ left: 25px;
+}
+
+@media (max-width: 1200px) {
+ #user-heatmap .total-contributions {
+ left: 21px;
+ }
+}
+
+@media (max-width: 1000px) {
+ #user-heatmap .total-contributions {
+ font-size: 10px;
+ left: 17px;
+ bottom: -4px;
+ }
+}
diff --git a/web_src/css/features/imagediff.css b/web_src/css/features/imagediff.css
new file mode 100644
index 0000000..c8ead2b
--- /dev/null
+++ b/web_src/css/features/imagediff.css
@@ -0,0 +1,114 @@
+.image-diff-tabs {
+ min-height: 60px;
+
+}
+.image-diff-tabs.is-loading .tab {
+ display: none;
+}
+
+.image-diff-container {
+ text-align: center;
+ padding: 0.5em 0 1em;
+}
+
+.image-diff-container img {
+ border: 1px solid var(--color-primary-light-7);
+ --gradient: conic-gradient(var(--checkerboard-color-1) 90deg, var(--checkerboard-color-2) 90deg 180deg, var(--checkerboard-color-1) 180deg 270deg, var(--checkerboard-color-2) 270deg);
+ background: var(--gradient);
+ background-size: 20px 20px;
+}
+
+.image-diff-container .before-container {
+ border: 1px solid var(--color-red);
+ display: block;
+}
+
+.image-diff-container .after-container {
+ border: 1px solid var(--color-green);
+ display: block;
+}
+
+.image-diff-container .diff-side-by-side .side {
+ display: inline-block;
+ line-height: 0;
+ vertical-align: top;
+ margin: 0 1em;
+}
+
+.image-diff-container .diff-side-by-side .side .side-header {
+ font-weight: var(--font-weight-semibold);
+}
+
+.image-diff-container .diff-swipe {
+ margin: auto;
+ padding: 1em 0;
+}
+
+.image-diff-container .diff-swipe .swipe-frame {
+ position: absolute;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .before-container {
+ position: absolute;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .swipe-container {
+ position: absolute;
+ right: 0;
+ display: block;
+ border-left: 2px solid var(--color-secondary-dark-8);
+ height: 100%;
+ overflow: hidden;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .swipe-container .after-container {
+ position: absolute;
+ right: 0;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .swipe-bar {
+ position: absolute;
+ height: 100%;
+ top: 0;
+ left: 0;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .swipe-bar .handle {
+ background: var(--color-secondary-dark-8);
+ left: -5px;
+ height: 12px;
+ width: 12px;
+ position: absolute;
+ transform: rotate(45deg);
+ box-sizing: border-box;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .swipe-bar .top-handle {
+ top: -12px;
+}
+
+.image-diff-container .diff-swipe .swipe-frame .swipe-bar .bottom-handle {
+ bottom: -14px;
+}
+
+.image-diff-container .diff-overlay {
+ margin: 0 auto;
+}
+
+.image-diff-container .diff-overlay .overlay-frame {
+ margin: 1em auto 0;
+ position: relative;
+}
+
+.image-diff-container .diff-overlay .before-container,
+.image-diff-container .diff-overlay .after-container {
+ position: absolute;
+}
+
+.image-diff-container .diff-overlay input {
+ max-width: 300px;
+}
diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css
new file mode 100644
index 0000000..1401916
--- /dev/null
+++ b/web_src/css/features/projects.css
@@ -0,0 +1,108 @@
+.board {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ overflow-x: auto;
+ margin: 0 0.5em;
+}
+
+.project-column {
+ background-color: var(--color-project-column-bg) !important;
+ border: 1px solid var(--color-secondary) !important;
+ border-radius: var(--border-radius);
+ margin: 0 0.5rem !important;
+ padding: 0.5rem !important;
+ width: 320px;
+ height: calc(100vh - 450px);
+ min-height: 60vh;
+ overflow-y: scroll;
+ flex: 0 0 auto;
+ overflow: visible;
+ display: flex;
+ flex-direction: column;
+ cursor: default;
+}
+
+.project-column .issue-card {
+ color: var(--color-text);
+}
+
+.project-column-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.project-column-title {
+ background: none !important;
+ line-height: 1.25 !important;
+ color: inherit !important;
+ cursor: inherit;
+}
+
+.project-column-title-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.project-column-header > .dropdown {
+ flex-shrink: 0;
+}
+
+.project-column > .cards {
+ flex: 1;
+ display: flex;
+ align-content: baseline;
+ margin: 0 !important;
+ padding: 0 !important;
+ flex-wrap: nowrap !important;
+ flex-direction: column;
+ overflow-x: auto;
+ gap: .25rem;
+}
+
+.project-column > .divider {
+ margin: 5px 0;
+ border-color: currentcolor;
+ opacity: .5;
+}
+
+.project-column:first-child {
+ margin-left: auto !important;
+}
+
+.project-column:last-child {
+ margin-right: auto !important;
+}
+
+.card-attachment-images {
+ display: inline-block;
+ white-space: nowrap;
+ overflow: scroll;
+ cursor: default;
+ text-align: center;
+}
+
+.card-attachment-images img {
+ display: inline-block;
+ max-height: 50px;
+ border-radius: var(--border-radius);
+ text-align: left;
+ margin-right: 2px;
+ aspect-ratio: 1;
+}
+
+.card-attachment-images img:only-child {
+ max-height: 90px;
+ margin: auto;
+}
+
+.card-ghost {
+ border-color: var(--color-secondary-dark-4) !important;
+ border-style: dashed !important;
+ background: none !important;
+}
+
+.card-ghost * {
+ opacity: 0;
+}
diff --git a/web_src/css/features/tribute.css b/web_src/css/features/tribute.css
new file mode 100644
index 0000000..bd84367
--- /dev/null
+++ b/web_src/css/features/tribute.css
@@ -0,0 +1,42 @@
+@import "tributejs/dist/tribute.css";
+
+.tribute-container {
+ box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);
+ border-radius: var(--border-radius);
+}
+
+.tribute-container ul {
+ margin-top: 0 !important;
+ background: var(--color-body) !important;
+}
+
+.tribute-container li {
+ padding: 3px 0.5rem !important;
+}
+
+.tribute-container li span.fullname {
+ font-weight: var(--font-weight-normal);
+ font-size: 0.8rem;
+ margin-left: 3px;
+}
+
+.tribute-container li.highlight,
+.tribute-container li:hover {
+ background: var(--color-primary) !important;
+ color: var(--color-primary-contrast) !important;
+}
+
+.tribute-item {
+ display: flex;
+ align-items: center;
+}
+
+.tribute-item .emoji,
+.tribute-item img[src*="/avatar/"] {
+ margin-right: 0.5rem;
+}
+
+.tribute-container img {
+ width: 1.5rem !important;
+ height: 1.5rem !important;
+}