diff options
author | zeripath <art27@cantab.net> | 2020-08-06 09:04:08 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-06 09:04:08 +0100 |
commit | 2c1ae6c82d0b3fa62dda7e6a30fb91e27aba6e04 (patch) | |
tree | be14ac1376125be2482e6ca7de3eedc276203304 /web_src | |
parent | f1a42f5d5ee0279ddec7973a1ba9236c70bd5b5e (diff) | |
download | gitea-2c1ae6c82d0b3fa62dda7e6a30fb91e27aba6e04.tar.gz gitea-2c1ae6c82d0b3fa62dda7e6a30fb91e27aba6e04.zip |
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>
Diffstat (limited to 'web_src')
-rw-r--r-- | web_src/js/features/gitgraph.js | 641 | ||||
-rw-r--r-- | web_src/less/_repository.less | 8 | ||||
-rw-r--r-- | web_src/less/features/gitgraph.less | 256 | ||||
-rw-r--r-- | web_src/less/index.less | 3 | ||||
-rw-r--r-- | web_src/less/themes/theme-arc-green.less | 47 | ||||
-rw-r--r-- | web_src/less/vendor/gitGraph.css | 15 | ||||
-rw-r--r-- | web_src/svg/material-invert-colors.svg | 1 | ||||
-rw-r--r-- | web_src/svg/material-palette.svg | 1 |
8 files changed, 383 insertions, 589 deletions
diff --git a/web_src/js/features/gitgraph.js b/web_src/js/features/gitgraph.js index 3e6b27436d..655cfb77c2 100644 --- a/web_src/js/features/gitgraph.js +++ b/web_src/js/features/gitgraph.js @@ -1,568 +1,81 @@ -// Although inspired by the https://github.com/bluef/gitgraph.js/blob/master/gitgraph.js -// this has been completely rewritten with almost no remaining code - -// GitGraphCanvas is a canvas for drawing gitgraphs on to -class GitGraphCanvas { - constructor(canvas, widthUnits, heightUnits, config) { - this.ctx = canvas.getContext('2d'); - - const width = widthUnits * config.unitSize; - this.height = heightUnits * config.unitSize; - - const ratio = window.devicePixelRatio || 1; - - canvas.width = width * ratio; - canvas.height = this.height * ratio; - - canvas.style.width = `${width}px`; - canvas.style.height = `${this.height}px`; - - this.ctx.lineWidth = config.lineWidth; - this.ctx.lineJoin = 'round'; - this.ctx.lineCap = 'round'; - - this.ctx.scale(ratio, ratio); - this.config = config; - } - drawLine(moveX, moveY, lineX, lineY, color) { - this.ctx.strokeStyle = color; - this.ctx.beginPath(); - this.ctx.moveTo(moveX, moveY); - this.ctx.lineTo(lineX, lineY); - this.ctx.stroke(); - } - drawLineRight(x, y, color) { - this.drawLine( - x - 0.5 * this.config.unitSize, - y + this.config.unitSize / 2, - x + 0.5 * this.config.unitSize, - y + this.config.unitSize / 2, - color - ); - } - drawLineUp(x, y, color) { - this.drawLine( - x, - y + this.config.unitSize / 2, - x, - y - this.config.unitSize / 2, - color - ); - } - drawNode(x, y, color) { - this.ctx.strokeStyle = color; - - this.drawLineUp(x, y, color); - - this.ctx.beginPath(); - this.ctx.arc(x, y, this.config.nodeRadius, 0, Math.PI * 2, true); - this.ctx.fillStyle = color; - this.ctx.fill(); - } - drawLineIn(x, y, color) { - this.drawLine( - x + 0.5 * this.config.unitSize, - y + this.config.unitSize / 2, - x - 0.5 * this.config.unitSize, - y - this.config.unitSize / 2, - color - ); - } - drawLineOut(x, y, color) { - this.drawLine( - x - 0.5 * this.config.unitSize, - y + this.config.unitSize / 2, - x + 0.5 * this.config.unitSize, - y - this.config.unitSize / 2, - color - ); - } - drawSymbol(symbol, columnNumber, rowNumber, color) { - const y = this.height - this.config.unitSize * (rowNumber + 0.5); - const x = this.config.unitSize * 0.5 * (columnNumber + 1); - switch (symbol) { - case '-': - if (columnNumber % 2 === 1) { - this.drawLineRight(x, y, color); - } - break; - case '_': - this.drawLineRight(x, y, color); - break; - case '*': - this.drawNode(x, y, color); - break; - case '|': - this.drawLineUp(x, y, color); - break; - case '/': - this.drawLineOut(x, y, color); - break; - case '\\': - this.drawLineIn(x, y, color); - break; - case '.': - case ' ': - break; - default: - console.error('Unknown symbol', symbol, color); - } - } -} - -class GitGraph { - constructor(canvas, rawRows, config) { - this.rows = []; - let maxWidth = 0; - - for (let i = 0; i < rawRows.length; i++) { - const rowStr = rawRows[i]; - maxWidth = Math.max(rowStr.replace(/([_\s.-])/g, '').length, maxWidth); - - const rowArray = rowStr.split(''); - - this.rows.unshift(rowArray); - } - - this.currentFlows = []; - this.previousFlows = []; - - this.gitGraphCanvas = new GitGraphCanvas( - canvas, - maxWidth, - this.rows.length, - config - ); - } - - generateNewFlow(column) { - let newId; - - do { - newId = generateRandomColorString(); - } while (this.hasFlow(newId, column)); - - return {id: newId, color: `#${newId}`}; - } - - hasFlow(id, column) { - // We want to find the flow with the current ID - // Possible flows are those in the currentFlows - // Or flows in previousFlows[column-2:...] - for ( - let idx = column - 2 < 0 ? 0 : column - 2; - idx < this.previousFlows.length; - idx++ - ) { - if (this.previousFlows[idx] && this.previousFlows[idx].id === id) { - return true; - } - } - for (let idx = 0; idx < this.currentFlows.length; idx++) { - if (this.currentFlows[idx] && this.currentFlows[idx].id === id) { - return true; - } - } - return false; - } - - takePreviousFlow(column) { - if (column < this.previousFlows.length && this.previousFlows[column]) { - const flow = this.previousFlows[column]; - this.previousFlows[column] = null; - return flow; - } - return this.generateNewFlow(column); - } - - draw() { - if (this.rows.length === 0) { - return; - } - - this.currentFlows = new Array(this.rows[0].length); - - // Generate flows for the first row - I do not believe that this can contain '_', '-', '.' - for (let column = 0; column < this.rows[0].length; column++) { - if (this.rows[0][column] === ' ') { - continue; - } - this.currentFlows[column] = this.generateNewFlow(column); - } - - // Draw the first row - for (let column = 0; column < this.rows[0].length; column++) { - const symbol = this.rows[0][column]; - const color = this.currentFlows[column] ? this.currentFlows[column].color : ''; - this.gitGraphCanvas.drawSymbol(symbol, column, 0, color); - } - - for (let row = 1; row < this.rows.length; row++) { - // Done previous row - step up the row - const currentRow = this.rows[row]; - const previousRow = this.rows[row - 1]; - - this.previousFlows = this.currentFlows; - this.currentFlows = new Array(currentRow.length); - - // Set flows for this row - for (let column = 0; column < currentRow.length; column++) { - column = this.setFlowFor(column, currentRow, previousRow); - } - - // Draw this row - for (let column = 0; column < currentRow.length; column++) { - const symbol = currentRow[column]; - const color = this.currentFlows[column] ? this.currentFlows[column].color : ''; - this.gitGraphCanvas.drawSymbol(symbol, column, row, color); - } - } - } - - setFlowFor(column, currentRow, previousRow) { - const symbol = currentRow[column]; - switch (symbol) { - case '|': - case '*': - return this.setUpFlow(column, currentRow, previousRow); - case '/': - return this.setOutFlow(column, currentRow, previousRow); - case '\\': - return this.setInFlow(column, currentRow, previousRow); - case '_': - return this.setRightFlow(column, currentRow, previousRow); - case '-': - return this.setLeftFlow(column, currentRow, previousRow); - case ' ': - // In space no one can hear you flow ... (?) - return column; - default: - // Unexpected so let's generate a new flow and wait for bug-reports - this.currentFlows[column] = this.generateNewFlow(column); - return column; - } - } - - // setUpFlow handles '|' or '*' - returns the last column that was set - // generally we prefer to take the left most flow from the previous row - setUpFlow(column, currentRow, previousRow) { - // If ' |/' or ' |_' - // '/|' '/|' -> Take the '|' flow directly beneath us - if ( - column + 1 < currentRow.length && - (currentRow[column + 1] === '/' || currentRow[column + 1] === '_') && - column < previousRow.length && - (previousRow[column] === '|' || previousRow[column] === '*') && - previousRow[column - 1] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column); - return column; - } - - // If ' |/' or ' |_' - // '/ ' '/ ' -> Take the '/' flow from the preceding column - if ( - column + 1 < currentRow.length && - (currentRow[column + 1] === '/' || currentRow[column + 1] === '_') && - column - 1 < previousRow.length && - previousRow[column - 1] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 1); - return column; - } - - // If ' |' - // '/' -> Take the '/' flow - (we always prefer the left-most flow) - if ( - column > 0 && - column - 1 < previousRow.length && - previousRow[column - 1] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 1); - return column; - } - - // If '|' OR '|' take the '|' flow - // '|' '*' - if ( - column < previousRow.length && - (previousRow[column] === '|' || previousRow[column] === '*') - ) { - this.currentFlows[column] = this.takePreviousFlow(column); - return column; - } - - // If '| ' keep the '\' flow - // ' \' - if (column + 1 < previousRow.length && previousRow[column + 1] === '\\') { - this.currentFlows[column] = this.takePreviousFlow(column + 1); - return column; - } - - // Otherwise just create a new flow - probably this is an error... - this.currentFlows[column] = this.generateNewFlow(column); - return column; - } - - // setOutFlow handles '/' - returns the last column that was set - // generally we prefer to take the left most flow from the previous row - setOutFlow(column, currentRow, previousRow) { - // If '_/' -> keep the '_' flow - if (column > 0 && currentRow[column - 1] === '_') { - this.currentFlows[column] = this.currentFlows[column - 1]; - return column; - } - - // If '_|/' -> keep the '_' flow - if ( - column > 1 && - (currentRow[column - 1] === '|' || currentRow[column - 1] === '*') && - currentRow[column - 2] === '_' - ) { - this.currentFlows[column] = this.currentFlows[column - 2]; - return column; - } - - // If '|/' - // '/' -> take the '/' flow (if it is still available) - if ( - column > 1 && - currentRow[column - 1] === '|' && - column - 2 < previousRow.length && - previousRow[column - 2] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 2); - return column; - } - - // If ' /' - // '/' -> take the '/' flow, but transform the symbol to '|' due to our spacing - // This should only happen if there are 3 '/' - in a row so we don't need to be cleverer here - if ( - column > 0 && - currentRow[column - 1] === ' ' && - column - 1 < previousRow.length && - previousRow[column - 1] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 1); - currentRow[column] = '|'; - return column; - } - - // If ' /' - // '|' -> take the '|' flow - if ( - column > 0 && - currentRow[column - 1] === ' ' && - column - 1 < previousRow.length && - (previousRow[column - 1] === '|' || previousRow[column - 1] === '*') - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 1); - return column; - } - - // If '/' <- Not sure this ever happens... but take the '\' flow - // '\' - if (column < previousRow.length && previousRow[column] === '\\') { - this.currentFlows[column] = this.takePreviousFlow(column); - return column; - } - - // Otherwise just generate a new flow and wait for bug-reports... - this.currentFlows[column] = this.generateNewFlow(column); - return column; - } - - // setInFlow handles '\' - returns the last column that was set - // generally we prefer to take the left most flow from the previous row - setInFlow(column, currentRow, previousRow) { - // If '\?' - // '/?' -> take the '/' flow - if (column < previousRow.length && previousRow[column] === '/') { - this.currentFlows[column] = this.takePreviousFlow(column); - return column; - } - - // If '\?' - // ' \' -> take the '\' flow and reassign to '|' - // This should only happen if there are 3 '\' - in a row so we don't need to be cleverer here - if (column + 1 < previousRow.length && previousRow[column + 1] === '\\') { - this.currentFlows[column] = this.takePreviousFlow(column + 1); - currentRow[column] = '|'; - return column; - } - - // If '\?' - // ' |' -> take the '|' flow - if ( - column + 1 < previousRow.length && - (previousRow[column + 1] === '|' || previousRow[column + 1] === '*') - ) { - this.currentFlows[column] = this.takePreviousFlow(column + 1); - return column; - } - - // Otherwise just generate a new flow and wait for bug-reports if we're wrong... - this.currentFlows[column] = this.generateNewFlow(column); - return column; - } - - // setRightFlow handles '_' - returns the last column that was set - // generally we prefer to take the left most flow from the previous row - setRightFlow(column, currentRow, previousRow) { - // if '__' keep the '_' flow - if (column > 0 && currentRow[column - 1] === '_') { - this.currentFlows[column] = this.currentFlows[column - 1]; - return column; - } - - // if '_|_' -> keep the '_' flow - if ( - column > 1 && - currentRow[column - 1] === '|' && - currentRow[column - 2] === '_' - ) { - this.currentFlows[column] = this.currentFlows[column - 2]; - return column; - } - - // if ' _' -> take the '/' flow - // '/ ' - if ( - column > 0 && - column - 1 < previousRow.length && - previousRow[column - 1] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 1); - return column; - } - - // if ' |_' - // '/? ' -> take the '/' flow (this may cause generation...) - // we can do this because we know that git graph - // doesn't create compact graphs like: ' |_' - // '//' - if ( - column > 1 && - column - 2 < previousRow.length && - previousRow[column - 2] === '/' - ) { - this.currentFlows[column] = this.takePreviousFlow(column - 2); - return column; - } - - // There really shouldn't be another way of doing this - generate and wait for bug-reports... - - this.currentFlows[column] = this.generateNewFlow(column); - return column; - } - - // setLeftFlow handles '----.' - returns the last column that was set - // generally we prefer to take the left most flow from the previous row that terminates this left recursion - setLeftFlow(column, currentRow, previousRow) { - // This is: '----------.' or the like - // ' \ \ /|\' - - // Find the end of the '-' or nearest '/|\' in the previousRow : - let originalColumn = column; - let flow; - for (; column < currentRow.length && currentRow[column] === '-'; column++) { - if (column > 0 && column - 1 < previousRow.length && previousRow[column - 1] === '/') { - flow = this.takePreviousFlow(column - 1); - break; - } else if (column < previousRow.length && previousRow[column] === '|') { - flow = this.takePreviousFlow(column); - break; - } else if ( - column + 1 < previousRow.length && - previousRow[column + 1] === '\\' - ) { - flow = this.takePreviousFlow(column + 1); - break; - } - } - - // if we have a flow then we found a '/|\' in the previousRow - if (flow) { - for (; originalColumn < column + 1; originalColumn++) { - this.currentFlows[originalColumn] = flow; - } - return column; - } - - // If the symbol in the column is not a '.' then there's likely an error - if (currentRow[column] !== '.') { - // It really should end in a '.' but this one doesn't... - // 1. Step back - we don't want to eat this column - column--; - // 2. Generate a new flow and await bug-reports... - this.currentFlows[column] = this.generateNewFlow(column); - - // 3. Assign all of the '-' to the same flow. - for (; originalColumn < column; originalColumn++) { - this.currentFlows[originalColumn] = this.currentFlows[column]; - } - return column; - } - - // We have a terminal '.' eg. the current row looks like '----.' - // the previous row should look like one of '/|\' eg. ' \' - if (column > 0 && column - 1 < previousRow.length && previousRow[column - 1] === '/') { - flow = this.takePreviousFlow(column - 1); - } else if (column < previousRow.length && previousRow[column] === '|') { - flow = this.takePreviousFlow(column); - } else if ( - column + 1 < previousRow.length && - previousRow[column + 1] === '\\' - ) { - flow = this.takePreviousFlow(column + 1); +export default async function initGitGraph() { + const graphContainer = document.getElementById('git-graph-container'); + if (!graphContainer) return; + + $('#flow-color-monochrome').on('click', () => { + $('#flow-color-monochrome').addClass('active'); + $('#flow-color-colored').removeClass('active'); + $('#git-graph-container').removeClass('colored').addClass('monochrome'); + const params = new URLSearchParams(window.location.search); + params.set('mode', 'monochrome'); + const queryString = params.toString(); + if (queryString) { + window.history.replaceState({}, '', `?${queryString}`); } else { - // Again unexpected so let's generate and wait the bug-report - flow = this.generateNewFlow(column); - } - - // Assign all of the rest of the ----. to this flow. - for (; originalColumn < column + 1; originalColumn++) { - this.currentFlows[originalColumn] = flow; + window.history.replaceState({}, '', window.location.pathname); + } + $('.pagination a').each((_, that) => { + const href = $(that).attr('href'); + if (!href) return; + const url = new URL(href, window.location); + const params = url.searchParams; + params.set('mode', 'monochrome'); + url.search = `?${params.toString()}`; + $(that).attr('href', url.href); + }); + }); + $('#flow-color-colored').on('click', () => { + $('#flow-color-colored').addClass('active'); + $('#flow-color-monochrome').removeClass('active'); + $('#git-graph-container').addClass('colored').removeClass('monochrome'); + $('.pagination a').each((_, that) => { + const href = $(that).attr('href'); + if (!href) return; + const url = new URL(href, window.location); + const params = url.searchParams; + params.delete('mode'); + url.search = `?${params.toString()}`; + $(that).attr('href', url.href); + }); + const params = new URLSearchParams(window.location.search); + params.delete('mode'); + const queryString = params.toString(); + if (queryString) { + window.history.replaceState({}, '', `?${queryString}`); + } else { + window.history.replaceState({}, '', window.location.pathname); } - - return column; - } -} - -function generateRandomColorString() { - const chars = '0123456789ABCDEF'; - const stringLength = 6; - let randomString = '', - rnum, - i; - for (i = 0; i < stringLength; i++) { - rnum = Math.floor(Math.random() * chars.length); - randomString += chars.substring(rnum, rnum + 1); - } - - return randomString; -} - -export default async function initGitGraph() { - const graphCanvas = document.getElementById('graph-canvas'); - if (!graphCanvas || !graphCanvas.getContext) return; - - // Grab the raw graphList - const graphList = []; - $('#graph-raw-list li span.node-relation').each(function () { - graphList.push($(this).text()); }); - - // Define some drawing parameters - const config = { - unitSize: 20, - lineWidth: 3, - nodeRadius: 4 - }; - - - const gitGraph = new GitGraph(graphCanvas, graphList, config); - gitGraph.draw(); - graphCanvas.closest('#git-graph-container').classList.add('in'); + $('#git-graph-container').on('mouseenter', '#rev-list li', (e) => { + const flow = $(e.currentTarget).data('flow'); + if (flow === 0) return; + $(`#flow-${flow}`).addClass('highlight'); + $(e.currentTarget).addClass('hover'); + $(`#rev-list li[data-flow='${flow}']`).addClass('highlight'); + }); + $('#git-graph-container').on('mouseleave', '#rev-list li', (e) => { + const flow = $(e.currentTarget).data('flow'); + if (flow === 0) return; + $(`#flow-${flow}`).removeClass('highlight'); + $(e.currentTarget).removeClass('hover'); + $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight'); + }); + $('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => { + $(e.currentTarget).addClass('highlight'); + const flow = $(e.currentTarget).data('flow'); + $(`#rev-list li[data-flow='${flow}']`).addClass('highlight'); + }); + $('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => { + $(e.currentTarget).removeClass('highlight'); + const flow = $(e.currentTarget).data('flow'); + $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight'); + }); + $('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => { + const rev = $(e.currentTarget).data('rev'); + $(`#rev-list li#commit-${rev}`).addClass('hover'); + }); + $('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => { + const rev = $(e.currentTarget).data('rev'); + $(`#rev-list li#commit-${rev}`).removeClass('hover'); + }); } diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 94b1d72a0b..81a3282ee4 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -3082,11 +3082,3 @@ tbody.commit-list { } } } - -#git-graph-container { - display: none; -} - -#git-graph-container.in { - display: block; -} diff --git a/web_src/less/features/gitgraph.less b/web_src/less/features/gitgraph.less new file mode 100644 index 0000000000..8a9c4239a7 --- /dev/null +++ b/web_src/less/features/gitgraph.less @@ -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; + } + } + } +} diff --git a/web_src/less/index.less b/web_src/less/index.less index ef38f863cd..92b25e1db1 100644 --- a/web_src/less/index.less +++ b/web_src/less/index.less @@ -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"; diff --git a/web_src/less/themes/theme-arc-green.less b/web_src/less/themes/theme-arc-green.less index 8de66fd251..1e8c118675 100644 --- a/web_src/less/themes/theme-arc-green.less +++ b/web_src/less/themes/theme-arc-green.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); } diff --git a/web_src/less/vendor/gitGraph.css b/web_src/less/vendor/gitGraph.css deleted file mode 100644 index bb7e708101..0000000000 --- a/web_src/less/vendor/gitGraph.css +++ /dev/null @@ -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;} diff --git a/web_src/svg/material-invert-colors.svg b/web_src/svg/material-invert-colors.svg new file mode 100644 index 0000000000..e6445ab230 --- /dev/null +++ b/web_src/svg/material-invert-colors.svg @@ -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>
\ No newline at end of file diff --git a/web_src/svg/material-palette.svg b/web_src/svg/material-palette.svg new file mode 100644 index 0000000000..df0e1756ff --- /dev/null +++ b/web_src/svg/material-palette.svg @@ -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>
\ No newline at end of file |