Merge branch 'main' into 'feat/save'

# Conflicts:
#   src/components/window/file.rs
merge-requests/21/head
Nefo Fortressia 2021-12-18 04:53:27 +00:00
commit 5e574299f3
14 changed files with 233 additions and 259 deletions

118
Cargo.lock generated
View File

@ -20,12 +20,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.14.7" version = "0.14.7"
@ -68,12 +62,6 @@ dependencies = [
"smallvec", "smallvec",
] ]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "echidna" name = "echidna"
version = "0.1.0" version = "0.1.0"
@ -87,7 +75,6 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sourceview5", "sourceview5",
"webbrowser",
] ]
[[package]] [[package]]
@ -438,36 +425,12 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "js-sys"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.102" version = "0.2.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.6.4" version = "0.6.4"
@ -843,87 +806,6 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasm-bindgen"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
[[package]]
name = "web-sys"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webbrowser"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecad156490d6b620308ed411cfee90d280b3cbd13e189ea0d3fada8acc89158a"
dependencies = [
"web-sys",
"widestring",
"winapi",
]
[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "echidna" name = "echidna"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -14,5 +14,4 @@ serde = { version = "^1.0.130", features = ["derive"] }
relative-path = "^1.5.0" relative-path = "^1.5.0"
gdk = { version = "^0.3.0", package = "gdk4"} gdk = { version = "^0.3.0", package = "gdk4"}
sourceview = { package = "sourceview5", version = "^0.1.0", git = "https://github.com/EchidnaHQ/sourceview" } sourceview = { package = "sourceview5", version = "^0.1.0", git = "https://github.com/EchidnaHQ/sourceview" }
webbrowser = { version = "^0.5.5" }
once_cell = "1" once_cell = "1"

View File

@ -70,7 +70,7 @@ impl EchidnaCoreEditor {
|_, _| {}, |_, _| {},
|result| { |result| {
if result.is_err() { if result.is_err() {
panic!(result.err()); panic!("Found an error when loading the file into the text editor's buffer. {:#?}", result.err());
} }
}, },
); );

View File

@ -5,4 +5,15 @@
pub mod app; pub mod app;
pub mod editor; pub mod editor;
pub mod sidebar; pub mod sidebar;
pub mod tab_label;
pub mod window; pub mod window;
pub use app::EchidnaEditor;
pub use editor::EchidnaCoreEditor;
pub use sidebar::EchidnaSidebar;
pub use tab_label::TabLabel;
pub use window::EchidnaWindow;
pub mod prelude {
pub use super::window::{file::*, menubar::*};
}

View File

@ -6,67 +6,71 @@
- file, You can obtain one at https://mozilla.org/MPL/2.0/. - file, You can obtain one at https://mozilla.org/MPL/2.0/.
--> -->
<template class="EchidnaSidebar"> <template class="EchidnaSidebar">
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<child> <child>
<object class="GtkStackSidebar"> <object class="GtkStackSidebar">
<property name="stack"> <property name="stack">sidebar_stack</property>
<object class="GtkStack">
<child>
<object class="GtkStackPage">
<!-- Explorer Tab -->
<property name="name">explorer</property>
<property name="title">Explorer</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkTreeView" id="treeview">
<property name="model">liststore1</property>
<child></child>
<child internal-child="selection">
<object class="GtkTreeSelection" id="selection">
<signal name="changed" handler="on_treeview_selection_changed" />
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<!-- Search Tab -->
<property name="name">search</property>
<property name="title">Search</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkLabel">
<property name="label">Search</property>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<!-- Extensions Tab -->
<property name="name">extensions</property>
<property name="title">Extensions</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
</object>
</property>
</object>
</child>
</object>
</property>
</object> </object>
</child> </child>
<child>
<object class="GtkStack" id="sidebar_stack">
<property name="hhomogeneous">1</property>
<property name="width-request">170</property>
<child>
<object class="GtkStackPage">
<!-- Explorer Tab -->
<property name="name">explorer</property>
<property name="title">Explorer</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkLabel">
<property name="label">Explorer</property>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<!-- Search Tab -->
<property name="name">search</property>
<property name="title">Search</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkLabel">
<property name="label">Search</property>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<!-- Extensions Tab -->
<property name="name">extensions</property>
<property name="title">Extensions</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkLabel">
<property name="label">Extensions</property>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
</template> </template>
</interface> </interface>

View File

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::CompositeTemplate;
#[derive(Default, CompositeTemplate)]
#[template(file = "./tab-label.ui")]
pub struct TabLabel {
#[template_child]
pub button: TemplateChild<gtk::Button>,
}
#[glib::object_subclass]
impl ObjectSubclass for TabLabel {
const NAME: &'static str = "TabLabel";
type Type = super::TabLabel;
type ParentType = gtk::Box;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
}
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for TabLabel {}
impl WidgetImpl for TabLabel {}
impl BoxImpl for TabLabel {}
impl BuildableImpl for TabLabel {
fn add_child(
&self,
buildable: &Self::Type,
builder: &gtk::Builder,
child: &glib::Object,
type_: Option<&str>,
) {
buildable.prepend(child.downcast_ref::<gtk::Widget>().unwrap());
}
}

View File

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
pub mod imp;
use glib::IsA;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
glib::wrapper! {
pub struct TabLabel(ObjectSubclass<imp::TabLabel>)
@extends gtk::Box, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}
impl TabLabel {
pub fn new<U: IsA<gtk::Widget>>(tab_label: Option<&U>) -> Self {
let this: Self = glib::Object::new(&[]).expect("Failed to create 'TabLabel' component.");
if tab_label.is_some() {
this.prepend(tab_label.unwrap());
}
this
}
pub fn to_imp(&self) -> &imp::TabLabel {
imp::TabLabel::from_instance(self)
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="4.5.0" />
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<template class="TabLabel">
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<child>
<object class="GtkButton" id="button">
<property name="has-frame">0</property>
<property name="icon-name">window-close-symbolic</property>
</object>
</child>
</template>
</interface>

View File

@ -6,7 +6,7 @@ use crate::components::editor::EchidnaCoreEditor;
use crate::lib::prelude::*; use crate::lib::prelude::*;
use glib::clone; use glib::clone;
use gtk::{ use gtk::{
prelude::*, subclass::prelude::*, FileChooserAction, FileChooserDialog, Label, ResponseType, prelude::*, subclass::prelude::*, FileChooserAction, FileChooserNative, Label, ResponseType,
}; };
use sourceview::{File, FileExt as SourceFileExt}; use sourceview::{File, FileExt as SourceFileExt};
@ -35,14 +35,12 @@ impl FileImplementedEditor for super::EchidnaWindow {
Perhaps some of the last points should not be implemented in this function but rather in another function that keeps track of every files. Perhaps some of the last points should not be implemented in this function but rather in another function that keeps track of every files.
*/ */
fn action_open_file(&self) { fn action_open_file(&self) {
let dialog: FileChooserDialog = FileChooserDialog::new( let dialog = FileChooserNative::new(
Some("Open a file"), Some("Open a file"),
Some(self), Some(self),
FileChooserAction::Open, FileChooserAction::Open,
&[ Some("Open"),
("Cancel", ResponseType::Cancel), Some("Cancel"),
("Open", ResponseType::Accept),
],
); );
dialog.set_visible(true); dialog.set_visible(true);
@ -79,14 +77,12 @@ impl FileImplementedEditor for super::EchidnaWindow {
); );
} }
fn action_save_file_as(&self) { fn action_save_file_as(&self) {
let dialog = FileChooserDialog::new( let dialog = FileChooserNative::new(
Some("Save File As"), Some("Save File As"),
Some(self), Some(self),
FileChooserAction::Save, FileChooserAction::Save,
&[ Some("Open"),
("Cancel", ResponseType::Cancel), Some("Cancel"),
("Save", ResponseType::Accept),
],
); );
dialog.set_current_name("untitled"); dialog.set_current_name("untitled");

View File

@ -78,20 +78,20 @@ impl MenubarImplementedEditor for EchidnaWindow {
app.add_action(&act_report_issue); app.add_action(&act_report_issue);
act_report_issue.connect_activate(|_action, _variant| { act_report_issue.connect_activate(clone!(@weak self as win =>
webbrowser::open("https://gitlab.com/EchidnaHQ/Echidna/-/issues/new?issue"); move |_action, _variant| {
}); gtk::show_uri(Some(&win), "https://github.com/EchidnaHQ/Echidna/issues/new", gdk::CURRENT_TIME);
}));
} }
{ {
let act_search_feature_requests = SimpleAction::new("search-feature-requests", None); let act_search_feature_requests = SimpleAction::new("search-feature-requests", None);
app.add_action(&act_search_feature_requests); app.add_action(&act_search_feature_requests);
act_search_feature_requests.connect_activate(|_action, _variant| { act_search_feature_requests.connect_activate(clone!(@weak self as win =>
webbrowser::open( move |_action, _variant| {
"https://gitlab.com/EchidnaHQ/Echidna/-/issues?label_name%5B%5D=feat", gtk::show_uri(Some(&win), "https://github.com/EchidnaHQ/Echidna/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement", gdk::CURRENT_TIME);
); }));
});
} }
{ {
let act_window_close = SimpleAction::new("close", None); let act_window_close = SimpleAction::new("close", None);

View File

@ -9,7 +9,7 @@ use glib::clone;
use glib::subclass::types::ObjectSubclassExt; use glib::subclass::types::ObjectSubclassExt;
use glib::types::Type; use glib::types::Type;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{ApplicationWindow, FileChooserAction, FileChooserDialog, ResponseType, TreeStore}; use gtk::{ApplicationWindow, FileChooserAction, FileChooserNative, ResponseType, TreeStore};
use relative_path::RelativePath; use relative_path::RelativePath;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::Path; use std::path::Path;
@ -46,14 +46,12 @@ impl WorkspaceImplementedEditor for EchidnaEditor {
_action: &SimpleAction, _action: &SimpleAction,
_variant: Option<&glib::Variant>, _variant: Option<&glib::Variant>,
) { ) {
let dialog: FileChooserDialog = FileChooserDialog::new( let dialog = FileChooserNative::new(
Some("Open a file"), Some("Open a file"),
Some(&window), Some(&window),
FileChooserAction::Open, FileChooserAction::Open,
&[ Some("Open"),
("Cancel", ResponseType::Cancel), Some("Cancel"),
("Open", ResponseType::Accept),
],
); );
dialog.set_visible(true); dialog.set_visible(true);
dialog.connect_response(clone!(@weak window, @weak app => dialog.connect_response(clone!(@weak window, @weak app =>
@ -78,7 +76,7 @@ impl WorkspaceImplementedEditor for EchidnaEditor {
* *
* Basically, this is just the same as Open Folder, but it's many folders. * Basically, this is just the same as Open Folder, but it's many folders.
* *
* - Open a FileChooserDialog, set to only view .code-workspace files. * - Open a FileChooserNative, set to only view .code-workspace files.
* - If the user pressed cancel, destroy the dialog. If the user opened a .code-workspace file: * - If the user pressed cancel, destroy the dialog. If the user opened a .code-workspace file:
* - Get the workspace file, load and parse its content, .code-workspace files are in JSON with comments. But JSON only should be fine, for now. * - Get the workspace file, load and parse its content, .code-workspace files are in JSON with comments. But JSON only should be fine, for now.
* - Iterate over folders listed in the workspace file. * - Iterate over folders listed in the workspace file.
@ -125,31 +123,30 @@ impl WorkspaceImplementedEditor for EchidnaEditor {
self.open_folder(folder); self.open_folder(folder);
} }
} }
}
/** /**
* *
* *
*/ */
fn recursive_add_files_into_tree_store(&self, parent_file: File, tree: &TreeStore) { fn recursive_add_files_into_tree_store(&self, parent_file: File, tree: &TreeStore) {
let child_enumerate_cancellable = Cancellable::new(); let child_enumerate_cancellable = Cancellable::new();
let child_files = parent_file let child_files = parent_file
.enumerate_children( .enumerate_children(
"*", "*",
FileQueryInfoFlags::NONE, FileQueryInfoFlags::NONE,
Some(&child_enumerate_cancellable), Some(&child_enumerate_cancellable),
)
.expect(
format!(
"Could not look up the children files of {:?} because:\n{:#?}",
filepath
) )
.as_str(), .expect(
); format!(
let filepath = &parent_file "Could not look up the children files of {:?} because:\n{:#?}",
.path() filepath
.expect("Could not get the file path of the file."); )
.as_str(),
);
let filepath = &parent_file
.path()
.expect("Could not get the file path of the file.");
for file_iter in files { for file_iter in files {
let file_info = file_iter.expect(); let file_info = file_iter.expect();
let file = parent_file.child(file_info.name()); let file = parent_file.child(file_info.name());

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::components::tab_label::TabLabel;
use glib::IsA; use glib::IsA;
use gtk::{prelude::*, Box, Button, Widget}; use gtk::{prelude::*, Box, Button, Widget};
@ -16,39 +17,24 @@ pub trait ClosableTabImplementedNotebook {
child: &T, child: &T,
tab_label: Option<&U>, tab_label: Option<&U>,
) -> u32; ) -> u32;
fn create_closable_tab<U: IsA<Widget>>(tab_label: Option<&U>) -> (Box, Button);
} }
impl ClosableTabImplementedNotebook for gtk::Notebook { impl ClosableTabImplementedNotebook for gtk::Notebook {
fn create_closable_tab<U: IsA<Widget>>(tab_label: Option<&U>) -> (Box, Button) {
let tab = Box::new(gtk::Orientation::Horizontal, 5);
if tab_label.is_some() {
tab.append(tab_label.unwrap());
}
let button = gtk::Button::new();
button.set_icon_name("window-close-symbolic");
button.set_has_frame(false);
tab.append(&button);
(tab, button)
}
fn prepend_closable_page<T: IsA<Widget>, U: IsA<Widget>>( fn prepend_closable_page<T: IsA<Widget>, U: IsA<Widget>>(
&self, &self,
child: &T, child: &T,
tab_label: Option<&U>, tab_label: Option<&U>,
) -> u32 { ) -> u32 {
let (tab, button) = &Self::create_closable_tab(tab_label); let tab_label_widget = TabLabel::new(tab_label);
let page = self.prepend_page(child, Some(tab)); let page = self.prepend_page(child, Some(&tab_label_widget));
button.connect_clicked(glib::clone!(@weak self as notebook => tab_label_widget
move |_| { .to_imp()
notebook.remove_page(Some(page)); .button
})); .connect_clicked(glib::clone!(@weak self as notebook =>
move |_| {
notebook.remove_page(Some(page));
}));
page page
} }
@ -58,13 +44,16 @@ impl ClosableTabImplementedNotebook for gtk::Notebook {
child: &T, child: &T,
tab_label: Option<&U>, tab_label: Option<&U>,
) -> u32 { ) -> u32 {
let (tab, button) = &Self::create_closable_tab(tab_label); let tab_label_widget = TabLabel::new(tab_label);
let page = self.append_page(child, Some(tab)); let page = self.append_page(child, Some(&tab_label_widget));
button.connect_clicked(glib::clone!(@weak self as notebook => tab_label_widget
move |_| { .to_imp()
notebook.remove_page(Some(page)); .button
})); .connect_clicked(glib::clone!(@weak self as notebook =>
move |_| {
notebook.remove_page(Some(page));
}));
page page
} }

View File

@ -5,5 +5,5 @@
pub mod closeable_tab; pub mod closeable_tab;
pub mod prelude { pub mod prelude {
pub use super::closeable_tab::ClosableTabImplementedNotebook; pub use super::closeable_tab::*;
} }

View File

@ -13,3 +13,8 @@ fn main() {
std::process::exit(app.run()); std::process::exit(app.run());
} }
pub mod prelude {
pub use super::components::prelude::*;
pub use super::lib::prelude::*;
}