diff --git a/README.md b/README.md index e9fae540df..3a841d7eaf 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,19 @@ [![License](https://img.shields.io/github/license/gabber235/Typewriter?logo=github)](LICENSE) Typewriter is a plugin for **Paper** Minecraft servers that allows for custom player interactions such as Quests, -NPC chat, Create branching story with ease, and more. It is easily configurable using the web panel specifically +NPC chat, Create branching story with ease, Creating Cinematic with camera paths, and more. It is easily configurable using the web panel specifically designed for this plugin. The plugin can also be extended using adapters, which are [pre-made](https://gabber235.github.io/TypeWriter/docs/pre-made-adapters) or can be custom-made by developers. To get started with Typewriter, see the [Getting Started](#getting-started) section. +### Examples + +![cinematic](readme/cinematic.gif) + +A cinematic fully in vanilla minecraft using Typewriter. View a demo [here](https://youtu.be/1pGBtJleEuQ). + ![dialogue sequence](readme/chat-messages.gif) A dialogue sequence with Typewriter where the player is asked to choose between multiple options. @@ -25,9 +31,10 @@ The web panel for Typewriter, where you can create quests, NPCs, and more. ### Features - Create custom player interactions, including quests and NPC chat, branching story, and more... +- Create cinematic sequences with camera movements, dialogue, animated NPCs, and more... - Configure interactions using a custom written visual interface - Extend the plugin using adapters - - [Pre-made adapters](https://gabber235.github.io/TypeWriter/docs/pre-made-adapters) available for Citizens, and more coming soon + - [Pre-made adapters](https://gabber235.github.io/TypeWriter/docs/pre-made-adapters) available for popular plugins - Custom adapters can be made by developers ## Getting started @@ -63,4 +70,4 @@ See [LICENSE](LICENSE) to see the full text. # Credits -- [Aarthificial](https://www.youtube.com/@aarthificial) For the inspiration on the base logic. \ No newline at end of file +- [Aarthificial](https://www.youtube.com/@aarthificial) For the inspiration on the base logic. diff --git a/app/assets/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf b/app/assets/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf new file mode 100644 index 0000000000..914e323363 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf b/app/assets/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf new file mode 100644 index 0000000000..d73994ad49 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/OFL.txt b/app/assets/fonts/JetBrains_Mono/OFL.txt new file mode 100644 index 0000000000..0d0d19ae96 --- /dev/null +++ b/app/assets/fonts/JetBrains_Mono/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/app/assets/fonts/JetBrains_Mono/README.txt b/app/assets/fonts/JetBrains_Mono/README.txt new file mode 100644 index 0000000000..0a8510da61 --- /dev/null +++ b/app/assets/fonts/JetBrains_Mono/README.txt @@ -0,0 +1,79 @@ +JetBrains Mono Variable Font +============================ + +This download contains JetBrains Mono as both variable fonts and static fonts. + +JetBrains Mono is a variable font with this axis: + wght + +This means all the styles are contained in these files: + JetBrainsMono-VariableFont_wght.ttf + JetBrainsMono-Italic-VariableFont_wght.ttf + +If your app fully supports variable fonts, you can now pick intermediate styles +that aren’t available as static fonts. Not all apps support variable fonts, and +in those cases you can use the static font files for JetBrains Mono: + static/JetBrainsMono-Thin.ttf + static/JetBrainsMono-ExtraLight.ttf + static/JetBrainsMono-Light.ttf + static/JetBrainsMono-Regular.ttf + static/JetBrainsMono-Medium.ttf + static/JetBrainsMono-SemiBold.ttf + static/JetBrainsMono-Bold.ttf + static/JetBrainsMono-ExtraBold.ttf + static/JetBrainsMono-ThinItalic.ttf + static/JetBrainsMono-ExtraLightItalic.ttf + static/JetBrainsMono-LightItalic.ttf + static/JetBrainsMono-Italic.ttf + static/JetBrainsMono-MediumItalic.ttf + static/JetBrainsMono-SemiBoldItalic.ttf + static/JetBrainsMono-BoldItalic.ttf + static/JetBrainsMono-ExtraBoldItalic.ttf + +Get started +----------- + +1. Install the font files you want to use + +2. Use your app's font picker to view the font family and all the +available styles + +Learn more about variable fonts +------------------------------- + + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts + https://variablefonts.typenetwork.com + https://medium.com/variable-fonts + +In desktop apps + + https://theblog.adobe.com/can-variable-fonts-illustrator-cc + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts + +Online + + https://developers.google.com/fonts/docs/getting_started + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts + +Installing fonts + + MacOS: https://support.apple.com/en-us/HT201749 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows + +Android Apps + + https://developers.google.com/fonts/docs/android + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts + +License +------- +Please read the full license text (OFL.txt) to understand the permissions, +restrictions and requirements for usage, redistribution, and modification. + +You can use them in your products & projects – print or digital, +commercial or otherwise. + +This isn't legal advice, please consider consulting a lawyer and see the full +license for all details. diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Bold.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Bold.ttf new file mode 100644 index 0000000000..b7484374e7 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Bold.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-BoldItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-BoldItalic.ttf new file mode 100644 index 0000000000..0091142300 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-BoldItalic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBold.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBold.ttf new file mode 100644 index 0000000000..88eab2f7ba Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBold.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.ttf new file mode 100644 index 0000000000..85e67db403 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLight.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLight.ttf new file mode 100644 index 0000000000..1f73714431 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLight.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.ttf new file mode 100644 index 0000000000..745b58eeaa Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Italic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Italic.ttf new file mode 100644 index 0000000000..5b484dd610 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Italic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Light.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Light.ttf new file mode 100644 index 0000000000..296186f1db Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Light.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-LightItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-LightItalic.ttf new file mode 100644 index 0000000000..399ede7440 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-LightItalic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Medium.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Medium.ttf new file mode 100644 index 0000000000..ad31fbd7f0 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Medium.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-MediumItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-MediumItalic.ttf new file mode 100644 index 0000000000..4f499f281d Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-MediumItalic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Regular.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000000..02bc07ea08 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Regular.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-SemiBold.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-SemiBold.ttf new file mode 100644 index 0000000000..c3adfd3151 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-SemiBold.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.ttf new file mode 100644 index 0000000000..62d58add47 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Thin.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Thin.ttf new file mode 100644 index 0000000000..6a6a556f10 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-Thin.ttf differ diff --git a/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ThinItalic.ttf b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ThinItalic.ttf new file mode 100644 index 0000000000..33a23d7ca8 Binary files /dev/null and b/app/assets/fonts/JetBrains_Mono/static/JetBrainsMono-ThinItalic.ttf differ diff --git a/app/lib/hooks/text_size.dart b/app/lib/hooks/text_size.dart index b9b1704f0d..6c0782f743 100644 --- a/app/lib/hooks/text_size.dart +++ b/app/lib/hooks/text_size.dart @@ -1,20 +1,35 @@ import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; +/// For some reason the differences between the Text widget and the TextPainter +/// are not the same. This is a correction factor to make them the same. +const flutterPainterErrorCorrection = 1.035; + /// Returns the size of the text with the given [style] in the given [context]. Size useTextSize(BuildContext context, String text, [TextStyle? style]) { return useMemoized( () { + final defaultTextStyle = DefaultTextStyle.of(context).style; + final defaultFontFamily = defaultTextStyle.fontFamily; + final textPainter = TextPainter( text: TextSpan( text: text, - style: style ?? DefaultTextStyle.of(context).style, + style: + style?.apply(fontFamily: style.fontFamily ?? defaultFontFamily) ?? + DefaultTextStyle.of(context).style, ), maxLines: 1, - textScaleFactor: MediaQuery.of(context).textScaleFactor, + textScaler: MediaQuery.of(context).textScaler, textDirection: TextDirection.ltr, )..layout(minWidth: 0, maxWidth: double.infinity); - return textPainter.size; + + // TODO: Remove this once the bug is fixed + final size = textPainter.size; + return Size( + (size.width * flutterPainterErrorCorrection).roundToDouble(), + size.height, + ); }, [text, style], ); diff --git a/app/lib/main.dart b/app/lib/main.dart index 044fd7c19b..8abf18ace4 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,8 +1,8 @@ import "package:flutter/material.dart"; -import "package:google_fonts/google_fonts.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:stack_trace/stack_trace.dart" as stack_trace; import "package:typewriter/app_router.dart"; +import "package:typewriter/utils/fonts.dart"; import "package:typewriter/widgets/components/general/toasts.dart"; import "package:typewriter/widgets/inspector/editors.dart"; import "package:uuid/uuid.dart"; @@ -46,7 +46,7 @@ class TypeWriterApp extends HookConsumerWidget { final baseTheme = ThemeData(brightness: brightness); return baseTheme.copyWith( - textTheme: GoogleFonts.jetBrainsMonoTextTheme(baseTheme.textTheme), + textTheme: baseTheme.textTheme.apply(fontFamily: "JetBrainsMono"), inputDecorationTheme: InputDecorationTheme( contentPadding: const EdgeInsets.symmetric(horizontal: 12), border: OutlineInputBorder( @@ -62,12 +62,12 @@ class TypeWriterApp extends HookConsumerWidget { color: Colors.redAccent, fontSize: 12, ), - hintStyle: GoogleFonts.jetBrainsMono( + hintStyle: TextStyle( color: brightness == Brightness.light ? const Color(0x99000000) : const Color(0x99FFFFFF), fontSize: 16, - fontWeight: FontWeight.w400, + fontVariations: const [normalWeight], ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), @@ -80,6 +80,7 @@ class TypeWriterApp extends HookConsumerWidget { ), hoverColor: Colors.black.withOpacity(0.1), colorScheme: baseTheme.colorScheme.copyWith( + primary: Colors.blueAccent, brightness: brightness, error: Colors.redAccent, ), diff --git a/app/lib/pages/connect_page.dart b/app/lib/pages/connect_page.dart index 347ef57942..9bbc0666cc 100644 --- a/app/lib/pages/connect_page.dart +++ b/app/lib/pages/connect_page.dart @@ -4,7 +4,6 @@ import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart" hide Page; import "package:flutter_animate/flutter_animate.dart"; import "package:flutter_hooks/flutter_hooks.dart"; -import "package:google_fonts/google_fonts.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:rive/rive.dart"; import "package:typewriter/app_router.dart"; @@ -40,35 +39,37 @@ class ConnectPage extends HookConsumerWidget { useEffect( () { final timer = Timer(1.seconds, () { - ref.read(socketProvider.notifier).init(hostname, port, token.isEmpty ? null : token); + ref + .read(socketProvider.notifier) + .init(hostname, port, token.isEmpty ? null : token); }); return timer.cancel; }, [], ); - return Scaffold( + return const Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Spacer(), - const Expanded( + Spacer(), + Expanded( flex: 8, child: RiveAnimation.asset( "assets/tour.riv", stateMachines: ["state_machine"], ), ), - const SizedBox(height: 24), - const Text( + SizedBox(height: 24), + Text( "Waiting for connection", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold), ), ConnectionScroller( - style: GoogleFonts.jetBrainsMono(fontSize: 20, color: Colors.grey), + style: TextStyle(fontSize: 20, color: Colors.grey), ), - const SizedBox(height: 24), - const Spacer(), + SizedBox(height: 24), + Spacer(), ], ), ); diff --git a/app/lib/pages/error_connect_page.dart b/app/lib/pages/error_connect_page.dart index 5b3fcab9f8..447eacb0a3 100644 --- a/app/lib/pages/error_connect_page.dart +++ b/app/lib/pages/error_connect_page.dart @@ -1,6 +1,5 @@ import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart" hide FilledButton; -import "package:google_fonts/google_fonts.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:rive/rive.dart"; import "package:typewriter/hooks/delayed_execution.dart"; @@ -9,7 +8,12 @@ import "package:typewriter/widgets/components/general/copyable_text.dart"; @RoutePage() class ErrorConnectPage extends HookConsumerWidget { - const ErrorConnectPage({required this.hostname, required this.port, this.token, super.key}); + const ErrorConnectPage({ + required this.hostname, + required this.port, + this.token, + super.key, + }); final String hostname; final int port; @@ -22,12 +26,12 @@ class ErrorConnectPage extends HookConsumerWidget { ref.invalidate(socketProvider); }); - return Scaffold( + return const Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Spacer(), - const Expanded( + Spacer(), + Expanded( flex: 6, child: MouseRegion( cursor: SystemMouseCursors.zoomIn, @@ -37,20 +41,24 @@ class ErrorConnectPage extends HookConsumerWidget { ), ), ), - const SizedBox(height: 24), - const Text( + SizedBox(height: 24), + Text( "Communication error", - style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold, color: Colors.red), + style: TextStyle( + fontSize: 40, + fontWeight: FontWeight.bold, + color: Colors.red, + ), ), Text( "There was an error while communicating to the server.\nPlease check your connection and try again.", textAlign: TextAlign.center, - style: GoogleFonts.jetBrainsMono(fontSize: 20, color: Colors.grey), + style: TextStyle(fontSize: 20, color: Colors.grey), ), - const SizedBox(height: 24), - const CopyableText(text: "/typewriter connect"), - const SizedBox(height: 24), - const Spacer(), + SizedBox(height: 24), + CopyableText(text: "/typewriter connect"), + SizedBox(height: 24), + Spacer(), ], ), ); diff --git a/app/lib/pages/home_page.dart b/app/lib/pages/home_page.dart index 667c91f953..a6c56e6316 100644 --- a/app/lib/pages/home_page.dart +++ b/app/lib/pages/home_page.dart @@ -1,11 +1,13 @@ +import "dart:async"; + import "package:auto_route/auto_route.dart"; import "package:flutter/foundation.dart"; import "package:flutter/material.dart" hide FilledButton; import "package:font_awesome_flutter/font_awesome_flutter.dart"; -import "package:google_fonts/google_fonts.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:rive/rive.dart"; import "package:typewriter/app_router.dart"; +import "package:typewriter/utils/fonts.dart"; import "package:typewriter/widgets/components/general/copyable_text.dart"; import "package:typewriter/widgets/components/general/filled_button.dart"; @@ -15,19 +17,19 @@ class HomePage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return Scaffold( + return const Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Spacer(), - const Expanded( + Spacer(), + Expanded( flex: 2, child: RiveAnimation.asset( "assets/game_character.riv", stateMachines: ["State Machine"], ), ), - const Text( + Text( "Your journey starts here", textAlign: TextAlign.center, style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold), @@ -35,16 +37,20 @@ class HomePage extends HookConsumerWidget { Text( "Run the following command on your server to start editing", textAlign: TextAlign.center, - style: GoogleFonts.jetBrainsMono( - fontSize: 20, fontWeight: FontWeight.w100, color: Colors.grey), + style: TextStyle( + fontSize: 20, + color: Colors.grey, + fontVariations: [thinWeight], + ), ), - const SizedBox(height: 24), - const CopyableText(text: "/typewriter connect"), + SizedBox(height: 24), + CopyableText(text: "/typewriter connect"), if (kDebugMode || kProfileMode) ...[ - const SizedBox(height: 24), - const _DebugConnectButton(), + SizedBox(height: 24), + _DebugConnectButton(), + SizedBox(height: 24), ], - const Spacer(), + Spacer(), ], ), ); @@ -54,12 +60,17 @@ class HomePage extends HookConsumerWidget { /// This is a debug widget that allows you to connect quickly to a server. /// It is only visible in debug mode. class _DebugConnectButton extends HookConsumerWidget { - const _DebugConnectButton({super.key}); + const _DebugConnectButton(); - void connectTo(WidgetRef ref, String hostname, int port, - [String token = ""]) { + void connectTo( + WidgetRef ref, + String hostname, + int port, [ + String token = "", + ]) { ref.read(appRouter).replaceAll( - [ConnectRoute(hostname: hostname, port: port, token: token)]); + [ConnectRoute(hostname: hostname, port: port, token: token)], + ); } Future customConnectToPopup(BuildContext context, WidgetRef ref) async { diff --git a/app/lib/pages/pages_list.dart b/app/lib/pages/pages_list.dart index faefb23a45..acd380c1b4 100644 --- a/app/lib/pages/pages_list.dart +++ b/app/lib/pages/pages_list.dart @@ -37,12 +37,19 @@ class _PageData with _$_PageData { @riverpod List<_PageData> _pagesData(_PagesDataRef ref) { - return ref.watch(bookProvider).pages.map((page) => _PageData(name: page.name, type: page.type)).toList(); + return ref + .watch(bookProvider) + .pages + .map((page) => _PageData(name: page.name, type: page.type)) + .toList(); } @riverpod List _pageNames(_PageNamesRef ref) { - return ref.watch(_pagesDataProvider.select((pages) => pages.map((page) => page.name).toList())); + return ref.watch( + _pagesDataProvider + .select((pages) => pages.map((page) => page.name).toList()), + ); } @RoutePage(name: "PagesListRoute") @@ -72,19 +79,36 @@ class _PagesSelector extends HookConsumerWidget { return Container( color: const Color(0xFF163260), width: 230, + height: double.infinity, child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), - Text("Pages", style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white)), - const SizedBox(height: 12), - for (var i = 0; i < pagesData.length; i++) _PageTile(index: i, pageData: pagesData[i]), + Text( + "Pages", + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(color: Colors.white), + ), const SizedBox(height: 12), - // When selecting entries we don't want to be able to add new pages - const SelectingEntriesBlocker( - child: _AddPageButton(), + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (var i = 0; i < pagesData.length; i++) + _PageTile(index: i, pageData: pagesData[i]), + const SizedBox(height: 12), + // When selecting entries we don't want to be able to add new pages + const SelectingEntriesBlocker( + child: _AddPageButton(), + ), + ], + ), + ), ), ], ), @@ -95,7 +119,10 @@ class _PagesSelector extends HookConsumerWidget { @riverpod List _writers(_WritersRef ref, String pageId) { - return ref.watch(writersProvider).where((writer) => writer.pageId.hasValue && writer.pageId == pageId).toList(); + return ref + .watch(writersProvider) + .where((writer) => writer.pageId.hasValue && writer.pageId == pageId) + .toList(); } class _PageTile extends HookConsumerWidget { @@ -116,12 +143,19 @@ class _PageTile extends HookConsumerWidget { return true; } - List _contextMenuItems(BuildContext context, WidgetRef ref, bool isSelected) { + List _contextMenuItems( + BuildContext context, + WidgetRef ref, + bool isSelected, + ) { return [ ContextMenuTile.button( title: "Rename", icon: FontAwesomeIcons.pen, - onTap: () => showDialog(context: context, builder: (_) => _RenamePageDialogue(old: pageId)), + onTap: () => showDialog( + context: context, + builder: (_) => _RenamePageDialogue(old: pageId), + ), ), ContextMenuTile.divider(), ContextMenuTile.button( @@ -131,14 +165,17 @@ class _PageTile extends HookConsumerWidget { onTap: () => showConfirmationDialogue( context: context, title: "Delete ${pageId.formatted}?", - content: "This will delete the page and all its content.\nTHIS CANNOT BE UNDONE.", + content: + "This will delete the page and all its content.\nTHIS CANNOT BE UNDONE.", delayConfirm: 3.seconds, confirmText: "Delete", confirmIcon: FontAwesomeIcons.trash, onConfirm: () async { await ref.read(bookProvider.notifier).deletePage(pageId); if (!isSelected) return; - unawaited(ref.read(appRouter).replace(const EmptyPageEditorRoute())); + unawaited( + ref.read(appRouter).replace(const EmptyPageEditorRoute()), + ); }, ), ), @@ -147,17 +184,23 @@ class _PageTile extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isSelected = ref.watch(currentPageIdProvider.select((e) => e == pageId)); + final isSelected = + ref.watch(currentPageIdProvider.select((e) => e == pageId)); return WritersIndicator( enabled: !isSelected, provider: _writersProvider(pageId), - shift: (amount) => _needsShift(amount) ? const Offset(4, 30) : Offset.zero, + shift: (amount) => + _needsShift(amount) ? const Offset(4, 30) : Offset.zero, builder: (amount) { return AnimatedPadding( duration: 200.ms, curve: Curves.easeInOut, - padding: EdgeInsets.only(left: 2.0, right: 2.0, top: _needsShift(amount) ? 30 : 0), + padding: EdgeInsets.only( + left: 2.0, + right: 2.0, + top: _needsShift(amount) ? 30 : 0, + ), child: Material( color: isSelected ? const Color(0xFF1e3f6f) : Colors.transparent, borderRadius: BorderRadius.circular(8), @@ -183,13 +226,18 @@ class _PageTile extends HookConsumerWidget { Expanded( child: Text( pageId.formatted, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.white, - ), + style: + Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.white, + ), ), ), const SizedBox(width: 8), - const Icon(Icons.chevron_right, size: 16, color: Colors.white), + const Icon( + Icons.chevron_right, + size: 16, + color: Colors.white, + ), ], ), ), @@ -305,7 +353,11 @@ class AddPageDialogue extends HookConsumerWidget { final type = useState(fixedType ?? PageType.sequence); return AlertDialog( - title: Text(fixedType != null ? "Add a new ${fixedType!.tag} page" : "Add a new page"), + title: Text( + fixedType != null + ? "Add a new ${fixedType!.tag} page" + : "Add a new page", + ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/app/lib/utils/fonts.dart b/app/lib/utils/fonts.dart new file mode 100644 index 0000000000..22b6866a26 --- /dev/null +++ b/app/lib/utils/fonts.dart @@ -0,0 +1,6 @@ +import "dart:ui"; + +const thinWeight = FontVariation("wght", 100); +const normalWeight = FontVariation("wght", 400); +const boldWeight = FontVariation("wght", 700); +const extraBoldWeight = FontVariation("wght", 800); diff --git a/app/lib/widgets/components/app/cinematic_view.dart b/app/lib/widgets/components/app/cinematic_view.dart index 87b2a5126e..7daf7a9174 100644 --- a/app/lib/widgets/components/app/cinematic_view.dart +++ b/app/lib/widgets/components/app/cinematic_view.dart @@ -7,7 +7,6 @@ import "package:flutter/services.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:freezed_annotation/freezed_annotation.dart"; -import "package:google_fonts/google_fonts.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:riverpod_annotation/riverpod_annotation.dart"; import "package:typewriter/hooks/global_key.dart"; @@ -21,6 +20,7 @@ import "package:typewriter/models/writers.dart"; import "package:typewriter/pages/page_editor.dart"; import "package:typewriter/utils/color_converter.dart"; import "package:typewriter/utils/extensions.dart"; +import "package:typewriter/utils/fonts.dart"; import "package:typewriter/utils/passing_reference.dart"; import "package:typewriter/utils/popups.dart"; import "package:typewriter/widgets/components/app/empty_screen.dart"; @@ -47,13 +47,7 @@ List _cinematicEntryIds(_CinematicEntryIdsRef ref) { final page = ref.watch(currentPageProvider); if (page == null) return []; - return page.entries - .where((entry) { - final tags = ref.watch(entryTagsProvider(entry.type)); - return tags.contains("cinematic"); - }) - .map((entry) => entry.id) - .toList(); + return page.entries.map((entry) => entry.id).toList(); } class CinematicView extends HookConsumerWidget { @@ -197,12 +191,12 @@ int _totalSequenceFrames(_TotalSequenceFramesRef ref) { @freezed class _TrackState with _$_TrackState { - const factory _TrackState({ + const factory _TrackState([ @Default(0) double start, @Default(1) double end, @Default(0) int totalFrames, @Default(0) double width, - }) = _$__TrackState; + ]) = _$__TrackState; } extension on _TrackState { @@ -214,7 +208,8 @@ extension on _TrackState { class _TrackStateProvider extends StateNotifier<_TrackState> { _TrackStateProvider(this.ref) : super(const _TrackState()) { state = state.copyWith( - totalFrames: max(100, ref.read(_totalSequenceFramesProvider))); + totalFrames: max(100, ref.read(_totalSequenceFramesProvider)), + ); } final AutoDisposeStateNotifierProviderRef<_TrackStateProvider, _TrackState> ref; @@ -332,24 +327,27 @@ class _Heading extends HookConsumerWidget { final longestNameSize = useTextSize( context, longestName, - GoogleFonts.jetBrainsMono(fontSize: 13), + const TextStyle( + fontSize: 13, + fontFamily: "JetBrainsMono", + ), ); return SizedBox( height: 36, child: Row( children: [ SizedBox( - width: longestNameSize.width + 103, + width: longestNameSize.width + 100, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ const SizedBox(width: 8), - Text( + const Text( "Track Duration", - style: GoogleFonts.jetBrainsMono( - fontWeight: FontWeight.bold, + style: TextStyle( fontSize: 15, + fontVariations: [boldWeight], ), ), const SizedBox(width: 8), @@ -395,10 +393,10 @@ class _DurationField extends HookConsumerWidget { inputFormatters: [ FilteringTextInputFormatter.digitsOnly, ], - style: GoogleFonts.jetBrainsMono(fontSize: 12), - decoration: InputDecoration( + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( hintText: "Duration", - hintStyle: GoogleFonts.jetBrainsMono(fontSize: 13), + hintStyle: TextStyle(fontSize: 13), ), onChanged: (value) { final frames = int.tryParse(value); @@ -461,14 +459,15 @@ class _SmartSpacer extends HookConsumerWidget { final longestNameSize = useTextSize( context, longestName, - GoogleFonts.jetBrainsMono(fontSize: 13), + const TextStyle(fontSize: 13), ); final entryName = ref.watch(entryNameProvider(entryId)); final entryNameSize = useTextSize( context, entryName ?? "", - GoogleFonts.jetBrainsMono(fontSize: 13), + const TextStyle(fontSize: 13), ); + return SizedBox( width: longestNameSize.width - entryNameSize.width, ); @@ -664,13 +663,16 @@ int _timeFractions(_TimeFractionsRef ref) { ref.watch(_trackStateProvider.select((state) => state.duration)); final width = ref.watch(_trackStateProvider.select((state) => state.width)); return _possibleFractions.firstWhereOrNull( - (fraction) => (_minimalFractionSize * duration) / fraction < width) ?? + (fraction) => (_minimalFractionSize * duration) / fraction < width, + ) ?? 1000; } @riverpod -List _timeFractionFrames(_TimeFractionFramesRef ref, - {double fractionModifier = 1.0}) { +List _timeFractionFrames( + _TimeFractionFramesRef ref, { + double fractionModifier = 1.0, +}) { final startFrame = ref.watch(_trackStateProvider.select((state) => state.startFrame)); final endFrame = @@ -684,7 +686,10 @@ List _timeFractionFrames(_TimeFractionFramesRef ref, @riverpod double _timePointOffset( - _TimePointOffsetRef ref, int frame, double widgetWidth) { + _TimePointOffsetRef ref, + int frame, + double widgetWidth, +) { final startFrame = ref.watch(_trackStateProvider.select((state) => state.startFrame)); final frameSpacing = ref.watch(_frameSpacingProvider); @@ -701,8 +706,7 @@ class _TrackTimings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final fractionFrames = ref.watch(_timeFractionFramesProvider()); final oneCharWidth = - useTextSize(context, "0", GoogleFonts.jetBrainsMono(fontSize: 10)) - .width; + useTextSize(context, "0", const TextStyle(fontSize: 10)).width; return SizedBox( height: 16, @@ -719,7 +723,7 @@ class _TrackTimings extends HookConsumerWidget { ), child: Text( frame.toString(), - style: GoogleFonts.jetBrainsMono( + style: TextStyle( fontSize: 10, color: Theme.of(context).hintColor, ), @@ -733,7 +737,8 @@ class _TrackTimings extends HookConsumerWidget { @riverpod double _trackBackgroundFractionModifier( - _TrackBackgroundFractionModifierRef ref) { + _TrackBackgroundFractionModifierRef ref, +) { final fractions = ref.watch(_timeFractionsProvider); if (fractions == 1) return 1.0; if (fractions == 5) return 1.0; @@ -910,7 +915,9 @@ class _SegmentsTrack extends HookConsumerWidget { SizedBox(width: ref.watch(_trackSizeProvider), height: 64), for (final segment in segments) _SegmentPosition( - segment: segment, child: segmentBuilder(context, segment)), + segment: segment, + child: segmentBuilder(context, segment), + ), ], ); } @@ -985,9 +992,10 @@ class _MoveNotifier extends StateNotifier<_MoveState?> { .minBy((_, s) => s.startFrame); state = _MoveState( - previousSegment: previousSegment, - nextSegment: nextSegment, - innerPercent: innerPercent); + previousSegment: previousSegment, + nextSegment: nextSegment, + innerPercent: innerPercent, + ); ref .read(inspectingSegmentIdProvider.notifier) @@ -1109,8 +1117,9 @@ class _MoveNotifier extends StateNotifier<_MoveState?> { } final _moveNotifierProvider = StateNotifierProvider<_MoveNotifier, _MoveState?>( - _MoveNotifier.new, - name: "_moveNotifierProvider"); + _MoveNotifier.new, + name: "_moveNotifierProvider", +); class _SegmentWidget extends HookConsumerWidget { const _SegmentWidget({ @@ -1124,13 +1133,18 @@ class _SegmentWidget extends HookConsumerWidget { final Segment segment; final GlobalKey parentKey; - double _getPercentFromDragUpdate(DragUpdateDetails details, - [double shift = 0]) { + double _getPercentFromDragUpdate( + DragUpdateDetails details, [ + double shift = 0, + ]) { return _getPercent(parentKey, details.globalPosition, shift); } - double _getPercent(GlobalKey key, - [Offset offset = Offset.zero, double shift = 0]) { + double _getPercent( + GlobalKey key, [ + Offset offset = Offset.zero, + double shift = 0, + ]) { final renderBox = key.currentContext?.findRenderObject() as RenderBox?; if (renderBox == null) return 0; @@ -1146,7 +1160,8 @@ class _SegmentWidget extends HookConsumerWidget { final showThumbs = ref.watch(_showThumbsProvider(segment.startFrame, segment.endFrame)); final isPathSelected = ref.watch( - inspectingSegmentIdProvider.select((id) => id == segment.truePath)); + inspectingSegmentIdProvider.select((id) => id == segment.truePath), + ); final isEntrySelected = ref.watch(inspectingEntryIdProvider.select((id) => id == entryId)); final isSelected = isPathSelected && isEntrySelected; @@ -1161,7 +1176,8 @@ class _SegmentWidget extends HookConsumerWidget { color: context.isDark ? Colors.white : Colors.black.withOpacity(0.4), - width: 2) + width: 2, + ) : Border.all(color: Colors.transparent, width: 2), ), child: Row( @@ -1290,7 +1306,10 @@ List _ignoreEntryFields(_IgnoreEntryFieldsRef ref) { /// Finds a segment that overlaps the given range /// If no segment is found, returns null Segment? _includesSegment( - int startFrame, int endFrame, List segments) { + int startFrame, + int endFrame, + List segments, +) { return segments .firstWhereOrNull((segment) => segment.overlaps(startFrame, endFrame)); } @@ -1355,8 +1374,11 @@ void _addSegment(PassingRef ref, String entryId, String segmentPath) { } if (endFrame - startFrame < minSpace) { - Toasts.showError(ref, "Could not add segment", - description: "There is not enough space to add a segment."); + Toasts.showError( + ref, + "Could not add segment", + description: "There is not enough space to add a segment.", + ); return; } @@ -1379,27 +1401,39 @@ void _addSegment(PassingRef ref, String entryId, String segmentPath) { void _deleteSegment(PassingRef ref, String entryId, String segmentPath) { final page = ref.read(currentPageProvider); if (page == null) { - Toasts.showError(ref, "Could not delete segment", - description: "No page is selected."); + Toasts.showError( + ref, + "Could not delete segment", + description: "No page is selected.", + ); return; } final entry = ref.read(entryProvider(page.name, entryId)); if (entry == null) { - Toasts.showError(ref, "Could not delete segment", - description: "No entry is selected."); + Toasts.showError( + ref, + "Could not delete segment", + description: "No entry is selected.", + ); return; } final blueprint = ref.read(entryBlueprintProvider(entry.type)); if (blueprint == null) { - Toasts.showError(ref, "Could not delete segment", - description: "No blueprint is found for the selected entry."); + Toasts.showError( + ref, + "Could not delete segment", + description: "No blueprint is found for the selected entry.", + ); return; } final segmentBlueprint = blueprint.getField(segmentPath); if (segmentBlueprint == null) { - Toasts.showError(ref, "Could not delete segment", - description: "No blueprint is found for the selected segment."); + Toasts.showError( + ref, + "Could not delete segment", + description: "No blueprint is found for the selected segment.", + ); return; } @@ -1415,7 +1449,9 @@ void _deleteSegment(PassingRef ref, String entryId, String segmentPath) { @riverpod List _entryContextActions( - _EntryContextActionsRef ref, String entryId) { + _EntryContextActionsRef ref, + String entryId, +) { final paths = ref.watch(_segmentPathsProvider(entryId)); return paths.entries.map((e) { @@ -1469,7 +1505,8 @@ class CinematicInspector extends HookConsumerWidget { ? EntryInspector( key: ValueKey(inspectingEntry.id), actions: ref.watch( - _entryContextActionsProvider(inspectingEntry.id)), + _entryContextActionsProvider(inspectingEntry.id), + ), ignoreFields: ref.watch(_ignoreEntryFieldsProvider), ) : const EmptyInspector(), diff --git a/app/lib/widgets/components/app/cinematic_view.freezed.dart b/app/lib/widgets/components/app/cinematic_view.freezed.dart index 420501dd88..9a0995c55b 100644 --- a/app/lib/widgets/components/app/cinematic_view.freezed.dart +++ b/app/lib/widgets/components/app/cinematic_view.freezed.dart @@ -102,19 +102,19 @@ class __$$_$__TrackStateCopyWithImpl<$Res> Object? width = null, }) { return _then(_$_$__TrackState( - start: null == start + null == start ? _value.start : start // ignore: cast_nullable_to_non_nullable as double, - end: null == end + null == end ? _value.end : end // ignore: cast_nullable_to_non_nullable as double, - totalFrames: null == totalFrames + null == totalFrames ? _value.totalFrames : totalFrames // ignore: cast_nullable_to_non_nullable as int, - width: null == width + null == width ? _value.width : width // ignore: cast_nullable_to_non_nullable as double, @@ -126,7 +126,7 @@ class __$$_$__TrackStateCopyWithImpl<$Res> class _$_$__TrackState implements _$__TrackState { const _$_$__TrackState( - {this.start = 0, this.end = 1, this.totalFrames = 0, this.width = 0}); + [this.start = 0, this.end = 1, this.totalFrames = 0, this.width = 0]); @override @JsonKey() @@ -170,10 +170,10 @@ class _$_$__TrackState implements _$__TrackState { abstract class _$__TrackState implements _TrackState { const factory _$__TrackState( - {final double start, + [final double start, final double end, final int totalFrames, - final double width}) = _$_$__TrackState; + final double width]) = _$_$__TrackState; @override double get start; diff --git a/app/lib/widgets/components/app/cinematic_view.g.dart b/app/lib/widgets/components/app/cinematic_view.g.dart index e5e7926bd7..de93bd8fb8 100644 --- a/app/lib/widgets/components/app/cinematic_view.g.dart +++ b/app/lib/widgets/components/app/cinematic_view.g.dart @@ -6,7 +6,7 @@ part of 'cinematic_view.dart'; // RiverpodGenerator // ************************************************************************** -String _$cinematicEntryIdsHash() => r'a2cd61ec3d1a8d66e273283ed93bfcf3ed2b3241'; +String _$cinematicEntryIdsHash() => r'd605052793e8cd783fa8fbb6ed8ea754ccc60023'; /// See also [_cinematicEntryIds]. @ProviderFor(_cinematicEntryIds) diff --git a/app/lib/widgets/components/app/entry_node.dart b/app/lib/widgets/components/app/entry_node.dart index 5a58e16700..26b3dba474 100644 --- a/app/lib/widgets/components/app/entry_node.dart +++ b/app/lib/widgets/components/app/entry_node.dart @@ -29,7 +29,8 @@ class EntryNode extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isSelected = ref.watch(inspectingEntryIdProvider.select((e) => e == entryId)); + final isSelected = + ref.watch(inspectingEntryIdProvider.select((e) => e == entryId)); final entryType = ref.watch(entryTypeProvider(entryId)); if (entryType == null) return const InvalidEntry(); @@ -54,7 +55,8 @@ class EntryNode extends HookConsumerWidget { icon: Icon(blueprint.icon, size: 18, color: Colors.white), isSelected: isSelected, contextActions: contextActions, - onTap: () => ref.read(inspectingEntryIdProvider.notifier).selectEntry(entryId), + onTap: () => + ref.read(inspectingEntryIdProvider.notifier).selectEntry(entryId), ); } } @@ -120,9 +122,14 @@ class _EntryNode extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final canTrigger = ref.read(modifierPathsProvider(type, "trigger").select((value) => value.contains("triggers.*"))); - final canBeTriggered = - ref.watch(entryBlueprintProvider(type).select((b) => b?.tags.contains("triggerable") ?? false)); + final canTrigger = ref.read( + modifierPathsProvider(type, "trigger") + .select((value) => value.contains("triggers.*")), + ); + final canBeTriggered = ref.watch( + entryBlueprintProvider(type) + .select((b) => b?.tags.contains("triggerable") ?? false), + ); return WritersIndicator( provider: _writersProvider(id), @@ -170,7 +177,9 @@ class _EntryNode extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: isSelected ? Theme.of(context).scaffoldBackgroundColor : backgroundColor, + color: isSelected + ? Theme.of(context).scaffoldBackgroundColor + : backgroundColor, width: 3, ), ), @@ -183,7 +192,11 @@ class _EntryNode extends HookConsumerWidget { const SizedBox(width: 12), Text( name, - style: TextStyle(fontSize: 13, color: foregroundColor), + style: TextStyle( + fontFamily: "JetBrainsMono", + fontSize: 13, + color: foregroundColor, + ), ), ], ), @@ -228,7 +241,11 @@ class _SelectingEntryNode extends HookConsumerWidget { isSelected: isSelected, opacity: canSelect ? 1 : 0.6, enableContextMenu: false, - onTap: canSelect ? () => ref.read(entrySelectionProvider.notifier).toggleEntrySelection(entryId) : null, + onTap: canSelect + ? () => ref + .read(entrySelectionProvider.notifier) + .toggleEntrySelection(entryId) + : null, ), ); } @@ -296,7 +313,10 @@ class InvalidEntry extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("Unknown entry", style: TextStyle(color: Colors.white)), + const Text( + "Unknown entry", + style: TextStyle(color: Colors.white), + ), Text( "(this should never happen)", style: Theme.of(context).textTheme.bodySmall?.copyWith( @@ -342,7 +362,9 @@ class ExternalEntryNode extends HookConsumerWidget { title: "Delete Reference", icon: Icons.delete, color: Colors.redAccent, - onTap: () => ref.read(currentPageProvider)?.removeReferencesTo(ref.passing, entry.id), + onTap: () => ref + .read(currentPageProvider) + ?.removeReferencesTo(ref.passing, entry.id), ), ]; }, @@ -357,7 +379,9 @@ class ExternalEntryNode extends HookConsumerWidget { ), child: InkWell( mouseCursor: SystemMouseCursors.click, - onTap: () => ref.read(inspectingEntryIdProvider.notifier).navigateAndSelectEntry(ref.passing, entry.id), + onTap: () => ref + .read(inspectingEntryIdProvider.notifier) + .navigateAndSelectEntry(ref.passing, entry.id), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( @@ -369,11 +393,15 @@ class ExternalEntryNode extends HookConsumerWidget { children: [ Text( entry.formattedName, - style: const TextStyle(color: Colors.white, fontSize: 13), + style: + const TextStyle(color: Colors.white, fontSize: 13), ), Text( pageName, - style: const TextStyle(color: Colors.white70, fontSize: 11), + style: const TextStyle( + color: Colors.white70, + fontSize: 11, + ), ), ], ), diff --git a/app/lib/widgets/components/general/context_menu_region.dart b/app/lib/widgets/components/general/context_menu_region.dart index bbf6b7b276..3da53d2d02 100644 --- a/app/lib/widgets/components/general/context_menu_region.dart +++ b/app/lib/widgets/components/general/context_menu_region.dart @@ -2,7 +2,6 @@ import "package:collection/collection.dart"; import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; -import "package:google_fonts/google_fonts.dart"; import "package:typewriter/hooks/text_size.dart"; typedef ContextMenuBuilder = List Function(BuildContext); @@ -95,7 +94,8 @@ class _ContextMenuRegionState extends State { Quadrant _getQuadrant(RenderBox box) { final boxSize = box.size; final boxPosition = box.localToGlobal(Offset.zero); - final boxCenter = boxPosition + Offset(boxSize.width / 2, boxSize.height / 2); + final boxCenter = + boxPosition + Offset(boxSize.width / 2, boxSize.height / 2); final screenSize = MediaQuery.of(context).size; @@ -186,7 +186,6 @@ class _ContextMenu extends HookWidget { required this.quadrant, required this.position, required this.tiles, - super.key, }); final ContextMenuController controller; @@ -201,23 +200,35 @@ class _ContextMenu extends HookWidget { Widget build(BuildContext context) { final size = MediaQuery.of(context).size; - final largestText = maxBy(tiles.map((e) => e.largestText), (text) => text.length) ?? ""; - final textSize = useTextSize(context, largestText, GoogleFonts.jetBrainsMono(fontSize: 13)); + final largestText = + maxBy(tiles.map((e) => e.largestText), (text) => text.length) ?? ""; + final textSize = + useTextSize(context, largestText, const TextStyle(fontSize: 13)); final maxWidth = textSize.width + _spacingWidth; return Positioned( - left: quadrant == Quadrant.topLeft || quadrant == Quadrant.bottomLeft ? position.dx : null, - right: quadrant == Quadrant.topRight || quadrant == Quadrant.bottomRight ? size.width - position.dx : null, - top: quadrant == Quadrant.topLeft || quadrant == Quadrant.topRight ? position.dy : null, - bottom: quadrant == Quadrant.bottomLeft || quadrant == Quadrant.bottomRight ? size.height - position.dy : null, + left: quadrant == Quadrant.topLeft || quadrant == Quadrant.bottomLeft + ? position.dx + : null, + right: quadrant == Quadrant.topRight || quadrant == Quadrant.bottomRight + ? size.width - position.dx + : null, + top: quadrant == Quadrant.topLeft || quadrant == Quadrant.topRight + ? position.dy + : null, + bottom: + quadrant == Quadrant.bottomLeft || quadrant == Quadrant.bottomRight + ? size.height - position.dy + : null, child: Listener( onPointerUp: (_) => controller.remove(), child: ConstrainedBox( constraints: BoxConstraints( // Don't let the menu go off screen - maxHeight: quadrant == Quadrant.topLeft || quadrant == Quadrant.topRight - ? size.height - position.dy - 10 - : position.dy - 10, + maxHeight: + quadrant == Quadrant.topLeft || quadrant == Quadrant.topRight + ? size.height - position.dy - 10 + : position.dy - 10, maxWidth: maxWidth, ), child: Material( @@ -249,7 +260,6 @@ class _ContextMenu extends HookWidget { class _ContextMenuButton extends StatelessWidget { const _ContextMenuButton({ required this.tile, - super.key, }); final ContextMenuButton tile; @@ -271,7 +281,13 @@ class _ContextMenuButton extends StatelessWidget { children: [ Icon(tile.icon, color: tile.color, size: 16), const SizedBox(width: 10), - Text(tile.title, style: GoogleFonts.jetBrainsMono(fontSize: 13, color: tile.color)), + Text( + tile.title, + style: TextStyle( + fontSize: 13, + color: tile.color, + ), + ), ], ), ), diff --git a/app/lib/widgets/components/general/dropdown.dart b/app/lib/widgets/components/general/dropdown.dart index aafcf38f38..c6adc880fc 100644 --- a/app/lib/widgets/components/general/dropdown.dart +++ b/app/lib/widgets/components/general/dropdown.dart @@ -1,7 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; -import "package:google_fonts/google_fonts.dart"; class Dropdown extends HookWidget { const Dropdown({ @@ -53,10 +52,8 @@ class Dropdown extends HookWidget { icon: Container(), underline: Container(), alignment: alignment ?? AlignmentDirectional.centerStart, - style: GoogleFonts.jetBrainsMono( - textStyle: TextStyle( - color: Theme.of(context).textTheme.bodyLarge!.color, - ), + style: TextStyle( + color: Theme.of(context).textTheme.bodyLarge!.color, ), focusColor: Colors.transparent, borderRadius: borderRadius ?? BorderRadius.circular(8), @@ -65,7 +62,9 @@ class Dropdown extends HookWidget { (e) => DropdownMenuItem( alignment: alignment ?? AlignmentDirectional.centerStart, value: e, - child: builder != null ? builder!(context, e) : Text(e.toString()), + child: builder != null + ? builder!(context, e) + : Text(e.toString()), ), ) .toList(), diff --git a/app/lib/widgets/components/general/toasts.dart b/app/lib/widgets/components/general/toasts.dart index d62130e33c..e9618817a8 100644 --- a/app/lib/widgets/components/general/toasts.dart +++ b/app/lib/widgets/components/general/toasts.dart @@ -6,11 +6,11 @@ import "package:flutter_animate/flutter_animate.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:freezed_annotation/freezed_annotation.dart"; -import "package:google_fonts/google_fonts.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:tinycolor2/tinycolor2.dart"; import "package:typewriter/hooks/text_size.dart"; import "package:typewriter/main.dart"; +import "package:typewriter/utils/fonts.dart"; import "package:typewriter/utils/passing_reference.dart"; part "toasts.freezed.dart"; @@ -247,10 +247,10 @@ class _TemporaryToast extends HookConsumerWidget { final messageSize = useTextSize( context, toast.message, - GoogleFonts.jetBrainsMono( + const TextStyle( color: Colors.white, fontSize: 16.0, - fontWeight: FontWeight.w900, + fontVariations: [extraBoldWeight], ), ); @@ -282,10 +282,10 @@ class _TemporaryToast extends HookConsumerWidget { children: [ Text( toast.message, - style: GoogleFonts.jetBrainsMono( + style: const TextStyle( color: Colors.white, fontSize: 16.0, - fontWeight: FontWeight.w900, + fontVariations: [extraBoldWeight], ), ), if (toast.description != null) ...[ diff --git a/app/macos/Podfile.lock b/app/macos/Podfile.lock index 2422d37ca6..0f2bdc2f1a 100644 --- a/app/macos/Podfile.lock +++ b/app/macos/Podfile.lock @@ -32,9 +32,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 - rive_common: fab8476ce8352bf54152a913f393a8696d3dc98c - url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + rive_common: acedcab7802c0ece4b0d838b71d7deb637e1309a + url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 diff --git a/app/pubspec.lock b/app/pubspec.lock index a5ee1ae260..beaf1fe08b 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -109,18 +109,18 @@ packages: dependency: "direct main" description: name: auto_route - sha256: "02120972925a567c37921fa28ac7e90680c7095dd0e70711353737ec2727cdc6" + sha256: "1d53010ec30ad3b91e6d4701b18fc503e3d1bbf570f4c3277b74b6359263ab16" url: "https://pub.dev" source: hosted - version: "7.4.0" + version: "7.7.0" auto_route_generator: dependency: "direct dev" description: name: auto_route_generator - sha256: d0555913cc54153c38b1dd4f69e0d6a623818ca7195e7c1437901d52b2596eec + sha256: e9245cc56f04a4473c281346d194efa2c89a0f8e4f1c02a5b3b43e242c3e1c75 url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.2.0" auto_size_text: dependency: "direct main" description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: build - sha256: "43865b79fbb78532e4bff7c33087aa43b1d488c4fdef014eaef568af6d8016dc" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" build_config: dependency: transitive description: @@ -165,18 +165,18 @@ packages: dependency: transitive description: name: build_resolvers - sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 + sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "5e1929ad37d48bd382b124266cb8e521de5548d406a45a5ae6656c13dab73e37" + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.6" build_runner_core: dependency: transitive description: @@ -341,10 +341,10 @@ packages: dependency: transitive description: name: dart_style - sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" dotted_border: dependency: "direct main" description: @@ -357,10 +357,10 @@ packages: dependency: "direct main" description: name: duration - sha256: d0b29d0a345429e3986ac56d60e4aef65b37d11e653022b2b9a4b361332b777f + sha256: "0548a12d235dab185c677ef660995f23fdc06a02a2b984aa23805f6a03d82815" url: "https://pub.dev" source: hosted - version: "3.0.12" + version: "3.0.13" fake_async: dependency: transitive description: @@ -402,10 +402,10 @@ packages: dependency: "direct main" description: name: flutter_animate - sha256: f611a67082d4c5ff9b8b7737ec7e675a22af70a94c8c3c22109f14b078d4d2bf + sha256: be54662837a6e66cc53ee88549e808c625275e0faf5a43e11cf3182cb0bd1b02 url: "https://pub.dev" source: hosted - version: "4.1.1+1" + version: "4.2.0" flutter_hooks: dependency: "direct main" description: @@ -418,10 +418,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_riverpod: dependency: transitive description: @@ -452,26 +452,26 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: "959ef4add147753f990b4a7c6cccb746d5792dbdc81b1cde99e62e7edb31b206" + sha256: "5fb789145cae1f4c3245c58b3f8fb287d055c26323879eab57a7bf0cfd1e45f3" url: "https://pub.dev" source: hosted - version: "10.4.0" + version: "10.5.0" freezed: dependency: "direct dev" description: name: freezed - sha256: a9520490532087cf38bf3f7de478ab6ebeb5f68bb1eb2641546d92719b224445 + sha256: "2df89855fe181baae3b6d714dc3c4317acf4fccd495a6f36e5e00f24144c6c3b" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.4.1" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.1" frontend_server_client: dependency: transitive description: @@ -496,14 +496,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - google_fonts: - dependency: "direct main" - description: - name: google_fonts - sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6" - url: "https://pub.dev" - source: hosted - version: "4.0.4" graphs: dependency: transitive description: @@ -564,10 +556,10 @@ packages: dependency: "direct dev" description: name: icons_launcher - sha256: "8f81b295f5de3796e9662ac385c174ee5255f7c4cee1df031393c17c616cff57" + sha256: af05397792f6d82b93375a8a0253b8db0d3f816ef1dd1bf5c35cbab55321d327 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" image: dependency: transitive description: @@ -604,10 +596,10 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: "61a60716544392a82726dd0fa1dd6f5f1fd32aec66422b6e229e7b90d52325c4" + sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "6.7.1" ktx: dependency: "direct main" description: @@ -724,10 +716,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" path_provider_linux: dependency: transitive description: @@ -800,14 +792,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" pub_semver: dependency: transitive description: @@ -828,18 +812,18 @@ packages: dependency: "direct main" description: name: rive - sha256: "55e1f8bf249444545a7c832830d2bbb9adae759193fb879294bc6018b9f0eedd" + sha256: f3b8af0898c987d68019e91d92257edd902c28c816e49de033a7272e86bd5425 url: "https://pub.dev" source: hosted - version: "0.11.3" + version: "0.11.4" rive_common: dependency: transitive description: name: rive_common - sha256: "7e17937b790bb2f631767b3d505da8c298309c0a6ab08cd317fa6fe081ed5b63" + sha256: f6687f9d70e6fd3888a9b0e9c0b307966d2ce74cf00cfb01dce906c3bbada52f url: "https://pub.dev" source: hosted - version: "0.0.10" + version: "0.1.0" riverpod: dependency: transitive description: @@ -929,18 +913,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" + sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" source_helper: dependency: transitive description: name: source_helper - sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.4" source_span: dependency: transitive description: @@ -1065,18 +1049,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" url: "https://pub.dev" source: hosted - version: "6.1.11" + version: "6.1.12" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 + sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03" url: "https://pub.dev" source: hosted - version: "6.0.35" + version: "6.0.36" url_launcher_ios: dependency: transitive description: @@ -1097,10 +1081,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" + sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" url_launcher_platform_interface: dependency: transitive description: @@ -1113,18 +1097,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" + sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.18" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" + sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" uuid: dependency: "direct main" description: @@ -1169,10 +1153,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: ada49637c27973c183dad90beb6bd781eea4c9f5f955d35da172de0af7bd3440 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.8.0" watcher: dependency: transitive description: @@ -1201,18 +1185,18 @@ packages: dependency: transitive description: name: win32 - sha256: "7dacfda1edcca378031db9905ad7d7bd56b29fd1a90b0908b71a52a12c41e36b" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.5" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" xml: dependency: transitive description: @@ -1231,4 +1215,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.7.0-0" + flutter: ">=3.10.0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 7aee7078de..7ac7172ca8 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -12,17 +12,17 @@ dependencies: cupertino_icons: ^1.0.5 collection: stack_trace: - freezed_annotation: ^2.2.0 + freezed_annotation: ^2.4.1 json_annotation: ^4.8.1 graphview: ^1.2.0 - flutter_hooks: ^0.18.6 + flutter_hooks: hooks_riverpod: ^2.3.6 auto_size_text: ^3.0.0 - google_fonts: ^4.0.4 - font_awesome_flutter: ^10.4.0 - rive: ^0.11.2 + # google_fonts: ^4.0.4 + font_awesome_flutter: ^10.5.0 + rive: ^0.11.4 fuzzy: ^0.4.0-nullsafety.0 - auto_route: ^7.4.0 + auto_route: ^7.7.0 collapsible: ^1.0.0 riverpod_annotation: ^2.1.1 ktx: ^1.1.6 @@ -30,12 +30,12 @@ dependencies: socket_io_client: 1.0.2 clipboard: ^0.1.3 dotted_border: ^2.0.0+3 - flutter_svg: ^2.0.6 + flutter_svg: ^2.0.7 color: ^3.0.0 - duration: ^3.0.12 - flutter_animate: ^4.1.1+1 + duration: ^3.0.13 + flutter_animate: ^4.2.0 text_scroll: ^0.2.0 - url_launcher: ^6.1.11 + url_launcher: ^6.1.12 http: audioplayers: ^4.1.0 collection_ext: ^1.0.0 @@ -47,10 +47,10 @@ dev_dependencies: sdk: flutter flutter_lints: build_runner: - freezed: ^2.3.5 - json_serializable: ^6.7.0 - icons_launcher: ^2.1.1 - auto_route_generator: ^7.1.1 + freezed: ^2.4.1 + json_serializable: ^6.7.1 + icons_launcher: ^2.1.3 + auto_route_generator: ^7.2.0 riverpod_generator: ^2.2.3 custom_lint: riverpod_lint: ^1.3.2 @@ -74,3 +74,9 @@ flutter: assets: - assets/ - assets/materials/ + fonts: + - family: JetBrainsMono + fonts: + - asset: assets/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf + - asset: assets/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf + style: italic diff --git a/readme/cinematic.gif b/readme/cinematic.gif new file mode 100644 index 0000000000..86777e86eb Binary files /dev/null and b/readme/cinematic.gif differ