Commit graph

8196 commits

Author SHA1 Message Date
Stefan Haller 1b2bd85fc5 Let a test assert how a view draws its selection
The selected line of a view says nothing about whether a selection is drawn
over it, or which of the two ways it is drawn in, and those are what the
tests coming up are about.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:19:04 +02:00
Stefan Haller e7de68331a
Validate the context names in the "context" field of custom commands (#5989)
If a custom command's "context" field contains a context name that
doesn't exist, lazygit panics when building the keybindings. This could
happen either because of a typo, or because a context is removed or
renamed in a later version. Prevent the panic by validating those names
at config load time, and rejecting the config as invalid there, like we
do for other config errors.
2026-09-03 21:18:48 +02:00
Stefan Haller e0927d4faf Validate the context names in the "context" field of custom commands
If a custom command's "context" field contains a context name that
doesn't exist, lazygit panics when building the keybindings. This could
happen either because of a typo, or because a context is removed or
renamed in a later version. Prevent the panic by validating those names
at config load time, and rejecting the config as invalid there, like we
do for other config errors.

The gui package owns the list, but can't be imported from here, so it is
mirrored and a test over there ensures the copies stay in sync.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:07:32 +02:00
Stefan Haller 3f6be3b3ee
Allow filtering the keybindings and recent repos menus more directly (simply by typing) (#5985)
Some checks failed
Continuous Integration / ci - ${{matrix.os}} (~/.cache/go-build, ubuntu-latest) (push) Has been cancelled
Continuous Integration / ci - ${{matrix.os}} (~\AppData\Local\go-build, windows-latest) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.32.0, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.38.2, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.44.0, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, true) (push) Has been cancelled
Continuous Integration / build (push) Has been cancelled
Continuous Integration / check-codebase (push) Has been cancelled
Continuous Integration / lint (push) Has been cancelled
Continuous Integration / check-for-fixups (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Generate Sponsors README / deploy (push) Has been cancelled
Continuous Integration / upload-coverage (push) Has been cancelled
All menus in lazygit can be filtered by pressing the `/` key; for most
menus which only show a handful of choices this is not really needed,
but with the two cases where it's useful, it was unnecessarily
inconvenient: you first have to press `/` to open the filter prompt, and
then press enter to confirm the filter before you could press enter
again to trigger the chosen item. It's much easier to simply type to
filter, and still use the arrow keys to select one of the filtered
items, or press enter to trigger it while the filter prompt is showing.

The consequence of this is that while the keybindings menu is open you
can no longer use the displayed key bindings to trigger the commands; I
think that's fine, that menu is more for looking up those keybindings
rather than for using them from within the menu.

Also: since `j`/`k` are bound to move the list selection by default, it
is not possible to filter for something that begins with `j`/`k`. I
didn't want to change this because I'm concerned that die-hard vim users
would perceive it as a regression if they can no longer type `j` to
select the next menu item. The workaround is to type some other letter
and backspace; this keeps the filter prompt open, so you can now type
`j` or `k`.
2026-08-31 21:25:23 +02:00
Stefan Haller 90f5348371 Add a hint about filtering menus to the docs 2026-08-31 20:59:15 +02:00
Stefan Haller 4f78a5576a Reword stale comment about filtering not being available in the files view
This was implemented quite a while ago.
2026-08-31 20:59:15 +02:00
Stefan Haller 8aa57264fa Point the note about the menu's essential keys at what it means
There is no `reservedKeys` any more; the list of keys that menu items must
not shadow is `essentialKeys` in the function that creates the menu.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 4116a15dae Filter the recent repositories menu as you type
Picking a repository out of that list is the other place where the menu is
a list to search rather than a set of commands, and its items have no keys
that typing could clash with.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller bb7e74968b Drop the menu-specific wording for the filter prompt
The only menu that ever asked for it was the keybindings menu, which now
filters as you type and doesn't use the prompt at all. That leaves every
filterable context with the same prompt, so the whole hook can go, and
with it the two implementations that only existed to satisfy it.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller fb761892aa Filter the keybindings menu as you type
Looking up a keybinding is a search, so the menu that lists them is the
one that most wants this. Its items do have keys, but only as a reminder
of what they do outside the menu, so nothing is lost by not binding them.

The prompt in front of the input field says what '@' does. It only ever
showed up while the user was typing in the search prompt, so it could
afford to be wordy; on a row that is on screen for as long as the menu
is, it can't.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 68355862c1 Add test helpers for a menu's filter row
Its footer, the hint in the menu's subtitle, where the row sits in
relation to the menu and the tooltip, and whether the text cursor is
showing are all things the tests for it need to look at.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 2048c7f0a6 Keep a filtering menu navigable whatever the keybindings are
The keys for paging through a menu are ',' and '.' by default, and there
is no non-printable alternative for them, so a menu that filters as you
type would lose paging altogether as soon as the user typed anything. The
same goes for confirming and cancelling if those keys are configured as
printable ones.

So bind the physical keys for all of it, on top of whatever is configured,
and only where they aren't the configured keys anyway.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller fe9b990c4c Filter a menu by typing into it
The filter input is where the keyboard points for as long as such a menu
is open, so that the first printable key can go straight into it. The menu
still gets every key the input doesn't take, because the input view is
embedded in the menu view, and the two are drawn as one focused panel.

Which keys the input takes changes once there is a filter: until then
printable keys still drive the menu, so that the configured navigation
keys work as usual, and afterwards they are all filter text. A menu item's
own keys are never bound in such a menu, because typing one has to reach
the filter rather than execute the item.

Escape gives up the filter and leaves the menu open; the next one closes
it. The filter prompt behind '/' is gone from these menus: the row already
does that job, and a second filter would only be confusing.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 1fb5f87d05 Lay out the filter row of a menu that filters as you type
The row is reserved for as long as such a menu is open, even while it is
still hidden, so that it can appear without moving the menu. That costs
two rows of the popup, which is why the screen has to be a little taller
before a menu is worth showing at all.

The prompt in front of the input field is dropped when the row gets too
narrow to type in, and the keybindings menu says what '@' does when it
still fits.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller c80035d7ee Add the views for a menu's filter row
Nothing shows or positions them yet. The row is two views because the
input field has to start after the "Filter:" prompt, and a gocui view is
a rectangle: the frame view draws the row and the prompt, the field sits
inside it.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller f1b0347111 Allow a list to render its footer elsewhere
The footer is drawn on the bottom border of the list's view, which is not
always a free row: a panel that puts something else below the list shares
that border with it, and has to render the footer there instead.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller a9fe055af4 Extract applying a filter to a context
A filter can come from somewhere other than the search prompt: a menu
that filters as you type has its own input field, and needs to apply what
is typed there without going through the prompt's state.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 45d68bccc2 Treat the views of a popup panel as a group when clicking
The check was a single set of view names, so it also let a click move
between two different panels, e.g. from the prompt to the commit message.
List the panels instead, and require both views to be in the same one.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller f9ec7adb61 Draw embedded views as one focused unit
A view can only be drawn with the focused frame and title colors while it
is the current view, but a panel made of an outer view and an editable
field embedded in it has to look focused as a whole, whichever of the two
the keyboard is pointed at.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 9e98b3d2f3 Let an editable view opt into receiving printable keys as keybindings
Printable keys are withheld from keybindings while the user is typing in
a field, so that they end up as text. Decide that from the field that has
the focus rather than from the view a binding happens to be registered
for: a field can be embedded in another view, and that view's keys must
be withheld too, or its bindings would swallow the characters.

That makes it worth honouring KeybindOnEdit, which has been documented
but ignored ever since it was introduced. A field that sets it sees
printable keys offered to the keybindings first, and still gets them if
no binding handles them, which is what lets a view keep its keys until
the field has something to type into.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller 28c5f5748c Give a parent view's keybindings the same precedence as a view's own
When a key matches several bindings of the same view, the first one wins;
when it matches several of the view's parent, the last one did.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller b2a684bec1 Add a helper for recognizing printable keys
Two places test for "a character the user typed" by hand, and a third
one is about to be needed. Give the test a name.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:22 +02:00
Stefan Haller c840013ca3 Remove error return value from functions that always return nil
Originally I thought we'd benefit from this change in this branch; turns
out that we didn't after all, because we changed the approach, but it's
a nice cleanup anyway, so we include it here.
2026-08-31 20:41:22 +02:00
Stefan Haller 8924bca76f
Delete cpu.out file that was committed accidentally (#5983)
Some checks failed
Continuous Integration / ci - ${{matrix.os}} (~/.cache/go-build, ubuntu-latest) (push) Has been cancelled
Continuous Integration / ci - ${{matrix.os}} (~\AppData\Local\go-build, windows-latest) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.32.0, false) (push) Has been cancelled
Continuous Integration / build (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.38.2, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.44.0, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, false) (push) Has been cancelled
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, true) (push) Has been cancelled
Continuous Integration / check-codebase (push) Has been cancelled
Continuous Integration / lint (push) Has been cancelled
Continuous Integration / check-for-fixups (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Generate Sponsors README / deploy (push) Has been cancelled
Continuous Integration / upload-coverage (push) Has been cancelled
2026-08-30 09:44:18 +02:00
Stefan Haller 07fa0ef732 Delete cpu.out file that was committed accidentally 2026-08-30 09:41:15 +02:00
Stefan Haller a8b762d03b
Improve performance of moving rebase todos (#5978)
Some checks are pending
Continuous Integration / ci - ${{matrix.os}} (~/.cache/go-build, ubuntu-latest) (push) Waiting to run
Continuous Integration / ci - ${{matrix.os}} (~\AppData\Local\go-build, windows-latest) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.32.0, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.38.2, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.44.0, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, true) (push) Waiting to run
Continuous Integration / build (push) Waiting to run
Continuous Integration / check-codebase (push) Waiting to run
Continuous Integration / lint (push) Waiting to run
Continuous Integration / upload-coverage (push) Blocked by required conditions
Continuous Integration / check-for-fixups (push) Waiting to run
Codespell / Check for spelling errors (push) Waiting to run
Generate Sponsors README / deploy (push) Waiting to run
In larger repos, moving a rebase todo in an interactive rebase was
slower than necessary, especially when moving it up/down all the way
using auto-repeat.
2026-08-29 17:42:27 +02:00
Stefan Haller eddbbdad23
Avoid rehydrating unchanged rebase todos
Narrow rebase refreshes already have complete metadata for existing
todos. Reuse it so repeated todo moves do not spawn git show for the
entire list. New hashes still fall back to hydration.
2026-08-29 17:25:59 +02:00
Stefan Haller 3914755c98
Fix visual glitches when moving a rebase todo
This fixes two problems:
1. the list selection updated before the list content did, which caused
a bit of a wobble effect in the list
2. the main view would first update to a different commit's diff and
then back to the one that is being moved, resulting in a very ugly
flicker especially when moving the todo multiple times with auto repeat
2026-08-29 17:25:59 +02:00
Stefan Haller d37f901ac1
Remove PostRefreshUpdateKeepingScrollPosition
Now that we have PostRefreshUpdateWithOptions there's no reason to offer
a bespoke method for setting one particular option.
2026-08-29 17:25:59 +02:00
Stefan Haller fa531dc518
Add PostRefreshUpdateWithOptions 2026-08-29 17:25:59 +02:00
Stefan Haller fd1b229f93
Cleanup: remove CommitSelection option when refreshing rebase todos
This option has no effect, it isn't respected by refreshRebaseCommits,
so it looks misleading to include it here.
2026-08-29 17:25:59 +02:00
Stefan Haller b7ffa93dc6
Allow filtering worktrees by branch name in Worktrees pane (#5980)
Closes #5945.
2026-08-29 17:25:30 +02:00
Stefan Haller 530d773052 Allow filtering worktrees by branch name in Worktrees pane
Co-authored-by: phanirithvij <phanirithvij2000@gmail.com>
2026-08-29 17:02:44 +02:00
Stefan Haller c3027caf6b Cleanup: use lowercase for function parameters 2026-08-29 17:02:07 +02:00
Stefan Haller 655f913f44 Add test for filtering worktrees 2026-08-29 17:02:03 +02:00
Stefan Haller c300c319f9
Fix broken commit auto-scrolling (#5972)
Some checks are pending
Continuous Integration / ci - ${{matrix.os}} (~/.cache/go-build, ubuntu-latest) (push) Waiting to run
Continuous Integration / ci - ${{matrix.os}} (~\AppData\Local\go-build, windows-latest) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.32.0, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.38.2, false) (push) Waiting to run
Continuous Integration / check-codebase (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (2.44.0, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, false) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}}${{ matrix.race && ' (race)' || '' }} (latest, true) (push) Waiting to run
Continuous Integration / build (push) Waiting to run
Continuous Integration / lint (push) Waiting to run
Continuous Integration / upload-coverage (push) Blocked by required conditions
Continuous Integration / check-for-fixups (push) Waiting to run
Codespell / Check for spelling errors (push) Waiting to run
Generate Sponsors README / deploy (push) Waiting to run
Fix a bug introduced in #5928: dragging a commit with auto-scrolling so
that the original commit leaves the viewport, and then dragging back
into the view would snap the original commit back into view.

Labeled as ignore-for-release because it's a regression in a PR that
wasn't released yet.
2026-08-27 09:55:00 +02:00
Stefan Haller 35753fd042 Fix scrolling back when dragging a commit
During auto-scrolling, turn off the automatic
scroll-to-make-the-selected-item-visible functionality of
PostRefreshUpdate.
2026-08-27 08:22:55 +02:00
Stefan Haller c8d610cb58 Demonstrate commit drag scroll reset on re-entry
When dragging a commit with auto-scrolling so that the original commit
leaves the viewport, dragging back into the view makes the original
commit snap back into view. This is a regression that was introduced by
aebf495dce.
2026-08-27 08:22:55 +02:00
Stefan Haller c09b682f63 Add some comments to the drag_to_reorder_with_autoscroll test 2026-08-27 08:22:55 +02:00
Stefan Haller 6a932f3057 Read mouse pointer geometry on the UI thread in tests
Drag autoscrolling deliberately keeps rendering after the test action
has returned, so view bounds can change while the test goroutine
prepares its next mouse event. Snapshot the geometry on the event loop
before translating view-relative coordinates to avoid data races. This
hasn't been a problem so far, but only because we were lucky; the added
test assertions later in this branch would cause consistent race
detector failures without this fix.
2026-08-27 08:22:55 +02:00
Stefan Haller ea91639546
Show renames when selecting a directory that a file was moved into or out of (#5924)
In a commit that moves a bunch of files from one directory to another,
showing the commit's files and selecting the target directory of those
moves would show these files as newly added rather than moved in the
main view's diff. Selecting the source directory would show them as
removed. Fix this to keep showing them as moved in both cases. The same
applies to the files panel when staging the move of a file, and when
filtering the file list down to just the source or target directory
using the `/` filter in either panel.

The decision to show them as renames when selecting the "moved-from"
directory was not an easy one; it's slightly weird because the list of
files in the side panel doesn't show them there (they appear in the
target directory), but the main view does. An alternative would have
been not to show them in that case, to match the side panel. However,
the point of selecting a directory is to see all the changes that affect
it, and the moved-out files are relevant changes you want to see there.

See
https://github.com/jesseduffield/lazygit/discussions/4899#discussioncomment-17976172.
2026-08-18 09:47:38 +02:00
Stefan Haller d7401559f0 Collapse the paths of a moved directory into the directory itself
A commit that moves an entire package elsewhere renames hundreds of
files, and passing every one of their old paths can push the command past
the length limit the OS imposes (~32k characters on Windows). Their
common parent directory does just as well whenever everything it holds
ends up in the diff anyway.

Deciding that needs to consider every file of the diff, not only those on
display, so the paths are now derived from the model rather than from the
tree; a status filter must not make a directory look emptier than it is.
2026-08-17 09:32:30 +02:00
Stefan Haller bee03d3b98 Show renames when diffing a directory that a file was moved into or out of
Git limits its tree diff by the pathspec before it looks for renames, so
a directory only ever gets one end of a rename whose other end is outside
it. Nothing is left to pair up, and the file turns into an addition or a
deletion that the commit doesn't contain.

Pass the other end along with the directory. This is bounded by the
number of renames that cross the directory's boundary, so it costs
nothing at all for the vast majority of commits.
2026-08-17 09:32:30 +02:00
Stefan Haller 40cb4bb24d Extract a single helper for the paths a node's diff is limited to
The files and commit files panels each had their own copy of this, one of
which used to be missing the previous path of a rename. Growing them
apart again is the last thing we want, since the next commit needs to
teach both of them about renames that cross a directory boundary.

The files panel version only returned paths for the filtered case, and
left it to WorktreeFileDiffCmdObj to derive the rest from the node; now
that all callers pass the paths in, that command doesn't need to know
about renames at all.
2026-08-17 09:32:30 +02:00
Stefan Haller 2c9187acb9 Pass the previous path when diffing a filtered directory in the files panel
Restricting the diff to the files that a filter leaves visible drops the
delete-side entry of a staged rename, so git shows the file as an
addition instead. Its commit files counterpart already passes both paths;
this brings the files panel in line.
2026-08-17 09:32:30 +02:00
Stefan Haller 6913f2afce Add tests for diffing a directory that files were renamed into or out of
Pathspec limiting happens before rename detection in git's tree diff, so
filtering the diff to a directory hides the delete-side entry of a rename
whose other end is outside that directory. Git then has nothing to pair
up, and reports a file moved into the directory as an addition and one
moved out of it as a deletion.

Selecting a directory is supposed to filter the commit's diff down, never
to change it, so both are wrong.
2026-08-17 09:32:30 +02:00
Stefan Haller c199ac69f5
Keep showing files whose conflicts have been resolved (#5940)
When several files have conflicts, resolving one of them makes it vanish
from the files panel as soon as it is auto-staged, and it only comes
back once the last conflict is resolved and the filter turns off again.
By then it sits among all the other changed files of the merge, so it is
hard to find the ones whose resulting diff you still wanted to check.

So remember which files had conflicts while the conflicted-files filter
is on, and keep showing them once they are resolved. This is the general
solution that #5936 called for; that PR only helped for the case of a
single conflicted file.

The consequence is that the selection no longer moves on to the next
conflicted file when one is resolved: it stays on the file you just
resolved, which shows you its diff right away.
2026-08-16 17:09:25 +02:00
Stefan Haller 43b47d16dd Keep showing files whose conflicts have been resolved
When several files have conflicts, resolving one of them makes it vanish
from the files panel as soon as it is auto-staged, and it only comes back
once the last conflict is resolved and the filter turns off again. By
then it sits among all the other changed files of the merge, so it is
hard to find the ones whose resulting diff you still wanted to check.

So remember which files had conflicts while the conflicted-files filter
is on, and keep showing them once they are resolved. This is the general
solution that 39513d244d called for; that commit only helped for the
case of a single conflicted file.

The consequence is that the selection no longer moves on to the next
conflicted file when one is resolved: it stays on the file you just
resolved, which shows you its diff right away.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 16:45:50 +02:00
Stefan Haller c10bc3b697 Collect the paths of the files with conflicts, rather than only counting them
The next commit needs to know which files have conflicts, not just how
many of them there are.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 16:45:50 +02:00
Stefan Haller adf59703ac AGENTS.md additions 2026-08-16 16:45:50 +02:00