rails_mini_profiler 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +13 -10
- data/app/javascript/images/check.svg +3 -0
- data/app/javascript/images/chevron.svg +3 -0
- data/app/javascript/images/filter.svg +1 -0
- data/app/javascript/images/logo_variant.svg +2 -2
- data/app/javascript/images/search.svg +4 -5
- data/app/javascript/js/checklist_controller.js +48 -0
- data/app/javascript/js/enable_controller.js +24 -0
- data/app/javascript/js/filter_controller.js +44 -0
- data/app/javascript/js/search_controller.js +18 -0
- data/app/javascript/js/select_controller.js +47 -0
- data/app/javascript/packs/rails-mini-profiler.js +23 -15
- data/app/javascript/stylesheets/components/page_header/page_header.scss +3 -0
- data/app/javascript/stylesheets/components/pagination.scss +14 -13
- data/app/javascript/stylesheets/components/profiled_request_table/placeholder.scss +33 -0
- data/app/javascript/stylesheets/components/profiled_request_table/profiled_request_table.scss +179 -0
- data/app/javascript/stylesheets/flamegraph.scss +3 -2
- data/app/javascript/stylesheets/flashes.scss +3 -5
- data/app/javascript/stylesheets/navbar.scss +7 -13
- data/app/javascript/stylesheets/profiled_requests.scss +35 -120
- data/app/javascript/stylesheets/rails-mini-profiler.scss +90 -61
- data/app/javascript/stylesheets/traces.scss +17 -17
- data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +8 -15
- data/app/search/rails_mini_profiler/base_search.rb +67 -0
- data/app/search/rails_mini_profiler/profiled_request_search.rb +34 -0
- data/app/views/rails_mini_profiler/badge.html.erb +2 -2
- data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +8 -58
- data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +20 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
- data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +1 -1
- data/lib/rails_mini_profiler/configuration.rb +3 -0
- data/lib/rails_mini_profiler/engine.rb +12 -8
- data/lib/rails_mini_profiler/middleware.rb +3 -3
- data/lib/rails_mini_profiler/tracing/controller_tracer.rb +15 -0
- data/lib/rails_mini_profiler/tracing/null_trace.rb +7 -0
- data/lib/rails_mini_profiler/tracing/sequel_tracer.rb +37 -0
- data/lib/rails_mini_profiler/tracing/sequel_tracker.rb +37 -0
- data/lib/rails_mini_profiler/tracing/subscriptions.rb +34 -0
- data/lib/rails_mini_profiler/{models → tracing}/trace.rb +10 -2
- data/lib/rails_mini_profiler/tracing/trace_factory.rb +37 -0
- data/lib/rails_mini_profiler/tracing/tracer.rb +31 -0
- data/lib/rails_mini_profiler/tracing/view_tracer.rb +12 -0
- data/lib/rails_mini_profiler/tracing.rb +11 -0
- data/lib/rails_mini_profiler/version.rb +1 -1
- data/lib/rails_mini_profiler.rb +4 -8
- data/vendor/assets/images/check.svg +3 -0
- data/vendor/assets/images/chevron.svg +3 -0
- data/vendor/assets/images/filter.svg +1 -0
- data/vendor/assets/images/logo_variant.svg +2 -2
- data/vendor/assets/images/search.svg +4 -5
- data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
- data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
- metadata +33 -4
- data/lib/rails_mini_profiler/tracers.rb +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e87dd3ae71d8ef0ab8021c31b037db8b290521782577dad5a7143cbbbd2f8f1
|
4
|
+
data.tar.gz: 1f7060bde727f2dbcdd7f4b6269d2436744cfb625484ed7bf43b5e4659743e6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e90925e2addb71264c09084a9b4244553b466f40e87f96902e60e9301b3befe3ed898a6578e6dd4a3c5c51cb18974fecda9036e6e5d82f27f3bac5bbce9e3aa
|
7
|
+
data.tar.gz: ed964b334db077211701b956c43f9f0e778bcd260e1097f1eaface96ffacbefe5c6e6bef01e6326f06db318da925a1a0d3a72790bf6c267e0e7d1309844ff95b
|
@@ -7,11 +7,9 @@ module RailsMiniProfiler
|
|
7
7
|
before_action :set_profiled_request, only: %i[show destroy]
|
8
8
|
|
9
9
|
def index
|
10
|
-
@profiled_requests = ProfiledRequest
|
11
|
-
|
12
|
-
|
13
|
-
@profiled_requests = @profiled_requests.where('request_path LIKE ?', "%#{params[:path]}%") if params[:path]
|
14
|
-
@pagy, @profiled_requests = pagy(@profiled_requests, items: configuration.ui.page_size)
|
10
|
+
@profiled_requests = ProfiledRequest.where(user_id: user_id).order(id: :desc)
|
11
|
+
search = ProfiledRequestSearch.new(index_params, scope: @profiled_requests)
|
12
|
+
@pagy, @profiled_requests = pagy(search.results, items: configuration.ui.page_size)
|
15
13
|
@profiled_requests = @profiled_requests.map { |request| present(request) }
|
16
14
|
end
|
17
15
|
|
@@ -31,16 +29,21 @@ module RailsMiniProfiler
|
|
31
29
|
|
32
30
|
def destroy_all
|
33
31
|
ProfiledRequest.transaction do
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
profiled_requests = ProfiledRequest.where(user_id: user_id)
|
33
|
+
profiled_requests = ProfiledRequestSearch.new(index_params, scope: profiled_requests).results
|
34
|
+
Flamegraph.joins(:profiled_request).merge(profiled_requests).delete_all
|
35
|
+
Trace.joins(:profiled_request).merge(profiled_requests).delete_all
|
36
|
+
profiled_requests.delete_all
|
38
37
|
end
|
39
|
-
redirect_to profiled_requests_url, notice: 'Profiled
|
38
|
+
redirect_to profiled_requests_url, notice: 'Profiled requests cleared', status: :see_other
|
40
39
|
end
|
41
40
|
|
42
41
|
private
|
43
42
|
|
43
|
+
def index_params
|
44
|
+
params.permit(:path, :duration, id: [], method: [], media_type: [], status: [])
|
45
|
+
end
|
46
|
+
|
44
47
|
def user_id
|
45
48
|
@user_id ||= User.get(request.env)
|
46
49
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-filter"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
2
|
-
<style
|
1
|
+
<svg id="logo-variant" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
2
|
+
<style>#logo-variant path{fill:none;stroke-width:64;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4}</style>
|
3
3
|
<defs>
|
4
4
|
<linearGradient id="lightShadow">
|
5
5
|
<stop offset="0" style="stop-color:#c22121;stop-opacity:1"/>
|
@@ -1,10 +1,9 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
3
|
-
<
|
4
|
-
|
5
|
-
<g id="Search" transform="translate(2.000000, 2.000000)" fill="#000000" fill-rule="nonzero">
|
3
|
+
<g id="Iconly/Bulk/Search" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
|
4
|
+
<g id="Search" transform="translate(2.000000, 2.000000)" fill="currentColor" fill-rule="nonzero">
|
6
5
|
<ellipse id="Ellipse_746" cx="8.59921927" cy="8.65324385" rx="8.59921927" ry="8.65324385"></ellipse>
|
7
|
-
<path d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
6
|
+
<path fill="currentColor" d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
8
7
|
</g>
|
9
8
|
</g>
|
10
|
-
</svg>
|
9
|
+
</svg>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import { Controller } from "stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["count"];
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.setCount();
|
8
|
+
}
|
9
|
+
|
10
|
+
checkAll() {
|
11
|
+
this.setAllCheckboxes(true);
|
12
|
+
this.setCount();
|
13
|
+
}
|
14
|
+
|
15
|
+
checkNone() {
|
16
|
+
this.setAllCheckboxes(false);
|
17
|
+
this.setCount();
|
18
|
+
}
|
19
|
+
|
20
|
+
onChecked() {
|
21
|
+
this.setCount();
|
22
|
+
}
|
23
|
+
|
24
|
+
setAllCheckboxes(checked) {
|
25
|
+
this.checkboxes.forEach((el) => {
|
26
|
+
const checkbox = el;
|
27
|
+
|
28
|
+
if (!checkbox.disabled) {
|
29
|
+
checkbox.checked = checked;
|
30
|
+
}
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
setCount() {
|
35
|
+
if (this.hasCountTarget) {
|
36
|
+
const count = this.selectedCheckboxes.length;
|
37
|
+
this.countTarget.innerHTML = `${count} selected`;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
get selectedCheckboxes() {
|
42
|
+
return this.checkboxes.filter((c) => c.checked);
|
43
|
+
}
|
44
|
+
|
45
|
+
get checkboxes() {
|
46
|
+
return new Array(...this.element.querySelectorAll("input[type=checkbox]"));
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { Controller } from 'stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["enable"];
|
5
|
+
|
6
|
+
enable() {
|
7
|
+
this.enableTarget.disabled = false;
|
8
|
+
}
|
9
|
+
|
10
|
+
disable() {
|
11
|
+
this.enableTarget.disabled = true;
|
12
|
+
}
|
13
|
+
|
14
|
+
change(event) {
|
15
|
+
if (event.type.match(/rmp:select:.*/)) {
|
16
|
+
if (event.detail.count > 0) {
|
17
|
+
this.enable();
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
this.disable()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import { Controller } from "stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["filter"];
|
5
|
+
|
6
|
+
apply() {
|
7
|
+
location.href = `${window.location.pathname}?${this.params}`;
|
8
|
+
}
|
9
|
+
|
10
|
+
post() {
|
11
|
+
const token = document.head.querySelector(
|
12
|
+
'meta[name="csrf-token"]'
|
13
|
+
).content;
|
14
|
+
const path = `${window.location.pathname}/destroy_all?${this.params}`;
|
15
|
+
fetch(path, {
|
16
|
+
method: "DELETE",
|
17
|
+
redirect: "follow",
|
18
|
+
headers: {
|
19
|
+
"Content-Type": "application/json",
|
20
|
+
credentials: "same-origin",
|
21
|
+
},
|
22
|
+
body: JSON.stringify({ authenticity_token: token }),
|
23
|
+
}).then((response) => {
|
24
|
+
if (response.redirected) {
|
25
|
+
window.location.href = response.url;
|
26
|
+
}
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
get params() {
|
31
|
+
return this.activeFilterTargets()
|
32
|
+
.map((t) => `${t.name}=${t.value}`)
|
33
|
+
.join("&");
|
34
|
+
}
|
35
|
+
|
36
|
+
activeFilterTargets() {
|
37
|
+
return this.filterTargets.filter(function (target) {
|
38
|
+
if (target.type === "checkbox" || target.type === "radio")
|
39
|
+
return target.checked;
|
40
|
+
|
41
|
+
return target.value.length > 0;
|
42
|
+
});
|
43
|
+
}
|
44
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { Controller } from "stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["field"];
|
5
|
+
|
6
|
+
clear(){
|
7
|
+
this.eventTarget.value = null;
|
8
|
+
window.dispatchEvent(new CustomEvent('search-controller:submit', {}))
|
9
|
+
}
|
10
|
+
|
11
|
+
submit(event) {
|
12
|
+
event.preventDefault();
|
13
|
+
|
14
|
+
if (event.key === 'Enter' || event.type === 'click') {
|
15
|
+
window.dispatchEvent(new CustomEvent('search-controller:submit', {}))
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { Controller } from 'stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ['all', 'selectable']
|
5
|
+
|
6
|
+
selectAll (event) {
|
7
|
+
const checked = event.target.checked
|
8
|
+
this.allTarget.indeterminate = false
|
9
|
+
this._setAllCheckboxes(checked)
|
10
|
+
this._dispatch('change', { count: this.selectedCount })
|
11
|
+
}
|
12
|
+
|
13
|
+
onSelected () {
|
14
|
+
this.allTarget.indeterminate = !!this._indeterminate
|
15
|
+
this._dispatch('change', { count: this.selectedCount })
|
16
|
+
}
|
17
|
+
|
18
|
+
get selectedCount () {
|
19
|
+
return this.selected.length
|
20
|
+
}
|
21
|
+
|
22
|
+
get selected () {
|
23
|
+
return this.selectables.filter((c) => c.checked)
|
24
|
+
}
|
25
|
+
|
26
|
+
get selectables () {
|
27
|
+
return new Array(...this.selectableTargets)
|
28
|
+
}
|
29
|
+
|
30
|
+
_setAllCheckboxes (checked) {
|
31
|
+
this.selectables.forEach((el) => {
|
32
|
+
const checkbox = el
|
33
|
+
|
34
|
+
if (!checkbox.disabled) {
|
35
|
+
checkbox.checked = checked
|
36
|
+
}
|
37
|
+
})
|
38
|
+
}
|
39
|
+
|
40
|
+
get _indeterminate () {
|
41
|
+
return this.selected.length !== this.selectableTargets.length && this.selected.length > 0
|
42
|
+
}
|
43
|
+
|
44
|
+
_dispatch (name, detail) {
|
45
|
+
window.dispatchEvent(new CustomEvent(`rmp:select:${name}`, { bubbles: true, detail }))
|
46
|
+
}
|
47
|
+
}
|
@@ -3,17 +3,23 @@ import '../stylesheets/rails-mini-profiler.scss'
|
|
3
3
|
import tippy from 'tippy.js'
|
4
4
|
import 'tippy.js/dist/tippy.css'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
import { Application } from 'stimulus'
|
7
|
+
import { Dropdown } from 'tailwindcss-stimulus-components'
|
8
|
+
import Checklist from '../js/checklist_controller'
|
9
|
+
import Selectable from '../js/select_controller'
|
10
|
+
import Filter from '../js/filter_controller'
|
11
|
+
import Search from '../js/search_controller'
|
12
|
+
import Enable from '../js/enable_controller'
|
13
|
+
|
14
|
+
const application = Application.start();
|
15
|
+
|
16
|
+
application.register('dropdown', Dropdown)
|
17
|
+
application.register('checklist', Checklist)
|
18
|
+
application.register('selectable', Selectable)
|
19
|
+
application.register('filters', Filter)
|
20
|
+
application.register('search', Search)
|
21
|
+
application.register('enable', Enable)
|
22
|
+
|
17
23
|
|
18
24
|
function setupTraceSearch() {
|
19
25
|
const traceNameSearch = document.getElementById('trace-search')
|
@@ -30,8 +36,8 @@ function setupTraceSearch() {
|
|
30
36
|
function setupRequestTable() {
|
31
37
|
const profiledRequestTable = document.getElementById('profiled-requests-table');
|
32
38
|
if (profiledRequestTable) {
|
33
|
-
const rows = profiledRequestTable.
|
34
|
-
for (let i =
|
39
|
+
const rows = profiledRequestTable.rows;
|
40
|
+
for (let i = 1; i < rows.length; i++) {
|
35
41
|
const currentRow = profiledRequestTable.rows[i]
|
36
42
|
const link = currentRow.dataset.link
|
37
43
|
const createClickHandler = function () {
|
@@ -39,7 +45,10 @@ function setupRequestTable() {
|
|
39
45
|
window.location.href = link
|
40
46
|
}
|
41
47
|
}
|
42
|
-
|
48
|
+
if (link) {
|
49
|
+
currentRow.onclick = createClickHandler(currentRow)
|
50
|
+
|
51
|
+
}
|
43
52
|
}
|
44
53
|
}
|
45
54
|
}
|
@@ -73,7 +82,6 @@ function setupTraceBars () {
|
|
73
82
|
// Trace Bar Popovers
|
74
83
|
document.addEventListener('DOMContentLoaded', () => {
|
75
84
|
setupRequestTable();
|
76
|
-
setupRequestSearch();
|
77
85
|
setupTraceBars();
|
78
86
|
setupTraceSearch();
|
79
87
|
}, false)
|
@@ -9,18 +9,12 @@
|
|
9
9
|
.pagy-nav > .page.active,
|
10
10
|
.pagy-nav > .page > a {
|
11
11
|
padding: 0.5rem 1rem;
|
12
|
-
|
13
|
-
color: var(--grey-900);
|
14
|
-
|
15
|
-
background: white;
|
16
12
|
border: 1px solid var(--border-color);
|
17
13
|
border-right: none;
|
18
|
-
|
14
|
+
background: white;
|
15
|
+
color: var(--grey-900);
|
19
16
|
cursor: pointer;
|
20
|
-
|
21
|
-
&:hover {
|
22
|
-
background: var(--grey-100);
|
23
|
-
}
|
17
|
+
text-decoration: none;
|
24
18
|
}
|
25
19
|
|
26
20
|
.pagy-nav > .page.prev a,
|
@@ -30,15 +24,15 @@
|
|
30
24
|
|
31
25
|
.pagy-nav > .page.next a,
|
32
26
|
.pagy-nav > .page.next.disabled {
|
33
|
-
border-radius: 0 5px 5px 0;
|
34
27
|
border-right: 1px solid var(--border-color);
|
28
|
+
border-radius: 0 5px 5px 0;
|
35
29
|
}
|
36
30
|
|
37
31
|
.pagy-nav > .page.active {
|
38
|
-
cursor: default;
|
39
|
-
background: var(--red-500);
|
40
32
|
border-color: var(--red-500);
|
33
|
+
background: var(--red-500);
|
41
34
|
color: white;
|
35
|
+
cursor: default;
|
42
36
|
|
43
37
|
&:hover {
|
44
38
|
background: var(--red-500);
|
@@ -46,9 +40,16 @@
|
|
46
40
|
}
|
47
41
|
|
48
42
|
.pagy-nav > .page.disabled {
|
49
|
-
cursor: default;
|
50
43
|
color: var(--grey-500);
|
44
|
+
cursor: default;
|
45
|
+
|
51
46
|
&:hover {
|
52
47
|
background: white;
|
53
48
|
}
|
54
49
|
}
|
50
|
+
|
51
|
+
.pagy-nav > .page.disabled:hover,
|
52
|
+
.pagy-nav > .page.active:hover,
|
53
|
+
.pagy-nav > .page > a:hover {
|
54
|
+
background: var(--grey-100);
|
55
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
.placeholder {
|
3
|
+
display: flex;
|
4
|
+
width: 100%;
|
5
|
+
flex-direction: column;
|
6
|
+
align-items: center;
|
7
|
+
justify-content: center;
|
8
|
+
padding-bottom: 2rem;
|
9
|
+
}
|
10
|
+
|
11
|
+
.placeholder-image {
|
12
|
+
width: 30%;
|
13
|
+
height: 30%;
|
14
|
+
-webkit-filter: grayscale(1) brightness(2.5);
|
15
|
+
}
|
16
|
+
|
17
|
+
.placeholder-text {
|
18
|
+
padding: 1rem 0;
|
19
|
+
color: var(--grey-400);
|
20
|
+
text-align: center;
|
21
|
+
}
|
22
|
+
|
23
|
+
.placeholder-link {
|
24
|
+
color: var(--grey-400);
|
25
|
+
|
26
|
+
&:visited {
|
27
|
+
color: var(--grey-400);
|
28
|
+
}
|
29
|
+
|
30
|
+
&:hover {
|
31
|
+
color: var(--grey-900);
|
32
|
+
}
|
33
|
+
}
|