onlylogs 0.11.0 โ 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +251 -77
- data/app/assets/images/onlylogs/favicon/apple-touch-icon.png +0 -0
- data/app/assets/images/onlylogs/favicon/favicon-96x96.png +0 -0
- data/app/assets/images/onlylogs/favicon/favicon.ico +0 -0
- data/app/assets/images/onlylogs/favicon/favicon.svg +1 -3
- data/app/assets/images/onlylogs/favicon/web-app-manifest-192x192.png +0 -0
- data/app/assets/images/onlylogs/favicon/web-app-manifest-512x512.png +0 -0
- data/app/assets/images/onlylogs/logo.png +0 -0
- data/app/assets/javascripts/onlylogs/clusterize.js +26 -1
- data/app/channels/onlylogs/logs_channel.rb +14 -9
- data/app/controllers/concerns/onlylogs/authentication.rb +26 -0
- data/app/controllers/onlylogs/application_controller.rb +1 -18
- data/app/controllers/onlylogs/queries_controller.rb +70 -0
- data/app/controllers/onlylogs/viewer_controller.rb +7 -0
- data/app/javascript/onlylogs/controllers/keyboard_shortcuts_controller.js +131 -21
- data/app/javascript/onlylogs/controllers/log_cursor_controller.js +286 -0
- data/app/javascript/onlylogs/controllers/log_streamer_controller.js +42 -2
- data/app/javascript/onlylogs/controllers/match_highlight_controller.js +82 -0
- data/app/javascript/onlylogs/controllers/queries_controller.js +470 -0
- data/app/javascript/onlylogs/controllers/range_slider_controller.js +5 -4
- data/app/javascript/onlylogs/controllers/text_selection_controller.js +136 -15
- data/app/models/onlylogs/file.rb +14 -5
- data/app/models/onlylogs/grep.rb +13 -6
- data/app/models/onlylogs/queries_record.rb +28 -0
- data/app/models/onlylogs/query.rb +29 -0
- data/app/views/onlylogs/logs/index.html.erb +1 -1
- data/app/views/onlylogs/shared/_log_container.html.erb +165 -27
- data/app/views/onlylogs/shared/_log_container_styles.html.erb +729 -44
- data/app/views/onlylogs/shared/_shortcuts_dialog.html.erb +62 -0
- data/bin/super_grep +7 -0
- data/bin/super_ripgrep +5 -0
- data/config/routes.rb +1 -0
- data/lib/onlylogs/configuration.rb +14 -1
- data/lib/onlylogs/http_device.rb +28 -13
- data/lib/onlylogs/version.rb +1 -1
- data/lib/onlylogs.rb +4 -0
- metadata +38 -1
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
autoscroll = local_assigns[:autoscroll] != false
|
|
9
9
|
filter = local_assigns[:filter] || ""
|
|
10
10
|
regexp_mode = local_assigns[:regexp_mode] == true
|
|
11
|
+
case_insensitive = local_assigns[:case_insensitive] == true
|
|
11
12
|
# A file nothing writes to any more (no drain attached, a rotated log) has nothing
|
|
12
13
|
# to follow, so live mode is not offered at all and the viewer opens in search.
|
|
13
14
|
mode = live_enabled ? (local_assigns[:mode] || "live") : "static"
|
|
@@ -19,21 +20,35 @@
|
|
|
19
20
|
raise SecurityError, "File path not allowed" unless Onlylogs.file_path_permitted?(log_file_path)
|
|
20
21
|
|
|
21
22
|
encrypted_log_file_path = Onlylogs::SecureFilePath.encrypt(log_file_path)
|
|
23
|
+
|
|
24
|
+
shortcut_modifier = request.user_agent.to_s.match?(/Macintosh|iPhone|iPad/) ? "control" : "alt"
|
|
25
|
+
shortcut_modifier_label = shortcut_modifier == "control" ? "โ Control" : "Alt"
|
|
22
26
|
%>
|
|
23
27
|
|
|
24
|
-
<div data-controller="log-streamer text-selection keyboard-shortcuts"
|
|
28
|
+
<div data-controller="log-streamer text-selection keyboard-shortcuts log-cursor match-highlight queries"
|
|
29
|
+
data-queries-log-file-path-value="<%= encrypted_log_file_path %>"
|
|
30
|
+
data-queries-endpoint-value="<%= onlylogs.queries_path %>"
|
|
31
|
+
data-keyboard-shortcuts-modifier-value="<%= shortcut_modifier %>"
|
|
25
32
|
data-log-streamer-file-path-value="<%= encrypted_log_file_path %>"
|
|
26
33
|
<% if start_position.present? %>data-log-streamer-start-position-value="<%= start_position %>"<% end %>
|
|
27
34
|
<% if end_position.present? %>data-log-streamer-end-position-value="<%= end_position %>"<% end %>
|
|
28
35
|
data-log-streamer-filter-value="<%= filter %>"
|
|
29
36
|
data-log-streamer-auto-scroll-value="<%= autoscroll %>"
|
|
30
37
|
data-log-streamer-regexp-mode-value="<%= regexp_mode %>"
|
|
38
|
+
data-log-streamer-case-insensitive-value="<%= case_insensitive %>"
|
|
31
39
|
data-log-streamer-mode-value="<%= mode %>"
|
|
32
40
|
data-log-streamer-live-enabled-value="<%= live_enabled %>"
|
|
33
41
|
data-log-streamer-file-size-value="<%= file_size %>"
|
|
34
|
-
data-action="text-selection:start->log-streamer#pauseForSelection text-selection:search->log-streamer#switchToSearch
|
|
42
|
+
data-action="text-selection:start->log-streamer#pauseForSelection text-selection:search->log-streamer#switchToSearch
|
|
43
|
+
log-streamer:cleared->log-cursor#reset log-streamer:cleared->text-selection#forgetSelection log-streamer:context-line->log-cursor#placeAt
|
|
44
|
+
log-streamer:rendering->text-selection#saveSelection log-streamer:rendered->text-selection#restoreSelection
|
|
45
|
+
log-cursor:move->log-streamer#pauseForSelection log-cursor:select->text-selection#showForSelection
|
|
46
|
+
log-cursor:search->text-selection#searchSelectedText keyboard-shortcuts:lines->log-cursor#focus
|
|
47
|
+
log-streamer:context-line->queries#cleared"
|
|
35
48
|
class="onlylogs-log-container" >
|
|
36
|
-
<div data-log-streamer-target="logLines" data-text-selection-target="logLines"
|
|
49
|
+
<div data-log-streamer-target="logLines" data-text-selection-target="logLines" data-log-cursor-target="lines" data-match-highlight-target="lines"
|
|
50
|
+
data-action="keydown->log-cursor#navigate mousedown->log-cursor#pointAt focusin->log-cursor#activate"
|
|
51
|
+
id="scrollArea" class="onlylogs-log-lines clusterize-scroll">
|
|
37
52
|
<div id="contentArea" class="clusterize-content">
|
|
38
53
|
</div>
|
|
39
54
|
</div>
|
|
@@ -55,8 +70,8 @@
|
|
|
55
70
|
data-text-selection-target="button"
|
|
56
71
|
class="onlylogs-context-menu hidden-button"
|
|
57
72
|
data-action="click->text-selection#searchSelectedText"
|
|
58
|
-
title="Search selected text">
|
|
59
|
-
๐ Search
|
|
73
|
+
title="Search selected text (Enter)">
|
|
74
|
+
๐ Search <kbd>โต</kbd>
|
|
60
75
|
</button>
|
|
61
76
|
|
|
62
77
|
<div class="onlylogs-log-toolbar">
|
|
@@ -66,68 +81,179 @@
|
|
|
66
81
|
<button type="button"
|
|
67
82
|
class="onlylogs-mode-switch__button"
|
|
68
83
|
data-onlylogs-mode-button="live"
|
|
84
|
+
data-onlylogs-shortcut="l"
|
|
69
85
|
data-log-streamer-target="liveButton"
|
|
70
|
-
data-keyboard-shortcuts-target="liveButton"
|
|
71
86
|
data-action="click->log-streamer#switchToLive"
|
|
72
87
|
aria-pressed="<%= mode == "live" %>"
|
|
73
|
-
title="Follow the file as it is written
|
|
74
|
-
<span>โถ
|
|
88
|
+
title="Follow the file as it is written">
|
|
89
|
+
<span>โถ Live</span>
|
|
75
90
|
</button>
|
|
76
91
|
<button type="button"
|
|
77
92
|
class="onlylogs-mode-switch__button"
|
|
78
93
|
data-onlylogs-mode-button="static"
|
|
94
|
+
data-onlylogs-shortcut="s"
|
|
79
95
|
data-log-streamer-target="searchButton"
|
|
80
|
-
data-keyboard-shortcuts-target="searchButton"
|
|
81
96
|
data-action="click->log-streamer#switchToSearch"
|
|
82
97
|
aria-pressed="<%= mode != "live" %>"
|
|
83
|
-
title="Search what is already in the file
|
|
84
|
-
<span>๐
|
|
98
|
+
title="Search what is already in the file">
|
|
99
|
+
<span>๐ Search</span>
|
|
85
100
|
</button>
|
|
86
101
|
</div>
|
|
87
102
|
<% end %>
|
|
88
|
-
<div>
|
|
89
|
-
<label class="toolbar-label">
|
|
103
|
+
<div class="onlylogs-query">
|
|
104
|
+
<label class="toolbar-label" for="onlylogs-filter">
|
|
90
105
|
<span data-onlylogs-mode="live">Follow:</span>
|
|
91
106
|
<span data-onlylogs-mode="static">Query:</span>
|
|
92
|
-
|
|
107
|
+
</label>
|
|
108
|
+
<div class="saved-searches" data-queries-target="box" data-action="keydown->queries#navigate">
|
|
109
|
+
<div class="filter-input-wrapper" data-onlylogs-shortcut="q">
|
|
93
110
|
<input type="text"
|
|
111
|
+
id="onlylogs-filter"
|
|
94
112
|
name="filter"
|
|
95
113
|
value="<%= filter %>"
|
|
96
114
|
placeholder="<%= mode == "live" ? "follow lines matchingโฆ" : "search the whole fileโฆ" %>"
|
|
115
|
+
spellcheck="false"
|
|
116
|
+
autocomplete="off"
|
|
97
117
|
data-log-streamer-target="filterInput"
|
|
98
118
|
data-text-selection-target="filterInput"
|
|
99
|
-
data-
|
|
119
|
+
data-keyboard-shortcuts-target="filterInput"
|
|
120
|
+
data-match-highlight-target="filterInput"
|
|
121
|
+
data-action="input->log-streamer#applyFilter input->queries#refresh keydown.enter->log-cursor#focus keydown.esc->log-cursor#focus">
|
|
122
|
+
<button type="button"
|
|
123
|
+
data-action="click->log-streamer#clearFilter click->queries#cleared"
|
|
124
|
+
data-onlylogs-shortcut="x"
|
|
125
|
+
class="filter-input-button clear-filter-button"
|
|
126
|
+
aria-label="Clear the query"
|
|
127
|
+
title="Clear the query">
|
|
128
|
+
<svg width="10" height="10" viewBox="0 0 10 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M2 2l6 6M8 2l-6 6"/></svg>
|
|
129
|
+
</button>
|
|
130
|
+
<button type="button"
|
|
131
|
+
class="filter-input-button saved-searches__toggle"
|
|
132
|
+
data-queries-target="toggle"
|
|
133
|
+
data-onlylogs-shortcut="v"
|
|
134
|
+
data-action="queries#toggle"
|
|
135
|
+
aria-haspopup="true"
|
|
136
|
+
aria-expanded="false"
|
|
137
|
+
aria-controls="onlylogs-saved-searches-panel"
|
|
138
|
+
aria-label="Saved searches"
|
|
139
|
+
title="Saved searches">
|
|
140
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" aria-hidden="true"><path d="M9 6h11M9 12h11M9 18h11"/><circle cx="4.5" cy="6" r="1" fill="currentColor"/><circle cx="4.5" cy="12" r="1" fill="currentColor"/><circle cx="4.5" cy="18" r="1" fill="currentColor"/></svg>
|
|
141
|
+
<span class="saved-searches__count" data-queries-target="count"></span>
|
|
142
|
+
</button>
|
|
100
143
|
<button type="button"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
144
|
+
class="filter-input-button saved-searches__star"
|
|
145
|
+
data-queries-target="star"
|
|
146
|
+
data-onlylogs-shortcut="f"
|
|
147
|
+
data-action="queries#toggleSave"
|
|
148
|
+
aria-haspopup="true"
|
|
149
|
+
aria-expanded="false"
|
|
150
|
+
aria-controls="onlylogs-save-search-panel"
|
|
151
|
+
disabled>
|
|
152
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true"><path d="M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"/></svg>
|
|
105
153
|
</button>
|
|
106
154
|
</div>
|
|
155
|
+
|
|
156
|
+
<div id="onlylogs-saved-searches-panel" class="saved-searches__panel" data-queries-target="panel" hidden>
|
|
157
|
+
<div class="saved-searches__header">
|
|
158
|
+
<span class="saved-searches__title">Saved searches</span>
|
|
159
|
+
<span class="saved-searches__keys">โโ move ยท โต apply ยท esc</span>
|
|
160
|
+
</div>
|
|
161
|
+
<ul class="saved-searches__list" data-queries-target="list" aria-label="Saved searches"></ul>
|
|
162
|
+
<div class="saved-searches__empty" data-queries-target="empty" hidden>
|
|
163
|
+
<strong>No saved searches yet</strong>
|
|
164
|
+
<span>Type a pattern, then click the star in the field to keep it here.</span>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="saved-searches__footer" data-queries-target="footer" hidden>
|
|
167
|
+
<button type="button" class="saved-searches__footer-button" data-action="queries#openSave">
|
|
168
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true"><path d="M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"/></svg>
|
|
169
|
+
<span data-queries-target="footerLabel"></span>
|
|
170
|
+
</button>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div id="onlylogs-save-search-panel" class="saved-searches__panel saved-searches__panel--save" data-queries-target="savePanel" hidden>
|
|
175
|
+
<div class="save-search__summary">
|
|
176
|
+
<span class="save-search__title" data-queries-target="saveTitle"></span>
|
|
177
|
+
<div class="save-search__current">
|
|
178
|
+
<code data-queries-target="saveFilter"></code>
|
|
179
|
+
<span data-queries-target="saveFlags"></span>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
<form class="save-search__form" data-action="queries#save">
|
|
183
|
+
<label for="onlylogs-save-search-name">Name</label>
|
|
184
|
+
<div class="save-search__row">
|
|
185
|
+
<input type="text"
|
|
186
|
+
id="onlylogs-save-search-name"
|
|
187
|
+
maxlength="255"
|
|
188
|
+
placeholder="e.g. Errors"
|
|
189
|
+
autocomplete="off"
|
|
190
|
+
data-queries-target="name"
|
|
191
|
+
data-action="input->queries#nameChanged">
|
|
192
|
+
<button type="submit" data-queries-target="saveButton" disabled>Save</button>
|
|
193
|
+
</div>
|
|
194
|
+
<p class="save-search__hint" data-queries-target="hint"></p>
|
|
195
|
+
</form>
|
|
196
|
+
<div class="save-search__saved" data-queries-target="savedRow" hidden>
|
|
197
|
+
<span>Already in your saved searches.</span>
|
|
198
|
+
<button type="button" data-action="queries#removeCurrent">Remove</button>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<template data-queries-target="rowTemplate">
|
|
203
|
+
<li class="saved-search">
|
|
204
|
+
<button type="button" class="saved-search__apply" data-action="queries#apply">
|
|
205
|
+
<span class="saved-search__line">
|
|
206
|
+
<span class="saved-search__name"></span>
|
|
207
|
+
<svg class="saved-search__check" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-label="Current search"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>
|
|
208
|
+
<span class="saved-search__edited">edited</span>
|
|
209
|
+
</span>
|
|
210
|
+
<span class="saved-search__line">
|
|
211
|
+
<code class="saved-search__filter"></code>
|
|
212
|
+
<span class="saved-search__flags"></span>
|
|
213
|
+
</span>
|
|
214
|
+
</button>
|
|
215
|
+
<input type="text" class="saved-search__rename-input" maxlength="255" autocomplete="off" hidden
|
|
216
|
+
data-action="keydown->queries#renameKey blur->queries#commitRename">
|
|
217
|
+
<button type="button" class="saved-search__action saved-search__rename" data-action="queries#rename">
|
|
218
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20h4L19 9l-4-4L4 16z"/></svg>
|
|
219
|
+
</button>
|
|
220
|
+
<button type="button" class="saved-search__action saved-search__delete" data-action="queries#destroy">
|
|
221
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13"/></svg>
|
|
222
|
+
<span class="saved-search__delete-label">Delete?</span>
|
|
223
|
+
</button>
|
|
224
|
+
</li>
|
|
225
|
+
</template>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
<div>
|
|
229
|
+
<label class="toolbar-label" data-onlylogs-shortcut="r">
|
|
230
|
+
<input id="regexpMode" type="checkbox" <%= regexp_mode ? "checked" : "" %> name="regexpMode" data-log-streamer-target="regexpMode" data-text-selection-target="regexpMode" data-match-highlight-target="regexpMode" data-action="change->log-streamer#toggleRegexpMode change->queries#refresh">
|
|
231
|
+
<span>Regexp</span>
|
|
107
232
|
</label>
|
|
108
233
|
</div>
|
|
109
234
|
<div>
|
|
110
|
-
<label class="toolbar-label">
|
|
111
|
-
<input id="
|
|
112
|
-
<span
|
|
235
|
+
<label class="toolbar-label" data-onlylogs-shortcut="i">
|
|
236
|
+
<input id="caseInsensitive" type="checkbox" <%= case_insensitive ? "checked" : "" %> name="caseInsensitive" data-log-streamer-target="caseInsensitive" data-match-highlight-target="caseInsensitive" data-action="change->log-streamer#toggleCaseInsensitive change->queries#refresh">
|
|
237
|
+
<span>Ignore case</span>
|
|
113
238
|
</label>
|
|
114
239
|
</div>
|
|
115
240
|
<div data-onlylogs-mode="live">
|
|
116
|
-
<label class="toolbar-label">
|
|
117
|
-
<input id="autoscroll" type="checkbox" <%= autoscroll ? "checked" : "" %> name="autoscroll" data-log-streamer-target="autoscroll" data-
|
|
118
|
-
<span
|
|
241
|
+
<label class="toolbar-label" data-onlylogs-shortcut="a">
|
|
242
|
+
<input id="autoscroll" type="checkbox" <%= autoscroll ? "checked" : "" %> name="autoscroll" data-log-streamer-target="autoscroll" data-action="change->log-streamer#toggleAutoScroll">
|
|
243
|
+
<span>Autoscroll</span>
|
|
119
244
|
</label>
|
|
120
245
|
</div>
|
|
121
246
|
<div data-onlylogs-mode="live">
|
|
122
247
|
<button type="button"
|
|
123
248
|
data-log-streamer-target="searchWholeFileButton"
|
|
124
249
|
class="search-whole-file-button"
|
|
250
|
+
data-onlylogs-shortcut="w"
|
|
125
251
|
data-action="click->log-streamer#searchWholeFile"
|
|
126
252
|
title="Search this text across the whole file">
|
|
127
253
|
Search whole file โ
|
|
128
254
|
</button>
|
|
129
255
|
</div>
|
|
130
|
-
<div class="range-slider-wrapper" data-onlylogs-mode="static">
|
|
256
|
+
<div class="range-slider-wrapper" data-onlylogs-mode="static" data-onlylogs-shortcut="g">
|
|
131
257
|
<%= render "onlylogs/shared/range_slider",
|
|
132
258
|
start_name: "start_position",
|
|
133
259
|
end_name: "end_position",
|
|
@@ -140,6 +266,7 @@
|
|
|
140
266
|
<button type="button"
|
|
141
267
|
data-action="click->log-streamer#resetRange"
|
|
142
268
|
class="reset-range-button"
|
|
269
|
+
data-onlylogs-shortcut="0"
|
|
143
270
|
title="Reset to full file">
|
|
144
271
|
โป
|
|
145
272
|
</button>
|
|
@@ -148,6 +275,7 @@
|
|
|
148
275
|
<button type="button"
|
|
149
276
|
data-log-streamer-target="stopButton"
|
|
150
277
|
class="stop-search-button hidden-button"
|
|
278
|
+
data-onlylogs-shortcut="."
|
|
151
279
|
data-action="click->log-streamer#stopSearch"
|
|
152
280
|
title="Stop current search">
|
|
153
281
|
โ Stop
|
|
@@ -156,7 +284,8 @@
|
|
|
156
284
|
</div>
|
|
157
285
|
|
|
158
286
|
<div class="onlylogs-toolbar-row onlylogs-toolbar-row--status">
|
|
159
|
-
<span data-log-streamer-target="results" class="results-text"
|
|
287
|
+
<span data-log-streamer-target="results" class="results-text" data-onlylogs-shortcut="j"
|
|
288
|
+
data-action="click->log-cursor#focus">Results: 0</span>
|
|
160
289
|
<span data-log-streamer-target="message" class="status-message"></span>
|
|
161
290
|
<div class="onlylogs-toolbar-row__end">
|
|
162
291
|
<% unless Onlylogs.ripgrep_enabled? %>
|
|
@@ -168,12 +297,21 @@
|
|
|
168
297
|
<button type="button"
|
|
169
298
|
data-log-streamer-target="clearButton"
|
|
170
299
|
class="clear-logs-button"
|
|
300
|
+
data-onlylogs-shortcut="c"
|
|
171
301
|
data-action="click->log-streamer#clearLogs"
|
|
172
302
|
aria-label="Clear all log lines"
|
|
173
303
|
title="Clear all log lines">
|
|
174
304
|
๐
|
|
175
305
|
</button>
|
|
306
|
+
<button type="button"
|
|
307
|
+
class="shortcuts-button"
|
|
308
|
+
data-action="keyboard-shortcuts#openHelp"
|
|
309
|
+
aria-label="Keyboard shortcuts"
|
|
310
|
+
title="Keyboard shortcuts (?) ยท hold <%= shortcut_modifier_label %> to see the letters">
|
|
311
|
+
โจ
|
|
312
|
+
</button>
|
|
176
313
|
<span data-log-streamer-target="websocketStatus" class="websocket-status websocket-status--disconnected" title="WebSocket Disconnected">๐ด</span>
|
|
314
|
+
<%= render "onlylogs/shared/shortcuts_dialog", modifier_label: shortcut_modifier_label %>
|
|
177
315
|
</div>
|
|
178
316
|
</div>
|
|
179
317
|
</div>
|