If you set a checkbox as required in a issue form at the moment, the
checkbox is checked and read only, what does not make much sense. With
this PR, the Checkbox actually needs to be checked. The label supports
now also Markdown. This matches GitHub's behaviour.
And yes, I know the CSS is a ugly workaround. It looks like the given
CSS code is part Fomantic and I don't know how to change that. The
Maintainers are free to change that.
![grafik](https://github.com/go-gitea/gitea/assets/15185051/3f35be75-b0b4-42a7-9048-a4970384a035)
- The review type '22' is a general comment type that is attached to
single codecomments, reviews with multiple comments or to simple approve
and request changes comment. This comment can be used to create a link
towards this action on an pull request.
- Adds an anchor to the review comment type, so that when its getting
linked to it, it actually jumps towards that event.
- This also now fixes the behavior that after you created a review you
will be redirected to that review and because this is an general comment
type other mails will also be 'fixed' such as the approved or request
changes.
- Resolves https://codeberg.org/forgejo/forgejo/issues/1248
(cherry picked from commit 1741a5f1fe)
---------
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-authored-by: Caesar Schinas <caesar@caesarschinas.com>
Part of #27065
This PR touches functions used in templates. As templates are not static
typed, errors are harder to find, but I hope I catch it all. I think
some tests from other persons do not hurt.
1. Use `gt-invisible` instead of `invisible`.
2. Use `gt-word-break` instead of `dont-break-out` (there is a slight
different "hyphens", but I think it won't affect too much since it is
only used for the "full name").
3. Remove `.small.button:has(svg)` , now our buttons could layout SVG
correctly, and actually I didn't see this CSS class is used in code.
Each change is tested manually line by line. There are too many changes
so I can't share dozens of screenshots.
In short:
1. `ui right` could be still used in `ui top attached header`, because
there is a special case.
2. A lot of `ui right` are just no-op, so they can be removed safely.
3. Some of the `ui right` should be replaced by `gt-float-right` (to
avoid breaking, leave them to the future).
4. A few of the `ui right` could be rewritten by flex.
Fix#26731
Almost all "tabindex" in code are incorrect.
1. All "input/button" by default are focusable, so no need to use "tabindex=0"
2. All "div/span" by default are not focusable, so no need to use "tabindex=-1"
3. All "dropdown" are focusable by framework, so no need to use "tabindex"
4. Some tabindex values are incorrect (eg: `new_form.tmpl`), so remove them
Co-authored-by: Giteabot <teabot@gitea.io>
https://github.com/go-gitea/gitea/issues/26567#issue-1855312074
> The terms `closest` and `furthest` don't describe the actual sorting
behavior as these two are semantically relative to the current date.
> Could we switch to `earliest` and `latest` instead?
close#26567
---------
Co-authored-by: yp05327 <576951401@qq.com>
Co-authored-by: Giteabot <teabot@gitea.io>
## Archived labels
This adds the structure to allow for archived labels.
Archived labels are, just like closed milestones or projects, a medium to hide information without deleting it.
It is especially useful if there are outdated labels that should no longer be used without deleting the label entirely.
## Changes
1. UI and API have been equipped with the support to mark a label as archived
2. The time when a label has been archived will be stored in the DB
## Outsourced for the future
There's no special handling for archived labels at the moment.
This will be done in the future.
## Screenshots
![image](https://github.com/go-gitea/gitea/assets/80308335/208f95cd-42e4-4ed7-9a1f-cd2050a645d4)
![image](https://github.com/go-gitea/gitea/assets/80308335/746428e0-40bb-45b3-b992-85602feb371d)
Part of https://github.com/go-gitea/gitea/issues/25237
---------
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This PR refactors a bunch of projects-related code, mostly the
templates.
The following things were done:
- rename boards to columns in frontend code
- use the new `ctx.Locale.Tr` method
- cleanup template, remove useless newlines, classes, comments
- merge org-/user and repo level project template together
- move "new column" button into project toolbar
- move issue card (shared by projects and pinned issues) to shared
template, remove useless duplicated styles
- add search function to projects (to make the layout more similar to
milestones list where it is inherited from 😆)
- maybe more changes I forgot I've done 😆Closes#24893
After:
![Bildschirmfoto vom 2023-08-10
23-02-00](https://github.com/go-gitea/gitea/assets/47871822/cab61456-1d23-4373-8163-e567f1b3b5f9)
![Bildschirmfoto vom 2023-08-10
23-02-26](https://github.com/go-gitea/gitea/assets/47871822/94b55d60-5572-48eb-8111-538a52d8bcc6)
![Bildschirmfoto vom 2023-08-10
23-02-46](https://github.com/go-gitea/gitea/assets/47871822/a0358f4b-4e05-4194-a7bc-6e0ecba5a9b6)
---------
Co-authored-by: silverwind <me@silverwind.io>
This problem occurs because in #25839, the warning status has been
removed, but there is something in the tmpl that hasn't been changed.
related #25839close#26118
Issue filters are being used on repo list page and on milestone issues
page, and the code is mostly duplicated.
This PR does the following changes:
- move issue filters into a shared template
- allow filtering milestone issues by project, so no need to hide this
filter on milestone issues page
- remove some dead code (e. g. issue actions in milestone issues
template)
- fix label filter dropdown width
---------
Co-authored-by: 6543 <6543@obermui.de>
this will allow us to fully localize it later
PS: we can not migrate back as the old value was a one-way conversion
prepare for #25213
---
*Sponsored by Kithara Software GmbH*
- Set
[type=search](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/search)
- Disable spellcheck
- Set maxLength 255 that I found in `templates/repo/issue/search.tmpl`
- Remove unnecessary `max-width`, it does nothing
---------
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
Numerous small UI fixes:
- Fix double border in collaborator list
- Fix system notice table background
- Mute links in repo and org lists
- Downsize projects edit buttons
- Improve milestones and project list rendering
- Condense milestone list entry to a single line of "metas"
- Mute ".." button in repo files list
So I found this [linter](https://github.com/Riverside-Healthcare/djlint)
which features a mode for go templates, so I gave it a try and it did
find a number of valid issue, like unbalanced tags etc. It also has a
number of bugs, I had to disable/workaround many issues.
Given that this linter is written in python, this does add a dependency
on `python` >= 3.8 and `poetry` to the development environment to be
able to run this linter locally.
- `e.g.` prefixes on placeholders are removed because the linter had a
false-positive on `placeholder="e.g. cn=Search"` for the `attr=value`
syntax and it's not ideal anyways to write `e.g.` into a placeholder
because a placeholder is meant to hold a sample value.
- In `templates/repo/settings/options.tmpl` I simplified the logic to
not conditionally create opening tags without closing tags because this
stuff confuses the linter (and possibly the reader as well).
Fix#25133
Thanks @wxiaoguang @silverwind.
I'm sorry I made a mistake, it will be fixed in this PR.
---------
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
Fixes https://github.com/go-gitea/gitea/issues/25130
The old code uses `$(this).next()` to get `dismiss-review-modal`.
At first, it will get `$(#dismiss-review-modal)`, but the next time it
will get `$(#dismiss-review-modal).next();`
and then `$(#dismiss-review-modal).next().next();`.
Because div `dismiss-review-modal` will be removed when
`dismiss-review-btn` clicked.
Maybe the right usage is adding `show-modal` class and `data-modal`
attribute.
This addressees some things from #24406 that came up after the PR was
merged. Mostly from @delvh.
---------
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
Don't really know a better name for this. I've gone through some Forms
and added missing HTML attributes (mostly `maxlength`). I tried to fill
the Forms with dummy Data and see if Gitea throws a Error (e.g. maximum
length). If yes, I added the missing HTML attribute.
While working on this, I discovered that the Form to add OAuth2 Apps
just silently fails when filled with invalid data, so I fixed that too.
This adds the ability to pin important Issues and Pull Requests. You can
also move pinned Issues around to change their Position. Resolves#2175.
## Screenshots
![grafik](https://user-images.githubusercontent.com/15185051/235123207-0aa39869-bb48-45c3-abe2-ba1e836046ec.png)
![grafik](https://user-images.githubusercontent.com/15185051/235123297-152a16ea-a857-451d-9a42-61f2cd54dd75.png)
![grafik](https://user-images.githubusercontent.com/15185051/235640782-cbfe25ec-6254-479a-a3de-133e585d7a2d.png)
The Design was mostly copied from the Projects Board.
## Implementation
This uses a new `pin_order` Column in the `issue` table. If the value is
set to 0, the Issue is not pinned. If it's set to a bigger value, the
value is the Position. 1 means it's the first pinned Issue, 2 means it's
the second one etc. This is dived into Issues and Pull requests for each
Repo.
## TODO
- [x] You can currently pin as many Issues as you want. Maybe we should
add a Limit, which is configurable. GitHub uses 3, but I prefer 6, as
this is better for bigger Projects, but I'm open for suggestions.
- [x] Pin and Unpin events need to be added to the Issue history.
- [x] Tests
- [x] Migration
**The feature itself is currently fully working, so tester who may find
weird edge cases are very welcome!**
---------
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
There was some recent discussion about this in Discord `ui-design`
channel and the conclusion was that
https://github.com/go-gitea/gitea/issues/24305 should have fixed their
OS font installation to have semibold weights.
I have now tested this 601 weight on a Windows 10 machine on Firefox
myself, and I immediately noticed that bold was excessivly bold and
rendering as 700 because browsers are biased towards bolder fonts. So
revert this back to the previous value.
Visually, nothing should have changed.
Changes include
- Convert most `<a [no href]>` to `<button>` when (re-)viewing files:
- `<a [no href]>` are, by HTML definition, not a link and hence cannot
be focused
- `<a class="ui button">` can now be clicked (again?) using
<kbd>Enter</kbd>
- Previously, the installed keypress handler on `.ui.button` elements
disabled it for links somehow
- The `(un)escape file`, the `expand section` and the `expand/collapse
file` buttons can now be focused (and subsequently clicked using only
the keyboard)
- You can now press <kbd>Space</kbd> on a focused `View file` checkbox
to mark the file as viewed.
- previously, this was impossible as this checkbox listened on the wrong
event listener
The `add code comment` button has been left inaccessible for now as it
requires quite a bit of extra logic so that it is unhidden when it is
focused (you can otherwise focus it without seeing it as you are not
hovering on the corresponding line).
---------
Co-authored-by: silverwind <me@silverwind.io>
Clean up a few cases where avatar dimensions were overwritten via CSS,
which were no longer needed or were possible to set via HTML width.
Also included are two small fixes:
- Fix one more case of incorrect avatar offset on review timeline
- Vertically center avatars in review sidebar
There is more to be done here, but some of the work depends on Fomantic
`comment` module removal, or in the case of org member lists, a refactor
of the `avatarlink` template to accept a size.
<img width="371" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/9c5902fb-2b89-4a7d-a152-60e74c3b2c56">
<img width="306" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/c8d92e2a-91c9-4f4a-a7de-6ae1a6bc0479">
---------
Co-authored-by: Giteabot <teabot@gitea.io>
Partial regression of #24393, not only regression, but broken for long
time, 24393 didn't really improve it but used wrong `overflow: scroll`.
Actually, that "ui secondary filter menu labels" shouldn't be set as
scrollable (I missed that at that time), the problem is: if a "ui menu"
has "dropdown" items, then it should not be scrollable. Otherwise the
dropdown menu can't be shown correctly.
And there are more problems:
* The "issue-filters" shouldn't be used anywhere else (copying&pasting
problem again ....)
* There is also an "issue-actions" container, it should also be fixed.
* There are similar problems on the milestone page.
* The old comment in code: "grid column" doesn't work well.
The major changes of this PR are: use "flex: 1" instead of "ui grid
column".
After this PR, not 100% perfect but much better than before.
Close#24302
Part of #24229, Follows #24246
This PR focused on CSS style fine-tune, main changes:
1. Give `.ui.ui.ui.container` a width of `1280px` with a max-width of
`calc(100vw - 64px)`, so the main contents looks better on large
devices.
2. Share styles for table elements in all levels settings pages to fix
overflow of runners table on mobile and for consistency (The headers on
mobile can be further improved, but haven't found a proper way yet).
3. Use [stackable
grid](https://fomantic-ui.com/collections/grid.html#stackable) and
[device column width](https://fomantic-ui.com/examples/responsive.html)
for responsiveness for some pages (repo/org collaborators settings
pages, org teams related page)
4. Fixed#24302 by sharing label related CSS in reporg.css
5. Fine tune repo tags settings page
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This refactors the `shared/datetime/short|long|full` templates into a
template helper function, which allows us to render absolute date times
within translatable phrases.
- Follows #23988
- The first attempt was in #24055
- This should help #22664
Changes:
1. Added the `DateTime` template helper that replaces the
`shared/datetime/short|long|full` templates
2. Used find-and-replace with varying regexes to replace the templates
from step 1 (for example, `\{\{template "shared/datetime/(\S+) \(dict
"Datetime" ([^"]+) "Fallback" ([^\)]+\)?) ?\)?\}\}` -> `{{DateTime "$1
$2 $3}}`)
3. Used the new `DateTime` helper in the issue due date timestamp
rendering
# Before
![image](https://user-images.githubusercontent.com/20454870/233791256-b454c455-aca0-4b76-b300-7866c7bd529e.png)
# After
![image](https://user-images.githubusercontent.com/20454870/233790809-c4913355-2822-4657-bb29-2298deb6d4b3.png)
---------
Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Follow #23328
The improvements:
1. The `contains` functions are covered by tests
2. The inconsistent behavior of `containGeneric` is replaced by
`StringUtils.Contains` and `SliceUtils.Contains`
3. In the future we can move more help functions into XxxUtils to
simplify the `helper.go` and reduce unnecessary global functions.
FAQ:
1. Why it's called `StringUtils.Contains` but not `strings.Contains`
like Golang?
Because our `StringUtils` is not Golang's `strings` package. There will
be our own string functions.
---------
Co-authored-by: silverwind <me@silverwind.io>
Close#24195
Some of the changes are taken from my another fix
f07b0de997
in #20147 (although that PR was discarded ....)
The bug is:
1. The old code doesn't handle `removedfile` event correctly
2. The old code doesn't provide attachments for type=CommentTypeReview
This PR doesn't intend to refactor the "upload" code to a perfect state
(to avoid making the review difficult), so some legacy styles are kept.
---------
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
Close: #23738
The actual cause of `500 Internal Server Error` in the issue is not what
is descirbed in the issue.
The actual cause is that after deleting team, if there is a PR which has
requested reivew from the deleted team, the comment could not match with
the deleted team by `assgin_team_id`. So the value of `.AssigneeTeam`
(see below code block) is `nil` which cause `500 error`.
1c8bc4081a/templates/repo/issue/view_content/comments.tmpl (L691-L695)
To fix this bug, there are the following problems to be resolved:
- [x] 1. ~~Stroe the name of the team in `content` column when inserting
`comment` into DB in case that we cannot get the name of team after it
is deleted. But for comments that already exist, just display "Unknown
Team"~~ Just display "Ghost Team" in the comment if the assgined team is
deleted.
- [x] 2. Delete the PR&team binding (the row of which `review_team_id =
${team_id} ` in table `review`) when deleting team.
- [x] 3.For already exist and undeleted binding rows in in table
`review`, ~~we can delete these rows when executing migrations.~~ they
do not affect the function, so won't delete them.
One of the steps in #23328
Before there were 3 different but similar functions: dict/Dict/mergeinto
The code was just copied & pasted, no test.
This PR defines a new stable `dict` function, it covers all the 3 old
functions behaviors, only +160 -171
Future developers do not need to think about or guess the different dict
functions, just use one: `dict`
Why use `dict` but not `Dict`? Because there are far more `dict` than
`Dict` in code already ......
Right now the authors search dropdown might take a long time to load if
amount of authors is huge.
Example: (In the video below, there are about 10000 authors, and it
takes about 10 seconds to open the author dropdown)
https://user-images.githubusercontent.com/17645053/229422229-98aa9656-3439-4f8c-9f4e-83bd8e2a2557.mov
Possible improvements can be made, which will take 2 steps (Thanks to
@wolfogre for advice):
Step 1:
Backend: Add a new api, which returns a limit of 30 posters with matched
prefix.
Frontend: Change the search behavior from frontend search(fomantic
search) to backend search(when input is changed, send a request to get
authors matching the current search prefix)
Step 2:
Backend: Optimize the api in step 1 using indexer to support fuzzy
search.
This PR is implements the first step. The main changes:
1. Added api: `GET /{type:issues|pulls}/posters` , which return a limit
of 30 users with matched prefix (prefix sent as query). If
`DEFAULT_SHOW_FULL_NAME` in `custom/conf/app.ini` is set to true, will
also include fullnames fuzzy search.
2. Added a tooltip saying "Shows a maximum of 30 users" to the author
search dropdown
3. Change the search behavior from frontend search to backend search
After:
https://user-images.githubusercontent.com/17645053/229430960-f88fafd8-fd5d-4f84-9df2-2677539d5d08.mov
Fixes: https://github.com/go-gitea/gitea/issues/22586
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Closes#20955
This PR adds the possibility to disable blank Issues, when the Repo has
templates. This can be done by creating the file
`.gitea/issue_config.yaml` with the content `blank_issues_enabled` in
the Repo.
Adds API endpoints to manage issue/PR dependencies
* `GET /repos/{owner}/{repo}/issues/{index}/blocks` List issues that are
blocked by this issue
* `POST /repos/{owner}/{repo}/issues/{index}/blocks` Block the issue
given in the body by the issue in path
* `DELETE /repos/{owner}/{repo}/issues/{index}/blocks` Unblock the issue
given in the body by the issue in path
* `GET /repos/{owner}/{repo}/issues/{index}/dependencies` List an
issue's dependencies
* `POST /repos/{owner}/{repo}/issues/{index}/dependencies` Create a new
issue dependencies
* `DELETE /repos/{owner}/{repo}/issues/{index}/dependencies` Remove an
issue dependency
Closes https://github.com/go-gitea/gitea/issues/15393Closes#22115
Co-authored-by: Andrew Thornton <art27@cantab.net>
Follow:
* #23574
* Remove all ".tooltip[data-content=...]"
Major changes:
* Remove "tooltip" class, use "[data-tooltip-content=...]" instead of
".tooltip[data-content=...]"
* Remove legacy `data-position`, it's dead code since last Fomantic
Tooltip -> Tippy Tooltip refactoring
* Rename reaction attribute from `data-content` to
`data-reaction-content`
* Add comments for some `data-content`: `{{/* used by the form */}}`
* Remove empty "ui" class
* Use "text color" for SVG icons (a few)
Remove `[repository.editor] PREVIEWABLE_FILE_MODES` setting that seemed
like it was intended to support this but did not work. Instead, whenever
viewing a file shows a preview, also have a Preview tab in the file
editor.
Add new `/markup` web and API endpoints with `comment`, `gfm`,
`markdown` and new `file` mode that uses a file path to determine the
renderer.
Remove `/markdown` web endpoint but keep the API for backwards and
GitHub compatibility.
## ⚠️ BREAKING ⚠️
The `[repository.editor] PREVIEWABLE_FILE_MODES` setting was removed.
This setting served no practical purpose and was not working correctly.
Instead a preview tab is always shown in the file editor when supported.
---------
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
1a4efa0ee9/web_src/js/components/PullRequestMergeForm.vue (L33-L36)
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
This PR is extracted from #23346 to address some unclear (I don't
understand) code-belonging concerns.
This PR needs to be backported, otherwise the `aria.js` is too buggy in
some cases. Since there would be two minor conflicts, I will do the
backport manually.
Before: the `aria.js` is still buggy in some cases.
After: tested with AppleVoice, Android TalkBack
* Fix incorrect dropdown init code
* Fix incorrect role element (the menu role should be on the `$menu`
element, but not on the `$focusable`)
* Fix the focus-show-click-hide problem on mobile. Now the language menu
works as expected
* Fix incorrect dropdown template function setting
* Clarify the logic in aria.js
* Hide item's tippy after menu gets hidden
* Fix incorrect tippy `setProps` after `destroy`
* Fix UI lag problem when page gets redirected during menu hiding
animation with screen reader
* Improve comments
* Implement the layout proposed by #19861
<details>
d74a7efb60/web_src/js/features/aria.md (L38-L47)
</details>
This improves a lot of accessibility shortcomings.
Every possible instance of `<div class="button">` matching the command
`ag '<[^ab].*?class=.*?[" ]button[ "]' templates/ | grep -v 'dropdown'`
has been converted when possible.
divs with the `dropdown` class and their children were omitted as
1. more analysis must be conducted whether the dropdowns still work as
intended when they are a `button` instead of a `div`.
2. most dropdowns have `div`s as children. The HTML standard disallows
`div`s inside `button`s.
3. When a dropdown child that's part of the displayed text content is
converted to a `button`, the dropdown can be focused twice
Further changes include that all "gitea-managed" buttons with JS code
received an `e.preventDefault()` so that they don't accidentally submit
an underlying form, which would execute instead of cancel the action.
Lastly, some minor issues were fixed as well during the refactoring.
## Future improvements
As mentioned in
https://github.com/go-gitea/gitea/pull/23337#discussion_r1127277391,
`<a>`s without `href` attribute are not focusable.
They should later on be converted to `<button>`s.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Replace #23342
Fix a regression of #23014: the `a` couldn't be used here because
Fomantic UI has style conflicts: `.ui.comments .comment .actions a {
display: inline-block; }`
And complete one more of my TODOs: "in the future there could be a
special CSS class for it"
This branch continues the work of #23092 and attempts to rid the
codebase of any `nil` contexts when using a `RenderContext`.
Anything that renders markdown or does post processing may call
`markup.sha1CurrentPatternProcessor()`, and this runs
`git.OpenRepository()`, which needs a context. It will panic if the
context is `nil`. This branch attempts to _always_ include a context
when creating a `RenderContext` to prevent future crashes.
Co-authored-by: Kyle D <kdumontnu@gmail.com>