mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-09 05:16:02 +00:00
Jump to error when clicking on log entry
This commit is contained in:
parent
8b6f962275
commit
797dcf5c84
13 changed files with 330 additions and 37 deletions
|
@ -16,12 +16,14 @@ div.full-size(
|
|||
font-size="settings.fontSize",
|
||||
auto-complete="settings.autoComplete",
|
||||
spell-check-language="project.spellCheckLanguage",
|
||||
annotations="onlineUserCursorAnnotations[editor.open_doc_id]"
|
||||
highlights="onlineUserCursorHighlights[editor.open_doc_id]"
|
||||
show-print-margin="false",
|
||||
sharejs-doc="editor.sharejs_doc",
|
||||
last-updated="editor.last_updated",
|
||||
cursor-position="editor.cursorPosition",
|
||||
resize-on="layout:main:resize,layout:pdf:resize"
|
||||
goto-line="editor.gotoLine",
|
||||
resize-on="layout:main:resize,layout:pdf:resize",
|
||||
annotations="pdf.logEntryAnnotations[editor.open_doc_id]"
|
||||
)
|
||||
|
||||
.ui-layout-east
|
||||
|
|
|
@ -59,13 +59,14 @@ div.full-size(ng-controller="PdfController")
|
|||
strong Compile Error.
|
||||
span Sorry, your LaTeX code couldn't compile for some reason. Please check the errors below for details, or view the raw log.
|
||||
|
||||
div(ng-repeat="entry in pdf.logEntries.all")
|
||||
div(ng-repeat="entry in pdf.logEntries.all", ng-controller="PdfLogEntryController")
|
||||
.alert(
|
||||
ng-class="{\
|
||||
'alert-danger': entry.level == 'error',\
|
||||
'alert-warning': entry.level == 'warning',\
|
||||
'alert-info': entry.level == 'typesetting'\
|
||||
}"
|
||||
ng-click="openInEditor(entry)"
|
||||
)
|
||||
span.line-no
|
||||
span(ng-show="entry.file") {{ entry.file }}
|
||||
|
|
|
@ -71,7 +71,7 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
|
|||
theme="settings.theme",
|
||||
font-size="settings.fontSize",
|
||||
text="trackChanges.diff.text",
|
||||
annotations="trackChanges.diff.annotations",
|
||||
highlights="trackChanges.diff.highlights",
|
||||
read-only="true",
|
||||
resize-on="layout:main:resize"
|
||||
)
|
||||
|
|
|
@ -10,6 +10,7 @@ define [
|
|||
open_doc_id: null
|
||||
opening: true
|
||||
cursorPosition: null
|
||||
gotoLine: null
|
||||
}
|
||||
|
||||
@$scope.$on "entity:selected", (event, entity) =>
|
||||
|
@ -33,18 +34,23 @@ define [
|
|||
|
||||
openDoc: (doc, options = {}) ->
|
||||
@$scope.ui.view = "editor"
|
||||
|
||||
done = () =>
|
||||
if options.gotoLine?
|
||||
@$scope.editor.gotoLine = options.gotoLine
|
||||
|
||||
console.log "Trying to open doc", doc.id
|
||||
return if doc.id == @$scope.editor.open_doc_id and !options.forceReopen
|
||||
if doc.id == @$scope.editor.open_doc_id and !options.forceReopen
|
||||
@$scope.$apply () =>
|
||||
done()
|
||||
return
|
||||
|
||||
@$scope.editor.open_doc_id = doc.id
|
||||
console.log "Actually opening doc", doc.id
|
||||
|
||||
$.localStorage "doc.open_id.#{@$scope.project_id}", doc.id
|
||||
@ide.fileTreeManager.selectEntity(doc)
|
||||
|
||||
@$scope.editor.opening = true
|
||||
@_openNewDocument doc, (error, sharejs_doc) =>
|
||||
console.log "OPENED DOC", error, sharejs_doc
|
||||
if error?
|
||||
@ide.showGenericServerErrorMessage()
|
||||
return
|
||||
|
@ -54,6 +60,7 @@ define [
|
|||
@$scope.$apply () =>
|
||||
@$scope.editor.opening = false
|
||||
@$scope.editor.sharejs_doc = sharejs_doc
|
||||
done()
|
||||
|
||||
_openNewDocument: (doc, callback = (error, sharejs_doc) ->) ->
|
||||
current_sharejs_doc = @$scope.editor.sharejs_doc
|
||||
|
|
|
@ -11,6 +11,13 @@ define [], () ->
|
|||
|
||||
@gotoStoredPosition()
|
||||
|
||||
@$scope.$watch "gotoLine", (value) =>
|
||||
console.log "Going to line", value
|
||||
if value?
|
||||
setTimeout () =>
|
||||
@gotoLine(value)
|
||||
, 0
|
||||
|
||||
onScrollTopChange: (event) ->
|
||||
if !@ignoreCursorPositionChanges and doc_id = @$scope.sharejsDoc?.doc_id
|
||||
docPosition = $.localStorage("doc.position.#{doc_id}") || {}
|
||||
|
@ -30,4 +37,7 @@ define [], () ->
|
|||
@ignoreCursorPositionChanges = true
|
||||
@editor.moveCursorToPosition(pos.cursorPosition or {row: 0, column: 0})
|
||||
@editor.getSession().setScrollTop(pos.scrollTop or 0)
|
||||
delete @ignoreCursorPositionChanges
|
||||
delete @ignoreCursorPositionChanges
|
||||
|
||||
gotoLine: (line) ->
|
||||
@editor.moveCursorToPosition({row: line, column: 0})
|
|
@ -4,7 +4,7 @@ define [
|
|||
"ide/editor/undo/UndoManager"
|
||||
"ide/editor/auto-complete/AutoCompleteManager"
|
||||
"ide/editor/spell-check/SpellCheckManager"
|
||||
"ide/editor/annotations/AnnotationsManager"
|
||||
"ide/editor/highlights/HighlightsManager"
|
||||
"ide/editor/cursor-position/CursorPositionManager"
|
||||
"ace/keyboard/vim"
|
||||
"ace/keyboard/emacs"
|
||||
|
@ -26,9 +26,11 @@ define [
|
|||
lastUpdated: "="
|
||||
spellCheckLanguage: "="
|
||||
cursorPosition: "="
|
||||
annotations: "="
|
||||
highlights: "="
|
||||
text: "="
|
||||
readOnly: "="
|
||||
gotoLine: "="
|
||||
annotations: "="
|
||||
}
|
||||
link: (scope, element, attrs) ->
|
||||
# Don't freak out if we're already in an apply callback
|
||||
|
@ -100,15 +102,26 @@ define [
|
|||
session.setUseWrapMode(true)
|
||||
session.setMode(new LatexMode())
|
||||
|
||||
scope.$watch "annotations", (annotations) ->
|
||||
console.log "SETTING ANNOTATIONS", annotations
|
||||
if annotations?
|
||||
session = editor.getSession()
|
||||
session.setAnnotations annotations
|
||||
|
||||
scope.$watch "readOnly", (value) ->
|
||||
editor.setReadOnly !!value
|
||||
|
||||
resetSession = () ->
|
||||
session = editor.getSession()
|
||||
session.setUseWrapMode(true)
|
||||
session.setMode(new LatexMode())
|
||||
session.setAnnotations scope.annotations
|
||||
|
||||
attachToAce = (sharejs_doc) ->
|
||||
lines = sharejs_doc.getSnapshot().split("\n")
|
||||
editor.setSession(new EditSession(lines))
|
||||
resetSession()
|
||||
session = editor.getSession()
|
||||
session.setUseWrapMode(true)
|
||||
session.setMode(new LatexMode())
|
||||
|
||||
autoCompleteManager.bindToSession(session)
|
||||
annotationsManager.redrawAnnotations()
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
define [
|
||||
"ace/range"
|
||||
], () ->
|
||||
Range = require("ace/range").Range
|
||||
|
||||
class HighlightsManager
|
||||
constructor: (@$scope, @editor, @element) ->
|
||||
@markerIds = []
|
||||
@labels = []
|
||||
|
||||
@$scope.annotationLabel = {
|
||||
show: false
|
||||
right: "auto"
|
||||
left: "auto"
|
||||
top: "auto"
|
||||
bottom: "auto"
|
||||
backgroundColor: "black"
|
||||
text: ""
|
||||
}
|
||||
|
||||
@$scope.$watch "highlights", (value) =>
|
||||
@redrawAnnotations()
|
||||
|
||||
@$scope.$watch "theme", (value) =>
|
||||
@redrawAnnotations()
|
||||
|
||||
@editor.on "mousemove", (e) =>
|
||||
position = @editor.renderer.screenToTextCoordinates(e.clientX, e.clientY)
|
||||
e.position = position
|
||||
@showAnnotationLabels(position)
|
||||
|
||||
redrawAnnotations: () ->
|
||||
@_clearMarkers()
|
||||
@_clearLabels()
|
||||
|
||||
for annotation in @$scope.highlights or []
|
||||
do (annotation) =>
|
||||
colorScheme = @_getColorScheme(annotation.hue)
|
||||
if annotation.cursor?
|
||||
@labels.push {
|
||||
text: annotation.text
|
||||
range: new Range(
|
||||
annotation.cursor.row, annotation.cursor.column,
|
||||
annotation.cursor.row, annotation.cursor.column + 1
|
||||
)
|
||||
colorScheme: colorScheme
|
||||
snapToStartOfRange: true
|
||||
}
|
||||
@_drawCursor(annotation, colorScheme)
|
||||
else if annotation.highlight?
|
||||
@labels.push {
|
||||
text: annotation.label
|
||||
range: new Range(
|
||||
annotation.highlight.start.row, annotation.highlight.start.column,
|
||||
annotation.highlight.end.row, annotation.highlight.end.column
|
||||
)
|
||||
colorScheme: colorScheme
|
||||
}
|
||||
@_drawHighlight(annotation, colorScheme)
|
||||
else if annotation.strikeThrough?
|
||||
@labels.push {
|
||||
text: annotation.label
|
||||
range: new Range(
|
||||
annotation.strikeThrough.start.row, annotation.strikeThrough.start.column,
|
||||
annotation.strikeThrough.end.row, annotation.strikeThrough.end.column
|
||||
)
|
||||
colorScheme: colorScheme
|
||||
}
|
||||
@_drawStrikeThrough(annotation, colorScheme)
|
||||
|
||||
showAnnotationLabels: (position) ->
|
||||
labelToShow = null
|
||||
for label in @labels or []
|
||||
if label.range.contains(position.row, position.column)
|
||||
labelToShow = label
|
||||
|
||||
if !labelToShow?
|
||||
@$scope.$apply () =>
|
||||
@$scope.annotationLabel.show = false
|
||||
else
|
||||
$ace = $(@editor.renderer.container).find(".ace_scroller")
|
||||
# Move the label into the Ace content area so that offsets and positions are easy to calculate.
|
||||
$ace.append(@element.find(".annotation-label"))
|
||||
|
||||
if labelToShow.snapToStartOfRange
|
||||
coords = @editor.renderer.textToScreenCoordinates(labelToShow.range.start.row, labelToShow.range.start.column)
|
||||
else
|
||||
coords = @editor.renderer.textToScreenCoordinates(position.row, position.column)
|
||||
|
||||
offset = $ace.offset()
|
||||
height = $ace.height()
|
||||
coords.pageX = coords.pageX - offset.left
|
||||
coords.pageY = coords.pageY - offset.top
|
||||
|
||||
if coords.pageY > @editor.renderer.lineHeight * 2
|
||||
top = "auto"
|
||||
bottom = height - coords.pageY
|
||||
else
|
||||
top = coords.pageY + @editor.renderer.lineHeight
|
||||
bottom = "auto"
|
||||
|
||||
# Apply this first that the label has the correct width when calculating below
|
||||
@$scope.$apply () =>
|
||||
@$scope.annotationLabel.text = labelToShow.text
|
||||
@$scope.annotationLabel.show = true
|
||||
|
||||
$label = @element.find(".annotation-label")
|
||||
console.log "pageX", coords.pageX, "label", $label.outerWidth(), "ace", $ace.width()
|
||||
|
||||
if coords.pageX + $label.outerWidth() < $ace.width()
|
||||
left = coords.pageX
|
||||
right = "auto"
|
||||
else
|
||||
right = 0
|
||||
left = "auto"
|
||||
|
||||
@$scope.$apply () =>
|
||||
@$scope.annotationLabel = {
|
||||
show: true
|
||||
left: left
|
||||
right: right
|
||||
bottom: bottom
|
||||
top: top
|
||||
backgroundColor: labelToShow.colorScheme.labelBackgroundColor
|
||||
text: labelToShow.text
|
||||
}
|
||||
|
||||
_clearMarkers: () ->
|
||||
for marker_id in @markerIds
|
||||
@editor.getSession().removeMarker(marker_id)
|
||||
@markerIds = []
|
||||
|
||||
_clearLabels: () ->
|
||||
@labels = []
|
||||
|
||||
_drawCursor: (annotation, colorScheme) ->
|
||||
@markerIds.push @editor.getSession().addMarker new Range(
|
||||
annotation.cursor.row, annotation.cursor.column,
|
||||
annotation.cursor.row, annotation.cursor.column + 1
|
||||
), "annotation remote-cursor", (html, range, left, top, config) ->
|
||||
div = """
|
||||
<div
|
||||
class='remote-cursor custom ace_start'
|
||||
style='height: #{config.lineHeight}px; top:#{top}px; left:#{left}px; border-color: #{colorScheme.cursor};'
|
||||
>
|
||||
<div class="nubbin" style="bottom: #{config.lineHeight}px; background-color: #{colorScheme.cursor};"></div>
|
||||
</div>
|
||||
"""
|
||||
html.push div
|
||||
, true
|
||||
|
||||
_drawHighlight: (annotation, colorScheme) ->
|
||||
@_addMarkerWithCustomStyle(
|
||||
new Range(
|
||||
annotation.highlight.start.row, annotation.highlight.start.column,
|
||||
annotation.highlight.end.row, annotation.highlight.end.column + 1
|
||||
),
|
||||
"annotation highlight",
|
||||
false,
|
||||
"background-color: #{colorScheme.highlightBackgroundColor}"
|
||||
)
|
||||
|
||||
_drawStrikeThrough: (annotation, colorScheme) ->
|
||||
lineHeight = @editor.renderer.lineHeight
|
||||
@_addMarkerWithCustomStyle(
|
||||
new Range(
|
||||
annotation.strikeThrough.start.row, annotation.strikeThrough.start.column,
|
||||
annotation.strikeThrough.end.row, annotation.strikeThrough.end.column + 1
|
||||
),
|
||||
"annotation strike-through-background",
|
||||
false,
|
||||
"background-color: #{colorScheme.strikeThroughBackgroundColor}"
|
||||
)
|
||||
@_addMarkerWithCustomStyle(
|
||||
new Range(
|
||||
annotation.strikeThrough.start.row, annotation.strikeThrough.start.column,
|
||||
annotation.strikeThrough.end.row, annotation.strikeThrough.end.column + 1
|
||||
),
|
||||
"annotation strike-through-foreground",
|
||||
true,
|
||||
"""
|
||||
height: #{Math.round(lineHeight/2) + 2}px;
|
||||
border-bottom: 2px solid #{colorScheme.strikeThroughForegroundColor};
|
||||
"""
|
||||
)
|
||||
|
||||
_addMarkerWithCustomStyle: (range, klass, foreground, style) ->
|
||||
if foreground?
|
||||
markerLayer = @editor.renderer.$markerBack
|
||||
else
|
||||
markerLayer = @editor.renderer.$markerFront
|
||||
|
||||
@markerIds.push @editor.getSession().addMarker range, klass, (html, range, left, top, config) ->
|
||||
if range.isMultiLine()
|
||||
markerLayer.drawTextMarker(html, range, klass, config, style)
|
||||
else
|
||||
markerLayer.drawSingleLineMarker(html, range, "#{klass} ace_start", config, 0, style)
|
||||
, foreground
|
||||
|
||||
_getColorScheme: (hue) ->
|
||||
if @_isDarkTheme()
|
||||
return {
|
||||
cursor: "hsl(#{hue}, 100%, 50%)"
|
||||
labelBackgroundColor: "hsl(#{hue}, 100%, 50%)"
|
||||
highlightBackgroundColor: "hsl(#{hue}, 100%, 28%);"
|
||||
strikeThroughBackgroundColor: "hsl(#{hue}, 100%, 20%);"
|
||||
strikeThroughForegroundColor: "hsl(#{hue}, 100%, 60%);"
|
||||
}
|
||||
else
|
||||
return {
|
||||
cursor: "hsl(#{hue}, 100%, 50%)"
|
||||
labelBackgroundColor: "hsl(#{hue}, 100%, 50%)"
|
||||
highlightBackgroundColor: "hsl(#{hue}, 70%, 85%);"
|
||||
strikeThroughBackgroundColor: "hsl(#{hue}, 70%, 95%);"
|
||||
strikeThroughForegroundColor: "hsl(#{hue}, 70%, 40%);"
|
||||
}
|
||||
|
||||
_isDarkTheme: () ->
|
||||
rgb = @element.find(".ace_editor").css("background-color");
|
||||
[m, r, g, b] = rgb.match(/rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)/)
|
||||
r = parseInt(r, 10)
|
||||
g = parseInt(g, 10)
|
||||
b = parseInt(b, 10)
|
||||
return r + g + b < 3 * 128
|
|
@ -88,6 +88,21 @@ define [
|
|||
|
||||
return null
|
||||
|
||||
findEntityByPath: (path) ->
|
||||
@_findEntityByPathInFolder @$scope.rootFolder, path
|
||||
|
||||
_findEntityByPathInFolder: (folder, path) ->
|
||||
parts = path.split("/")
|
||||
name = parts.shift()
|
||||
rest = parts.join("/")
|
||||
for entity in folder.children
|
||||
if entity.name == name
|
||||
if rest == ""
|
||||
return entity
|
||||
else if entity.type == "folder"
|
||||
return @_findEntityByPathInFolder(entity, rest)
|
||||
return null
|
||||
|
||||
forEachEntity: (callback = (entity, parent_folder) ->) ->
|
||||
@_forEachEntityInFolder(@$scope.rootFolder, callback)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ define [
|
|||
class OnlineUsersManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
@$scope.onlineUsers = {}
|
||||
@$scope.onlineUserCursorAnnotations = {}
|
||||
@$scope.onlineUserCursorHighlights = {}
|
||||
|
||||
@$scope.$watch "editor.cursorPosition", (position) =>
|
||||
if position?
|
||||
|
@ -23,12 +23,12 @@ define [
|
|||
|
||||
updateCursorHighlights: () ->
|
||||
console.log "UPDATING CURSOR HIGHLIGHTS"
|
||||
@$scope.onlineUserCursorAnnotations = {}
|
||||
@$scope.onlineUserCursorHighlights = {}
|
||||
for client_id, client of @$scope.onlineUsers
|
||||
doc_id = client.doc_id
|
||||
continue if !doc_id?
|
||||
@$scope.onlineUserCursorAnnotations[doc_id] ||= []
|
||||
@$scope.onlineUserCursorAnnotations[doc_id].push {
|
||||
@$scope.onlineUserCursorHighlights[doc_id] ||= []
|
||||
@$scope.onlineUserCursorHighlights[doc_id].push {
|
||||
label: client.name
|
||||
cursor:
|
||||
row: client.row
|
||||
|
|
|
@ -4,5 +4,15 @@ define [
|
|||
], () ->
|
||||
class PdfManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
|
||||
# All the logic actually happens in the controller
|
||||
@$scope.pdf =
|
||||
url: null # Pdf Url
|
||||
error: false # Server error
|
||||
timeout: false # Server timed out
|
||||
failure: false # PDF failed to compile
|
||||
compiling: false
|
||||
uncompiled: true
|
||||
logEntries: []
|
||||
logEntryAnnotations: {}
|
||||
rawLog: ""
|
||||
view: null # 'pdf' 'logs'
|
||||
showRawLog: false
|
||||
|
|
|
@ -3,18 +3,6 @@ define [
|
|||
"libs/latex-log-parser"
|
||||
], (App, LogParser) ->
|
||||
App.controller "PdfController", ["$scope", "$http", "ide", "$modal", ($scope, $http, ide, $modal) ->
|
||||
$scope.pdf =
|
||||
url: null # Pdf Url
|
||||
error: false # Server error
|
||||
timeout: false # Server timed out
|
||||
failure: false # PDF failed to compile
|
||||
compiling: false
|
||||
uncompiled: true
|
||||
logEntries: []
|
||||
rawLog: ""
|
||||
view: null # 'pdf' 'logs'
|
||||
showRawLog: false
|
||||
|
||||
autoCompile = true
|
||||
$scope.$on "doc:opened", () ->
|
||||
return if !autoCompile
|
||||
|
@ -68,9 +56,21 @@ define [
|
|||
logEntries = LogParser.parse(log, ignoreDuplicates: true)
|
||||
$scope.pdf.logEntries = logEntries
|
||||
$scope.pdf.logEntries.all = logEntries.errors.concat(logEntries.warnings).concat(logEntries.typesetting)
|
||||
|
||||
$scope.pdf.logEntryAnnotations = {}
|
||||
for entry in logEntries.all
|
||||
entry.file = entry.file.replace(/^(.*)\/compiles\/[0-9a-f]{24}\/(\.\/)?/, "")
|
||||
entry.file = entry.file.replace(/^\/compile\//, "")
|
||||
|
||||
entity = ide.fileTreeManager.findEntityByPath(entry.file)
|
||||
if entity?
|
||||
$scope.pdf.logEntryAnnotations[entity.id] ||= []
|
||||
$scope.pdf.logEntryAnnotations[entity.id].push {
|
||||
row: entry.line - 1
|
||||
type: if entry.level == "error" then "error" else "warning"
|
||||
text: entry.message
|
||||
}
|
||||
|
||||
.error () ->
|
||||
$scope.pdf.logEntries = []
|
||||
$scope.pdf.rawLog = ""
|
||||
|
@ -131,6 +131,16 @@ define [
|
|||
)
|
||||
]
|
||||
|
||||
App.controller "PdfLogEntryController", ["$scope", "ide", ($scope, ide) ->
|
||||
$scope.openInEditor = (entry) ->
|
||||
console.log "OPENING", entry.file, entry.line
|
||||
entity = ide.fileTreeManager.findEntityByPath(entry.file)
|
||||
return if entity.type != "doc"
|
||||
if entry.line?
|
||||
line = entry.line - 1
|
||||
ide.editorManager.openDoc(entity, gotoLine: line)
|
||||
]
|
||||
|
||||
App.controller 'ClearCacheModalController', ["$scope", "$modalInstance", ($scope, $modalInstance) ->
|
||||
$scope.state =
|
||||
inflight: false
|
||||
|
|
|
@ -110,9 +110,9 @@ define [
|
|||
.get(url)
|
||||
.success (data) =>
|
||||
diff.loading = false
|
||||
{text, annotations} = @_parseDiff(data)
|
||||
{text, highlights} = @_parseDiff(data)
|
||||
diff.text = text
|
||||
diff.annotations = annotations
|
||||
diff.highlights = highlights
|
||||
.error () ->
|
||||
diff.loading = false
|
||||
diff.error = true
|
||||
|
@ -129,7 +129,7 @@ define [
|
|||
_parseDiff: (diff) ->
|
||||
row = 0
|
||||
column = 0
|
||||
annotations = []
|
||||
highlights = []
|
||||
text = ""
|
||||
for entry, i in diff.diff or []
|
||||
content = entry.u or entry.i or entry.d
|
||||
|
@ -162,19 +162,19 @@ define [
|
|||
name = "you"
|
||||
date = moment(entry.meta.end_ts).format("Do MMM YYYY, h:mm a")
|
||||
if entry.i?
|
||||
annotations.push {
|
||||
highlights.push {
|
||||
label: "Added by #{name} on #{date}"
|
||||
highlight: range
|
||||
hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user.id)
|
||||
}
|
||||
else if entry.d?
|
||||
annotations.push {
|
||||
highlights.push {
|
||||
label: "Deleted by #{name} on #{date}"
|
||||
strikeThrough: range
|
||||
hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user.id)
|
||||
}
|
||||
|
||||
return {text, annotations}
|
||||
return {text, highlights}
|
||||
|
||||
_loadUpdates: (updates = []) ->
|
||||
previousUpdate = @$scope.trackChanges.updates[@$scope.trackChanges.updates.length - 1]
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
.alert {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: @line-height-computed / 2;
|
||||
cursor: pointer;
|
||||
.line-no {
|
||||
float: right;
|
||||
color: @gray;
|
||||
|
|
Loading…
Add table
Reference in a new issue