As explained [here](https://twos.dev/splitsio.html), splits.io will shut
down at the end of the month. The website is already not working
properly anymore, so it's time to remove support for it.
Changelog: [Splits.io](https://splits.io) is shutting down at the end of
the month. Integration with it has been removed.
[Ben Carlsson's Goodbye to Splits.io](https://twos.dev/splitsio.html)
We have had a really outdated version of Font Awesome directly vendored
into our repository. Not only was it outdated, but it also is shipped as
a font file, including all the icons, even though we only use a small
subset of them. By switching to Lucide, we not only are depending on
something more up to date, but now the icons can also be tree shaked.
This also updates some of our CSS to use flex box and grid instead of
manually positioning with margins.
WebKit outside of Safari apparently ignores the background color of
`<select>` elements. By using `appearance: none`, we can override the
default style of the combo boxes and make them look consistent across
all browsers.
Co-authored-by: aaronghiurau <[email protected]>
This replaces the context menu with our own. By doing that we no longer
need to force install our npm packages, as there are no longer any
conflicts. In fact, as part of this change, I updated us to the latest
React 19 and even enabled the React compiler.
I also explored CSS modules for the first time, which are going to allow
us to reduce conflicts between CSS rules and possibly even allow us to
fully remove SASS, as the need for it is going to be reduced.
I also found out that the latest Rust nightly is broken due to their
update to LLVM 20. So I pinned the version to the last working nightly
version.
The `react-toggle` library is unmaintained and is preventing us from
properly updating React. I already wrote a Switch for the new Auto
Splitting IDE, so I converted it once again with Copilot.
Apparently React behaves differently from Dioxus in that it doesn't
allow changing inputs if they don't affect the value that they are bound
to. So I spent some time on implementing some proper validation logic
for the color inputs.
The `react-color-picker` library has been unmaintained for quite a
while and it (among other libraries) has been holding us back from
upgrading React properly. I have written a color picker for our Druid
based desktop version before and if we are going to use Dioxus for the
desktop version, I would want to port the Druid version over anyway. And
considering Dioxus is so close to React, I did just that. I ported it to
Dioxus and then let Copilot port it to React, which worked on the very
first try.
The `commonmark-react-renderer` and `react-linkifier` libraries are no
longer maintained and have held us back from upgrading our dependencies
for quite some time. This replaces the markdown rendering with an
actually maintained library: `markdown-it`.
Apparently the entire configuration format changed. I didn't fully
replicate the old configuration in order to reduce the complexity of it
all, hopefully resulting in fewer maintenance headaches in the future.
Additionally, I removed some unused dependencies that were no longer
needed.
The tests do not work anymore. They have always been flaky and added a
lot of dependencies, that made it harder to maintain them. Additionally,
the rendering is now handled by `livesplit-core`, which has all the same
tests for the rendering, so it also makes little sense to have the same
rendering tests again in this repository. All other dependencies that
are currently updatable have been updated, except:
1. I'm not sure if you need Tauri 2 for the CLI version 2.
2. The `css-loader` causes problems with the way we import variables
from the SASS files.
3. React 19 does not seem to just work, we get a bunch of TypeScript
issues.
4. `commonmark` is stuck as usual until we replace it entirely.
5. `eslint` changed completely it seems, we probably need to set it up
completely from scratch.
This allows us to skip letting `webpack` parse the wasm files, which
means we are not blocked by its limitations anymore. This allows us to
finally start using reference types.
Instead of manually specifying the variables via text boxes, you can now
choose them from combo boxes. This makes it easier to select the
variables you want to use for the layout.
Changelog: You can now choose variables to display on the layout from
combo boxes instead of manually specifying them via text boxes.
This allows the user to choose between specifying segment times and
split times when using the manual game timer.
Changelog: When using the **Manual Game Time Input**, you can now choose
between specifying **Segment Times** and **Split Times**.
This adds support for the `Always On Top` setting in the Tauri version
of LiveSplit One. This setting allows you to keep the LiveSplit One
window on top of all other windows.
This introduces a Tauri based desktop version of LiveSplit One. For now
very few changes have been done. It's mostly global hotkeys that work
now. It's unclear if Tauri is the long term solution for LiveSplit One,
but it gets us global hotkeys and auto splitting with very little
effort, so we'll use it until something better comes along.
This moves the server protocol to `livesplit-core` and improves on it in
various ways:
- The protocol is based on JSON messages. This allows for example for
more structured commands where it's easier to provide multiple
arguments for a command and even have optional arguments.
- For each command, there is a corresponding response. It is either a
`success` response with possibly the value that you requested, or an
`error` response with an error `code`.
- On top of the responses you also get sent `event` messages that
indicate changes to the timer. These can either be changes triggered
via a command that you sent or by changes that happened through other
sources, such as the user directly interacting with the timer or an
auto splitter.
The protocol is still work in progress and we will evolve it into a
protocol that fully allows synchronizing timers over the network.
The event sink has now been renamed to command sink, because there is
now a clear distinction between incoming commands and events that are
the results of these commands.
Changelog: The protocol used for the server connection has been
significantly improved. There is a response for each command and clear
errors when something goes wrong. Additionally, there are now event
messages that indicate changes to the timer.
We introduce a new `indexDelayed.ts` file that bundles all the imports
that are used in the `index.ts` file. They are not imported directly
there, because we want to keep the size of the `index.html` as small as
possible, so it can focus on requesting the WASM file as soon as
possible. This should result in the `indexDelayed.ts` bundle containing
basically all the JS. This JS bundle and the WASM file should then be
requested in parallel. While it would be possible to request these all
individually over in the `index.ts`, browsers don't actually like doing
too many requests in parallel and it turns out that big JavaScript files
actually compress better than having many small ones. The WASM file also
is pretty big, so ideally we are not bottlenecked by the large JS file.
If necessary, we could do some more bundle splitting for stuff that
isn't used until much later, like the Markdown library.
The dialogs are now asynchronous as opposed to the browser built-in ones
that are synchronous, probably because they are incredibly old. The
problem with the new dialogs being asynchronous is that for example the
hotkeys, the server protocol and honestly any sort of async download
that's still running in the background can all mess with the timer while
the dialog is open. This could lead to all sorts of weird issues, like
the timer being in a different state than what the dialog expects, the
timer being reset twice and creating two dialogs that are open at the
same time and possibly even memory corruption in the Rust code.
This commit ensures that the timer can not be interacted with while it
is "locked for interaction". In fact we already had such a notion where
when you navigate to the settings, layout or run editor, the hotkeys
would be disabled. Now this is concept is extended so the event sink
also can prevent interaction with the timer. Navigating to these menus
and opening dialogs will now trigger this locking mechanism.
Additionally this improves various async race conditions in the splits
editor related to downloading resources from speedrun.com or splits.io.
This also touches the CSS of the dialogs again, adding some spacing
around the dialog, so it doesn't touch the edges of the window and
allowing the buttons to have more dynamic sizing. In particular I've
noticed that the text sometimes doesn't fit into the buttons on iOS, so
they are now allowed to grow a little to handle that situation.
File open dialogs also now know the file extensions of the files we are
looking for.
This introduces custom dialogs to LiveSplit One. Previously we've used
the builtin browser dialogs that are very limited in what they can do.
Additionally this introduces a bunch of smaller changes:
- The splits editor is now more optimized and only searches for the game
name whenever it or the list of games changes.
- The PWA badge now resets when the app is reloaded.
- Guests on the leaderboard can now have flags.
- If a user encounters a bug, there's now dedicated toast popups that
don't disappear and direct the user to the issue tracker.
- The Sum of Best cleaner now ensures the run can't be modified while
the cleaning is in progress, which would lead to memory corruption,
especially with the async nature of the new dialogs.
- Cancelling out of opening files doesn't lead to an error anymore.
The following changes are introduced by updating `livesplit-core`:
- When resizing images / reencoding them as PNG, we now use a higher
compression setting.
- Invalid timer states are not unrepresentable anymore.
Instead of assuming each column has the width of the text `88:88:88`, we
now determine the largest string per column (based on unicode scalar
values) and adjust the column width accordingly. There is still a fixed
minimum width based on the string `88:88`, which ensures that the
columns don't switch sizes too often, especially if you start with empty
splits. We could've also chosen `8:88:88`, which would ensure there's no
column size change as you approach the hour mark. However, this would
increase the size of delta columns unnecessarily. So it's a little bit
of a trade-off.
We now automatically store the current comparison and timing method into
the Indexed DB. This allows us to restore the comparison and timing
method when the user reloads the page.
Changelog: The comparison and timing method (Real Time / Game Time) that
you choose now stay chosen when you reload LiveSplit One.
This adds functionality to the run editor to allow copying comparisons.
This is not only useful for copying custom comparisons, but also for
"baking" the times of a generated comparison, such as the `Latest Run` or
the `Average Segments` to a custom comparison that you can keep around
as long as you want. This is somewhat also meant to replace the
functionality of storing the current run as a Personal Best. Instead you
can just store the `Latest Run` as a custom comparison after you are
done with it.
Changelog: The splits editor now allows copying comparisons. This can
also be used to keep the times of an automatic comparison around as a
custom comparison. One example for using this is keeping around the
latest run as a custom comparison, so that you can compare against it
later on, even if you have already done another run.
When it's the first install of the service worker, or when the user clears the site data, the controllerchange event gets triggered. However, in these situations, we want to avoid prompting the user about an update.
The APIs that are not supported by every browser or may not always be
allowed (Battery and Wake Lock) are requested in try catch blocks. The
problem was that both of them actually yield promises. If the promises
fail, then those exceptions would not be caught properly, because we
forgot to await them. So especially for the wake lock you would
sometimes see an error message when it gets hot reloaded. This should be
fixed now.
Additionally this changes the window title for when there are unsaved
changes. Apparently when LiveSplit One is used as a PWA, then
at least Chrome wants to always keep the app name in the title. If the
title itself is changed, then it gets shown as `LiveSplit One - The new
title`. By us putting an asterisk in the front of the title, the PWA
then shows `LiveSplit One - *LiveSplit One`, which is pretty bad. I
noticed that if the app name is also the prefix of the title, Chrome
recognizes that and does not end up showing the app name twice.
There also was a small bug in the event sink where the
`togglePauseOrStart` event did not update the splits modified state.
This changes it so the layout dimensions are not immediately saved when
resizing a layout. Instead they are saved when saving the layout.
Additionally, resizing marks the layout as unsaved, so you don't forget about
saving the new dimensions.
This adds indicators to various parts of the UI to indicate that there
are unsaved changes. The indicators are mainly shown as white dots in
the sidebar highlighting what is unsaved. The title is also updated to
reflect this. The Badging API is also used to show a badge on the "app
icon" if it's installed as a PWA. In addition to the splits, the layouts
are also now properly tracked when it comes to unsaved changes.
Changelog: There are now indicators in the user interface that remind
you about unsaved changes to your splits and layouts.
This changes the way the user chooses the comparisons. This affects both
the sidebar and the way you specify comparison overrides for the
components. In both cases you now choose the comparison from a list of
all the comparisons that are available.
Changelog: You can now choose the comparisons from a list in the
sidebar. Settings that allow you to specify a comparison now also
provide a list to choose from.
This adds a save on reset setting to the general settings. When enabled,
the splits are automatically saved when the timer is reset.
Changelog: You can now enable **Save On Reset** in the settings, which
will automatically save the splits when you reset the timer.
We will almost certainly not be using Electron in the future. Nowadays
there's Tauri, which is basically a more lightweight Electron that also
allows us to use Rust code on the native side, so it's the much better
solution, if we ever even decide to go down that road.
This improves the contributors section in the About page. It now uses
flexbox to dynamically place the contributors. Additionally their avatar
is now being shown.
Also the ten most recent changes are now being shown in the About page.
The information is extracted from the git history. The idea is to mark
commit messages with a special changelog section at the end that's
specifically meant to be shown as a user facing change. It can be
arbitrary markdown and looks like this (which is also the first one):
Changelog: The About page now shows the most recent changes. The
contributors section has also been improved. It now shows the avatar of
each contributor.
This makes use of the new event system that got added to
`livesplit-core`. This allows the UI to react to events that happen.
Right now this is used for three things:
1. Resetting a run can now ask you for confirmation of whether to save
the current run if it contains new best segments or it's a new
Personal Best.
2. The side bar used to refresh the comparison and timing method 10
times per second. Now we can just react to those being changed,
resulting in better performance.
3. The control buttons underneath the timer now get disabled when they
don't apply to the current situation. The pause button also changes
its icon based on the state of the timer.
This moves the server connection button to the settings and cleans the
entire functionality up in various ways. The server connection no longer
gets closed by navigating around the application. Additionally the
server URL is now being remembered between sessions.
This also improves the design of the toast messages.
This adds settings for turning off the integration with speedrun.com and
splits.io. This is useful for people who don't want to use these
features and want to avoid the extra network requests. While splits.io
doesn't do any automatic network requests in the background, it still
adds buttons to the UI that someone may not want to see. It's also more
consistent to have a toggle for both.
While TypeScript can polyfill almost everything regarding the
`Symbol.dispose` functionality, it does not actually polyfill the symbol
itself for some reason?!
There is a [JavaScript
proposal](https://github.com/tc39/proposal-explicit-resource-management)
for using `Symbol.dispose` as a way to automatically clean up resources.
You do that by declaring variables like so:
```js
using resource = new Resource();
```
The variable `resource` will be automatically disposed when it goes out
of scope. We used to have our own `dispose` method and `with` helper
method that allows scoped access to the resource, before it gets cleaned
up at the end. TypeScript already fully implements and polyfills support
for it, so we can already fully switch over to it.
When switching from mobile to desktop, it was previously only retrieving the stored layout width, but not the stored layout height. This started being an issue after we added auto adjusting to the height on mobile.
Resolves#878
This commit adds support for manual game time. This is useful for games
that have a built-in timer and it's fine to simply type in the times
that the game displays. Currently there's no support for specifying
segment times instead, but that can be added later.
This adds the following two settings:
1. Show Control Buttons: Determines whether to show buttons beneath the
timer that allow controlling it. When disabled, you have to use the
hotkeys instead.
2. Frame Rate: Determines the frame rate at which to display the timer.
"Battery Aware" tries determining the type of device and charging
status to select a good frame rate. "Match Screen" makes the timer
match the screen's refresh rate.
Additionally this fixes a bug where the fonts were not properly loaded
by the time the web renderer gets created, which then cached information
about the fallback fonts instead of the actual fonts.
This adds tooltips to all settings. It's not optimal yet, because it
doesn't support mobile yet, where you don't have a cursor that could
hover the settings.