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
|
@@ -1,3 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="286" height="233" viewBox="0 0 286 233"><image width="286" height="233" xlink:href="data:image/png;base64,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"></image><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
|
|
2
|
-
@media (prefers-color-scheme: dark) { :root { filter: none; } }
|
|
3
|
-
</style></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0ea5e9" stroke-width="1.8" stroke-linecap="round"><metadata><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><rdf:Description><dc:creator>RealFaviconGenerator</dc:creator><dc:source>https://realfavicongenerator.net</dc:source></rdf:Description></rdf:RDF></metadata><path d="M4 8h16M6 8v11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8M4 8a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3"></path><path d="M9 13h6M9 16.5h4"></path></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -231,6 +231,7 @@
|
|
|
231
231
|
rows_len = rows.length;
|
|
232
232
|
if (rows_len < opts.rows_in_block) {
|
|
233
233
|
return {
|
|
234
|
+
items_start: 0,
|
|
234
235
|
top_offset: 0,
|
|
235
236
|
bottom_offset: 0,
|
|
236
237
|
rows_above: 0,
|
|
@@ -250,6 +251,7 @@
|
|
|
250
251
|
rows[i] && this_cluster_rows.push(rows[i]);
|
|
251
252
|
}
|
|
252
253
|
return {
|
|
254
|
+
items_start: items_start,
|
|
253
255
|
top_offset: top_offset,
|
|
254
256
|
bottom_offset: bottom_offset,
|
|
255
257
|
rows_above: rows_above,
|
|
@@ -271,13 +273,19 @@
|
|
|
271
273
|
}
|
|
272
274
|
var data = this.generate(rows),
|
|
273
275
|
this_cluster_rows = data.rows.join(''),
|
|
276
|
+
previous_cluster_rows = cache.data,
|
|
274
277
|
this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache),
|
|
275
278
|
top_offset_changed = this.checkChanges('top', data.top_offset, cache),
|
|
276
279
|
only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache),
|
|
277
280
|
callbacks = this.options.callbacks,
|
|
278
281
|
layout = [];
|
|
279
282
|
|
|
280
|
-
if(this_cluster_content_changed
|
|
283
|
+
if(this_cluster_content_changed && ! top_offset_changed
|
|
284
|
+
&& typeof previous_cluster_rows == 'string'
|
|
285
|
+
&& this_cluster_rows.indexOf(previous_cluster_rows) === 0) {
|
|
286
|
+
this.appendToDOM(this_cluster_rows.slice(previous_cluster_rows.length), data.bottom_offset);
|
|
287
|
+
callbacks.clusterChanged && callbacks.clusterChanged();
|
|
288
|
+
} else if(this_cluster_content_changed || top_offset_changed) {
|
|
281
289
|
if(data.top_offset) {
|
|
282
290
|
this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity'));
|
|
283
291
|
layout.push(this.renderExtraTag('top-space', data.top_offset));
|
|
@@ -286,6 +294,7 @@
|
|
|
286
294
|
data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset));
|
|
287
295
|
callbacks.clusterWillChange && callbacks.clusterWillChange();
|
|
288
296
|
this.html(layout.join(''));
|
|
297
|
+
this.rendered_start = data.items_start;
|
|
289
298
|
this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above);
|
|
290
299
|
this.content_elem.style['counter-increment'] = 'clusterize-counter ' + (data.rows_above-1);
|
|
291
300
|
callbacks.clusterChanged && callbacks.clusterChanged();
|
|
@@ -293,6 +302,18 @@
|
|
|
293
302
|
this.content_elem.lastChild.style.height = data.bottom_offset + 'px';
|
|
294
303
|
}
|
|
295
304
|
},
|
|
305
|
+
appendToDOM: function(new_rows, bottom_offset) {
|
|
306
|
+
var content_elem = this.content_elem,
|
|
307
|
+
bottom_space = content_elem.lastElementChild;
|
|
308
|
+
if(bottom_space && bottom_space.classList.contains('clusterize-bottom-space')) {
|
|
309
|
+
bottom_space.insertAdjacentHTML('beforebegin', new_rows);
|
|
310
|
+
if(bottom_offset) bottom_space.style.height = bottom_offset + 'px';
|
|
311
|
+
else content_elem.removeChild(bottom_space);
|
|
312
|
+
} else {
|
|
313
|
+
content_elem.insertAdjacentHTML('beforeend', new_rows);
|
|
314
|
+
bottom_offset && content_elem.insertAdjacentHTML('beforeend', this.renderExtraTag('bottom-space', bottom_offset));
|
|
315
|
+
}
|
|
316
|
+
},
|
|
296
317
|
// unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround
|
|
297
318
|
html: function(data) {
|
|
298
319
|
var content_elem = this.content_elem;
|
|
@@ -310,6 +331,10 @@
|
|
|
310
331
|
content_elem.innerHTML = data;
|
|
311
332
|
}
|
|
312
333
|
},
|
|
334
|
+
// index in rows of the first row currently in the DOM
|
|
335
|
+
getRenderedStart: function() {
|
|
336
|
+
return this.rendered_start || 0;
|
|
337
|
+
},
|
|
313
338
|
getChildNodes: function(tag) {
|
|
314
339
|
var child_nodes = tag.children, nodes = [];
|
|
315
340
|
for (var i = 0, ii = child_nodes.length; i < ii; i++) {
|
|
@@ -28,6 +28,7 @@ module Onlylogs
|
|
|
28
28
|
filter = data["filter"].presence
|
|
29
29
|
mode = data["mode"] || "live"
|
|
30
30
|
regexp_mode = data["regexp_mode"] == true || data["regexp_mode"] == "true"
|
|
31
|
+
case_sensitive = !(data["case_insensitive"] == true || data["case_insensitive"] == "true")
|
|
31
32
|
|
|
32
33
|
file_size = ::File.size(file_path)
|
|
33
34
|
start_position = (data["start_position"]&.to_i || 0).clamp(0, file_size)
|
|
@@ -35,10 +36,10 @@ module Onlylogs
|
|
|
35
36
|
|
|
36
37
|
if mode == "static"
|
|
37
38
|
# Read the entire file with filter and send all matching lines
|
|
38
|
-
read_static(file_path, filter, regexp_mode, start_position, end_position)
|
|
39
|
+
read_static(file_path, filter, regexp_mode, start_position, end_position, case_sensitive: case_sensitive)
|
|
39
40
|
else
|
|
40
41
|
# Follow the tail of the file indefinitely
|
|
41
|
-
start_log_watcher(file_path, filter, regexp_mode)
|
|
42
|
+
start_log_watcher(file_path, filter, regexp_mode, case_sensitive: case_sensitive)
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
@@ -96,12 +97,13 @@ module Onlylogs
|
|
|
96
97
|
ROTATED_MESSAGE = 'The file was rotated. <button type="button" class="reload-button" ' \
|
|
97
98
|
'data-action="click->log-streamer#reset">Reload</button>'
|
|
98
99
|
|
|
99
|
-
def start_log_watcher(file_path, filter = nil, regexp_mode = false)
|
|
100
|
+
def start_log_watcher(file_path, filter = nil, regexp_mode = false, case_sensitive: true)
|
|
100
101
|
return if @log_watcher_running
|
|
101
102
|
|
|
102
103
|
@log_watcher_running = true
|
|
103
104
|
@filter = filter
|
|
104
105
|
@regexp_mode = regexp_mode
|
|
106
|
+
@case_sensitive = case_sensitive
|
|
105
107
|
|
|
106
108
|
transmit({action: "message", content: "Reading file. Please wait..."})
|
|
107
109
|
|
|
@@ -120,7 +122,8 @@ module Onlylogs
|
|
|
120
122
|
lines_to_send = []
|
|
121
123
|
|
|
122
124
|
new_lines.each do |log_line|
|
|
123
|
-
if @filter.present? && !Onlylogs::Grep.match_line?(log_line, @filter, regexp_mode: @regexp_mode
|
|
125
|
+
if @filter.present? && !Onlylogs::Grep.match_line?(log_line, @filter, regexp_mode: @regexp_mode,
|
|
126
|
+
case_sensitive: @case_sensitive)
|
|
124
127
|
next
|
|
125
128
|
end
|
|
126
129
|
|
|
@@ -169,7 +172,8 @@ module Onlylogs
|
|
|
169
172
|
@log_file = nil
|
|
170
173
|
end
|
|
171
174
|
|
|
172
|
-
def read_static(file_path, filter = nil, regexp_mode = false, start_position = 0, end_position = nil
|
|
175
|
+
def read_static(file_path, filter = nil, regexp_mode = false, start_position = 0, end_position = nil,
|
|
176
|
+
case_sensitive: true)
|
|
173
177
|
@log_watcher_running = true
|
|
174
178
|
@log_file = Onlylogs::File.new(file_path, last_position: 0)
|
|
175
179
|
|
|
@@ -186,7 +190,8 @@ module Onlylogs
|
|
|
186
190
|
show_expand_button = filter.present?
|
|
187
191
|
|
|
188
192
|
Rails.logger.silence(Logger::ERROR) do
|
|
189
|
-
each_matching_line(file_path, filter, regexp_mode, start_position, end_position
|
|
193
|
+
each_matching_line(file_path, filter, regexp_mode, start_position, end_position,
|
|
194
|
+
case_sensitive: case_sensitive) do |log_line, byte_offset|
|
|
190
195
|
# Buffer previous line and skip it to avoid cut-off lines at boundaries
|
|
191
196
|
if last_line
|
|
192
197
|
@batch_sender.add_line(render_log_line(last_line, byte_offset: last_byte_offset,
|
|
@@ -232,12 +237,12 @@ module Onlylogs
|
|
|
232
237
|
#
|
|
233
238
|
# A range that starts mid-line opens with a fragment of a line, which is
|
|
234
239
|
# dropped - but its bytes still count towards every offset after it.
|
|
235
|
-
def each_matching_line(file_path, filter, regexp_mode, start_position, end_position)
|
|
240
|
+
def each_matching_line(file_path, filter, regexp_mode, start_position, end_position, case_sensitive: true)
|
|
236
241
|
skip_first = start_position > 0
|
|
237
242
|
|
|
238
243
|
if filter.present?
|
|
239
|
-
@log_file.grep(filter, regexp_mode: regexp_mode,
|
|
240
|
-
end_position: end_position, timeout: Onlylogs.search_timeout) do |result|
|
|
244
|
+
@log_file.grep(filter, regexp_mode: regexp_mode, case_sensitive: case_sensitive,
|
|
245
|
+
start_position: start_position, end_position: end_position, timeout: Onlylogs.search_timeout) do |result|
|
|
241
246
|
break if reading_stopped?
|
|
242
247
|
|
|
243
248
|
if skip_first
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Onlylogs
|
|
2
|
+
module Authentication
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
before_action :authenticate_onlylogs_user!
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def authenticate_onlylogs_user!
|
|
12
|
+
return super if defined?(super)
|
|
13
|
+
|
|
14
|
+
return if Onlylogs.disable_basic_authentication?
|
|
15
|
+
|
|
16
|
+
unless Onlylogs.basic_auth_configured?
|
|
17
|
+
render plain: "Onlylogs authentication not configured. Please configure basic auth credentials.", status: :forbidden
|
|
18
|
+
return
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
authenticate_or_request_with_http_basic("onlylogs") do |username, password|
|
|
22
|
+
username == Onlylogs.basic_auth_user && password == Onlylogs.basic_auth_password
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,22 +1,5 @@
|
|
|
1
1
|
module Onlylogs
|
|
2
2
|
class ApplicationController < Onlylogs.parent_controller&.constantize || ActionController::Base
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
def authenticate_onlylogs_user!
|
|
8
|
-
return super if defined?(super)
|
|
9
|
-
|
|
10
|
-
return if Onlylogs.disable_basic_authentication?
|
|
11
|
-
|
|
12
|
-
unless Onlylogs.basic_auth_configured?
|
|
13
|
-
render plain: "Onlylogs authentication not configured. Please configure basic auth credentials.", status: :forbidden
|
|
14
|
-
return
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
authenticate_or_request_with_http_basic("onlylogs") do |username, password|
|
|
18
|
-
username == Onlylogs.basic_auth_user && password == Onlylogs.basic_auth_password
|
|
19
|
-
end
|
|
20
|
-
end
|
|
3
|
+
include Authentication
|
|
21
4
|
end
|
|
22
5
|
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Onlylogs
|
|
4
|
+
class QueriesController < ViewerController
|
|
5
|
+
before_action :set_log_file
|
|
6
|
+
|
|
7
|
+
rescue_from(ActiveRecord::RecordInvalid) { |e| render_invalid(e.record) }
|
|
8
|
+
rescue_from(ActiveRecord::RecordNotUnique) { render_error("Name has already been taken", :unprocessable_entity) }
|
|
9
|
+
rescue_from(ActiveRecord::RecordNotFound) { render_error("Query not found", :not_found) }
|
|
10
|
+
rescue_from(Onlylogs::Error) { render_error("Log file not found", :not_found) }
|
|
11
|
+
rescue_from(SqliteShards::UnavailableError) { |e| render_error("Saved queries are unavailable: #{e.message}", :service_unavailable) }
|
|
12
|
+
rescue_from(ForbiddenPathError) { |e| render_error(e.message, :forbidden) }
|
|
13
|
+
rescue_from(SecureFilePath::SecurityError) { |e| render_error(e.message, :bad_request) }
|
|
14
|
+
|
|
15
|
+
def index
|
|
16
|
+
render json: {queries: @log_file.queries}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def show
|
|
20
|
+
render json: query
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create
|
|
24
|
+
render json: @log_file.queries.create!(query_params), status: :created
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update
|
|
28
|
+
query.update!(query_params)
|
|
29
|
+
|
|
30
|
+
render json: query
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def destroy
|
|
34
|
+
query.destroy!
|
|
35
|
+
|
|
36
|
+
head :no_content
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def query
|
|
42
|
+
@query ||= @log_file.queries.find(params[:id])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def query_params
|
|
46
|
+
params.expect(query: [:name, :filter, :regexp_mode, :case_insensitive])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def set_log_file
|
|
50
|
+
path = SecureFilePath.decrypt(params.require(:log_file_path))
|
|
51
|
+
|
|
52
|
+
unless Onlylogs.file_path_permitted?(path) && Onlylogs.log_file_authorized?(self, path)
|
|
53
|
+
raise ForbiddenPathError, "Access denied to this log file"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@log_file = File.new(path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def render_invalid(record)
|
|
60
|
+
render json: {
|
|
61
|
+
error: record.errors.full_messages.to_sentence,
|
|
62
|
+
errors: record.errors.to_hash
|
|
63
|
+
}, status: :unprocessable_entity
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def render_error(message, status)
|
|
67
|
+
render json: {error: message}, status: status
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
module Onlylogs
|
|
2
|
+
# Base for the endpoints the embedded log viewer calls. A host that shows the
|
|
3
|
+
# viewer to more people than the engine's own pages can give it a different parent.
|
|
4
|
+
class ViewerController < (Onlylogs.viewer_parent_controller || Onlylogs.parent_controller)&.constantize || ActionController::Base
|
|
5
|
+
include Authentication
|
|
6
|
+
end
|
|
7
|
+
end
|
|
@@ -1,50 +1,160 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
|
2
2
|
|
|
3
|
+
// Holding the modifier shows a badge with a letter on every control marked with
|
|
4
|
+
// `data-onlylogs-shortcut`; modifier + letter triggers it from anywhere, also while
|
|
5
|
+
// typing a query. The modifier is Control on macOS and Alt elsewhere: neither types
|
|
6
|
+
// characters there (Option does on a Mac - `@`, `|`, `[` on a Swiss layout), and
|
|
7
|
+
// Ctrl+Alt is AltGr on Windows, which is why the other one must not be held.
|
|
8
|
+
// Host apps can mark their own controls with the same attribute.
|
|
3
9
|
export default class KeyboardShortcutsController extends Controller {
|
|
4
|
-
static targets = ["
|
|
10
|
+
static targets = ["filterInput", "dialog"]
|
|
11
|
+
static values = { modifier: { type: String, default: "alt" } }
|
|
5
12
|
|
|
6
13
|
connect() {
|
|
7
14
|
this.boundHandleKeydown = this.handleKeydown.bind(this)
|
|
8
|
-
|
|
15
|
+
this.boundHandleKeyup = this.handleKeyup.bind(this)
|
|
16
|
+
this.boundHideHints = this.hideHints.bind(this)
|
|
17
|
+
document.addEventListener("keydown", this.boundHandleKeydown)
|
|
18
|
+
document.addEventListener("keyup", this.boundHandleKeyup)
|
|
19
|
+
window.addEventListener("blur", this.boundHideHints)
|
|
20
|
+
|
|
21
|
+
this.#hosts().forEach(host => {
|
|
22
|
+
this.#actionable(host).setAttribute("aria-keyshortcuts", `${this.#modifierKey}+${host.dataset.onlylogsShortcut.toUpperCase()}`)
|
|
23
|
+
})
|
|
9
24
|
}
|
|
10
25
|
|
|
11
26
|
disconnect() {
|
|
12
|
-
document.removeEventListener(
|
|
27
|
+
document.removeEventListener("keydown", this.boundHandleKeydown)
|
|
28
|
+
document.removeEventListener("keyup", this.boundHandleKeyup)
|
|
29
|
+
window.removeEventListener("blur", this.boundHideHints)
|
|
30
|
+
this.hideHints()
|
|
13
31
|
}
|
|
14
32
|
|
|
15
33
|
handleKeydown(event) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (event.metaKey || event.ctrlKey || event.altKey) {
|
|
34
|
+
if (event.key === this.#modifierKey) {
|
|
35
|
+
if (!event.repeat && this.#modifierHeld(event)) this.#scheduleHints()
|
|
19
36
|
return
|
|
20
37
|
}
|
|
21
38
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
// Modifier + Tab and the like are someone else's; only a modifier held on its own shows hints.
|
|
40
|
+
clearTimeout(this.hintTimer)
|
|
41
|
+
|
|
42
|
+
if (this.#modifierHeld(event)) {
|
|
43
|
+
this.#handleChord(event)
|
|
44
|
+
} else if (!event.metaKey && !event.ctrlKey && !event.altKey && !event.defaultPrevented) {
|
|
45
|
+
this.#handleBareKey(event)
|
|
25
46
|
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
handleKeyup(event) {
|
|
50
|
+
if (event.key !== this.#modifierKey) return
|
|
51
|
+
|
|
52
|
+
// Releasing a lone Alt opens the menu bar in Firefox on Windows and Linux.
|
|
53
|
+
if (this.hintsShown || this.chordUsed) event.preventDefault()
|
|
54
|
+
this.chordUsed = false
|
|
55
|
+
this.hideHints()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
hideHints() {
|
|
59
|
+
clearTimeout(this.hintTimer)
|
|
60
|
+
this.hintsShown = false
|
|
61
|
+
document.documentElement.classList.remove("onlylogs-shortcuts-visible")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
openHelp() {
|
|
65
|
+
this.hideHints()
|
|
66
|
+
this.dialogTarget.showModal()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#scheduleHints() {
|
|
70
|
+
clearTimeout(this.hintTimer)
|
|
71
|
+
this.hintTimer = setTimeout(() => {
|
|
72
|
+
this.hintsShown = true
|
|
73
|
+
document.documentElement.classList.add("onlylogs-shortcuts-visible")
|
|
74
|
+
}, 250)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#handleChord(event) {
|
|
78
|
+
if (event.shiftKey || this.dialogTarget.open) return
|
|
79
|
+
|
|
80
|
+
const letter = this.#letterFor(event)
|
|
81
|
+
const host = this.#hosts().find(host => host.dataset.onlylogsShortcut === letter && this.#visible(host))
|
|
82
|
+
if (!host) return
|
|
83
|
+
|
|
84
|
+
event.preventDefault()
|
|
85
|
+
this.chordUsed = true
|
|
86
|
+
this.#trigger(this.#actionable(host))
|
|
87
|
+
}
|
|
26
88
|
|
|
27
|
-
|
|
28
|
-
|
|
89
|
+
#handleBareKey(event) {
|
|
90
|
+
if (this.#typing(event.target) || this.dialogTarget.open) return
|
|
91
|
+
|
|
92
|
+
switch (event.key) {
|
|
93
|
+
case "/":
|
|
29
94
|
event.preventDefault()
|
|
30
|
-
|
|
95
|
+
this.#trigger(this.filterInputTarget)
|
|
31
96
|
break
|
|
32
|
-
case
|
|
97
|
+
case "?":
|
|
33
98
|
event.preventDefault()
|
|
34
|
-
|
|
99
|
+
this.openHelp()
|
|
35
100
|
break
|
|
36
|
-
case
|
|
101
|
+
case "ArrowDown":
|
|
102
|
+
case "j":
|
|
37
103
|
event.preventDefault()
|
|
38
|
-
this.
|
|
104
|
+
this.dispatch("lines")
|
|
39
105
|
break
|
|
40
106
|
}
|
|
41
107
|
}
|
|
42
108
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
109
|
+
#trigger(element) {
|
|
110
|
+
if (element.matches("input[type=text], input[type=search], textarea")) {
|
|
111
|
+
element.focus()
|
|
112
|
+
element.select()
|
|
113
|
+
} else if (element.matches("select")) {
|
|
114
|
+
element.focus()
|
|
115
|
+
try { element.showPicker() } catch { /* not supported: focused is enough */ }
|
|
116
|
+
} else if (element.matches("input[type=range]")) {
|
|
117
|
+
element.focus()
|
|
118
|
+
} else {
|
|
119
|
+
element.click()
|
|
48
120
|
}
|
|
49
121
|
}
|
|
122
|
+
|
|
123
|
+
// The badge sits on a wrapper when the control itself cannot carry one (inputs, selects).
|
|
124
|
+
#actionable(host) {
|
|
125
|
+
if (host.matches("input, select, button, textarea")) return host
|
|
126
|
+
return host.querySelector("input, select, button, textarea") ?? host
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// event.key is the character the layout produced; with Alt on Windows that is the
|
|
130
|
+
// letter itself, but a Mac turns some combinations into symbols, so fall back to
|
|
131
|
+
// the physical key.
|
|
132
|
+
#letterFor(event) {
|
|
133
|
+
if (/^[a-z0-9.]$/i.test(event.key)) return event.key.toLowerCase()
|
|
134
|
+
|
|
135
|
+
const [, letter] = event.code.match(/^(?:Key|Digit)(\w)$/) ?? []
|
|
136
|
+
if (letter) return letter.toLowerCase()
|
|
137
|
+
return event.code === "Period" ? "." : null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#modifierHeld(event) {
|
|
141
|
+
if (event.metaKey) return false
|
|
142
|
+
return this.modifierValue === "control" ? event.ctrlKey && !event.altKey : event.altKey && !event.ctrlKey
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#typing(element) {
|
|
146
|
+
return element.matches?.("input, textarea, select") || element.isContentEditable
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#visible(element) {
|
|
150
|
+
return element.getClientRects().length > 0
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#hosts() {
|
|
154
|
+
return [...document.querySelectorAll("[data-onlylogs-shortcut]")]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get #modifierKey() {
|
|
158
|
+
return this.modifierValue === "control" ? "Control" : "Alt"
|
|
159
|
+
}
|
|
50
160
|
}
|