Render the git graph on the server (#12333)
Rendering the git graph on the server means that we can properly track flows and switch from the Canvas implementation to a SVG implementation. * This implementation provides a 16 limited color selection * The uniqued color numbers are also provided * And there is also a monochrome version *In addition is a hover highlight that allows users to highlight commits on the same flow. Closes #12209 Signed-off-by: Andrew Thornton art27@cantab.net Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@ -1,8 +1,5 @@
|
||||
extends: stylelint-config-standard
|
||||
|
||||
ignoreFiles:
|
||||
- web_src/less/vendor/**/*
|
||||
|
||||
rules:
|
||||
at-rule-empty-line-before: null
|
||||
block-closing-brace-empty-line-before: null
|
||||
|
@ -16,26 +16,9 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// GraphItem represent one commit, or one relation in timeline
|
||||
type GraphItem struct {
|
||||
GraphAcii string
|
||||
Relation string
|
||||
Branch string
|
||||
Rev string
|
||||
Date string
|
||||
Author string
|
||||
AuthorEmail string
|
||||
ShortRev string
|
||||
Subject string
|
||||
OnlyRelation bool
|
||||
}
|
||||
|
||||
// GraphItems is a list of commits from all branches
|
||||
type GraphItems []GraphItem
|
||||
|
||||
// GetCommitGraph return a list of commit (GraphItems) from all branches
|
||||
func GetCommitGraph(r *git.Repository, page int) (GraphItems, error) {
|
||||
format := "DATA:|%d|%H|%ad|%an|%ae|%h|%s"
|
||||
func GetCommitGraph(r *git.Repository, page int, maxAllowedColors int) (*Graph, error) {
|
||||
format := "DATA:%d|%H|%ad|%an|%ae|%h|%s"
|
||||
|
||||
if page == 0 {
|
||||
page = 1
|
||||
@ -51,7 +34,8 @@ func GetCommitGraph(r *git.Repository, page int) (GraphItems, error) {
|
||||
"--date=iso",
|
||||
fmt.Sprintf("--pretty=format:%s", format),
|
||||
)
|
||||
commitGraph := make([]GraphItem, 0, 100)
|
||||
graph := NewGraph()
|
||||
|
||||
stderr := new(strings.Builder)
|
||||
stdoutReader, stdoutWriter, err := os.Pipe()
|
||||
if err != nil {
|
||||
@ -64,86 +48,56 @@ func GetCommitGraph(r *git.Repository, page int) (GraphItems, error) {
|
||||
if err := graphCmd.RunInDirTimeoutEnvFullPipelineFunc(nil, -1, r.Path, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) error {
|
||||
_ = stdoutWriter.Close()
|
||||
defer stdoutReader.Close()
|
||||
parser := &Parser{}
|
||||
parser.firstInUse = -1
|
||||
parser.maxAllowedColors = maxAllowedColors
|
||||
if maxAllowedColors > 0 {
|
||||
parser.availableColors = make([]int, maxAllowedColors)
|
||||
for i := range parser.availableColors {
|
||||
parser.availableColors[i] = i + 1
|
||||
}
|
||||
} else {
|
||||
parser.availableColors = []int{1, 2}
|
||||
}
|
||||
for commitsToSkip > 0 && scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
dataIdx := bytes.Index(line, []byte("DATA:"))
|
||||
if dataIdx < 0 {
|
||||
dataIdx = len(line)
|
||||
}
|
||||
starIdx := bytes.IndexByte(line, '*')
|
||||
if starIdx >= 0 && starIdx < dataIdx {
|
||||
commitsToSkip--
|
||||
}
|
||||
parser.ParseGlyphs(line[:dataIdx])
|
||||
}
|
||||
|
||||
row := 0
|
||||
|
||||
// Skip initial non-commit lines
|
||||
for scanner.Scan() {
|
||||
if bytes.IndexByte(scanner.Bytes(), '*') >= 0 {
|
||||
line := scanner.Text()
|
||||
graphItem, err := graphItemFromString(line, r)
|
||||
if err != nil {
|
||||
line := scanner.Bytes()
|
||||
if bytes.IndexByte(line, '*') >= 0 {
|
||||
if err := parser.AddLineToGraph(graph, row, line); err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
commitGraph = append(commitGraph, graphItem)
|
||||
break
|
||||
}
|
||||
parser.ParseGlyphs(line)
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
graphItem, err := graphItemFromString(line, r)
|
||||
if err != nil {
|
||||
row++
|
||||
line := scanner.Bytes()
|
||||
if err := parser.AddLineToGraph(graph, row, line); err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
commitGraph = append(commitGraph, graphItem)
|
||||
}
|
||||
return scanner.Err()
|
||||
}); err != nil {
|
||||
return commitGraph, err
|
||||
return graph, err
|
||||
}
|
||||
|
||||
return commitGraph, nil
|
||||
}
|
||||
|
||||
func graphItemFromString(s string, r *git.Repository) (GraphItem, error) {
|
||||
|
||||
var ascii string
|
||||
var data = "|||||||"
|
||||
lines := strings.SplitN(s, "DATA:", 2)
|
||||
|
||||
switch len(lines) {
|
||||
case 1:
|
||||
ascii = lines[0]
|
||||
case 2:
|
||||
ascii = lines[0]
|
||||
data = lines[1]
|
||||
default:
|
||||
return GraphItem{}, fmt.Errorf("Failed parsing grap line:%s. Expect 1 or two fields", s)
|
||||
}
|
||||
|
||||
rows := strings.SplitN(data, "|", 8)
|
||||
if len(rows) < 8 {
|
||||
return GraphItem{}, fmt.Errorf("Failed parsing grap line:%s - Should containt 8 datafields", s)
|
||||
}
|
||||
|
||||
/* // see format in getCommitGraph()
|
||||
0 Relation string
|
||||
1 Branch string
|
||||
2 Rev string
|
||||
3 Date string
|
||||
4 Author string
|
||||
5 AuthorEmail string
|
||||
6 ShortRev string
|
||||
7 Subject string
|
||||
*/
|
||||
gi := GraphItem{ascii,
|
||||
rows[0],
|
||||
rows[1],
|
||||
rows[2],
|
||||
rows[3],
|
||||
rows[4],
|
||||
rows[5],
|
||||
rows[6],
|
||||
rows[7],
|
||||
len(rows[2]) == 0, // no commits referred to, only relation in current line.
|
||||
}
|
||||
return gi, nil
|
||||
return graph, nil
|
||||
}
|
||||
|
186
modules/gitgraph/graph_models.go
Normal file
186
modules/gitgraph/graph_models.go
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitgraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NewGraph creates a basic graph
|
||||
func NewGraph() *Graph {
|
||||
graph := &Graph{}
|
||||
graph.relationCommit = &Commit{
|
||||
Row: -1,
|
||||
Column: -1,
|
||||
}
|
||||
graph.Flows = map[int64]*Flow{}
|
||||
return graph
|
||||
}
|
||||
|
||||
// Graph represents a collection of flows
|
||||
type Graph struct {
|
||||
Flows map[int64]*Flow
|
||||
Commits []*Commit
|
||||
MinRow int
|
||||
MinColumn int
|
||||
MaxRow int
|
||||
MaxColumn int
|
||||
relationCommit *Commit
|
||||
}
|
||||
|
||||
// Width returns the width of the graph
|
||||
func (graph *Graph) Width() int {
|
||||
return graph.MaxColumn - graph.MinColumn + 1
|
||||
}
|
||||
|
||||
// Height returns the height of the graph
|
||||
func (graph *Graph) Height() int {
|
||||
return graph.MaxRow - graph.MinRow + 1
|
||||
}
|
||||
|
||||
// AddGlyph adds glyph to flows
|
||||
func (graph *Graph) AddGlyph(row, column int, flowID int64, color int, glyph byte) {
|
||||
flow, ok := graph.Flows[flowID]
|
||||
if !ok {
|
||||
flow = NewFlow(flowID, color, row, column)
|
||||
graph.Flows[flowID] = flow
|
||||
}
|
||||
flow.AddGlyph(row, column, glyph)
|
||||
|
||||
if row < graph.MinRow {
|
||||
graph.MinRow = row
|
||||
}
|
||||
if row > graph.MaxRow {
|
||||
graph.MaxRow = row
|
||||
}
|
||||
if column < graph.MinColumn {
|
||||
graph.MinColumn = column
|
||||
}
|
||||
if column > graph.MaxColumn {
|
||||
graph.MaxColumn = column
|
||||
}
|
||||
}
|
||||
|
||||
// AddCommit adds a commit at row, column on flowID with the provided data
|
||||
func (graph *Graph) AddCommit(row, column int, flowID int64, data []byte) error {
|
||||
commit, err := NewCommit(row, column, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commit.Flow = flowID
|
||||
graph.Commits = append(graph.Commits, commit)
|
||||
|
||||
graph.Flows[flowID].Commits = append(graph.Flows[flowID].Commits, commit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFlow creates a new flow
|
||||
func NewFlow(flowID int64, color, row, column int) *Flow {
|
||||
return &Flow{
|
||||
ID: flowID,
|
||||
ColorNumber: color,
|
||||
MinRow: row,
|
||||
MinColumn: column,
|
||||
MaxRow: row,
|
||||
MaxColumn: column,
|
||||
}
|
||||
}
|
||||
|
||||
// Flow represents a series of glyphs
|
||||
type Flow struct {
|
||||
ID int64
|
||||
ColorNumber int
|
||||
Glyphs []Glyph
|
||||
Commits []*Commit
|
||||
MinRow int
|
||||
MinColumn int
|
||||
MaxRow int
|
||||
MaxColumn int
|
||||
}
|
||||
|
||||
// Color16 wraps the color numbers around mod 16
|
||||
func (flow *Flow) Color16() int {
|
||||
return flow.ColorNumber % 16
|
||||
}
|
||||
|
||||
// AddGlyph adds glyph at row and column
|
||||
func (flow *Flow) AddGlyph(row, column int, glyph byte) {
|
||||
if row < flow.MinRow {
|
||||
flow.MinRow = row
|
||||
}
|
||||
if row > flow.MaxRow {
|
||||
flow.MaxRow = row
|
||||
}
|
||||
if column < flow.MinColumn {
|
||||
flow.MinColumn = column
|
||||
}
|
||||
if column > flow.MaxColumn {
|
||||
flow.MaxColumn = column
|
||||
}
|
||||
|
||||
flow.Glyphs = append(flow.Glyphs, Glyph{
|
||||
row,
|
||||
column,
|
||||
glyph,
|
||||
})
|
||||
}
|
||||
|
||||
// Glyph represents a co-ordinate and glyph
|
||||
type Glyph struct {
|
||||
Row int
|
||||
Column int
|
||||
Glyph byte
|
||||
}
|
||||
|
||||
// RelationCommit represents an empty relation commit
|
||||
var RelationCommit = &Commit{
|
||||
Row: -1,
|
||||
}
|
||||
|
||||
// NewCommit creates a new commit from a provided line
|
||||
func NewCommit(row, column int, line []byte) (*Commit, error) {
|
||||
data := bytes.SplitN(line, []byte("|"), 7)
|
||||
if len(data) < 7 {
|
||||
return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line))
|
||||
}
|
||||
return &Commit{
|
||||
Row: row,
|
||||
Column: column,
|
||||
// 0 matches git log --pretty=format:%d => ref names, like the --decorate option of git-log(1)
|
||||
Branch: string(data[0]),
|
||||
// 1 matches git log --pretty=format:%H => commit hash
|
||||
Rev: string(data[1]),
|
||||
// 2 matches git log --pretty=format:%ad => author date (format respects --date= option)
|
||||
Date: string(data[2]),
|
||||
// 3 matches git log --pretty=format:%an => author name
|
||||
Author: string(data[3]),
|
||||
// 4 matches git log --pretty=format:%ae => author email
|
||||
AuthorEmail: string(data[4]),
|
||||
// 5 matches git log --pretty=format:%h => abbreviated commit hash
|
||||
ShortRev: string(data[5]),
|
||||
// 6 matches git log --pretty=format:%s => subject
|
||||
Subject: string(data[6]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Commit represents a commit at co-ordinate X, Y with the data
|
||||
type Commit struct {
|
||||
Flow int64
|
||||
Row int
|
||||
Column int
|
||||
Branch string
|
||||
Rev string
|
||||
Date string
|
||||
Author string
|
||||
AuthorEmail string
|
||||
ShortRev string
|
||||
Subject string
|
||||
}
|
||||
|
||||
// OnlyRelation returns whether this a relation only commit
|
||||
func (c *Commit) OnlyRelation() bool {
|
||||
return c.Row == -1
|
||||
}
|
File diff suppressed because it is too large
Load Diff
338
modules/gitgraph/parser.go
Normal file
338
modules/gitgraph/parser.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -99,8 +99,19 @@ func NewFuncMap() []template.FuncMap {
|
||||
"Subtract": base.Subtract,
|
||||
"EntryIcon": base.EntryIcon,
|
||||
"MigrationIcon": MigrationIcon,
|
||||
"Add": func(a, b int) int {
|
||||
return a + b
|
||||
"Add": func(a ...int) int {
|
||||
sum := 0
|
||||
for _, val := range a {
|
||||
sum += val
|
||||
}
|
||||
return sum
|
||||
},
|
||||
"Mul": func(a ...int) int {
|
||||
sum := 1
|
||||
for _, val := range a {
|
||||
sum *= val
|
||||
}
|
||||
return sum
|
||||
},
|
||||
"ActionIcon": ActionIcon,
|
||||
"DateFmtLong": func(t time.Time) string {
|
||||
@ -437,6 +448,20 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
}
|
||||
return float32(n) * 100 / float32(sum)
|
||||
},
|
||||
"Add": func(a ...int) int {
|
||||
sum := 0
|
||||
for _, val := range a {
|
||||
sum += val
|
||||
}
|
||||
return sum
|
||||
},
|
||||
"Mul": func(a ...int) int {
|
||||
sum := 1
|
||||
for _, val := range a {
|
||||
sum *= val
|
||||
}
|
||||
return sum
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -775,6 +775,8 @@ audio_not_supported_in_browser = Your browser does not support the HTML5 'audio'
|
||||
stored_lfs = Stored with Git LFS
|
||||
symbolic_link = Symbolic link
|
||||
commit_graph = Commit Graph
|
||||
commit_graph.monochrome = Mono
|
||||
commit_graph.color = Color
|
||||
blame = Blame
|
||||
normal_view = Normal View
|
||||
line = line
|
||||
|
1
public/img/svg/material-invert-colors.svg
Normal file
1
public/img/svg/material-invert-colors.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 768 768" class="svg material-invert-colors" width="16" height="16" aria-hidden="true"><path d="M384 627V163.5l-135 135c-36 36-57 85.5-57 136.5 0 103.19 88.8 192 192 192zm181.5-373.5C666 354 666 514.5 565.5 615 516 664.5 450 690 384 690s-132-25.5-181.5-75C102 514.5 102 354 202.5 253.5L384 72z"/></svg>
|
After Width: | Height: | Size: 319 B |
1
public/img/svg/material-palette.svg
Normal file
1
public/img/svg/material-palette.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 768 768" class="svg material-palette" width="16" height="16" aria-hidden="true"><path d="M559.5 384c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zm-96-127.5c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zm-159 0c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zm-96 127.5c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zM384 96c159 0 288 115.5 288 256.5 0 88.5-72 159-160.5 159H456c-27 0-48 21-48 48 0 12 4.5 22.5 12 31.5s12 21 12 33c0 27-21 48-48 48-159 0-288-129-288-288S225 96 384 96z"/></svg>
|
After Width: | Height: | Size: 540 B |
@ -90,6 +90,11 @@ func Commits(ctx *context.Context) {
|
||||
func Graph(ctx *context.Context) {
|
||||
ctx.Data["PageIsCommits"] = true
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
mode := strings.ToLower(ctx.QueryTrim("mode"))
|
||||
if mode != "monochrome" {
|
||||
mode = "color"
|
||||
}
|
||||
ctx.Data["Mode"] = mode
|
||||
|
||||
commitsCount, err := ctx.Repo.GetCommitsCount()
|
||||
if err != nil {
|
||||
@ -105,7 +110,7 @@ func Graph(ctx *context.Context) {
|
||||
|
||||
page := ctx.QueryInt("page")
|
||||
|
||||
graph, err := gitgraph.GetCommitGraph(ctx.Repo.GitRepo, page)
|
||||
graph, err := gitgraph.GetCommitGraph(ctx.Repo.GitRepo, page, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitGraph", err)
|
||||
return
|
||||
@ -116,7 +121,9 @@ func Graph(ctx *context.Context) {
|
||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
ctx.Data["Branch"] = ctx.Repo.BranchName
|
||||
ctx.Data["Page"] = context.NewPagination(int(allCommitsCount), setting.UI.GraphMaxCommitNum, page, 5)
|
||||
paginator := context.NewPagination(int(allCommitsCount), setting.UI.GraphMaxCommitNum, page, 5)
|
||||
paginator.AddParam(ctx, "mode", "Mode")
|
||||
ctx.Data["Page"] = paginator
|
||||
ctx.HTML(200, tplGraph)
|
||||
}
|
||||
|
||||
|
@ -2,21 +2,44 @@
|
||||
<div class="repository commits">
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
<div id="git-graph-container" class="ui segment">
|
||||
<h1>{{.i18n.Tr "repo.commit_graph"}}</h1>
|
||||
<div id="git-graph-container" class="ui segment{{if eq .Mode "monochrome"}} monochrome{{end}}">
|
||||
<h2 class="ui header dividing">{{.i18n.Tr "repo.commit_graph"}}
|
||||
<div class="ui right">
|
||||
<div class="ui icon buttons tiny color-buttons">
|
||||
<button id="flow-color-monochrome" class="ui labelled icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "repo.commit_graph.monochrome"}}"><span class="emoji">{{svg "material-invert-colors" 16}}</span> {{.i18n.Tr "repo.commit_graph.monochrome"}}</button>
|
||||
<button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "repo.commit_graph.color"}}"><span class="emoji">{{svg "material-palette" 16}}</span> {{.i18n.Tr "repo.commit_graph.color"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="ui dividing"></div>
|
||||
<div id="rel-container">
|
||||
<canvas id="graph-canvas">
|
||||
<ul id="graph-raw-list">
|
||||
{{ range .Graph }}
|
||||
<li><span class="node-relation">{{ .GraphAcii -}}</span></li>
|
||||
<svg viewbox="{{Mul .Graph.MinColumn 5}} {{Mul .Graph.MinRow 10}} {{Add (Mul .Graph.Width 5) 5}} {{Mul .Graph.Height 10}}" width="{{Add (Mul .Graph.Width 10) 10}}px">
|
||||
{{range $flowid, $flow := .Graph.Flows}}
|
||||
<g id="flow-{{$flow.ID}}" class="flow-group flow-color-{{$flow.ColorNumber}} flow-color-16-{{$flow.Color16}}" data-flow="{{$flow.ID}}" data-color="{{$flow.ColorNumber}}">
|
||||
<path d="{{range $i, $glyph := $flow.Glyphs -}}
|
||||
{{- if or (eq $glyph.Glyph '*') (eq $glyph.Glyph '|') -}}
|
||||
M {{Add (Mul $glyph.Column 5) 5}} {{Add (Mul $glyph.Row 10) 0}} v 10 {{/* */ -}}
|
||||
{{- else if eq $glyph.Glyph '/' -}}
|
||||
M {{Add (Mul $glyph.Column 5) 10}} {{Add (Mul $glyph.Row 10) 0}} l -10 10 {{/* */ -}}
|
||||
{{- else if eq $glyph.Glyph '\\' -}}
|
||||
M {{Add (Mul $glyph.Column 5) 0}} {{Add (Mul $glyph.Row 10) 0}} l 10 10 {{/* */ -}}
|
||||
{{- else if or (eq $glyph.Glyph '-') (eq $glyph.Glyph '.') -}}
|
||||
M {{Add (Mul $glyph.Column 5) 0}} {{Add (Mul $glyph.Row 10) 10}} h 5 {{/* */ -}}
|
||||
{{- else if eq $glyph.Glyph '_' -}}
|
||||
M {{Add (Mul $glyph.Column 5) 0}} {{Add (Mul $glyph.Row 10) 10}} h 10 {{/* */ -}}
|
||||
{{- end -}}
|
||||
{{- end}}" stroke-width="1" fill="none" id="flow-{{$flow.ID}}-path" stroke-linecap="round"/>
|
||||
{{range $flow.Commits}}
|
||||
<circle class="flow-commit" cx="{{Add (Mul .Column 5) 5}}" cy="{{Add (Mul .Row 10) 5}}" r="2.5" stroke="none" id="flow-commit-{{.Rev}}" data-rev="{{.Rev}}"/>
|
||||
{{end}}
|
||||
</ul>
|
||||
</canvas>
|
||||
</g>
|
||||
{{end}}
|
||||
</svg>
|
||||
</div>
|
||||
<div id="rev-container">
|
||||
<ul id="rev-list">
|
||||
{{ range .Graph }}
|
||||
<li>
|
||||
{{ range .Graph.Commits }}
|
||||
<li id="commit-{{.Rev}}" data-flow="{{.Flow}}">
|
||||
{{ if .OnlyRelation }}
|
||||
<span />
|
||||
{{ else }}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3082,11 +3082,3 @@ tbody.commit-list {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#git-graph-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#git-graph-container.in {
|
||||
display: block;
|
||||
}
|
||||
|
256
web_src/less/features/gitgraph.less
Normal file
256
web_src/less/features/gitgraph.less
Normal file
@ -0,0 +1,256 @@
|
||||
#git-graph-container {
|
||||
float: left;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
|
||||
.color-buttons {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ui.header.dividing {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
|
||||
.node-relation {
|
||||
font-family: "Bitstream Vera Sans Mono", "Courier", monospace;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #999999;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a em {
|
||||
color: #bb0000;
|
||||
border-bottom: 1px dotted #bbbbbb;
|
||||
text-decoration: none;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
#rel-container {
|
||||
max-width: 30%;
|
||||
overflow-x: auto;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#rev-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#rev-list {
|
||||
margin: 0;
|
||||
padding: 0 5px;
|
||||
min-width: 95%;
|
||||
|
||||
li.highlight,
|
||||
li.hover {
|
||||
background-color: rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
li.highlight.hover {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
}
|
||||
}
|
||||
|
||||
#graph-raw-list {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.monochrome #rel-container {
|
||||
.flow-group {
|
||||
stroke: grey;
|
||||
fill: grey;
|
||||
}
|
||||
|
||||
.flow-group.highlight {
|
||||
stroke: black;
|
||||
fill: black;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.monochrome) #rel-container {
|
||||
.flow-group {
|
||||
&.flow-color-16-1 {
|
||||
stroke: #499a37;
|
||||
fill: #499a37;
|
||||
}
|
||||
|
||||
&.flow-color-16-2 {
|
||||
stroke: hsl(356, 58%, 54%);
|
||||
fill: #ce4751;
|
||||
}
|
||||
|
||||
&.flow-color-16-3 {
|
||||
stroke: #8f9121;
|
||||
fill: #8f9121;
|
||||
}
|
||||
|
||||
&.flow-color-16-4 {
|
||||
stroke: #ac32a6;
|
||||
fill: #ac32a6;
|
||||
}
|
||||
|
||||
&.flow-color-16-5 {
|
||||
stroke: #3d27aa;
|
||||
fill: #3d27aa;
|
||||
}
|
||||
|
||||
&.flow-color-16-6 {
|
||||
stroke: #c67d28;
|
||||
fill: #c67d28;
|
||||
}
|
||||
|
||||
&.flow-color-16-7 {
|
||||
stroke: #4db392;
|
||||
fill: #4db392;
|
||||
}
|
||||
|
||||
&.flow-color-16-8 {
|
||||
stroke: #aa4d30;
|
||||
fill: #aa4d30;
|
||||
}
|
||||
|
||||
&.flow-color-16-9 {
|
||||
stroke: #2a6f84;
|
||||
fill: #2a6f84;
|
||||
}
|
||||
|
||||
&.flow-color-16-10 {
|
||||
stroke: #c45327;
|
||||
fill: #c45327;
|
||||
}
|
||||
|
||||
&.flow-color-16-11 {
|
||||
stroke: #3d965c;
|
||||
fill: #3d965c;
|
||||
}
|
||||
|
||||
&.flow-color-16-12 {
|
||||
stroke: #792a93;
|
||||
fill: #792a93;
|
||||
}
|
||||
|
||||
&.flow-color-16-13 {
|
||||
stroke: #439d73;
|
||||
fill: #439d73;
|
||||
}
|
||||
|
||||
&.flow-color-16-14 {
|
||||
stroke: #103aad;
|
||||
fill: #103aad;
|
||||
}
|
||||
|
||||
&.flow-color-16-15 {
|
||||
stroke: #982e85;
|
||||
fill: #982e85;
|
||||
}
|
||||
|
||||
&.flow-color-16-0 {
|
||||
stroke: #7db233;
|
||||
fill: #7db233;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-group.highlight {
|
||||
&.flow-color-16-1 {
|
||||
stroke: #5ac144;
|
||||
fill: #5ac144;
|
||||
}
|
||||
|
||||
&.flow-color-16-2 {
|
||||
stroke: #ed5a8b;
|
||||
fill: #ed5a8b;
|
||||
}
|
||||
|
||||
&.flow-color-16-3 {
|
||||
stroke: #ced049;
|
||||
fill: #ced048;
|
||||
}
|
||||
|
||||
&.flow-color-16-4 {
|
||||
stroke: #db61d7;
|
||||
fill: #db62d6;
|
||||
}
|
||||
|
||||
&.flow-color-16-5 {
|
||||
stroke: #4e33d1;
|
||||
fill: #4f35d1;
|
||||
}
|
||||
|
||||
&.flow-color-16-6 {
|
||||
stroke: #e6a151;
|
||||
fill: #e6a151;
|
||||
}
|
||||
|
||||
&.flow-color-16-7 {
|
||||
stroke: #44daaa;
|
||||
fill: #44daaa;
|
||||
}
|
||||
|
||||
&.flow-color-16-8 {
|
||||
stroke: #dd7a5c;
|
||||
fill: #dd7a5c;
|
||||
}
|
||||
|
||||
&.flow-color-16-9 {
|
||||
stroke: #38859c;
|
||||
fill: #38859c;
|
||||
}
|
||||
|
||||
&.flow-color-16-10 {
|
||||
stroke: #d95520;
|
||||
fill: #d95520;
|
||||
}
|
||||
|
||||
&.flow-color-16-11 {
|
||||
stroke: #42ae68;
|
||||
fill: #42ae68;
|
||||
}
|
||||
|
||||
&.flow-color-16-12 {
|
||||
stroke: #9126b5;
|
||||
fill: #9126b5;
|
||||
}
|
||||
|
||||
&.flow-color-16-13 {
|
||||
stroke: #4ab080;
|
||||
fill: #4ab080;
|
||||
}
|
||||
|
||||
&.flow-color-16-14 {
|
||||
stroke: #284fb8;
|
||||
fill: #284fb8;
|
||||
}
|
||||
|
||||
&.flow-color-16-15 {
|
||||
stroke: #971c80;
|
||||
fill: #971c80;
|
||||
}
|
||||
|
||||
&.flow-color-16-0 {
|
||||
stroke: #87ca28;
|
||||
fill: #87ca28;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
@import "~font-awesome/css/font-awesome.css";
|
||||
@import "./vendor/gitGraph.css";
|
||||
|
||||
@import "./features/gitgraph.less";
|
||||
@import "./features/animations.less";
|
||||
@import "./markdown/mermaid.less";
|
||||
|
||||
|
@ -919,11 +919,17 @@ a.ui.basic.green.label:hover {
|
||||
|
||||
.ui.active.button:active,
|
||||
.ui.button:active,
|
||||
.ui.button:focus {
|
||||
.ui.button:focus,
|
||||
.ui.active.button {
|
||||
background-color: #2e3e4e;
|
||||
color: #dbdbdb;
|
||||
}
|
||||
|
||||
.ui.active.button:hover {
|
||||
background-color: #475e75;
|
||||
color: #dbdbdb;
|
||||
}
|
||||
|
||||
.ui.dropdown .menu .selected.item,
|
||||
.ui.dropdown.selected {
|
||||
color: #dbdbdb;
|
||||
@ -1921,6 +1927,45 @@ footer .container .links > * {
|
||||
color: #2a2e3a;
|
||||
}
|
||||
|
||||
#git-graph-container.monochrome #rel-container .flow-group {
|
||||
stroke: dimgrey;
|
||||
fill: dimgrey;
|
||||
}
|
||||
|
||||
#git-graph-container.monochrome #rel-container .flow-group.highlight {
|
||||
stroke: darkgrey;
|
||||
fill: darkgrey;
|
||||
}
|
||||
|
||||
#git-graph-container:not(.monochrome) #rel-container .flow-group {
|
||||
&.flow-color-16-5 {
|
||||
stroke: #5543b1;
|
||||
fill: #5543b1;
|
||||
}
|
||||
}
|
||||
|
||||
#git-graph-container:not(.monochrome) #rel-container .flow-group.highlight {
|
||||
&.flow-color-16-5 {
|
||||
stroke: #7058e6;
|
||||
fill: #7058e6;
|
||||
}
|
||||
}
|
||||
|
||||
#git-graph-container #rev-list li.highlight,
|
||||
#git-graph-container #rev-list li.hover {
|
||||
background-color: rgba(255, 255, 255, .05);
|
||||
}
|
||||
|
||||
#git-graph-container #rev-list li.highlight.hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
#git-graph-container .ui.buttons button#flow-color-monochrome.ui.button {
|
||||
border-left-color: rgb(76, 80, 92);
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.mermaid-chart {
|
||||
filter: invert(84%) hue-rotate(180deg);
|
||||
}
|
||||
|
15
web_src/less/vendor/gitGraph.css
vendored
15
web_src/less/vendor/gitGraph.css
vendored
@ -1,15 +0,0 @@
|
||||
/* This is a customized version of https://github.com/bluef/gitgraph.js/blob/master/gitgraph.css
|
||||
Changes include the removal of `body` and `em` styles */
|
||||
#git-graph-container, #rel-container {float:left;}
|
||||
#rel-container {max-width:30%; overflow-x:auto;}
|
||||
#git-graph-container {overflow-x:auto; width:100%}
|
||||
#git-graph-container li {list-style-type:none;height:20px;line-height:20px; white-space:nowrap;}
|
||||
#git-graph-container li .node-relation {font-family:'Bitstream Vera Sans Mono', 'Courier', monospace;}
|
||||
#git-graph-container li .author {color:#666666;}
|
||||
#git-graph-container li .time {color:#999999;font-size:80%}
|
||||
#git-graph-container li a {color:#000000;}
|
||||
#git-graph-container li a:hover {text-decoration:underline;}
|
||||
#git-graph-container li a em {color:#BB0000;border-bottom:1px dotted #BBBBBB;text-decoration:none;font-style:normal;}
|
||||
#rev-container {width:100%}
|
||||
#rev-list {margin:0;padding:0 5px 0 5px;min-width:95%}
|
||||
#graph-raw-list {margin:0px;}
|
1
web_src/svg/material-invert-colors.svg
Normal file
1
web_src/svg/material-invert-colors.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 768 768"><path d="M384 627V163.5l-135 135c-36 36-57 85.5-57 136.5 0 103.19 88.8 192 192 192zm181.5-373.5C666 354 666 514.5 565.5 615 516 664.5 450 690 384 690s-132-25.5-181.5-75C102 514.5 102 354 202.5 253.5L384 72z"/></svg>
|
After Width: | Height: | Size: 242 B |
1
web_src/svg/material-palette.svg
Normal file
1
web_src/svg/material-palette.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 768 768"><path d="M559.5 384c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zm-96-127.5c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zm-159 0c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zm-96 127.5c27 0 48-21 48-48s-21-48-48-48-48 21-48 48 21 48 48 48zM384 96c159 0 288 115.5 288 256.5 0 88.5-72 159-160.5 159H456c-27 0-48 21-48 48 0 12 4.5 22.5 12 31.5s12 21 12 33c0 27-21 48-48 48-159 0-288-129-288-288S225 96 384 96z"/></svg>
|
After Width: | Height: | Size: 469 B |
Reference in New Issue
Block a user