Compare commits
31 Commits
v6.0.0-rc.
...
v7.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78755b6130 | ||
|
|
8bf59e5cbb | ||
|
|
8f71d13714 | ||
|
|
f4a790eb50 | ||
|
|
0d951c8ad1 | ||
|
|
59ef5f05ea | ||
|
|
b84aab0960 | ||
|
|
0e89b91c38 | ||
|
|
7888a704e1 | ||
|
|
804fd3de8e | ||
|
|
4992c36f08 | ||
|
|
27af0c841b | ||
|
|
cc4f4df4f9 | ||
|
|
5989ab3b85 | ||
|
|
040b9c1452 | ||
|
|
bb7bd2e27e | ||
|
|
02dbd4ba75 | ||
|
|
3b149cc875 | ||
|
|
f3745c68d2 | ||
|
|
eddbe7d0cf | ||
|
|
faf86d006a | ||
|
|
71e4dfeb14 | ||
|
|
da8ed6ddf9 | ||
|
|
503e687c55 | ||
|
|
dd667cce0b | ||
|
|
f911009752 | ||
|
|
cae4c46669 | ||
|
|
a937fc38db | ||
|
|
ff43ffce07 | ||
|
|
5bb3dc258a | ||
|
|
98238e4516 |
5
.gitmodules
vendored
@@ -9,4 +9,7 @@
|
|||||||
url = https://github.com/LPGhatguy/roblox-lua-promise.git
|
url = https://github.com/LPGhatguy/roblox-lua-promise.git
|
||||||
[submodule "plugin/modules/t"]
|
[submodule "plugin/modules/t"]
|
||||||
path = plugin/modules/t
|
path = plugin/modules/t
|
||||||
url = https://github.com/osyrisrblx/t.git
|
url = https://github.com/osyrisrblx/t.git
|
||||||
|
[submodule "plugin/modules/flipper"]
|
||||||
|
path = plugin/modules/flipper
|
||||||
|
url = https://github.com/Reselim/Flipper
|
||||||
|
|||||||
37
CHANGELOG.md
@@ -2,8 +2,43 @@
|
|||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## [7.0.0-alpha.2][7.0.0-alpha.2] (February 19, 2021)
|
||||||
|
* Fixed incorrect protocol version between the client and server.
|
||||||
|
|
||||||
|
[7.0.0-alpha.2]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.2
|
||||||
|
|
||||||
|
## [7.0.0-alpha.1][7.0.0-alpha.1] (February 18, 2021)
|
||||||
|
This release includes a brand new implementation of the Roblox DOM. It brings performance improvements, much better support for `rbxl` and `rbxm` files, and a better internal API.
|
||||||
|
|
||||||
|
* Added support for all remaining property types.
|
||||||
|
* Added support for the entire Roblox binary model format.
|
||||||
|
* Changed `rojo upload` to upload binary places and models instead of XML.
|
||||||
|
* This should make using `rojo upload` much more feasible for large places.
|
||||||
|
* **Breaking**: Changed format of some types of values in `project.json`, `model.json`, and `meta.json` files.
|
||||||
|
* This should impact few projects. See [this file][allValues.json] for new examples of each property type.
|
||||||
|
|
||||||
|
Formatting of types will change more before the stable release of Rojo 7. We're hoping to use this opportunity to normalize some of the case inconsistency introduced in Rojo 0.5.
|
||||||
|
|
||||||
|
[7.0.0-alpha.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.1
|
||||||
|
[allValues.json]: https://github.com/rojo-rbx/rojo/blob/f4a790eb50b74e482000bad1dcfe22533992fb20/plugin/rbx_dom_lua/src/allValues.json
|
||||||
|
|
||||||
|
## [6.0.2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.2) (February 9, 2021)
|
||||||
|
* Fixed `rojo upload` to handle CSRF challenges.
|
||||||
|
|
||||||
|
## [6.0.1](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.1) (January 22, 2021)
|
||||||
|
* Fixed `rojo upload` requests being rejected by Roblox
|
||||||
|
|
||||||
|
## [6.0.0](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0) (January 16, 2021)
|
||||||
|
* Improved server error messages
|
||||||
|
* The server will now keep running in more error cases
|
||||||
|
* Fixed Rojo being unable to diff ClassName changes
|
||||||
|
|
||||||
|
## [6.0.0 Release Candidate 4](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.4) (December 14, 2020)
|
||||||
|
* Added brand new Rojo UI ([#367](https://github.com/rojo-rbx/rojo/pull/367))
|
||||||
|
* Added `projectName` to `/api/rojo` output.
|
||||||
|
|
||||||
## [6.0.0 Release Candidate 3](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.3) (November 19, 2020)
|
## [6.0.0 Release Candidate 3](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.3) (November 19, 2020)
|
||||||
* Fixed the Rojo plugin attempted to write the non-scriptable properties `Instance.SourceAssetId` and `HttpServer.HttpEnabled`.
|
* Fixed the Rojo plugin attempting to write the non-scriptable properties `Instance.SourceAssetId` and `HttpServer.HttpEnabled`.
|
||||||
* Fixed the Rojo plugin's handling of null referents.
|
* Fixed the Rojo plugin's handling of null referents.
|
||||||
|
|
||||||
## [6.0.0 Release Candidate 2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.2) (November 19, 2020)
|
## [6.0.0 Release Candidate 2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.2) (November 19, 2020)
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ The Rojo release process is pretty manual right now. If you need to do it, here'
|
|||||||
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
|
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
|
||||||
7. Publish the CLI
|
7. Publish the CLI
|
||||||
* `cargo publish`
|
* `cargo publish`
|
||||||
8. Build and upload the plugin
|
8. Publish the Plugin
|
||||||
|
* `rojo publish plugin --asset_id 6415005344`
|
||||||
* `rojo build plugin -o Rojo.rbxm`
|
* `rojo build plugin -o Rojo.rbxm`
|
||||||
* Upload `Rojo.rbxm` to Roblox.com, keep it for later
|
|
||||||
9. Push commits and tags
|
9. Push commits and tags
|
||||||
* `git push && git push --tags`
|
* `git push && git push --tags`
|
||||||
10. Copy GitHub release content from previous release
|
10. Copy GitHub release content from previous release
|
||||||
|
|||||||
684
Cargo.lock
generated
28
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "6.0.0-rc.3"
|
version = "7.0.0-alpha.2"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "Enables professional-grade development tools for Roblox developers"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
@@ -28,13 +28,6 @@ dev_live_assets = []
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"rojo-insta-ext",
|
|
||||||
"clibrojo",
|
|
||||||
"memofs",
|
|
||||||
]
|
|
||||||
|
|
||||||
default-members = [
|
|
||||||
".",
|
|
||||||
"rojo-insta-ext",
|
"rojo-insta-ext",
|
||||||
"memofs",
|
"memofs",
|
||||||
]
|
]
|
||||||
@@ -54,6 +47,19 @@ harness = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
memofs = { version = "0.1.2", path = "memofs" }
|
memofs = { version = "0.1.2", path = "memofs" }
|
||||||
|
|
||||||
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
|
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
||||||
|
# rbx_dom_weak = { path = "../rbx-dom/rbx_dom_weak" }
|
||||||
|
# rbx_reflection = { path = "../rbx-dom/rbx_reflection" }
|
||||||
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
|
rbx_binary = "0.6.0-alpha.1"
|
||||||
|
rbx_dom_weak = "2.0.0-alpha.1"
|
||||||
|
rbx_reflection = "4.0.0-alpha.1"
|
||||||
|
rbx_reflection_database = "0.1.0"
|
||||||
|
rbx_xml = "0.12.0-alpha.1"
|
||||||
|
|
||||||
anyhow = "1.0.27"
|
anyhow = "1.0.27"
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bincode = "1.2.1"
|
bincode = "1.2.1"
|
||||||
@@ -71,10 +77,6 @@ log = "0.4.8"
|
|||||||
maplit = "1.0.1"
|
maplit = "1.0.1"
|
||||||
notify = "4.0.14"
|
notify = "4.0.14"
|
||||||
opener = "0.4.1"
|
opener = "0.4.1"
|
||||||
rbx_binary = "0.5.0"
|
|
||||||
rbx_dom_weak = "1.10.1"
|
|
||||||
rbx_reflection = "3.3.408"
|
|
||||||
rbx_xml = "0.11.3"
|
|
||||||
regex = "1.3.1"
|
regex = "1.3.1"
|
||||||
reqwest = "0.9.20"
|
reqwest = "0.9.20"
|
||||||
ritz = "0.1.0"
|
ritz = "0.1.0"
|
||||||
@@ -103,7 +105,7 @@ maplit = "1.0.1"
|
|||||||
rojo-insta-ext = { path = "rojo-insta-ext" }
|
rojo-insta-ext = { path = "rojo-insta-ext" }
|
||||||
|
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
insta = { version = "0.13.1", features = ["redactions"] }
|
insta = { version = "1.3.0", features = ["redactions"] }
|
||||||
lazy_static = "1.2"
|
lazy_static = "1.2"
|
||||||
paste = "0.1"
|
paste = "0.1"
|
||||||
pretty_assertions = "0.6.1"
|
pretty_assertions = "0.6.1"
|
||||||
|
|||||||
BIN
assets/images/back.png
Normal file
|
After Width: | Height: | Size: 229 B |
5
assets/images/back.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<path d="M20,11L20,13L8,13L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11L20,11Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 584 B |
BIN
assets/images/checkbox-off.png
Normal file
|
After Width: | Height: | Size: 269 B |
15
assets/images/checkbox-off.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,-54,-1)">
|
||||||
|
<g id="Artboard3" transform="matrix(1.77778,0,0,1.45455,-42,-0.454545)">
|
||||||
|
<rect x="54" y="1" width="9" height="11" style="fill:none;"/>
|
||||||
|
<g transform="matrix(3.375,0,0,4.125,-3654,-2753.12)">
|
||||||
|
<path d="M1099,670L1101,668" style="fill:none;stroke:white;stroke-width:0.5px;"/>
|
||||||
|
<g transform="matrix(-1,0,0,1,2200,0)">
|
||||||
|
<path d="M1099,670L1101,668" style="fill:none;stroke:white;stroke-width:0.5px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/checkbox-on.png
Normal file
|
After Width: | Height: | Size: 249 B |
12
assets/images/checkbox-on.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,-32,0)">
|
||||||
|
<g id="Artboard2" transform="matrix(0.8,0,0,0.941176,6.4,0)">
|
||||||
|
<rect x="32" y="0" width="20" height="17" style="fill:none;"/>
|
||||||
|
<g transform="matrix(5,0,0,4.25,-5470.5,-2371.5)">
|
||||||
|
<path d="M1101,560L1102,561L1104,559" style="fill:none;stroke:white;stroke-width:0.75px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 870 B |
BIN
assets/images/circle-128.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/images/circle-16.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
assets/images/circle-32.png
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
assets/images/circle-500.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
assets/images/circle-64.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
assets/images/close.png
Normal file
|
After Width: | Height: | Size: 295 B |
15
assets/images/close.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.0666667,0,0,0.097561,-31,-18.6341)">
|
||||||
|
<rect x="465" y="191" width="360" height="246" style="fill:none;"/>
|
||||||
|
<g transform="matrix(134.328,0,0,102.5,-74228.5,-15214.7)">
|
||||||
|
<g transform="matrix(1.11667,0,0,1,-57.3333,0)">
|
||||||
|
<path d="M551,152L550,151" style="fill:none;stroke:white;stroke-width:0.3px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.11667,0,0,1,-57.3333,0)">
|
||||||
|
<path d="M550,152L551,151" style="fill:none;stroke:white;stroke-width:0.3px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/header-logo.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
21
assets/images/header-logo.svg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 60 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.952381,0,0,0.710526,-228.571,-156.316)">
|
||||||
|
<rect x="240" y="220" width="63" height="38" style="fill:none;"/>
|
||||||
|
<g transform="matrix(0.0789166,0,0,0.105779,211.848,170.749)">
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,376,753)">
|
||||||
|
<path d="M0.302,-0.836L0.306,-0.836L0.302,-0.829L0.302,-0.814C0.333,-0.82 0.349,-0.828 0.349,-0.836L0.371,-0.833C0.408,-0.835 0.441,-0.836 0.472,-0.836L0.476,-0.836C0.524,-0.836 0.58,-0.81 0.646,-0.757C0.66,-0.734 0.668,-0.714 0.672,-0.695L0.672,-0.659C0.664,-0.609 0.65,-0.571 0.632,-0.546C0.621,-0.537 0.597,-0.507 0.56,-0.456L0.556,-0.456L0.556,-0.463L0.566,-0.478L0.56,-0.478C0.474,-0.405 0.378,-0.349 0.27,-0.311C0.243,-0.304 0.216,-0.3 0.19,-0.3C0.224,-0.26 0.287,-0.207 0.378,-0.141C0.387,-0.136 0.445,-0.099 0.552,-0.028C0.641,0.027 0.747,0.087 0.871,0.153L0.871,0.157L0.856,0.157C0.832,0.148 0.82,0.141 0.82,0.138L0.813,0.142L0.806,0.142C0.803,0.142 0.802,0.141 0.802,0.138C0.793,0.14 0.786,0.149 0.78,0.164C0.795,0.168 0.802,0.173 0.802,0.178C0.796,0.183 0.792,0.186 0.788,0.186L0.77,0.182L0.77,0.186C0.77,0.19 0.773,0.193 0.78,0.193L0.78,0.204L0.777,0.204C0.772,0.204 0.717,0.172 0.614,0.109C0.541,0.072 0.451,0.015 0.346,-0.061C0.311,-0.077 0.247,-0.124 0.153,-0.202L0.143,-0.202C0.129,-0.181 0.111,-0.129 0.088,-0.046C0.083,-0.027 0.059,0.001 0.016,0.037L0.008,0.037L0.001,0.026L0.001,0.022C0.001,0.017 0.005,0.006 0.012,-0.01L0.012,-0.014L0.008,-0.014C0.008,-0.007 -0.007,0.004 -0.035,0.019L-0.039,0.019L-0.039,0.012C0.046,-0.24 0.105,-0.404 0.139,-0.481C0.197,-0.614 0.226,-0.69 0.226,-0.709C0.213,-0.709 0.184,-0.693 0.139,-0.659L0.132,-0.659L0.135,-0.666L0.135,-0.673C0.122,-0.673 0.092,-0.652 0.045,-0.608L0.041,-0.608L0.041,-0.612L0.088,-0.666L0.096,-0.677L0.096,-0.681L0.088,-0.681L0.045,-0.645C0.04,-0.645 0.038,-0.647 0.038,-0.651C0.083,-0.701 0.138,-0.744 0.204,-0.778C0.265,-0.794 0.295,-0.812 0.295,-0.833L0.302,-0.836ZM0.632,-0.735L0.632,-0.731C0.632,-0.727 0.635,-0.724 0.639,-0.724L0.639,-0.728C0.639,-0.732 0.637,-0.735 0.632,-0.735ZM0.208,-0.387L0.211,-0.387C0.255,-0.396 0.277,-0.403 0.277,-0.409C0.274,-0.414 0.273,-0.417 0.273,-0.42C0.365,-0.451 0.427,-0.482 0.458,-0.514C0.461,-0.514 0.48,-0.534 0.516,-0.576L0.52,-0.576L0.52,-0.572C0.515,-0.564 0.512,-0.558 0.512,-0.554L0.516,-0.554C0.547,-0.578 0.563,-0.604 0.563,-0.633L0.563,-0.655C0.556,-0.655 0.552,-0.658 0.552,-0.663C0.564,-0.665 0.57,-0.671 0.57,-0.681C0.57,-0.693 0.546,-0.705 0.498,-0.717C0.459,-0.727 0.417,-0.731 0.371,-0.731C0.348,-0.731 0.325,-0.689 0.302,-0.604C0.292,-0.594 0.261,-0.522 0.208,-0.387ZM0.251,-0.695L0.251,-0.691C0.255,-0.691 0.258,-0.695 0.262,-0.702L0.262,-0.706C0.259,-0.706 0.255,-0.702 0.251,-0.695ZM0.255,-0.626L0.259,-0.626C0.266,-0.629 0.27,-0.636 0.27,-0.648C0.266,-0.648 0.261,-0.641 0.255,-0.626ZM0.596,-0.612L0.596,-0.604C0.599,-0.604 0.603,-0.608 0.606,-0.615L0.606,-0.619L0.603,-0.619C0.598,-0.618 0.596,-0.616 0.596,-0.612ZM0.204,-0.369L0.204,-0.365L0.211,-0.365C0.216,-0.368 0.22,-0.369 0.222,-0.369L0.222,-0.365C0.281,-0.376 0.316,-0.388 0.328,-0.401L0.324,-0.401C0.271,-0.389 0.231,-0.378 0.204,-0.369ZM0.19,-0.333L0.193,-0.333L0.259,-0.347L0.255,-0.354C0.212,-0.349 0.19,-0.341 0.19,-0.333ZM0.334,-0.108L0.334,-0.101C0.372,-0.072 0.395,-0.057 0.403,-0.057C0.394,-0.064 0.389,-0.07 0.389,-0.075L0.334,-0.108ZM0.44,-0.028C0.442,-0.019 0.449,-0.014 0.461,-0.014L0.465,-0.014L0.465,-0.018C0.459,-0.018 0.452,-0.021 0.443,-0.028L0.44,-0.028ZM0.675,0.102C0.694,0.119 0.723,0.136 0.762,0.153L0.766,0.146C0.736,0.128 0.708,0.113 0.683,0.102L0.675,0.102ZM0.875,0.16L0.886,0.16C0.89,0.161 0.893,0.163 0.893,0.167C0.888,0.167 0.886,0.171 0.886,0.178L0.878,0.178L0.882,0.171L0.882,0.167L0.875,0.167L0.875,0.16Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,588.398,753)">
|
||||||
|
<path d="M0.631,-0.705C0.646,-0.705 0.661,-0.696 0.673,-0.678L0.646,-0.678C0.62,-0.678 0.587,-0.67 0.546,-0.653L0.546,-0.65L0.549,-0.65C0.607,-0.654 0.649,-0.656 0.677,-0.656L0.683,-0.656L0.683,-0.65L0.67,-0.65C0.668,-0.65 0.667,-0.651 0.667,-0.653L0.503,-0.629C0.503,-0.627 0.493,-0.624 0.473,-0.62L0.473,-0.614L0.519,-0.623L0.521,-0.623L0.521,-0.617C0.386,-0.581 0.318,-0.555 0.318,-0.538C0.252,-0.483 0.204,-0.431 0.175,-0.383C0.131,-0.304 0.108,-0.243 0.108,-0.201C0.108,-0.157 0.131,-0.125 0.175,-0.104C0.183,-0.102 0.19,-0.101 0.196,-0.101C0.286,-0.136 0.353,-0.196 0.397,-0.28C0.409,-0.318 0.419,-0.363 0.427,-0.417L0.434,-0.417L0.427,-0.359C0.433,-0.359 0.439,-0.374 0.443,-0.404C0.441,-0.412 0.439,-0.42 0.439,-0.429L0.439,-0.438L0.446,-0.438C0.448,-0.407 0.449,-0.386 0.449,-0.374C0.449,-0.358 0.444,-0.331 0.434,-0.292C0.445,-0.302 0.458,-0.335 0.473,-0.392C0.48,-0.447 0.486,-0.474 0.491,-0.474C0.491,-0.472 0.492,-0.471 0.494,-0.471C0.492,-0.469 0.49,-0.457 0.488,-0.435L0.488,-0.432L0.491,-0.432C0.499,-0.454 0.504,-0.48 0.506,-0.508C0.502,-0.516 0.5,-0.522 0.5,-0.526L0.506,-0.529C0.512,-0.529 0.518,-0.513 0.525,-0.48C0.525,-0.4 0.491,-0.297 0.424,-0.17C0.372,-0.093 0.305,-0.042 0.224,-0.019C0.188,-0.006 0.154,-0 0.121,-0C0.04,-0.022 -0.001,-0.083 -0.001,-0.183C-0.001,-0.296 0.045,-0.398 0.136,-0.489C0.165,-0.522 0.227,-0.568 0.321,-0.629C0.425,-0.68 0.529,-0.705 0.631,-0.705ZM0.482,-0.656L0.482,-0.653C0.499,-0.653 0.508,-0.652 0.509,-0.65C0.526,-0.659 0.536,-0.663 0.54,-0.663L0.54,-0.668L0.482,-0.656ZM0.099,-0.417C0.144,-0.458 0.179,-0.494 0.206,-0.523C0.23,-0.538 0.242,-0.55 0.242,-0.559C0.201,-0.535 0.156,-0.493 0.108,-0.432C0.102,-0.427 0.099,-0.422 0.099,-0.417ZM0.105,-0.261C0.108,-0.261 0.118,-0.284 0.136,-0.328C0.158,-0.378 0.203,-0.439 0.272,-0.511L0.272,-0.514C0.204,-0.463 0.167,-0.422 0.16,-0.392C0.142,-0.371 0.124,-0.328 0.105,-0.261ZM0.069,-0.334L0.069,-0.332L0.072,-0.332C0.116,-0.393 0.139,-0.429 0.139,-0.438C0.11,-0.413 0.086,-0.378 0.069,-0.334ZM0.467,-0.31L0.467,-0.307L0.47,-0.307C0.492,-0.371 0.503,-0.413 0.503,-0.432C0.49,-0.409 0.478,-0.369 0.467,-0.31ZM0.087,-0.404L0.087,-0.401C0.091,-0.401 0.093,-0.403 0.093,-0.407L0.093,-0.41C0.089,-0.41 0.087,-0.408 0.087,-0.404ZM0.063,-0.368C0.032,-0.297 0.017,-0.245 0.017,-0.213C0.021,-0.148 0.028,-0.107 0.039,-0.088L0.042,-0.088L0.042,-0.094C0.035,-0.107 0.032,-0.129 0.032,-0.158C0.032,-0.191 0.035,-0.227 0.042,-0.268L0.039,-0.273C0.045,-0.3 0.054,-0.331 0.066,-0.365L0.066,-0.368L0.063,-0.368ZM0.096,-0.24L0.096,-0.237C0.1,-0.237 0.103,-0.239 0.103,-0.243L0.103,-0.246C0.098,-0.245 0.096,-0.243 0.096,-0.24ZM0.397,-0.222L0.397,-0.219L0.4,-0.219C0.408,-0.232 0.412,-0.241 0.412,-0.243C0.407,-0.242 0.402,-0.235 0.397,-0.222ZM0.066,-0.24C0.06,-0.222 0.055,-0.202 0.051,-0.179C0.055,-0.138 0.061,-0.108 0.069,-0.088L0.072,-0.088L0.072,-0.094C0.066,-0.121 0.063,-0.139 0.063,-0.146L0.066,-0.152C0.064,-0.155 0.063,-0.16 0.063,-0.164C0.063,-0.169 0.064,-0.173 0.066,-0.176C0.064,-0.18 0.063,-0.183 0.063,-0.186C0.067,-0.218 0.069,-0.235 0.069,-0.237L0.069,-0.24L0.066,-0.24ZM0.294,-0.125L0.294,-0.122C0.3,-0.122 0.322,-0.143 0.357,-0.186L0.357,-0.188L0.354,-0.188C0.314,-0.151 0.294,-0.129 0.294,-0.125ZM0.379,-0.149C0.379,-0.146 0.36,-0.126 0.321,-0.088L0.324,-0.088C0.351,-0.112 0.37,-0.132 0.382,-0.146L0.385,-0.146L0.385,-0.149L0.379,-0.149ZM0.09,-0.104L0.09,-0.101C0.094,-0.08 0.104,-0.07 0.121,-0.07L0.13,-0.07L0.13,-0.073L0.09,-0.104ZM0.151,-0.055L0.188,-0.055L0.203,-0.058L0.203,-0.061L0.157,-0.061C0.153,-0.06 0.151,-0.058 0.151,-0.055ZM0.096,-0.058L0.096,-0.055C0.104,-0.045 0.113,-0.04 0.124,-0.04L0.13,-0.04L0.13,-0.042C0.127,-0.042 0.116,-0.048 0.096,-0.058Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,758.882,753)">
|
||||||
|
<path d="M0.208,-0.7L0.223,-0.7C0.227,-0.7 0.229,-0.697 0.229,-0.691C0.294,-0.687 0.328,-0.684 0.331,-0.682C0.333,-0.682 0.333,-0.683 0.333,-0.685C0.364,-0.683 0.387,-0.682 0.401,-0.682L0.462,-0.682C0.466,-0.682 0.468,-0.684 0.468,-0.688L0.45,-0.688L0.45,-0.694L0.49,-0.694L0.49,-0.688L0.475,-0.688L0.475,-0.682L0.557,-0.682C0.564,-0.682 0.573,-0.681 0.584,-0.679C0.603,-0.681 0.612,-0.685 0.612,-0.691L0.658,-0.691C0.678,-0.691 0.688,-0.692 0.689,-0.694C0.706,-0.692 0.72,-0.691 0.731,-0.691L0.903,-0.691L0.921,-0.688L0.928,-0.691C1.035,-0.686 1.096,-0.681 1.111,-0.676L1.13,-0.682L1.13,-0.676L1.068,-0.667L1.068,-0.664C1.075,-0.664 1.078,-0.661 1.078,-0.657C1.078,-0.654 1.075,-0.651 1.068,-0.648C1.064,-0.65 1.061,-0.651 1.059,-0.651C1.059,-0.647 1.055,-0.645 1.047,-0.645L1.047,-0.642C1.048,-0.638 1.05,-0.636 1.053,-0.636L1.059,-0.636C1.072,-0.636 1.087,-0.635 1.105,-0.633C1.113,-0.635 1.12,-0.636 1.126,-0.636C1.139,-0.625 1.145,-0.619 1.145,-0.618C1.138,-0.613 1.132,-0.611 1.126,-0.611L1.099,-0.611C1.088,-0.611 1.079,-0.608 1.071,-0.602L1.029,-0.602C1.024,-0.602 1.02,-0.603 1.017,-0.605L1.01,-0.602C0.993,-0.604 0.98,-0.605 0.971,-0.605C0.962,-0.605 0.958,-0.604 0.958,-0.602L0.928,-0.605L0.695,-0.605C0.69,-0.605 0.686,-0.604 0.683,-0.602C0.679,-0.604 0.675,-0.605 0.67,-0.605L0.563,-0.605C0.555,-0.605 0.547,-0.604 0.539,-0.602C0.531,-0.604 0.523,-0.605 0.514,-0.605L0.453,-0.605C0.429,-0.578 0.401,-0.525 0.37,-0.446C0.37,-0.439 0.334,-0.358 0.263,-0.201L0.177,-0.021C0.185,-0.001 0.189,0.011 0.189,0.013C0.186,0.013 0.177,0.001 0.162,-0.023L0.156,-0.023L0.153,-0.008C0.159,-0.001 0.162,0.006 0.162,0.013L0.162,0.017L0.15,0.007L0.128,0.007C0.128,-0.005 0.108,-0.045 0.067,-0.112C0.012,-0.235 -0.028,-0.332 -0.052,-0.403L-0.052,-0.412C-0.048,-0.412 -0.046,-0.409 -0.046,-0.403L-0.043,-0.403C-0.039,-0.403 -0.037,-0.405 -0.037,-0.409L-0.046,-0.443L-0.046,-0.446L-0.04,-0.446L0.034,-0.293L0.037,-0.293L0.037,-0.296C0.008,-0.37 -0.006,-0.41 -0.006,-0.416L-0.006,-0.421C0.009,-0.398 0.037,-0.343 0.076,-0.256C0.118,-0.177 0.142,-0.135 0.147,-0.13C0.212,-0.285 0.285,-0.442 0.364,-0.602C0.36,-0.602 0.358,-0.604 0.358,-0.608L0.352,-0.605L0.333,-0.605C0.332,-0.605 0.331,-0.606 0.331,-0.608C0.327,-0.606 0.324,-0.605 0.321,-0.605L0.315,-0.608C0.313,-0.608 0.312,-0.607 0.312,-0.605L0.291,-0.608L0.263,-0.608C0.263,-0.615 0.261,-0.618 0.257,-0.618L0.254,-0.618C0.247,-0.618 0.215,-0.621 0.159,-0.626L0.159,-0.63L0.162,-0.636L0.156,-0.636L0.156,-0.639C0.156,-0.641 0.163,-0.646 0.177,-0.654L0.184,-0.654L0.189,-0.651C0.189,-0.653 0.191,-0.659 0.193,-0.669C0.188,-0.671 0.178,-0.674 0.162,-0.676L0.162,-0.682L0.184,-0.682C0.191,-0.682 0.199,-0.688 0.208,-0.7ZM0.291,-0.697L0.352,-0.697C0.356,-0.697 0.358,-0.694 0.358,-0.691L0.291,-0.691L0.291,-0.697ZM0.41,-0.694L0.419,-0.694L0.419,-0.688L0.41,-0.688L0.41,-0.694ZM0.505,-0.694L0.533,-0.694L0.533,-0.688L0.505,-0.688L0.505,-0.694ZM0.603,-0.694L0.603,-0.691C0.603,-0.687 0.601,-0.685 0.597,-0.685L0.563,-0.685L0.563,-0.691L0.588,-0.691L0.603,-0.694ZM0.202,-0.667L0.202,-0.664L0.239,-0.664L0.239,-0.667L0.202,-0.667ZM0.269,-0.339L0.266,-0.327L0.266,-0.324L0.269,-0.324L0.272,-0.336L0.272,-0.339L0.269,-0.339ZM0.257,-0.308C0.237,-0.266 0.211,-0.205 0.177,-0.125C0.195,-0.147 0.223,-0.207 0.26,-0.305L0.26,-0.308L0.257,-0.308Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,891.444,753)">
|
||||||
|
<path d="M0.631,-0.705C0.646,-0.705 0.661,-0.696 0.673,-0.678L0.646,-0.678C0.62,-0.678 0.587,-0.67 0.546,-0.653L0.546,-0.65L0.549,-0.65C0.607,-0.654 0.649,-0.656 0.677,-0.656L0.683,-0.656L0.683,-0.65L0.67,-0.65C0.668,-0.65 0.667,-0.651 0.667,-0.653L0.503,-0.629C0.503,-0.627 0.493,-0.624 0.473,-0.62L0.473,-0.614L0.519,-0.623L0.521,-0.623L0.521,-0.617C0.386,-0.581 0.318,-0.555 0.318,-0.538C0.252,-0.483 0.204,-0.431 0.175,-0.383C0.131,-0.304 0.108,-0.243 0.108,-0.201C0.108,-0.157 0.131,-0.125 0.175,-0.104C0.183,-0.102 0.19,-0.101 0.196,-0.101C0.286,-0.136 0.353,-0.196 0.397,-0.28C0.409,-0.318 0.419,-0.363 0.427,-0.417L0.434,-0.417L0.427,-0.359C0.433,-0.359 0.439,-0.374 0.443,-0.404C0.441,-0.412 0.439,-0.42 0.439,-0.429L0.439,-0.438L0.446,-0.438C0.448,-0.407 0.449,-0.386 0.449,-0.374C0.449,-0.358 0.444,-0.331 0.434,-0.292C0.445,-0.302 0.458,-0.335 0.473,-0.392C0.48,-0.447 0.486,-0.474 0.491,-0.474C0.491,-0.472 0.492,-0.471 0.494,-0.471C0.492,-0.469 0.49,-0.457 0.488,-0.435L0.488,-0.432L0.491,-0.432C0.499,-0.454 0.504,-0.48 0.506,-0.508C0.502,-0.516 0.5,-0.522 0.5,-0.526L0.506,-0.529C0.512,-0.529 0.518,-0.513 0.525,-0.48C0.525,-0.4 0.491,-0.297 0.424,-0.17C0.372,-0.093 0.305,-0.042 0.224,-0.019C0.188,-0.006 0.154,-0 0.121,-0C0.04,-0.022 -0.001,-0.083 -0.001,-0.183C-0.001,-0.296 0.045,-0.398 0.136,-0.489C0.165,-0.522 0.227,-0.568 0.321,-0.629C0.425,-0.68 0.529,-0.705 0.631,-0.705ZM0.482,-0.656L0.482,-0.653C0.499,-0.653 0.508,-0.652 0.509,-0.65C0.526,-0.659 0.536,-0.663 0.54,-0.663L0.54,-0.668L0.482,-0.656ZM0.099,-0.417C0.144,-0.458 0.179,-0.494 0.206,-0.523C0.23,-0.538 0.242,-0.55 0.242,-0.559C0.201,-0.535 0.156,-0.493 0.108,-0.432C0.102,-0.427 0.099,-0.422 0.099,-0.417ZM0.105,-0.261C0.108,-0.261 0.118,-0.284 0.136,-0.328C0.158,-0.378 0.203,-0.439 0.272,-0.511L0.272,-0.514C0.204,-0.463 0.167,-0.422 0.16,-0.392C0.142,-0.371 0.124,-0.328 0.105,-0.261ZM0.069,-0.334L0.069,-0.332L0.072,-0.332C0.116,-0.393 0.139,-0.429 0.139,-0.438C0.11,-0.413 0.086,-0.378 0.069,-0.334ZM0.467,-0.31L0.467,-0.307L0.47,-0.307C0.492,-0.371 0.503,-0.413 0.503,-0.432C0.49,-0.409 0.478,-0.369 0.467,-0.31ZM0.087,-0.404L0.087,-0.401C0.091,-0.401 0.093,-0.403 0.093,-0.407L0.093,-0.41C0.089,-0.41 0.087,-0.408 0.087,-0.404ZM0.063,-0.368C0.032,-0.297 0.017,-0.245 0.017,-0.213C0.021,-0.148 0.028,-0.107 0.039,-0.088L0.042,-0.088L0.042,-0.094C0.035,-0.107 0.032,-0.129 0.032,-0.158C0.032,-0.191 0.035,-0.227 0.042,-0.268L0.039,-0.273C0.045,-0.3 0.054,-0.331 0.066,-0.365L0.066,-0.368L0.063,-0.368ZM0.096,-0.24L0.096,-0.237C0.1,-0.237 0.103,-0.239 0.103,-0.243L0.103,-0.246C0.098,-0.245 0.096,-0.243 0.096,-0.24ZM0.397,-0.222L0.397,-0.219L0.4,-0.219C0.408,-0.232 0.412,-0.241 0.412,-0.243C0.407,-0.242 0.402,-0.235 0.397,-0.222ZM0.066,-0.24C0.06,-0.222 0.055,-0.202 0.051,-0.179C0.055,-0.138 0.061,-0.108 0.069,-0.088L0.072,-0.088L0.072,-0.094C0.066,-0.121 0.063,-0.139 0.063,-0.146L0.066,-0.152C0.064,-0.155 0.063,-0.16 0.063,-0.164C0.063,-0.169 0.064,-0.173 0.066,-0.176C0.064,-0.18 0.063,-0.183 0.063,-0.186C0.067,-0.218 0.069,-0.235 0.069,-0.237L0.069,-0.24L0.066,-0.24ZM0.294,-0.125L0.294,-0.122C0.3,-0.122 0.322,-0.143 0.357,-0.186L0.357,-0.188L0.354,-0.188C0.314,-0.151 0.294,-0.129 0.294,-0.125ZM0.379,-0.149C0.379,-0.146 0.36,-0.126 0.321,-0.088L0.324,-0.088C0.351,-0.112 0.37,-0.132 0.382,-0.146L0.385,-0.146L0.385,-0.149L0.379,-0.149ZM0.09,-0.104L0.09,-0.101C0.094,-0.08 0.104,-0.07 0.121,-0.07L0.13,-0.07L0.13,-0.073L0.09,-0.104ZM0.151,-0.055L0.188,-0.055L0.203,-0.058L0.203,-0.061L0.157,-0.061C0.153,-0.06 0.151,-0.058 0.151,-0.055ZM0.096,-0.058L0.096,-0.055C0.104,-0.045 0.113,-0.04 0.124,-0.04L0.13,-0.04L0.13,-0.042C0.127,-0.042 0.116,-0.048 0.096,-0.058Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
BIN
assets/images/rounded-background.png
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
assets/images/rounded-border.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
assets/images/scrollbar-bottom.png
Normal file
|
After Width: | Height: | Size: 105 B |
17
assets/images/scrollbar-bottom.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-10)">
|
||||||
|
<g id="Bottom" transform="matrix(0.243243,0,0,0.179487,-59.1081,-30.2051)">
|
||||||
|
<rect x="243" y="224" width="37" height="39" style="fill:none;"/>
|
||||||
|
<clipPath id="_clip1">
|
||||||
|
<rect x="243" y="224" width="37" height="39"/>
|
||||||
|
</clipPath>
|
||||||
|
<g clip-path="url(#_clip1)">
|
||||||
|
<g transform="matrix(-4.11111,6.82303e-16,-3.59619e-16,-3.97959,280,259.816)">
|
||||||
|
<path d="M7,5.5C7,3.568 5.88,2 4.5,2C3.12,2 2,3.568 2,5.5L2,9L7,9L7,5.5Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/scrollbar-middle.png
Normal file
|
After Width: | Height: | Size: 75 B |
12
assets/images/scrollbar-middle.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 1" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-8)">
|
||||||
|
<g id="Middle" transform="matrix(1,0,0,0.111111,0,7.11111)">
|
||||||
|
<rect x="0" y="8" width="9" height="9" style="fill:none;"/>
|
||||||
|
<g transform="matrix(1,0,0,9,0,-64)">
|
||||||
|
<rect x="2" y="8" width="5" height="1" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 796 B |
BIN
assets/images/scrollbar-top.png
Normal file
|
After Width: | Height: | Size: 132 B |
10
assets/images/scrollbar-top.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Top" transform="matrix(0.243243,0,0,0.179487,-59.1081,-40.2051)">
|
||||||
|
<rect x="243" y="224" width="37" height="39" style="fill:none;"/>
|
||||||
|
<g transform="matrix(4.11111,0,0,3.97959,243,227.184)">
|
||||||
|
<path d="M7,5.5C7,3.568 5.88,2 4.5,2C3.12,2 2,3.568 2,5.5L2,9L7,9L7,5.5Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 793 B |
BIN
assets/images/spinner-background.png
Normal file
|
After Width: | Height: | Size: 684 B |
8
assets/images/spinner-background.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<rect x="0" y="0" width="48" height="48" style="fill:none;"/>
|
||||||
|
<path d="M24,0C37.246,0 48,10.754 48,24C48,37.246 37.246,48 24,48C10.754,48 0,37.246 0,24C0,10.754 10.754,0 24,0ZM24,8.4C32.61,8.4 39.6,15.39 39.6,24C39.6,32.61 32.61,39.6 24,39.6C15.39,39.6 8.4,32.61 8.4,24C8.4,15.39 15.39,8.4 24,8.4Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 855 B |
BIN
assets/images/spinner-foreground.png
Normal file
|
After Width: | Height: | Size: 340 B |
8
assets/images/spinner-foreground.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<rect x="0" y="0" width="48" height="48" style="fill:none;"/>
|
||||||
|
<path d="M48,24C48,10.745 37.255,0 24,0L24,8.4C32.616,8.4 39.6,15.384 39.6,24L48,24Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 704 B |
3
build.rs
@@ -62,6 +62,9 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
"t" => VfsSnapshot::dir(hashmap! {
|
"t" => VfsSnapshot::dir(hashmap! {
|
||||||
"lib" => snapshot_from_fs_path(&plugin_modules.join("t").join("lib"))?
|
"lib" => snapshot_from_fs_path(&plugin_modules.join("t").join("lib"))?
|
||||||
}),
|
}),
|
||||||
|
"flipper" => VfsSnapshot::dir(hashmap! {
|
||||||
|
"src" => snapshot_from_fs_path(&plugin_modules.join("flipper").join("src"))?
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "clibrojo"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rojo = { path = ".." }
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Rojo as a C Library
|
|
||||||
This is an experiment to expose a C API for Rojo that would be suitable for embedding it into an existing C/C++ application.
|
|
||||||
|
|
||||||
I'm hoping to expand it to drop the HTTP layer and communicate through a channel, which could make it feasible to embed into an existing Roblox IDE with minimal changes or additional code.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
This project is currently not built by default and could break/disappear at any time.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo build -p clibrojo
|
|
||||||
```
|
|
||||||
|
|
||||||
On Windows, Cargo will generate a `clibrojo.dll` and associated `.lib` file. Link these into your project.
|
|
||||||
|
|
||||||
To generate the associated C header file to include in the project, use [cbindgen](https://github.com/eqrion/cbindgen):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cbindgen --crate clibrojo --output include/rojo.h
|
|
||||||
```
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use std::{ffi::CStr, os::raw::c_char, path::PathBuf};
|
|
||||||
|
|
||||||
use librojo::commands::{serve, ServeOptions};
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn rojo_serve(path: *const c_char) {
|
|
||||||
let path = unsafe { PathBuf::from(CStr::from_ptr(path).to_str().unwrap()) };
|
|
||||||
|
|
||||||
serve(&ServeOptions {
|
|
||||||
fuzzy_project_path: path,
|
|
||||||
port: None,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
[tools]
|
[tools]
|
||||||
rojo = { source = "rojo-rbx/rojo", version = "6.0.0-rc.1" }
|
rojo = { source = "rojo-rbx/rojo", version = "6.0.0-rc.3" }
|
||||||
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
||||||
|
|||||||
10
perf-test.sh
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo build --release
|
|
||||||
|
|
||||||
echo "Known good:"
|
|
||||||
time rojo build ../uiblox/test-place.project.json -o UIBlox.rbxlx
|
|
||||||
|
|
||||||
echo "Current:"
|
|
||||||
time ./target/release/rojo build ../uiblox/test-place.project.json -o UIBlox.rbxlx
|
|
||||||
@@ -25,6 +25,9 @@
|
|||||||
},
|
},
|
||||||
"t": {
|
"t": {
|
||||||
"$path": "modules/t/lib"
|
"$path": "modules/t/lib"
|
||||||
|
},
|
||||||
|
"Flipper": {
|
||||||
|
"$path": "modules/flipper/src"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ Error.Kind = {
|
|||||||
},
|
},
|
||||||
ConnectFailed = {
|
ConnectFailed = {
|
||||||
message = "Couldn't connect to the Rojo server.\n" ..
|
message = "Couldn't connect to the Rojo server.\n" ..
|
||||||
"Make sure the server is running -- use 'rojo serve' to run it!",
|
"Make sure the server is running — use 'rojo serve' to run it!",
|
||||||
},
|
},
|
||||||
Timeout = {
|
Timeout = {
|
||||||
message = "HTTP request timed out.",
|
message = "HTTP request timed out.",
|
||||||
@@ -63,4 +63,4 @@ function Error.fromRobloxErrorString(message)
|
|||||||
return Error.new(Error.Kind.Unknown, message)
|
return Error.new(Error.Kind.Unknown, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
return Error
|
return Error
|
||||||
|
|||||||
1
plugin/modules/flipper
Submodule
1
plugin/rbx_dom_lua/run-tests.lua
Normal file
@@ -0,0 +1 @@
|
|||||||
|
require(game.ReplicatedStorage.TestEZ).TestBootstrap:run({game.ReplicatedStorage.RbxDom})
|
||||||
@@ -20,223 +20,437 @@ local function serializeFloat(value)
|
|||||||
return value
|
return value
|
||||||
end
|
end
|
||||||
|
|
||||||
local encoders
|
local ALL_AXES = {"X", "Y", "Z"}
|
||||||
encoders = {
|
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
|
||||||
Bool = identity,
|
|
||||||
Content = identity,
|
|
||||||
Float32 = serializeFloat,
|
|
||||||
Float64 = serializeFloat,
|
|
||||||
Int32 = identity,
|
|
||||||
Int64 = identity,
|
|
||||||
String = identity,
|
|
||||||
|
|
||||||
BinaryString = base64.encode,
|
local types
|
||||||
SharedString = base64.encode,
|
types = {
|
||||||
|
Axes = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local axes = {}
|
||||||
|
|
||||||
BrickColor = function(value)
|
for index, axisName in ipairs(pod) do
|
||||||
return value.Number
|
axes[index] = Enum.Axis[axisName]
|
||||||
end,
|
end
|
||||||
|
|
||||||
CFrame = function(value)
|
return Axes.new(unpack(axes))
|
||||||
return {value:GetComponents()}
|
end,
|
||||||
end,
|
|
||||||
Color3 = function(value)
|
|
||||||
return {value.r, value.g, value.b}
|
|
||||||
end,
|
|
||||||
NumberRange = function(value)
|
|
||||||
return {value.Min, value.Max}
|
|
||||||
end,
|
|
||||||
NumberSequence = function(value)
|
|
||||||
local keypoints = {}
|
|
||||||
|
|
||||||
for index, keypoint in ipairs(value.Keypoints) do
|
toPod = function(roblox)
|
||||||
keypoints[index] = {
|
local json = {}
|
||||||
Time = keypoint.Time,
|
|
||||||
Value = keypoint.Value,
|
|
||||||
Envelope = keypoint.Envelope,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
for _, axis in ipairs(ALL_AXES) do
|
||||||
Keypoints = keypoints,
|
if roblox[axis] then
|
||||||
}
|
table.insert(json, axis)
|
||||||
end,
|
end
|
||||||
ColorSequence = function(value)
|
end
|
||||||
local keypoints = {}
|
|
||||||
|
|
||||||
for index, keypoint in ipairs(value.Keypoints) do
|
return json
|
||||||
keypoints[index] = {
|
end,
|
||||||
Time = keypoint.Time,
|
},
|
||||||
Color = encoders.Color3(keypoint.Value),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
BinaryString = {
|
||||||
Keypoints = keypoints,
|
fromPod = base64.decode,
|
||||||
}
|
toPod = base64.encode,
|
||||||
end,
|
},
|
||||||
Rect = function(value)
|
|
||||||
return {
|
Bool = {
|
||||||
Min = {value.Min.X, value.Min.Y},
|
fromPod = identity,
|
||||||
Max = {value.Max.X, value.Max.Y},
|
toPod = identity,
|
||||||
}
|
},
|
||||||
end,
|
|
||||||
UDim = function(value)
|
BrickColor = {
|
||||||
return {value.Scale, value.Offset}
|
fromPod = function(pod)
|
||||||
end,
|
return BrickColor.new(pod)
|
||||||
UDim2 = function(value)
|
end,
|
||||||
return {value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset}
|
|
||||||
end,
|
toPod = function(roblox)
|
||||||
Vector2 = function(value)
|
return roblox.Number
|
||||||
return {
|
end,
|
||||||
serializeFloat(value.X),
|
},
|
||||||
serializeFloat(value.Y),
|
|
||||||
}
|
CFrame = {
|
||||||
end,
|
fromPod = function(pod)
|
||||||
Vector2int16 = function(value)
|
local pos = pod.Position
|
||||||
return {value.X, value.Y}
|
local orient = pod.Orientation
|
||||||
end,
|
|
||||||
Vector3 = function(value)
|
return CFrame.new(
|
||||||
return {
|
pos[1], pos[2], pos[3],
|
||||||
serializeFloat(value.X),
|
orient[1][1], orient[1][2], orient[1][3],
|
||||||
serializeFloat(value.Y),
|
orient[2][1], orient[2][2], orient[2][3],
|
||||||
serializeFloat(value.Z),
|
orient[3][1], orient[3][2], orient[3][3]
|
||||||
}
|
)
|
||||||
end,
|
end,
|
||||||
Vector3int16 = function(value)
|
|
||||||
return {value.X, value.Y, value.Z}
|
toPod = function(roblox)
|
||||||
end,
|
local x, y, z,
|
||||||
|
r00, r01, r02,
|
||||||
|
r10, r11, r12,
|
||||||
|
r20, r21, r22 = roblox:GetComponents()
|
||||||
|
|
||||||
PhysicalProperties = function(value)
|
|
||||||
if value == nil then
|
|
||||||
return nil
|
|
||||||
else
|
|
||||||
return {
|
return {
|
||||||
Density = value.Density,
|
Position = {x, y, z},
|
||||||
Friction = value.Friction,
|
Orientation = {
|
||||||
Elasticity = value.Elasticity,
|
{r00, r01, r02},
|
||||||
FrictionWeight = value.FrictionWeight,
|
{r10, r11, r12},
|
||||||
ElasticityWeight = value.ElasticityWeight,
|
{r20, r21, r22},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
end
|
end,
|
||||||
end,
|
},
|
||||||
|
|
||||||
Ref = function(value)
|
Color3 = {
|
||||||
return nil
|
fromPod = unpackDecoder(Color3.new),
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
local decoders = {
|
toPod = function(roblox)
|
||||||
Bool = identity,
|
return {roblox.r, roblox.g, roblox.b}
|
||||||
Content = identity,
|
end,
|
||||||
Enum = identity,
|
},
|
||||||
Float32 = identity,
|
|
||||||
Float64 = identity,
|
|
||||||
Int32 = identity,
|
|
||||||
Int64 = identity,
|
|
||||||
String = identity,
|
|
||||||
|
|
||||||
BinaryString = base64.decode,
|
Color3uint8 = {
|
||||||
SharedString = base64.decode,
|
fromPod = unpackDecoder(Color3.fromRGB),
|
||||||
|
|
||||||
BrickColor = BrickColor.new,
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
math.round(roblox.R * 255),
|
||||||
|
math.round(roblox.G * 255),
|
||||||
|
math.round(roblox.B * 255),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
CFrame = unpackDecoder(CFrame.new),
|
ColorSequence = {
|
||||||
Color3 = unpackDecoder(Color3.new),
|
fromPod = function(pod)
|
||||||
Color3uint8 = unpackDecoder(Color3.fromRGB),
|
local keypoints = {}
|
||||||
NumberRange = unpackDecoder(NumberRange.new),
|
|
||||||
UDim = unpackDecoder(UDim.new),
|
|
||||||
UDim2 = unpackDecoder(UDim2.new),
|
|
||||||
Vector2 = unpackDecoder(Vector2.new),
|
|
||||||
Vector2int16 = unpackDecoder(Vector2int16.new),
|
|
||||||
Vector3 = unpackDecoder(Vector3.new),
|
|
||||||
Vector3int16 = unpackDecoder(Vector3int16.new),
|
|
||||||
|
|
||||||
Rect = function(value)
|
for index, keypoint in ipairs(pod.Keypoints) do
|
||||||
return Rect.new(value.Min[1], value.Min[2], value.Max[1], value.Max[2])
|
keypoints[index] = ColorSequenceKeypoint.new(
|
||||||
end,
|
keypoint.Time,
|
||||||
|
types.Color3.fromPod(keypoint.Color)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
NumberSequence = function(value)
|
return ColorSequence.new(keypoints)
|
||||||
local keypoints = {}
|
end,
|
||||||
|
|
||||||
for index, keypoint in ipairs(value.Keypoints) do
|
toPod = function(roblox)
|
||||||
keypoints[index] = NumberSequenceKeypoint.new(
|
local keypoints = {}
|
||||||
keypoint.Time,
|
|
||||||
keypoint.Value,
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
keypoint.Envelope
|
keypoints[index] = {
|
||||||
|
Time = keypoint.Time,
|
||||||
|
Color = types.Color3.toPod(keypoint.Value),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
Keypoints = keypoints,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Content = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
Enum = {
|
||||||
|
fromPod = identity,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
-- FIXME: More robust handling of enums
|
||||||
|
if typeof(roblox) == "number" then
|
||||||
|
return roblox
|
||||||
|
else
|
||||||
|
return roblox.Value
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Faces = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local faces = {}
|
||||||
|
|
||||||
|
for index, faceName in ipairs(pod) do
|
||||||
|
faces[index] = Enum.NormalId[faceName]
|
||||||
|
end
|
||||||
|
|
||||||
|
return Faces.new(unpack(faces))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local pod = {}
|
||||||
|
|
||||||
|
for _, face in ipairs(ALL_FACES) do
|
||||||
|
if roblox[face] then
|
||||||
|
table.insert(pod, face)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return pod
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float32 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = serializeFloat,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float64 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = serializeFloat,
|
||||||
|
},
|
||||||
|
|
||||||
|
Int32 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
Int64 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
NumberRange = {
|
||||||
|
fromPod = unpackDecoder(NumberRange.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {roblox.Min, roblox.Max}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
NumberSequence = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local keypoints = {}
|
||||||
|
|
||||||
|
for index, keypoint in ipairs(pod.Keypoints) do
|
||||||
|
keypoints[index] = NumberSequenceKeypoint.new(
|
||||||
|
keypoint.Time,
|
||||||
|
keypoint.Value,
|
||||||
|
keypoint.Envelope
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return NumberSequence.new(keypoints)
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local keypoints = {}
|
||||||
|
|
||||||
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
|
keypoints[index] = {
|
||||||
|
Time = keypoint.Time,
|
||||||
|
Value = keypoint.Value,
|
||||||
|
Envelope = keypoint.Envelope,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
Keypoints = keypoints,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
PhysicalProperties = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
if pod == "Default" then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return PhysicalProperties.new(
|
||||||
|
pod.Density,
|
||||||
|
pod.Friction,
|
||||||
|
pod.Elasticity,
|
||||||
|
pod.FrictionWeight,
|
||||||
|
pod.ElasticityWeight
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
if roblox == nil then
|
||||||
|
return "Default"
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
Density = roblox.Density,
|
||||||
|
Friction = roblox.Friction,
|
||||||
|
Elasticity = roblox.Elasticity,
|
||||||
|
FrictionWeight = roblox.FrictionWeight,
|
||||||
|
ElasticityWeight = roblox.ElasticityWeight,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Ray = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Ray.new(
|
||||||
|
types.Vector3.fromPod(pod.Origin),
|
||||||
|
types.Vector3.fromPod(pod.Direction)
|
||||||
)
|
)
|
||||||
end
|
end,
|
||||||
|
|
||||||
return NumberSequence.new(keypoints)
|
toPod = function(roblox)
|
||||||
end,
|
return {
|
||||||
|
Origin = types.Vector3.toPod(roblox.Origin),
|
||||||
|
Direction = types.Vector3.toPod(roblox.Direction),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
ColorSequence = function(value)
|
Rect = {
|
||||||
local keypoints = {}
|
fromPod = function(pod)
|
||||||
|
return Rect.new(
|
||||||
for index, keypoint in ipairs(value.Keypoints) do
|
types.Vector2.fromPod(pod[1]),
|
||||||
keypoints[index] = ColorSequenceKeypoint.new(
|
types.Vector2.fromPod(pod[2])
|
||||||
keypoint.Time,
|
|
||||||
Color3.new(unpack(keypoint.Color))
|
|
||||||
)
|
)
|
||||||
end
|
end,
|
||||||
|
|
||||||
return ColorSequence.new(keypoints)
|
toPod = function(roblox)
|
||||||
end,
|
return {
|
||||||
|
types.Vector2.toPod(roblox.Min),
|
||||||
|
types.Vector2.toPod(roblox.Max),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
PhysicalProperties = function(properties)
|
Ref = {
|
||||||
if properties == nil then
|
fromPod = function(_pod)
|
||||||
return nil
|
error("Ref cannot be decoded on its own")
|
||||||
else
|
end,
|
||||||
return PhysicalProperties.new(
|
|
||||||
properties.Density,
|
toPod = function(_roblox)
|
||||||
properties.Friction,
|
error("Ref can not be encoded on its own")
|
||||||
properties.Elasticity,
|
end,
|
||||||
properties.FrictionWeight,
|
},
|
||||||
properties.ElasticityWeight
|
|
||||||
|
Region3 = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
error("Region3 is not implemented")
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
error("Region3 is not implemented")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Region3int16 = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Region3int16.new(
|
||||||
|
types.Vector3int16.fromPod(pod[1]),
|
||||||
|
types.Vector3int16.fromPod(pod[2])
|
||||||
)
|
)
|
||||||
end
|
end,
|
||||||
end,
|
|
||||||
|
|
||||||
Ref = function()
|
toPod = function(roblox)
|
||||||
return nil
|
return {
|
||||||
end,
|
types.Vector3int16.toPod(roblox.Min),
|
||||||
|
types.Vector3int16.toPod(roblox.Max),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
SharedString = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
error("SharedString is not supported")
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
error("SharedString is not supported")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
String = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
UDim = {
|
||||||
|
fromPod = unpackDecoder(UDim.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {roblox.Scale, roblox.Offset}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
UDim2 = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return UDim2.new(
|
||||||
|
types.UDim.fromPod(pod[1]),
|
||||||
|
types.UDim.fromPod(pod[2])
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
types.UDim.toPod(roblox.X),
|
||||||
|
types.UDim.toPod(roblox.Y),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector2 = {
|
||||||
|
fromPod = unpackDecoder(Vector2.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
serializeFloat(roblox.X),
|
||||||
|
serializeFloat(roblox.Y),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector2int16 = {
|
||||||
|
fromPod = unpackDecoder(Vector2int16.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {roblox.X, roblox.Y}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector3 = {
|
||||||
|
fromPod = unpackDecoder(Vector3.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
serializeFloat(roblox.X),
|
||||||
|
serializeFloat(roblox.Y),
|
||||||
|
serializeFloat(roblox.Z),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector3int16 = {
|
||||||
|
fromPod = unpackDecoder(Vector3int16.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {roblox.X, roblox.Y, roblox.Z}
|
||||||
|
end,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local EncodedValue = {}
|
local EncodedValue = {}
|
||||||
|
|
||||||
function EncodedValue.decode(encodedValue)
|
function EncodedValue.decode(encodedValue)
|
||||||
local decoder = decoders[encodedValue.Type]
|
local typeImpl = types[encodedValue.Type]
|
||||||
if decoder ~= nil then
|
if typeImpl == nil then
|
||||||
return true, decoder(encodedValue.Value)
|
return false, "Couldn't decode value " .. tostring(encodedValue.Type)
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, "Couldn't decode value " .. tostring(encodedValue.Type)
|
return true, typeImpl.fromPod(encodedValue.Value)
|
||||||
end
|
end
|
||||||
|
|
||||||
function EncodedValue.encode(rbxValue, propertyType)
|
function EncodedValue.encode(rbxValue, propertyType)
|
||||||
assert(propertyType ~= nil, "Property type descriptor is required")
|
assert(propertyType ~= nil, "Property type descriptor is required")
|
||||||
|
|
||||||
if propertyType.type == "Data" then
|
local typeImpl = types[propertyType]
|
||||||
local encoder = encoders[propertyType.name]
|
if typeImpl == nil then
|
||||||
|
return false, ("Missing encoder for property type %q"):format(propertyType)
|
||||||
if encoder == nil then
|
|
||||||
return false, ("Missing encoder for property type %q"):format(propertyType.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
if encoder ~= nil then
|
|
||||||
return true, {
|
|
||||||
Type = propertyType.name,
|
|
||||||
Value = encoder(rbxValue),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
elseif propertyType.type == "Enum" then
|
|
||||||
return true, {
|
|
||||||
Type = "Enum",
|
|
||||||
Value = rbxValue.Value,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, ("Unknown property descriptor type %q"):format(tostring(propertyType.type))
|
return true, {
|
||||||
|
Type = propertyType,
|
||||||
|
Value = typeImpl.toPod(rbxValue),
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return EncodedValue
|
return EncodedValue
|
||||||
|
|||||||
@@ -1,127 +1,72 @@
|
|||||||
return function()
|
return function()
|
||||||
local RbxDom = require(script.Parent)
|
local HttpService = game:GetService("HttpService")
|
||||||
|
|
||||||
local EncodedValue = require(script.Parent.EncodedValue)
|
local EncodedValue = require(script.Parent.EncodedValue)
|
||||||
|
local allValues = require(script.Parent.allValues)
|
||||||
|
|
||||||
it("should decode Rect values", function()
|
local function deepEq(a, b)
|
||||||
local input = {
|
if typeof(a) ~= typeof(b) then
|
||||||
Type = "Rect",
|
return false
|
||||||
Value = {
|
end
|
||||||
Min = {1, 2},
|
|
||||||
Max = {3, 4},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local output = Rect.new(1, 2, 3, 4)
|
local ty = typeof(a)
|
||||||
|
|
||||||
local ok, decoded = EncodedValue.decode(input)
|
if ty == "table" then
|
||||||
|
local visited = {}
|
||||||
|
|
||||||
|
for key, valueA in pairs(a) do
|
||||||
|
visited[key] = true
|
||||||
|
|
||||||
|
if not deepEq(valueA, b[key]) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
assert(ok, decoded)
|
for key, valueB in pairs(b) do
|
||||||
expect(decoded).to.equal(output)
|
if visited[key] then
|
||||||
end)
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
it("should decode ColorSequence values", function()
|
if not deepEq(valueB, a[key]) then
|
||||||
local input = {
|
return false
|
||||||
Type = "ColorSequence",
|
end
|
||||||
Value = {
|
end
|
||||||
Keypoints = {
|
|
||||||
{
|
|
||||||
Time = 0,
|
|
||||||
Color = { 0.12, 0.34, 0.56 },
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
return true
|
||||||
Time = 1,
|
else
|
||||||
Color = { 0.13, 0.33, 0.37 },
|
return a == b
|
||||||
},
|
end
|
||||||
}
|
end
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local output = ColorSequence.new({
|
local extraAssertions = {
|
||||||
ColorSequenceKeypoint.new(0, Color3.new(0.12, 0.34, 0.56)),
|
CFrame = function(value)
|
||||||
ColorSequenceKeypoint.new(1, Color3.new(0.13, 0.33, 0.37)),
|
expect(value).to.equal(CFrame.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
|
||||||
})
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
local ok, decoded = EncodedValue.decode(input)
|
for testName, testEntry in pairs(allValues) do
|
||||||
assert(ok, decoded)
|
it("round trip " .. testName, function()
|
||||||
expect(decoded).to.equal(output)
|
local ok, decoded = EncodedValue.decode(testEntry.value)
|
||||||
end)
|
assert(ok, decoded)
|
||||||
|
|
||||||
it("should decode NumberSequence values", function()
|
if extraAssertions[testName] ~= nil then
|
||||||
local input = {
|
extraAssertions[testName](decoded)
|
||||||
Type = "NumberSequence",
|
end
|
||||||
Value = {
|
|
||||||
Keypoints = {
|
|
||||||
{
|
|
||||||
Time = 0,
|
|
||||||
Value = 0.5,
|
|
||||||
Envelope = 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
local ok, encoded = EncodedValue.encode(decoded, testEntry.ty)
|
||||||
Time = 1,
|
assert(ok, encoded)
|
||||||
Value = 0.5,
|
|
||||||
Envelope = 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local output = NumberSequence.new({
|
if not deepEq(encoded, testEntry.value) then
|
||||||
NumberSequenceKeypoint.new(0, 0.5, 0),
|
local expected = HttpService:JSONEncode(testEntry.value)
|
||||||
NumberSequenceKeypoint.new(1, 0.5, 0),
|
local actual = HttpService:JSONEncode(encoded)
|
||||||
})
|
|
||||||
|
|
||||||
local ok, decoded = EncodedValue.decode(input)
|
local message = string.format(
|
||||||
assert(ok, decoded)
|
"Round-trip results did not match.\nExpected:\n%s\nActual:\n%s",
|
||||||
expect(decoded).to.equal(output)
|
expected, actual
|
||||||
end)
|
)
|
||||||
|
|
||||||
it("should decode PhysicalProperties values", function()
|
error(message)
|
||||||
local input = {
|
end
|
||||||
Type = "PhysicalProperties",
|
end)
|
||||||
Value = {
|
end
|
||||||
Density = 0.1,
|
end
|
||||||
Friction = 0.2,
|
|
||||||
Elasticity = 0.3,
|
|
||||||
FrictionWeight = 0.4,
|
|
||||||
ElasticityWeight = 0.5,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local output = PhysicalProperties.new(
|
|
||||||
0.1,
|
|
||||||
0.2,
|
|
||||||
0.3,
|
|
||||||
0.4,
|
|
||||||
0.5
|
|
||||||
)
|
|
||||||
|
|
||||||
local ok, decoded = EncodedValue.decode(input)
|
|
||||||
assert(ok, decoded)
|
|
||||||
expect(decoded).to.equal(output)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- This part of rbx_dom_lua needs some work still.
|
|
||||||
itSKIP("should encode Rect values", function()
|
|
||||||
local input = Rect.new(10, 20, 30, 40)
|
|
||||||
|
|
||||||
local output = {
|
|
||||||
Type = "Rect",
|
|
||||||
Value = {
|
|
||||||
Min = {10, 20},
|
|
||||||
Max = {30, 40},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local descriptor = RbxDom.findCanonicalPropertyDescriptor("ImageLabel", "SliceCenter")
|
|
||||||
local ok, encoded = EncodedValue.encode(input, descriptor)
|
|
||||||
|
|
||||||
assert(ok, encoded)
|
|
||||||
expect(encoded.Type).to.equal(output.Type)
|
|
||||||
expect(encoded.Value.Min[1]).to.equal(output.Value.Min[1])
|
|
||||||
expect(encoded.Value.Min[2]).to.equal(output.Value.Min[2])
|
|
||||||
expect(encoded.Value.Max[1]).to.equal(output.Value.Max[1])
|
|
||||||
expect(encoded.Value.Max[2]).to.equal(output.Value.Max[2])
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ end
|
|||||||
|
|
||||||
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
scriptability = data.scriptability,
|
scriptability = data.Scriptability,
|
||||||
className = className,
|
className = className,
|
||||||
name = propertyName,
|
name = propertyName,
|
||||||
}, PropertyDescriptor)
|
}, PropertyDescriptor)
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
return {
|
|
||||||
classes = require(script.classes)
|
|
||||||
}
|
|
||||||
345
plugin/rbx_dom_lua/src/allValues.json
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
{
|
||||||
|
"Axes": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Axes",
|
||||||
|
"Value": [
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Axes"
|
||||||
|
},
|
||||||
|
"BinaryString": {
|
||||||
|
"value": {
|
||||||
|
"Type": "BinaryString",
|
||||||
|
"Value": "SGVsbG8h"
|
||||||
|
},
|
||||||
|
"ty": "BinaryString"
|
||||||
|
},
|
||||||
|
"Bool": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Bool",
|
||||||
|
"Value": true
|
||||||
|
},
|
||||||
|
"ty": "Bool"
|
||||||
|
},
|
||||||
|
"BrickColor": {
|
||||||
|
"value": {
|
||||||
|
"Type": "BrickColor",
|
||||||
|
"Value": 1004
|
||||||
|
},
|
||||||
|
"ty": "BrickColor"
|
||||||
|
},
|
||||||
|
"CFrame": {
|
||||||
|
"value": {
|
||||||
|
"Type": "CFrame",
|
||||||
|
"Value": {
|
||||||
|
"Position": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"Orientation": [
|
||||||
|
[
|
||||||
|
4.0,
|
||||||
|
5.0,
|
||||||
|
6.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
7.0,
|
||||||
|
8.0,
|
||||||
|
9.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10.0,
|
||||||
|
11.0,
|
||||||
|
12.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "CFrame"
|
||||||
|
},
|
||||||
|
"Color3": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Color3",
|
||||||
|
"Value": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Color3"
|
||||||
|
},
|
||||||
|
"Color3uint8": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Color3uint8",
|
||||||
|
"Value": [
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
255
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Color3uint8"
|
||||||
|
},
|
||||||
|
"ColorSequence": {
|
||||||
|
"value": {
|
||||||
|
"Type": "ColorSequence",
|
||||||
|
"Value": {
|
||||||
|
"Keypoints": [
|
||||||
|
{
|
||||||
|
"Time": 0.0,
|
||||||
|
"Color": [
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Time": 1.0,
|
||||||
|
"Color": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "ColorSequence"
|
||||||
|
},
|
||||||
|
"Content": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Content",
|
||||||
|
"Value": "rbxassetid://12345"
|
||||||
|
},
|
||||||
|
"ty": "Content"
|
||||||
|
},
|
||||||
|
"Enum": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Enum",
|
||||||
|
"Value": 1234
|
||||||
|
},
|
||||||
|
"ty": "Enum"
|
||||||
|
},
|
||||||
|
"Faces": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Faces",
|
||||||
|
"Value": [
|
||||||
|
"Right",
|
||||||
|
"Top",
|
||||||
|
"Back",
|
||||||
|
"Left",
|
||||||
|
"Bottom",
|
||||||
|
"Front"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Faces"
|
||||||
|
},
|
||||||
|
"Float32": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Float32",
|
||||||
|
"Value": 15.0
|
||||||
|
},
|
||||||
|
"ty": "Float32"
|
||||||
|
},
|
||||||
|
"Float64": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Float64",
|
||||||
|
"Value": 15123.0
|
||||||
|
},
|
||||||
|
"ty": "Float64"
|
||||||
|
},
|
||||||
|
"Int32": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Int32",
|
||||||
|
"Value": 6014
|
||||||
|
},
|
||||||
|
"ty": "Int32"
|
||||||
|
},
|
||||||
|
"Int64": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Int64",
|
||||||
|
"Value": 23491023
|
||||||
|
},
|
||||||
|
"ty": "Int64"
|
||||||
|
},
|
||||||
|
"NumberRange": {
|
||||||
|
"value": {
|
||||||
|
"Type": "NumberRange",
|
||||||
|
"Value": [
|
||||||
|
-36.0,
|
||||||
|
94.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "NumberRange"
|
||||||
|
},
|
||||||
|
"NumberSequence": {
|
||||||
|
"value": {
|
||||||
|
"Type": "NumberSequence",
|
||||||
|
"Value": {
|
||||||
|
"Keypoints": [
|
||||||
|
{
|
||||||
|
"Time": 0.0,
|
||||||
|
"Value": 5.0,
|
||||||
|
"Envelope": 2.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Time": 1.0,
|
||||||
|
"Value": 22.0,
|
||||||
|
"Envelope": 0.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "NumberSequence"
|
||||||
|
},
|
||||||
|
"PhysicalProperties-Custom": {
|
||||||
|
"value": {
|
||||||
|
"Type": "PhysicalProperties",
|
||||||
|
"Value": {
|
||||||
|
"Density": 0.5,
|
||||||
|
"Friction": 1.0,
|
||||||
|
"Elasticity": 0.0,
|
||||||
|
"FrictionWeight": 50.0,
|
||||||
|
"ElasticityWeight": 25.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "PhysicalProperties"
|
||||||
|
},
|
||||||
|
"PhysicalProperties-Default": {
|
||||||
|
"value": {
|
||||||
|
"Type": "PhysicalProperties",
|
||||||
|
"Value": "Default"
|
||||||
|
},
|
||||||
|
"ty": "PhysicalProperties"
|
||||||
|
},
|
||||||
|
"Ray": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Ray",
|
||||||
|
"Value": {
|
||||||
|
"Origin": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"Direction": [
|
||||||
|
4.0,
|
||||||
|
5.0,
|
||||||
|
6.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Ray"
|
||||||
|
},
|
||||||
|
"Rect": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Rect",
|
||||||
|
"Value": [
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
5.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Rect"
|
||||||
|
},
|
||||||
|
"Region3int16": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Region3int16",
|
||||||
|
"Value": [
|
||||||
|
[
|
||||||
|
-10,
|
||||||
|
-5,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Region3int16"
|
||||||
|
},
|
||||||
|
"String": {
|
||||||
|
"value": {
|
||||||
|
"Type": "String",
|
||||||
|
"Value": "Hello, world!"
|
||||||
|
},
|
||||||
|
"ty": "String"
|
||||||
|
},
|
||||||
|
"UDim": {
|
||||||
|
"value": {
|
||||||
|
"Type": "UDim",
|
||||||
|
"Value": [
|
||||||
|
1.0,
|
||||||
|
32
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "UDim"
|
||||||
|
},
|
||||||
|
"UDim2": {
|
||||||
|
"value": {
|
||||||
|
"Type": "UDim2",
|
||||||
|
"Value": [
|
||||||
|
[
|
||||||
|
-1.0,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
-100
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "UDim2"
|
||||||
|
},
|
||||||
|
"Vector2": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Vector2",
|
||||||
|
"Value": [
|
||||||
|
-50.0,
|
||||||
|
50.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector2"
|
||||||
|
},
|
||||||
|
"Vector2int16": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Vector2int16",
|
||||||
|
"Value": [
|
||||||
|
-300,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector2int16"
|
||||||
|
},
|
||||||
|
"Vector3": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Vector3",
|
||||||
|
"Value": [
|
||||||
|
-300.0,
|
||||||
|
0.0,
|
||||||
|
1500.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector3"
|
||||||
|
},
|
||||||
|
"Vector3int16": {
|
||||||
|
"value": {
|
||||||
|
"Type": "Vector3int16",
|
||||||
|
"Value": [
|
||||||
|
60,
|
||||||
|
37,
|
||||||
|
-450
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector3int16"
|
||||||
|
}
|
||||||
|
}
|
||||||
44022
plugin/rbx_dom_lua/src/database.json
Normal file
@@ -1,4 +1,4 @@
|
|||||||
local ReflectionDatabase = require(script.ReflectionDatabase)
|
local database = require(script.database)
|
||||||
local Error = require(script.Error)
|
local Error = require(script.Error)
|
||||||
local PropertyDescriptor = require(script.PropertyDescriptor)
|
local PropertyDescriptor = require(script.PropertyDescriptor)
|
||||||
|
|
||||||
@@ -6,29 +6,31 @@ local function findCanonicalPropertyDescriptor(className, propertyName)
|
|||||||
local currentClassName = className
|
local currentClassName = className
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local currentClass = ReflectionDatabase.classes[currentClassName]
|
local currentClass = database.Classes[currentClassName]
|
||||||
|
|
||||||
if currentClass == nil then
|
if currentClass == nil then
|
||||||
return currentClass
|
return currentClass
|
||||||
end
|
end
|
||||||
|
|
||||||
local propertyData = currentClass.properties[propertyName]
|
local propertyData = currentClass.Properties[propertyName]
|
||||||
if propertyData ~= nil then
|
if propertyData ~= nil then
|
||||||
if propertyData.isCanonical then
|
local canonicalData = propertyData.Kind.Canonical
|
||||||
|
if canonicalData ~= nil then
|
||||||
return PropertyDescriptor.fromRaw(propertyData, currentClassName, propertyName)
|
return PropertyDescriptor.fromRaw(propertyData, currentClassName, propertyName)
|
||||||
end
|
end
|
||||||
|
|
||||||
if propertyData.canonicalName ~= nil then
|
local aliasData = propertyData.Kind.Alias
|
||||||
|
if aliasData ~= nil then
|
||||||
return PropertyDescriptor.fromRaw(
|
return PropertyDescriptor.fromRaw(
|
||||||
currentClass.properties[propertyData.canonicalName],
|
currentClass.properties[aliasData.AliasFor],
|
||||||
currentClassName,
|
currentClassName,
|
||||||
propertyData.canonicalName)
|
aliasData.AliasFor)
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
currentClassName = currentClass.superclass
|
currentClassName = currentClass.Superclass
|
||||||
until currentClassName == nil
|
until currentClassName == nil
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
4
plugin/rbx_dom_lua/test
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
rojo build test-place.project.json -o TestPlace.rbxlx
|
||||||
|
run-in-roblox --script run-tests.lua --place TestPlace.rbxlx
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"$className": "ServerScriptService",
|
"$className": "ServerScriptService",
|
||||||
|
|
||||||
"Run Tests": {
|
"Run Tests": {
|
||||||
"$path": "test.server.lua"
|
"$path": "run-tests.lua"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Players": {
|
"Players": {
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
|
|
||||||
local LIB_ROOT = ReplicatedStorage.RbxDom
|
|
||||||
|
|
||||||
local TestEZ = require(ReplicatedStorage.TestEZ)
|
|
||||||
|
|
||||||
TestEZ.TestBootstrap:run({LIB_ROOT})
|
|
||||||
41
plugin/src/App/Components/BorderedContainer.lua
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
|
||||||
|
local SlicedImage = require(script.Parent.SlicedImage)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function BorderedContainer(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBackground,
|
||||||
|
color = theme.BorderedContainer.BackgroundColor,
|
||||||
|
transparency = props.transparency,
|
||||||
|
|
||||||
|
size = props.size,
|
||||||
|
position = props.position,
|
||||||
|
anchorPoint = props.anchorPoint,
|
||||||
|
layoutOrder = props.layoutOrder,
|
||||||
|
}, {
|
||||||
|
Content = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, props[Roact.Children]),
|
||||||
|
|
||||||
|
Border = e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBorder,
|
||||||
|
color = theme.BorderedContainer.BorderColor,
|
||||||
|
transparency = props.transparency,
|
||||||
|
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return BorderedContainer
|
||||||
96
plugin/src/App/Components/Checkbox.lua
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Flipper = require(Rojo.Flipper)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local SlicedImage = require(script.Parent.SlicedImage)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local Checkbox = Roact.Component:extend("Checkbox")
|
||||||
|
|
||||||
|
function Checkbox:init()
|
||||||
|
self.motor = Flipper.SingleMotor.new(self.props.active and 1 or 0)
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Checkbox:didUpdate(lastProps)
|
||||||
|
if lastProps.active ~= self.props.active then
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(self.props.active and 1 or 0, {
|
||||||
|
frequency = 6,
|
||||||
|
dampingRatio = 1.1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Checkbox:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
theme = theme.Checkbox
|
||||||
|
|
||||||
|
local activeTransparency = Roact.joinBindings({
|
||||||
|
self.binding:map(function(value)
|
||||||
|
return 1 - value
|
||||||
|
end),
|
||||||
|
self.props.transparency,
|
||||||
|
}):map(bindingUtil.blendAlpha)
|
||||||
|
|
||||||
|
return e("ImageButton", {
|
||||||
|
Size = UDim2.new(0, 28, 0, 28),
|
||||||
|
Position = self.props.position,
|
||||||
|
AnchorPoint = self.props.anchorPoint,
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
ZIndex = self.props.zIndex,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = self.props.onClick,
|
||||||
|
}, {
|
||||||
|
Active = e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBackground,
|
||||||
|
color = theme.Active.BackgroundColor,
|
||||||
|
transparency = activeTransparency,
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
zIndex = 2,
|
||||||
|
}, {
|
||||||
|
Icon = e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Checkbox.Active,
|
||||||
|
ImageColor3 = theme.Active.IconColor,
|
||||||
|
ImageTransparency = activeTransparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, 16, 0, 16),
|
||||||
|
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Inactive = e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBorder,
|
||||||
|
color = theme.Inactive.BorderColor,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
}, {
|
||||||
|
Icon = e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Checkbox.Inactive,
|
||||||
|
ImageColor3 = theme.Inactive.IconColor,
|
||||||
|
ImageTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, 16, 0, 16),
|
||||||
|
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Checkbox
|
||||||
55
plugin/src/App/Components/Header.lua
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Config = require(Plugin.Config)
|
||||||
|
local Version = require(Plugin.Version)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function Header(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 32),
|
||||||
|
LayoutOrder = props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Logo = e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Logo,
|
||||||
|
ImageColor3 = theme.Header.LogoColor,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, 60, 0, 27),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Version = e("TextLabel", {
|
||||||
|
Text = Version.display(Config.version),
|
||||||
|
Font = Enum.Font.Gotham,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Header.VersionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 0, 14),
|
||||||
|
|
||||||
|
LayoutOrder = 2,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 15),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Header
|
||||||
79
plugin/src/App/Components/IconButton.lua
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Flipper = require(Rojo.Flipper)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local HOVER_SPRING_PROPS = {
|
||||||
|
frequency = 5,
|
||||||
|
dampingRatio = 1.1,
|
||||||
|
}
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local IconButton = Roact.Component:extend("IconButton")
|
||||||
|
|
||||||
|
function IconButton:init()
|
||||||
|
self.motor = Flipper.SingleMotor.new(0)
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
end
|
||||||
|
|
||||||
|
function IconButton:render()
|
||||||
|
local iconSize = self.props.iconSize
|
||||||
|
|
||||||
|
return e("ImageButton", {
|
||||||
|
Size = UDim2.new(0, iconSize * 1.5, 0, iconSize * 1.5),
|
||||||
|
Position = self.props.position,
|
||||||
|
AnchorPoint = self.props.anchorPoint,
|
||||||
|
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
ZIndex = self.props.zIndex,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = self.props.onClick,
|
||||||
|
|
||||||
|
[Roact.Event.MouseEnter] = function()
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(1, HOVER_SPRING_PROPS)
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
|
||||||
|
[Roact.Event.MouseLeave] = function()
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(0, HOVER_SPRING_PROPS)
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Icon = e("ImageLabel", {
|
||||||
|
Image = self.props.icon,
|
||||||
|
ImageColor3 = self.props.color,
|
||||||
|
ImageTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, iconSize, 0, iconSize),
|
||||||
|
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
HoverCircle = e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Circles[128],
|
||||||
|
ImageColor3 = self.props.color,
|
||||||
|
ImageTransparency = Roact.joinBindings({
|
||||||
|
hover = self.binding,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
}):map(function(values)
|
||||||
|
return bindingUtil.blendAlpha({ 0.85, 1 - values.hover, values.transparency })
|
||||||
|
end),
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return IconButton
|
||||||
42
plugin/src/App/Components/ScrollingFrame.lua
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function ScrollingFrame(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("ScrollingFrame", {
|
||||||
|
ScrollBarThickness = 9,
|
||||||
|
ScrollBarImageColor3 = theme.ScrollBarColor,
|
||||||
|
ScrollBarImageTransparency = props.transparency:map(function(value)
|
||||||
|
return bindingUtil.blendAlpha({ 0.65, value })
|
||||||
|
end),
|
||||||
|
TopImage = Assets.Images.ScrollBar.Top,
|
||||||
|
MidImage = Assets.Images.ScrollBar.Middle,
|
||||||
|
BottomImage = Assets.Images.ScrollBar.Bottom,
|
||||||
|
|
||||||
|
ElasticBehavior = Enum.ElasticBehavior.Always,
|
||||||
|
ScrollingDirection = Enum.ScrollingDirection.Y,
|
||||||
|
|
||||||
|
Size = props.size,
|
||||||
|
Position = props.position,
|
||||||
|
AnchorPoint = props.anchorPoint,
|
||||||
|
CanvasSize = props.contentSize:map(function(value)
|
||||||
|
return UDim2.new(0, 0, 0, value.Y)
|
||||||
|
end),
|
||||||
|
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteSize] = props[Roact.Change.AbsoluteSize]
|
||||||
|
}, props[Roact.Children])
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ScrollingFrame
|
||||||
29
plugin/src/App/Components/SlicedImage.lua
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function SlicedImage(props)
|
||||||
|
local slice = props.slice
|
||||||
|
|
||||||
|
return e("ImageLabel", {
|
||||||
|
Image = slice.Image,
|
||||||
|
ImageColor3 = props.color,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
|
||||||
|
ScaleType = Enum.ScaleType.Slice,
|
||||||
|
SliceCenter = slice.Center,
|
||||||
|
SliceScale = slice.Scale,
|
||||||
|
|
||||||
|
Size = props.size,
|
||||||
|
Position = props.position,
|
||||||
|
AnchorPoint = props.anchorPoint,
|
||||||
|
|
||||||
|
ZIndex = props.zIndex,
|
||||||
|
LayoutOrder = props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, props[Roact.Children])
|
||||||
|
end
|
||||||
|
|
||||||
|
return SlicedImage
|
||||||
66
plugin/src/App/Components/Spinner.lua
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
|
||||||
|
local ROTATIONS_PER_SECOND = 1.75
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local Spinner = Roact.PureComponent:extend("Spinner")
|
||||||
|
|
||||||
|
function Spinner:init()
|
||||||
|
self.rotation, self.setRotation = Roact.createBinding(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Spinner:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Spinner.Background,
|
||||||
|
ImageColor3 = theme.Spinner.BackgroundColor,
|
||||||
|
ImageTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, 24, 0, 24),
|
||||||
|
Position = self.props.position,
|
||||||
|
AnchorPoint = self.props.anchorPoint,
|
||||||
|
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Foreground = e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Spinner.Foreground,
|
||||||
|
ImageColor3 = theme.Spinner.ForegroundColor,
|
||||||
|
ImageTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
Rotation = self.rotation:map(function(value)
|
||||||
|
return value * 360
|
||||||
|
end),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Spinner:didMount()
|
||||||
|
self.stepper = RunService.RenderStepped:Connect(function(deltaTime)
|
||||||
|
local rotation = self.rotation:getValue()
|
||||||
|
|
||||||
|
rotation = rotation + deltaTime * ROTATIONS_PER_SECOND
|
||||||
|
rotation = rotation % 1
|
||||||
|
|
||||||
|
self.setRotation(rotation)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Spinner:willUnmount()
|
||||||
|
self.stepper:Disconnect()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Spinner
|
||||||
7
plugin/src/App/Components/Studio/StudioPluginContext.lua
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local StudioPluginContext = Roact.createContext(nil)
|
||||||
|
|
||||||
|
return StudioPluginContext
|
||||||
84
plugin/src/App/Components/Studio/StudioPluginGui.lua
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
|
local StudioPluginContext = require(script.Parent.StudioPluginContext)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local StudioPluginGui = Roact.PureComponent:extend("StudioPluginGui")
|
||||||
|
|
||||||
|
StudioPluginGui.defaultProps = {
|
||||||
|
initDockState = Enum.InitialDockState.Right,
|
||||||
|
active = false,
|
||||||
|
overridePreviousState = false,
|
||||||
|
floatingSize = Vector2.new(0, 0),
|
||||||
|
minimumSize = Vector2.new(0, 0),
|
||||||
|
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||||
|
}
|
||||||
|
|
||||||
|
function StudioPluginGui:init()
|
||||||
|
local floatingSize = self.props.floatingSize
|
||||||
|
local minimumSize = self.props.minimumSize
|
||||||
|
|
||||||
|
local dockWidgetPluginGuiInfo = DockWidgetPluginGuiInfo.new(
|
||||||
|
self.props.initDockState,
|
||||||
|
self.props.active,
|
||||||
|
self.props.overridePreviousState,
|
||||||
|
floatingSize.X, floatingSize.Y,
|
||||||
|
minimumSize.X, minimumSize.Y
|
||||||
|
)
|
||||||
|
|
||||||
|
local pluginGui = self.props.plugin:CreateDockWidgetPluginGui(self.props.id, dockWidgetPluginGuiInfo)
|
||||||
|
|
||||||
|
pluginGui.Name = self.props.id
|
||||||
|
pluginGui.Title = self.props.title
|
||||||
|
pluginGui.ZIndexBehavior = self.props.zIndexBehavior
|
||||||
|
|
||||||
|
if self.props.onInitialState then
|
||||||
|
self.props.onInitialState(pluginGui.Enabled)
|
||||||
|
end
|
||||||
|
|
||||||
|
pluginGui:BindToClose(function()
|
||||||
|
if self.props.onClose then
|
||||||
|
self.props.onClose()
|
||||||
|
else
|
||||||
|
pluginGui.Enabled = false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.pluginGui = pluginGui
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginGui:render()
|
||||||
|
return e(Roact.Portal, {
|
||||||
|
target = self.pluginGui,
|
||||||
|
}, self.props[Roact.Children])
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginGui:didUpdate(lastProps)
|
||||||
|
if self.props.active ~= lastProps.active then
|
||||||
|
-- This is intentionally in didUpdate to make sure the initial active state
|
||||||
|
-- (if the PluginGui is open initially) is preserved.
|
||||||
|
self.pluginGui.Enabled = self.props.active
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginGui:willUnmount()
|
||||||
|
self.pluginGui:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function StudioPluginGuiWrapper(props)
|
||||||
|
return e(StudioPluginContext.Consumer, {
|
||||||
|
render = function(plugin)
|
||||||
|
return e(StudioPluginGui, Dictionary.merge(props, {
|
||||||
|
plugin = plugin,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return StudioPluginGuiWrapper
|
||||||
66
plugin/src/App/Components/Studio/StudioToggleButton.lua
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
|
local StudioToolbarContext = require(script.Parent.StudioToolbarContext)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local StudioToggleButton = Roact.Component:extend("StudioToggleButton")
|
||||||
|
|
||||||
|
StudioToggleButton.defaultProps = {
|
||||||
|
enabled = true,
|
||||||
|
active = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
function StudioToggleButton:init()
|
||||||
|
local button = self.props.toolbar:CreateButton(
|
||||||
|
self.props.name,
|
||||||
|
self.props.tooltip,
|
||||||
|
self.props.icon,
|
||||||
|
self.props.text
|
||||||
|
)
|
||||||
|
|
||||||
|
button.Click:Connect(function()
|
||||||
|
if self.props.onClick then
|
||||||
|
self.props.onClick()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
button.ClickableWhenViewportHidden = true
|
||||||
|
|
||||||
|
self.button = button
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioToggleButton:render()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioToggleButton:didUpdate(lastProps)
|
||||||
|
if self.props.enabled ~= lastProps.enabled then
|
||||||
|
self.button.Enabled = self.props.enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.props.active ~= lastProps.active then
|
||||||
|
self.button:SetActive(self.props.active)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioToggleButton:willUnmount()
|
||||||
|
self.button:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function StudioToggleButtonWrapper(props)
|
||||||
|
return e(StudioToolbarContext.Consumer, {
|
||||||
|
render = function(toolbar)
|
||||||
|
return e(StudioToggleButton, Dictionary.merge(props, {
|
||||||
|
toolbar = toolbar,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return StudioToggleButtonWrapper
|
||||||
45
plugin/src/App/Components/Studio/StudioToolbar.lua
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
|
local StudioToolbarContext = require(script.Parent.StudioToolbarContext)
|
||||||
|
local StudioPluginContext = require(script.Parent.StudioPluginContext)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local StudioToolbar = Roact.Component:extend("StudioToolbar")
|
||||||
|
|
||||||
|
function StudioToolbar:init()
|
||||||
|
self.toolbar = self.props.plugin:CreateToolbar(self.props.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioToolbar:render()
|
||||||
|
return e(StudioToolbarContext.Provider, {
|
||||||
|
value = self.toolbar,
|
||||||
|
}, self.props[Roact.Children])
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioToolbar:didUpdate(lastProps)
|
||||||
|
if self.props.name ~= lastProps.name then
|
||||||
|
self.toolbar.Name = self.props.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioToolbar:willUnmount()
|
||||||
|
self.toolbar:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function StudioToolbarWrapper(props)
|
||||||
|
return e(StudioPluginContext.Consumer, {
|
||||||
|
render = function(plugin)
|
||||||
|
return e(StudioToolbar, Dictionary.merge(props, {
|
||||||
|
plugin = plugin,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return StudioToolbarWrapper
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local StudioToolbarContext = Roact.createContext(nil)
|
||||||
|
|
||||||
|
return StudioToolbarContext
|
||||||
137
plugin/src/App/Components/TextButton.lua
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Flipper = require(Rojo.Flipper)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local SlicedImage = require(script.Parent.SlicedImage)
|
||||||
|
local TouchRipple = require(script.Parent.TouchRipple)
|
||||||
|
|
||||||
|
local SPRING_PROPS = {
|
||||||
|
frequency = 5,
|
||||||
|
dampingRatio = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local TextButton = Roact.Component:extend("TextButton")
|
||||||
|
|
||||||
|
function TextButton:init()
|
||||||
|
self.motor = Flipper.GroupMotor.new({
|
||||||
|
hover = 0,
|
||||||
|
enabled = self.props.enabled and 1 or 0,
|
||||||
|
})
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TextButton:didUpdate(lastProps)
|
||||||
|
if lastProps.enabled ~= self.props.enabled then
|
||||||
|
self.motor:setGoal({
|
||||||
|
enabled = Flipper.Spring.new(self.props.enabled and 1 or 0),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TextButton:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
local textSize = TextService:GetTextSize(
|
||||||
|
self.props.text, 18, Enum.Font.GothamSemibold,
|
||||||
|
Vector2.new(math.huge, math.huge)
|
||||||
|
)
|
||||||
|
|
||||||
|
local style = self.props.style
|
||||||
|
|
||||||
|
theme = theme.Button[style]
|
||||||
|
|
||||||
|
local bindingHover = bindingUtil.deriveProperty(self.binding, "hover")
|
||||||
|
local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled")
|
||||||
|
|
||||||
|
return e("ImageButton", {
|
||||||
|
Size = UDim2.new(0, 15 + textSize.X + 15, 0, 34),
|
||||||
|
Position = self.props.position,
|
||||||
|
AnchorPoint = self.props.anchorPoint,
|
||||||
|
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = self.props.onClick,
|
||||||
|
|
||||||
|
[Roact.Event.MouseEnter] = function()
|
||||||
|
self.motor:setGoal({
|
||||||
|
hover = Flipper.Spring.new(1, SPRING_PROPS),
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
|
||||||
|
[Roact.Event.MouseLeave] = function()
|
||||||
|
self.motor:setGoal({
|
||||||
|
hover = Flipper.Spring.new(0, SPRING_PROPS),
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
TouchRipple = e(TouchRipple, {
|
||||||
|
color = theme.ActionFillColor,
|
||||||
|
transparency = self.props.transparency:map(function(value)
|
||||||
|
return bindingUtil.blendAlpha({ theme.ActionFillTransparency, value })
|
||||||
|
end),
|
||||||
|
zIndex = 2,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Text = e("TextLabel", {
|
||||||
|
Text = self.props.text,
|
||||||
|
Font = Enum.Font.GothamSemibold,
|
||||||
|
TextSize = 18,
|
||||||
|
TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.TextColor, theme.Disabled.TextColor),
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Border = style == "Bordered" and e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBorder,
|
||||||
|
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
zIndex = 0,
|
||||||
|
}),
|
||||||
|
|
||||||
|
HoverOverlay = e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBackground,
|
||||||
|
color = theme.ActionFillColor,
|
||||||
|
transparency = Roact.joinBindings({
|
||||||
|
hover = bindingHover:map(function(value)
|
||||||
|
return 1 - value
|
||||||
|
end),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
}):map(function(values)
|
||||||
|
return bindingUtil.blendAlpha({ theme.ActionFillTransparency, values.hover, values.transparency })
|
||||||
|
end),
|
||||||
|
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
zIndex = -1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Background = style == "Solid" and e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBackground,
|
||||||
|
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BackgroundColor, theme.Disabled.BackgroundColor),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
zIndex = -2,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return TextButton
|
||||||
145
plugin/src/App/Components/TouchRipple.lua
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Flipper = require(Rojo.Flipper)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local EXPAND_SPRING = {
|
||||||
|
frequency = 7,
|
||||||
|
dampingRatio = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
local TouchRipple = Roact.Component:extend("TouchRipple")
|
||||||
|
|
||||||
|
function TouchRipple:init()
|
||||||
|
self.ref = Roact.createRef()
|
||||||
|
|
||||||
|
self.motor = Flipper.GroupMotor.new({
|
||||||
|
scale = 0,
|
||||||
|
opacity = 0,
|
||||||
|
})
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
|
||||||
|
self.position, self.setPosition = Roact.createBinding(
|
||||||
|
Vector2.new(0, 0)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TouchRipple:reset()
|
||||||
|
self.motor:setGoal({
|
||||||
|
scale = Flipper.Instant.new(0),
|
||||||
|
opacity = Flipper.Instant.new(0),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Forces motor to update
|
||||||
|
self.motor:step(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TouchRipple:calculateRadius(position)
|
||||||
|
local container = self.ref.current
|
||||||
|
|
||||||
|
if container then
|
||||||
|
local corner = Vector2.new(
|
||||||
|
math.floor((1 - position.X) + 0.5),
|
||||||
|
math.floor((1 - position.Y) + 0.5)
|
||||||
|
)
|
||||||
|
|
||||||
|
local size = container.AbsoluteSize
|
||||||
|
local ratio = size / math.min(size.X, size.Y)
|
||||||
|
|
||||||
|
return ((corner * ratio) - (position * ratio)).Magnitude
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TouchRipple:render()
|
||||||
|
local scale = bindingUtil.deriveProperty(self.binding, "scale")
|
||||||
|
local transparency = bindingUtil.deriveProperty(self.binding, "opacity"):map(function(value)
|
||||||
|
return 1 - value
|
||||||
|
end)
|
||||||
|
|
||||||
|
transparency = Roact.joinBindings({
|
||||||
|
transparency,
|
||||||
|
self.props.transparency,
|
||||||
|
}):map(bindingUtil.blendAlpha)
|
||||||
|
|
||||||
|
return Roact.createElement("Frame", {
|
||||||
|
ClipsDescendants = true,
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
ZIndex = self.props.zIndex,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Ref] = self.ref,
|
||||||
|
|
||||||
|
[Roact.Event.InputBegan] = function(object, input)
|
||||||
|
if input.UserInputType == Enum.UserInputType.MouseButton1 then
|
||||||
|
self:reset()
|
||||||
|
|
||||||
|
local position = Vector2.new(input.Position.X, input.Position.Y)
|
||||||
|
local relativePosition = (position - object.AbsolutePosition) / object.AbsoluteSize
|
||||||
|
|
||||||
|
self.setPosition(relativePosition)
|
||||||
|
|
||||||
|
self.motor:setGoal({
|
||||||
|
scale = Flipper.Spring.new(1, EXPAND_SPRING),
|
||||||
|
opacity = Flipper.Spring.new(1, EXPAND_SPRING),
|
||||||
|
})
|
||||||
|
|
||||||
|
input:GetPropertyChangedSignal("UserInputState"):Connect(function()
|
||||||
|
local userInputState = input.UserInputState
|
||||||
|
|
||||||
|
if
|
||||||
|
userInputState == Enum.UserInputState.Cancel
|
||||||
|
or userInputState == Enum.UserInputState.End
|
||||||
|
then
|
||||||
|
self.motor:setGoal({
|
||||||
|
opacity = Flipper.Spring.new(0, {
|
||||||
|
frequency = 5,
|
||||||
|
dampingRatio = 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Circle = Roact.createElement("ImageLabel", {
|
||||||
|
Image = Assets.Images.Circles[500],
|
||||||
|
ImageColor3 = self.props.color,
|
||||||
|
ImageTransparency = transparency,
|
||||||
|
|
||||||
|
Size = Roact.joinBindings({
|
||||||
|
scale = scale,
|
||||||
|
position = self.position,
|
||||||
|
}):map(function(values)
|
||||||
|
local targetSize = self:calculateRadius(values.position) * 2
|
||||||
|
local currentSize = targetSize * values.scale
|
||||||
|
|
||||||
|
local container = self.ref.current
|
||||||
|
|
||||||
|
if container then
|
||||||
|
local containerSize = container.AbsoluteSize
|
||||||
|
local containerAspect = containerSize.X / containerSize.Y
|
||||||
|
|
||||||
|
return UDim2.new(
|
||||||
|
currentSize / math.max(containerAspect, 1), 0,
|
||||||
|
currentSize * math.min(containerAspect, 1), 0
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end),
|
||||||
|
|
||||||
|
Position = self.position:map(function(value)
|
||||||
|
return UDim2.new(value.X, 0, value.Y, 0)
|
||||||
|
end),
|
||||||
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return TouchRipple
|
||||||
70
plugin/src/App/Page.lua
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Flipper = require(Rojo.Flipper)
|
||||||
|
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
|
local bindingUtil = require(script.Parent.bindingUtil)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local Page = Roact.Component:extend("Page")
|
||||||
|
|
||||||
|
function Page:init()
|
||||||
|
self:setState({
|
||||||
|
rendered = self.props.active
|
||||||
|
})
|
||||||
|
|
||||||
|
self.motor = Flipper.SingleMotor.new(self.props.active and 1 or 0)
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
|
||||||
|
self.motor:onStep(function(value)
|
||||||
|
local rendered = value > 0
|
||||||
|
|
||||||
|
self:setState(function(state)
|
||||||
|
if state.rendered ~= rendered then
|
||||||
|
return {
|
||||||
|
rendered = rendered,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Page:render()
|
||||||
|
if not self.state.rendered then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local transparency = self.binding:map(function(value)
|
||||||
|
return 1 - value
|
||||||
|
end)
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
Position = transparency:map(function(value)
|
||||||
|
value = self.props.active and value or -value
|
||||||
|
return UDim2.new(0, value * 30, 0, 0)
|
||||||
|
end),
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Component = e(self.props.component, Dictionary.merge(self.props, {
|
||||||
|
transparency = transparency,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Page:didUpdate(lastProps)
|
||||||
|
if self.props.active ~= lastProps.active then
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(self.props.active and 1 or 0, {
|
||||||
|
frequency = 6,
|
||||||
|
dampingRatio = 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Page
|
||||||
125
plugin/src/App/StatusPages/Connected.lua
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
|
||||||
|
local Header = require(Plugin.App.Components.Header)
|
||||||
|
local IconButton = require(Plugin.App.Components.IconButton)
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function ConnectionDetails(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e(BorderedContainer, {
|
||||||
|
transparency = props.transparency,
|
||||||
|
size = UDim2.new(1, 0, 0, 70),
|
||||||
|
layoutOrder = props.layoutOrder,
|
||||||
|
}, {
|
||||||
|
TextContainer = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1
|
||||||
|
}, {
|
||||||
|
ProjectName = e("TextLabel", {
|
||||||
|
Text = props.projectName,
|
||||||
|
Font = Enum.Font.GothamBold,
|
||||||
|
TextSize = 20,
|
||||||
|
TextColor3 = theme.ConnectionDetails.ProjectNameColor,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 0, 20),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Address = e("TextLabel", {
|
||||||
|
Text = props.address,
|
||||||
|
Font = Enum.Font.Code,
|
||||||
|
TextSize = 15,
|
||||||
|
TextColor3 = theme.ConnectionDetails.AddressColor,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 0, 15),
|
||||||
|
|
||||||
|
LayoutOrder = 2,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 6),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Disconnect = e(IconButton, {
|
||||||
|
icon = Assets.Images.Icons.Close,
|
||||||
|
iconSize = 24,
|
||||||
|
color = theme.ConnectionDetails.DisconnectColor,
|
||||||
|
transparency = props.transparency,
|
||||||
|
|
||||||
|
position = UDim2.new(1, 0, 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(1, 0.5),
|
||||||
|
|
||||||
|
onClick = props.onDisconnect,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 17),
|
||||||
|
PaddingRight = UDim.new(0, 15),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ConnectedPage = Roact.Component:extend("ConnectedPage")
|
||||||
|
|
||||||
|
function ConnectedPage:render()
|
||||||
|
return Roact.createFragment({
|
||||||
|
Header = e(Header, {
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ConnectionDetails = e(ConnectionDetails, {
|
||||||
|
projectName = self.state.projectName,
|
||||||
|
address = self.state.address,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
|
||||||
|
onDisconnect = self.props.onDisconnect,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 20),
|
||||||
|
PaddingRight = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function ConnectedPage.getDerivedStateFromProps(props)
|
||||||
|
-- If projectName or address ever get removed from props, make sure we still have
|
||||||
|
-- the properties! The component still needs to have its data for it to be properly
|
||||||
|
-- animated out without the labels changing.
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectName = props.projectName,
|
||||||
|
address = props.address,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return ConnectedPage
|
||||||
20
plugin/src/App/StatusPages/Connecting.lua
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Spinner = require(Plugin.App.Components.Spinner)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local ConnectingPage = Roact.Component:extend("ConnectingPage")
|
||||||
|
|
||||||
|
function ConnectingPage:render()
|
||||||
|
return e(Spinner, {
|
||||||
|
position = UDim2.new(0.5, 0, 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(0.5, 0.5),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return ConnectingPage
|
||||||
153
plugin/src/App/StatusPages/Error.lua
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
|
||||||
|
local TextButton = require(Plugin.App.Components.TextButton)
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local ERROR_PADDING = Vector2.new(13, 10)
|
||||||
|
|
||||||
|
local Error = Roact.Component:extend("Error")
|
||||||
|
|
||||||
|
function Error:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Error:render()
|
||||||
|
return e(BorderedContainer, {
|
||||||
|
size = Roact.joinBindings({
|
||||||
|
containerSize = self.props.containerSize,
|
||||||
|
contentSize = self.contentSize,
|
||||||
|
}):map(function(values)
|
||||||
|
local maximumSize = values.containerSize
|
||||||
|
maximumSize -= Vector2.new(14, 14) * 2 -- Page padding
|
||||||
|
maximumSize -= Vector2.new(0, 34 + 10) -- Buttons and spacing
|
||||||
|
|
||||||
|
local outerSize = values.contentSize + ERROR_PADDING * 2
|
||||||
|
|
||||||
|
return UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y))
|
||||||
|
end),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
}, {
|
||||||
|
ScrollingFrame = e(ScrollingFrame, {
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
contentSize = self.contentSize:map(function(value)
|
||||||
|
return value + ERROR_PADDING * 2
|
||||||
|
end),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteSize] = function(object)
|
||||||
|
local containerSize = object.AbsoluteSize - ERROR_PADDING * 2
|
||||||
|
|
||||||
|
local textBounds = TextService:GetTextSize(
|
||||||
|
self.props.errorMessage, 16, Enum.Font.Code,
|
||||||
|
Vector2.new(containerSize.X, math.huge)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.setContentSize(Vector2.new(containerSize.X, textBounds.Y))
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
ErrorMessage = Theme.with(function(theme)
|
||||||
|
return e("TextLabel", {
|
||||||
|
Text = self.props.errorMessage,
|
||||||
|
Font = Enum.Font.Code,
|
||||||
|
TextSize = 16,
|
||||||
|
TextColor3 = theme.ErrorColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextYAlignment = Enum.TextYAlignment.Top,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
TextWrapped = true,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
})
|
||||||
|
end),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, ERROR_PADDING.X),
|
||||||
|
PaddingRight = UDim.new(0, ERROR_PADDING.X),
|
||||||
|
PaddingTop = UDim.new(0, ERROR_PADDING.Y),
|
||||||
|
PaddingBottom = UDim.new(0, ERROR_PADDING.Y),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local ErrorPage = Roact.Component:extend("ErrorPage")
|
||||||
|
|
||||||
|
function ErrorPage:init()
|
||||||
|
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
function ErrorPage:render()
|
||||||
|
return Roact.createElement("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteSize] = function(object)
|
||||||
|
self.setContainerSize(object.AbsoluteSize)
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Error = e(Error, {
|
||||||
|
errorMessage = self.state.errorMessage,
|
||||||
|
containerSize = self.containerSize,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Buttons = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 35),
|
||||||
|
LayoutOrder = 2,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Close = e(TextButton, {
|
||||||
|
text = "Okay",
|
||||||
|
style = "Bordered",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
onClick = self.props.onClose,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 14),
|
||||||
|
PaddingRight = UDim.new(0, 14),
|
||||||
|
PaddingTop = UDim.new(0, 14),
|
||||||
|
PaddingBottom = UDim.new(0, 14),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function ErrorPage.getDerivedStateFromProps(props)
|
||||||
|
-- If errorMessage ever gets removed from props, make sure we still have the
|
||||||
|
-- property! The component still needs to have its data for it to be properly
|
||||||
|
-- animated out without the labels changing.
|
||||||
|
|
||||||
|
return {
|
||||||
|
errorMessage = props.errorMessage,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return ErrorPage
|
||||||
154
plugin/src/App/StatusPages/NotConnected.lua
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Config = require(Plugin.Config)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
local TextButton = require(Plugin.App.Components.TextButton)
|
||||||
|
local Header = require(Plugin.App.Components.Header)
|
||||||
|
|
||||||
|
local PORT_WIDTH = 74
|
||||||
|
local DIVIDER_WIDTH = 1
|
||||||
|
local HOST_OFFSET = 12
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function AddressEntry(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e(BorderedContainer, {
|
||||||
|
transparency = props.transparency,
|
||||||
|
size = UDim2.new(1, 0, 0, 36),
|
||||||
|
layoutOrder = props.layoutOrder,
|
||||||
|
}, {
|
||||||
|
Host = e("TextBox", {
|
||||||
|
Text = "",
|
||||||
|
Font = Enum.Font.Code,
|
||||||
|
TextSize = 18,
|
||||||
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
PlaceholderText = Config.defaultHost,
|
||||||
|
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, -(HOST_OFFSET + DIVIDER_WIDTH + PORT_WIDTH), 1, 0),
|
||||||
|
Position = UDim2.new(0, HOST_OFFSET, 0, 0),
|
||||||
|
|
||||||
|
ClipsDescendants = true,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Ref] = props.hostRef,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Port = e("TextBox", {
|
||||||
|
Text = "",
|
||||||
|
Font = Enum.Font.Code,
|
||||||
|
TextSize = 18,
|
||||||
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
PlaceholderText = Config.defaultPort,
|
||||||
|
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, PORT_WIDTH, 1, 0),
|
||||||
|
Position = UDim2.new(1, 0, 0, 0),
|
||||||
|
AnchorPoint = Vector2.new(1, 0),
|
||||||
|
|
||||||
|
ClipsDescendants = true,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Ref] = props.portRef,
|
||||||
|
|
||||||
|
[Roact.Change.Text] = function(object)
|
||||||
|
local text = object.Text
|
||||||
|
text = text:gsub("%D", "")
|
||||||
|
object.Text = text
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Divider = e("Frame", {
|
||||||
|
BackgroundColor3 = theme.BorderedContainer.BorderColor,
|
||||||
|
BackgroundTransparency = props.transparency,
|
||||||
|
Size = UDim2.new(0, DIVIDER_WIDTH, 1, 0),
|
||||||
|
AnchorPoint = Vector2.new(1, 0),
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local NotConnectedPage = Roact.Component:extend("NotConnectedPage")
|
||||||
|
|
||||||
|
function NotConnectedPage:init()
|
||||||
|
self.hostRef = Roact.createRef()
|
||||||
|
self.portRef = Roact.createRef()
|
||||||
|
end
|
||||||
|
|
||||||
|
function NotConnectedPage:render()
|
||||||
|
return Roact.createFragment({
|
||||||
|
Header = e(Header, {
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
AddressEntry = e(AddressEntry, {
|
||||||
|
hostRef = self.hostRef,
|
||||||
|
portRef = self.portRef,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Buttons = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 34),
|
||||||
|
LayoutOrder = 3,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Settings = e(TextButton, {
|
||||||
|
text = "Settings",
|
||||||
|
style = "Bordered",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
onClick = self.props.onNavigateSettings,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Connect = e(TextButton, {
|
||||||
|
text = "Connect",
|
||||||
|
style = "Solid",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
onClick = function()
|
||||||
|
local hostText = self.hostRef.current.Text
|
||||||
|
local portText = self.portRef.current.Text
|
||||||
|
|
||||||
|
self.props.onConnect(
|
||||||
|
#hostText > 0 and hostText or Config.defaultHost,
|
||||||
|
#portText > 0 and portText or Config.defaultPort
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 20),
|
||||||
|
PaddingRight = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return NotConnectedPage
|
||||||
230
plugin/src/App/StatusPages/Settings.lua
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local PluginSettings = require(Plugin.App.PluginSettings)
|
||||||
|
|
||||||
|
local Checkbox = require(Plugin.App.Components.Checkbox)
|
||||||
|
local IconButton = require(Plugin.App.Components.IconButton)
|
||||||
|
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local DIVIDER_FADE_SIZE = 0.1
|
||||||
|
|
||||||
|
local function getTextBounds(text, textSize, font, lineHeight, bounds)
|
||||||
|
local textBounds = TextService:GetTextSize(text, textSize, font, bounds)
|
||||||
|
|
||||||
|
local lineCount = textBounds.Y / textSize
|
||||||
|
local lineHeightAbsolute = textSize * lineHeight
|
||||||
|
|
||||||
|
return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Navbar(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
theme = theme.Settings.Navbar
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 46),
|
||||||
|
LayoutOrder = props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Back = e(IconButton, {
|
||||||
|
icon = Assets.Images.Icons.Back,
|
||||||
|
iconSize = 24,
|
||||||
|
color = theme.BackButtonColor,
|
||||||
|
transparency = props.transparency,
|
||||||
|
|
||||||
|
position = UDim2.new(0, 0, 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(0, 0.5),
|
||||||
|
|
||||||
|
onClick = props.onBack,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Text = e("TextLabel", {
|
||||||
|
Text = "Settings",
|
||||||
|
Font = Enum.Font.Gotham,
|
||||||
|
TextSize = 18,
|
||||||
|
TextColor3 = theme.TextColor,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Setting = Roact.Component:extend("Setting")
|
||||||
|
|
||||||
|
function Setting:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Setting:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
theme = theme.Settings
|
||||||
|
|
||||||
|
return PluginSettings.with(function(settings)
|
||||||
|
return e("Frame", {
|
||||||
|
Size = self.contentSize:map(function(value)
|
||||||
|
return UDim2.new(1, 0, 0, 20 + value.Y + 20)
|
||||||
|
end),
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteSize] = function(object)
|
||||||
|
self.setContainerSize(object.AbsoluteSize)
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Checkbox = e(Checkbox, {
|
||||||
|
active = settings:get(self.props.id),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
position = UDim2.new(1, 0, 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(1, 0.5),
|
||||||
|
onClick = function()
|
||||||
|
local currentValue = settings:get(self.props.id)
|
||||||
|
settings:set(self.props.id, not currentValue)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Text = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Name = e("TextLabel", {
|
||||||
|
Text = self.props.name,
|
||||||
|
Font = Enum.Font.GothamBold,
|
||||||
|
TextSize = 17,
|
||||||
|
TextColor3 = theme.Setting.NameColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 0, 17),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Description = e("TextLabel", {
|
||||||
|
Text = self.props.description,
|
||||||
|
Font = Enum.Font.Gotham,
|
||||||
|
LineHeight = 1.2,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
TextWrapped = true,
|
||||||
|
|
||||||
|
Size = self.containerSize:map(function(value)
|
||||||
|
local textBounds = getTextBounds(
|
||||||
|
self.props.description, 14, Enum.Font.Gotham, 1.2,
|
||||||
|
Vector2.new(value.X - 50, math.huge)
|
||||||
|
)
|
||||||
|
return UDim2.new(1, -50, 0, textBounds.Y)
|
||||||
|
end),
|
||||||
|
|
||||||
|
LayoutOrder = 2,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 6),
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteContentSize] = function(object)
|
||||||
|
self.setContentSize(object.AbsoluteContentSize)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingTop = UDim.new(0, 20),
|
||||||
|
PaddingBottom = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Divider = e("Frame", {
|
||||||
|
BackgroundColor3 = theme.DividerColor,
|
||||||
|
BackgroundTransparency = self.props.transparency,
|
||||||
|
Size = UDim2.new(1, 0, 0, 1),
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
}, {
|
||||||
|
Gradient = e("UIGradient", {
|
||||||
|
Transparency = NumberSequence.new({
|
||||||
|
NumberSequenceKeypoint.new(0, 1),
|
||||||
|
NumberSequenceKeypoint.new(DIVIDER_FADE_SIZE, 0),
|
||||||
|
NumberSequenceKeypoint.new(1 - DIVIDER_FADE_SIZE, 0),
|
||||||
|
NumberSequenceKeypoint.new(1, 1),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local SettingsPage = Roact.Component:extend("SettingsPage")
|
||||||
|
|
||||||
|
function SettingsPage:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
function SettingsPage:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
theme = theme.Settings
|
||||||
|
|
||||||
|
return e(ScrollingFrame, {
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
contentSize = self.contentSize,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
}, {
|
||||||
|
Navbar = e(Navbar, {
|
||||||
|
onBack = self.props.onBack,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 0,
|
||||||
|
}),
|
||||||
|
|
||||||
|
OpenScriptsExternally = e(Setting, {
|
||||||
|
id = "openScriptsExternally",
|
||||||
|
name = "Open Scripts Externally",
|
||||||
|
description = "Attempt to open scripts in an external editor",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
TwoWaySync = e(Setting, {
|
||||||
|
id = "twoWaySync",
|
||||||
|
name = "Two-Way Sync",
|
||||||
|
description = "Editing files in Studio will sync them into the filesystem",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteContentSize] = function(object)
|
||||||
|
self.setContentSize(object.AbsoluteContentSize)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 20),
|
||||||
|
PaddingRight = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return SettingsPage
|
||||||
7
plugin/src/App/StatusPages/init.lua
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
return {
|
||||||
|
NotConnected = require(script.NotConnected),
|
||||||
|
Settings = require(script.Settings),
|
||||||
|
Connecting = require(script.Connecting),
|
||||||
|
Connected = require(script.Connected),
|
||||||
|
Error = require(script.Error),
|
||||||
|
}
|
||||||
239
plugin/src/App/Theme.lua
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
--[[
|
||||||
|
Theming system taking advantage of Roact's new context API.
|
||||||
|
Doesn't use colors provided by Studio and instead just branches on theme
|
||||||
|
name. This isn't exactly best practice.
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- Studio does not exist outside Roblox Studio, so we'll lazily initialize it
|
||||||
|
-- when possible.
|
||||||
|
local _Studio
|
||||||
|
local function getStudio()
|
||||||
|
if _Studio == nil then
|
||||||
|
_Studio = settings():GetService("Studio")
|
||||||
|
end
|
||||||
|
|
||||||
|
return _Studio
|
||||||
|
end
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Log = require(Rojo.Log)
|
||||||
|
|
||||||
|
local strict = require(script.Parent.Parent.strict)
|
||||||
|
|
||||||
|
-- Copying hex colors back and forth from design programs is faster
|
||||||
|
local function hexColor(decimal)
|
||||||
|
local red = bit32.band(bit32.rshift(decimal, 16), 2^8 - 1)
|
||||||
|
local green = bit32.band(bit32.rshift(decimal, 8), 2^8 - 1)
|
||||||
|
local blue = bit32.band(decimal, 2^8 - 1)
|
||||||
|
|
||||||
|
return Color3.fromRGB(red, green, blue)
|
||||||
|
end
|
||||||
|
|
||||||
|
local BRAND_COLOR = hexColor(0xE13835)
|
||||||
|
|
||||||
|
local lightTheme = strict("LightTheme", {
|
||||||
|
BackgroundColor = hexColor(0xF0F0F0),
|
||||||
|
Button = {
|
||||||
|
Solid = {
|
||||||
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
|
ActionFillTransparency = 0.8,
|
||||||
|
Enabled = {
|
||||||
|
TextColor = hexColor(0xFFFFFF),
|
||||||
|
BackgroundColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
Disabled = {
|
||||||
|
TextColor = hexColor(0xFFFFFF),
|
||||||
|
BackgroundColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bordered = {
|
||||||
|
ActionFillColor = hexColor(0x000000),
|
||||||
|
ActionFillTransparency = 0.9,
|
||||||
|
Enabled = {
|
||||||
|
TextColor = hexColor(0x393939),
|
||||||
|
BorderColor = hexColor(0xACACAC),
|
||||||
|
},
|
||||||
|
Disabled = {
|
||||||
|
TextColor = hexColor(0x393939),
|
||||||
|
BorderColor = hexColor(0xACACAC),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Checkbox = {
|
||||||
|
Active = {
|
||||||
|
IconColor = hexColor(0xFFFFFF),
|
||||||
|
BackgroundColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
Inactive = {
|
||||||
|
IconColor = hexColor(0xCACACA),
|
||||||
|
BorderColor = hexColor(0xAFAFAF),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AddressEntry = {
|
||||||
|
TextColor = hexColor(0x000000),
|
||||||
|
PlaceholderColor = hexColor(0x8C8C8C)
|
||||||
|
},
|
||||||
|
BorderedContainer = {
|
||||||
|
BorderColor = hexColor(0xCBCBCB),
|
||||||
|
BackgroundColor = hexColor(0xE0E0E0),
|
||||||
|
},
|
||||||
|
Spinner = {
|
||||||
|
ForegroundColor = BRAND_COLOR,
|
||||||
|
BackgroundColor = hexColor(0xE0E0E0),
|
||||||
|
},
|
||||||
|
ConnectionDetails = {
|
||||||
|
ProjectNameColor = hexColor(0x00000),
|
||||||
|
AddressColor = hexColor(0x00000),
|
||||||
|
DisconnectColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
Settings = {
|
||||||
|
DividerColor = hexColor(0xCBCBCB),
|
||||||
|
Navbar = {
|
||||||
|
BackButtonColor = hexColor(0x000000),
|
||||||
|
TextColor = hexColor(0x000000),
|
||||||
|
},
|
||||||
|
Setting = {
|
||||||
|
NameColor = hexColor(0x000000),
|
||||||
|
DescriptionColor = hexColor(0x5F5F5F),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Header = {
|
||||||
|
LogoColor = BRAND_COLOR,
|
||||||
|
VersionColor = hexColor(0x727272),
|
||||||
|
},
|
||||||
|
ErrorColor = hexColor(0x000000),
|
||||||
|
ScrollBarColor = hexColor(0x000000),
|
||||||
|
})
|
||||||
|
|
||||||
|
local darkTheme = strict("DarkTheme", {
|
||||||
|
BackgroundColor = hexColor(0x272727),
|
||||||
|
Button = {
|
||||||
|
Solid = {
|
||||||
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
|
ActionFillTransparency = 0.8,
|
||||||
|
Enabled = {
|
||||||
|
TextColor = hexColor(0xFFFFFF),
|
||||||
|
BackgroundColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
Disabled = {
|
||||||
|
TextColor = hexColor(0xFFFFFF),
|
||||||
|
BackgroundColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bordered = {
|
||||||
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
|
ActionFillTransparency = 0.9,
|
||||||
|
Enabled = {
|
||||||
|
TextColor = hexColor(0xDBDBDB),
|
||||||
|
BorderColor = hexColor(0x535353),
|
||||||
|
},
|
||||||
|
Disabled = {
|
||||||
|
TextColor = hexColor(0xDBDBDB),
|
||||||
|
BorderColor = hexColor(0x535353),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Checkbox = {
|
||||||
|
Active = {
|
||||||
|
IconColor = hexColor(0xFFFFFF),
|
||||||
|
BackgroundColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
|
Inactive = {
|
||||||
|
IconColor = hexColor(0x484848),
|
||||||
|
BorderColor = hexColor(0x5A5A5A),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AddressEntry = {
|
||||||
|
TextColor = hexColor(0xFFFFFF),
|
||||||
|
PlaceholderColor = hexColor(0x717171)
|
||||||
|
},
|
||||||
|
BorderedContainer = {
|
||||||
|
BorderColor = hexColor(0x535353),
|
||||||
|
BackgroundColor = hexColor(0x323232),
|
||||||
|
},
|
||||||
|
Spinner = {
|
||||||
|
ForegroundColor = BRAND_COLOR,
|
||||||
|
BackgroundColor = hexColor(0x323232),
|
||||||
|
},
|
||||||
|
ConnectionDetails = {
|
||||||
|
ProjectNameColor = hexColor(0xFFFFFF),
|
||||||
|
AddressColor = hexColor(0xFFFFFF),
|
||||||
|
DisconnectColor = hexColor(0xFFFFFF),
|
||||||
|
},
|
||||||
|
Settings = {
|
||||||
|
DividerColor = hexColor(0x535353),
|
||||||
|
Navbar = {
|
||||||
|
BackButtonColor = hexColor(0xFFFFFF),
|
||||||
|
TextColor = hexColor(0xFFFFFF),
|
||||||
|
},
|
||||||
|
Setting = {
|
||||||
|
NameColor = hexColor(0xFFFFFF),
|
||||||
|
DescriptionColor = hexColor(0xD3D3D3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Header = {
|
||||||
|
LogoColor = BRAND_COLOR,
|
||||||
|
VersionColor = hexColor(0xD3D3D3)
|
||||||
|
},
|
||||||
|
ErrorColor = hexColor(0xFFFFFF),
|
||||||
|
ScrollBarColor = hexColor(0xFFFFFF),
|
||||||
|
})
|
||||||
|
|
||||||
|
local Context = Roact.createContext(lightTheme)
|
||||||
|
|
||||||
|
local StudioProvider = Roact.Component:extend("StudioProvider")
|
||||||
|
|
||||||
|
-- Pull the current theme from Roblox Studio and update state with it.
|
||||||
|
function StudioProvider:updateTheme()
|
||||||
|
local studioTheme = getStudio().Theme
|
||||||
|
|
||||||
|
if studioTheme.Name == "Light" then
|
||||||
|
self:setState({
|
||||||
|
theme = lightTheme,
|
||||||
|
})
|
||||||
|
elseif studioTheme.Name == "Dark" then
|
||||||
|
self:setState({
|
||||||
|
theme = darkTheme,
|
||||||
|
})
|
||||||
|
else
|
||||||
|
Log.warn("Unexpected theme '{}'' -- falling back to light theme!", studioTheme.Name)
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
theme = lightTheme,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioProvider:init()
|
||||||
|
self:updateTheme()
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioProvider:render()
|
||||||
|
return Roact.createElement(Context.Provider, {
|
||||||
|
value = self.state.theme,
|
||||||
|
}, self.props[Roact.Children])
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioProvider:didMount()
|
||||||
|
self.connection = getStudio().ThemeChanged:Connect(function()
|
||||||
|
self:updateTheme()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioProvider:willUnmount()
|
||||||
|
self.connection:Disconnect()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function with(callback)
|
||||||
|
return Roact.createElement(Context.Consumer, {
|
||||||
|
render = callback,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
StudioProvider = StudioProvider,
|
||||||
|
Consumer = Context.Consumer,
|
||||||
|
with = with,
|
||||||
|
}
|
||||||
58
plugin/src/App/bindingUtil.lua
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Log = require(Rojo.Log)
|
||||||
|
|
||||||
|
local LERP_DATA_TYPES = {
|
||||||
|
Color3 = true,
|
||||||
|
UDim = true,
|
||||||
|
UDim2 = true,
|
||||||
|
Vector2 = true,
|
||||||
|
Vector3 = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function fromMotor(motor)
|
||||||
|
local motorBinding, setMotorBinding = Roact.createBinding(motor:getValue())
|
||||||
|
motor:onStep(setMotorBinding)
|
||||||
|
return motorBinding
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mapLerp(binding, value1, value2)
|
||||||
|
local valueType = typeof(value1)
|
||||||
|
if valueType ~= typeof(value2) then
|
||||||
|
Log.error("Type mismatch between values ({}, {}})", valueType, typeof(value2))
|
||||||
|
end
|
||||||
|
|
||||||
|
return binding:map(function(position)
|
||||||
|
if valueType == "number" then
|
||||||
|
return value1 - (value2 - value1) * position
|
||||||
|
elseif LERP_DATA_TYPES[valueType] then
|
||||||
|
return value1:lerp(value2, position)
|
||||||
|
else
|
||||||
|
Log.error("Unable to interpolate type {}", valueType)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function deriveProperty(binding, propertyName)
|
||||||
|
return binding:map(function(values)
|
||||||
|
return values[propertyName]
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function blendAlpha(alphaValues)
|
||||||
|
local alpha = 0
|
||||||
|
|
||||||
|
for _, value in pairs(alphaValues) do
|
||||||
|
alpha = alpha + (1 - alpha) * value
|
||||||
|
end
|
||||||
|
|
||||||
|
return alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
fromMotor = fromMotor,
|
||||||
|
mapLerp = mapLerp,
|
||||||
|
deriveProperty = deriveProperty,
|
||||||
|
blendAlpha = blendAlpha,
|
||||||
|
}
|
||||||
226
plugin/src/App/init.lua
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Log = require(Rojo.Log)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Version = require(Plugin.Version)
|
||||||
|
local Config = require(Plugin.Config)
|
||||||
|
local strict = require(Plugin.strict)
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
local ServeSession = require(Plugin.ServeSession)
|
||||||
|
local ApiContext = require(Plugin.ApiContext)
|
||||||
|
local preloadAssets = require(Plugin.preloadAssets)
|
||||||
|
local Theme = require(script.Theme)
|
||||||
|
local PluginSettings = require(script.PluginSettings)
|
||||||
|
|
||||||
|
local Page = require(script.Page)
|
||||||
|
local StudioToolbar = require(script.Components.Studio.StudioToolbar)
|
||||||
|
local StudioToggleButton = require(script.Components.Studio.StudioToggleButton)
|
||||||
|
local StudioPluginGui = require(script.Components.Studio.StudioPluginGui)
|
||||||
|
local StudioPluginContext = require(script.Components.Studio.StudioPluginContext)
|
||||||
|
local StatusPages = require(script.StatusPages)
|
||||||
|
|
||||||
|
local AppStatus = strict("AppStatus", {
|
||||||
|
NotConnected = "NotConnected",
|
||||||
|
Settings = "Settings",
|
||||||
|
Connecting = "Connecting",
|
||||||
|
Connected = "Connected",
|
||||||
|
Error = "Error",
|
||||||
|
})
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local App = Roact.Component:extend("App")
|
||||||
|
|
||||||
|
function App:init()
|
||||||
|
preloadAssets()
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
guiEnabled = false,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function App:startSession(host, port, sessionOptions)
|
||||||
|
local baseUrl = ("http://%s:%s"):format(host, port)
|
||||||
|
local apiContext = ApiContext.new(baseUrl)
|
||||||
|
|
||||||
|
local serveSession = ServeSession.new({
|
||||||
|
apiContext = apiContext,
|
||||||
|
openScriptsExternally = sessionOptions.openScriptsExternally,
|
||||||
|
twoWaySync = sessionOptions.twoWaySync,
|
||||||
|
})
|
||||||
|
|
||||||
|
serveSession:onStatusChanged(function(status, details)
|
||||||
|
if status == ServeSession.Status.Connecting then
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.Connecting,
|
||||||
|
})
|
||||||
|
elseif status == ServeSession.Status.Connected then
|
||||||
|
local address = ("%s:%s"):format(host, port)
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.Connected,
|
||||||
|
projectName = details,
|
||||||
|
address = address,
|
||||||
|
})
|
||||||
|
elseif status == ServeSession.Status.Disconnected then
|
||||||
|
self.serveSession = nil
|
||||||
|
|
||||||
|
-- Details being present indicates that this
|
||||||
|
-- disconnection was from an error.
|
||||||
|
if details ~= nil then
|
||||||
|
Log.warn("Disconnected from an error: {}", details)
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.Error,
|
||||||
|
errorMessage = tostring(details),
|
||||||
|
})
|
||||||
|
else
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
serveSession:start()
|
||||||
|
|
||||||
|
self.serveSession = serveSession
|
||||||
|
end
|
||||||
|
|
||||||
|
function App:render()
|
||||||
|
local pluginName = "Rojo " .. Version.display(Config.version)
|
||||||
|
|
||||||
|
local function createPageElement(appStatus, additionalProps)
|
||||||
|
additionalProps = additionalProps or {}
|
||||||
|
|
||||||
|
local props = Dictionary.merge(additionalProps, {
|
||||||
|
component = StatusPages[appStatus],
|
||||||
|
active = self.state.appStatus == appStatus,
|
||||||
|
})
|
||||||
|
|
||||||
|
return e(Page, props)
|
||||||
|
end
|
||||||
|
|
||||||
|
return e(StudioPluginContext.Provider, {
|
||||||
|
value = self.props.plugin,
|
||||||
|
}, {
|
||||||
|
e(Theme.StudioProvider, nil, {
|
||||||
|
e(PluginSettings.StudioProvider, {
|
||||||
|
plugin = self.props.plugin,
|
||||||
|
}, {
|
||||||
|
gui = e(StudioPluginGui, {
|
||||||
|
id = pluginName,
|
||||||
|
title = pluginName,
|
||||||
|
active = self.state.guiEnabled,
|
||||||
|
|
||||||
|
initDockState = Enum.InitialDockState.Right,
|
||||||
|
initEnabled = false,
|
||||||
|
overridePreviousState = false,
|
||||||
|
floatingSize = Vector2.new(300, 200),
|
||||||
|
minimumSize = Vector2.new(300, 200),
|
||||||
|
|
||||||
|
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||||
|
|
||||||
|
onInitialState = function(initialState)
|
||||||
|
self:setState({
|
||||||
|
guiEnabled = initialState,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
|
||||||
|
onClose = function()
|
||||||
|
self:setState({
|
||||||
|
guiEnabled = false,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
NotConnectedPage = PluginSettings.with(function(settings)
|
||||||
|
return createPageElement(AppStatus.NotConnected, {
|
||||||
|
onConnect = function(host, port)
|
||||||
|
self:startSession(host, port, {
|
||||||
|
openScriptsExternally = settings:get("openScriptsExternally"),
|
||||||
|
twoWaySync = settings:get("twoWaySync"),
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
|
||||||
|
onNavigateSettings = function()
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.Settings,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end),
|
||||||
|
|
||||||
|
Connecting = createPageElement(AppStatus.Connecting),
|
||||||
|
|
||||||
|
Connected = createPageElement(AppStatus.Connected, {
|
||||||
|
projectName = self.state.projectName,
|
||||||
|
address = self.state.address,
|
||||||
|
|
||||||
|
onDisconnect = function()
|
||||||
|
Log.trace("Disconnecting session")
|
||||||
|
|
||||||
|
self.serveSession:stop()
|
||||||
|
self.serveSession = nil
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
|
||||||
|
Log.trace("Session terminated by user")
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Settings = createPageElement(AppStatus.Settings, {
|
||||||
|
onBack = function()
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Error = createPageElement(AppStatus.Error, {
|
||||||
|
errorMessage = self.state.errorMessage,
|
||||||
|
|
||||||
|
onClose = function()
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Background = Theme.with(function(theme)
|
||||||
|
return e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundColor3 = theme.BackgroundColor,
|
||||||
|
ZIndex = 0,
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
})
|
||||||
|
end),
|
||||||
|
}),
|
||||||
|
|
||||||
|
toolbar = e(StudioToolbar, {
|
||||||
|
name = pluginName,
|
||||||
|
}, {
|
||||||
|
button = e(StudioToggleButton, {
|
||||||
|
name = "Rojo",
|
||||||
|
tooltip = "Show or hide the Rojo panel",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
active = self.state.guiEnabled,
|
||||||
|
enabled = true,
|
||||||
|
onClick = function()
|
||||||
|
self:setState(function(state)
|
||||||
|
return {
|
||||||
|
guiEnabled = not state.guiEnabled,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return App
|
||||||
@@ -3,16 +3,45 @@ local strict = require(script.Parent.strict)
|
|||||||
local Assets = {
|
local Assets = {
|
||||||
Sprites = {},
|
Sprites = {},
|
||||||
Slices = {
|
Slices = {
|
||||||
RoundBox = {
|
RoundedBackground = {
|
||||||
asset = "rbxassetid://2773204550",
|
Image = "rbxassetid://5981360418",
|
||||||
offset = Vector2.new(0, 0),
|
Center = Rect.new(10, 10, 10, 10),
|
||||||
size = Vector2.new(32, 32),
|
Scale = 0.5,
|
||||||
center = Rect.new(4, 4, 4, 4),
|
},
|
||||||
|
|
||||||
|
RoundedBorder = {
|
||||||
|
Image = "rbxassetid://5981360137",
|
||||||
|
Center = Rect.new(10, 10, 10, 10),
|
||||||
|
Scale = 0.5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Images = {
|
Images = {
|
||||||
Logo = "rbxassetid://3405346157",
|
Logo = "rbxassetid://5990772764",
|
||||||
Icon = "rbxassetid://3405341609",
|
PluginButton = "rbxassetid://3405341609",
|
||||||
|
Icons = {
|
||||||
|
Close = "rbxassetid://6012985953",
|
||||||
|
Back = "rbxassetid://6017213752",
|
||||||
|
},
|
||||||
|
Checkbox = {
|
||||||
|
Active = "rbxassetid://6016251644",
|
||||||
|
Inactive = "rbxassetid://6016251963",
|
||||||
|
},
|
||||||
|
Spinner = {
|
||||||
|
Foreground = "rbxassetid://3222731032",
|
||||||
|
Background = "rbxassetid://3222730627",
|
||||||
|
},
|
||||||
|
ScrollBar = {
|
||||||
|
Top = "rbxassetid://6017290134",
|
||||||
|
Middle = "rbxassetid://6017289904",
|
||||||
|
Bottom = "rbxassetid://6017289712",
|
||||||
|
},
|
||||||
|
Circles = {
|
||||||
|
[16] = "rbxassetid://3056541177",
|
||||||
|
[32] = "rbxassetid://3088713341",
|
||||||
|
[64] = "rbxassetid://4918677124",
|
||||||
|
[128] = "rbxassetid://2600845734",
|
||||||
|
[500] = "rbxassetid://2609138523"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
StartSession = "",
|
StartSession = "",
|
||||||
SessionActive = "",
|
SessionActive = "",
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
local Log = require(Rojo.Log)
|
|
||||||
|
|
||||||
local ApiContext = require(Plugin.ApiContext)
|
|
||||||
local Assets = require(Plugin.Assets)
|
|
||||||
local Config = require(Plugin.Config)
|
|
||||||
local DevSettings = require(Plugin.DevSettings)
|
|
||||||
local ServeSession = require(Plugin.ServeSession)
|
|
||||||
local Version = require(Plugin.Version)
|
|
||||||
local preloadAssets = require(Plugin.preloadAssets)
|
|
||||||
local strict = require(Plugin.strict)
|
|
||||||
|
|
||||||
local ConnectPanel = require(Plugin.Components.ConnectPanel)
|
|
||||||
local ConnectingPanel = require(Plugin.Components.ConnectingPanel)
|
|
||||||
local ConnectionActivePanel = require(Plugin.Components.ConnectionActivePanel)
|
|
||||||
local ErrorPanel = require(Plugin.Components.ErrorPanel)
|
|
||||||
local SettingsPanel = require(Plugin.Components.SettingsPanel)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local function showUpgradeMessage(lastVersion)
|
|
||||||
local message = (
|
|
||||||
"Rojo detected an upgrade from version %s to version %s." ..
|
|
||||||
"\nMake sure you have also upgraded your server!" ..
|
|
||||||
"\n\nRojo plugin version %s is intended for use with server version %s."
|
|
||||||
):format(
|
|
||||||
Version.display(lastVersion), Version.display(Config.version),
|
|
||||||
Version.display(Config.version), Config.expectedServerVersionString
|
|
||||||
)
|
|
||||||
|
|
||||||
Log.info(message)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Check if the user is using a newer version of Rojo than last time. If they
|
|
||||||
are, show them a reminder to make sure they check their server version.
|
|
||||||
]]
|
|
||||||
local function checkUpgrade(plugin)
|
|
||||||
-- When developing Rojo, there's no use in doing version checks
|
|
||||||
if DevSettings:isEnabled() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local lastVersion = plugin:GetSetting("LastRojoVersion")
|
|
||||||
|
|
||||||
if lastVersion then
|
|
||||||
local wasUpgraded = Version.compare(Config.version, lastVersion) == 1
|
|
||||||
|
|
||||||
if wasUpgraded then
|
|
||||||
showUpgradeMessage(lastVersion)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
plugin:SetSetting("LastRojoVersion", Config.version)
|
|
||||||
end
|
|
||||||
|
|
||||||
local AppStatus = strict("AppStatus", {
|
|
||||||
NotStarted = "NotStarted",
|
|
||||||
Connecting = "Connecting",
|
|
||||||
Connected = "Connected",
|
|
||||||
Error = "Error",
|
|
||||||
Settings = "Settings",
|
|
||||||
})
|
|
||||||
|
|
||||||
local App = Roact.Component:extend("App")
|
|
||||||
|
|
||||||
function App:init()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotStarted,
|
|
||||||
errorMessage = nil,
|
|
||||||
})
|
|
||||||
|
|
||||||
self.signals = {}
|
|
||||||
self.serveSession = nil
|
|
||||||
self.displayedVersion = Version.display(Config.version)
|
|
||||||
|
|
||||||
local toolbar = self.props.plugin:CreateToolbar("Rojo " .. self.displayedVersion)
|
|
||||||
|
|
||||||
self.toggleButton = toolbar:CreateButton(
|
|
||||||
"Rojo",
|
|
||||||
"Show or hide the Rojo panel",
|
|
||||||
Assets.Images.Icon)
|
|
||||||
self.toggleButton.ClickableWhenViewportHidden = true
|
|
||||||
self.toggleButton.Click:Connect(function()
|
|
||||||
self.dockWidget.Enabled = not self.dockWidget.Enabled
|
|
||||||
end)
|
|
||||||
|
|
||||||
local widgetInfo = DockWidgetPluginGuiInfo.new(
|
|
||||||
Enum.InitialDockState.Right,
|
|
||||||
false, -- Initially enabled state
|
|
||||||
false, -- Whether to override the widget's previous state
|
|
||||||
360, 190, -- Floating size
|
|
||||||
360, 190 -- Minimum size
|
|
||||||
)
|
|
||||||
|
|
||||||
self.dockWidget = self.props.plugin:CreateDockWidgetPluginGui("Rojo-" .. self.displayedVersion, widgetInfo)
|
|
||||||
self.dockWidget.Name = "Rojo " .. self.displayedVersion
|
|
||||||
self.dockWidget.Title = "Rojo " .. self.displayedVersion
|
|
||||||
self.dockWidget.AutoLocalize = false
|
|
||||||
self.dockWidget.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
|
||||||
|
|
||||||
self.signals.dockWidgetEnabled = self.dockWidget:GetPropertyChangedSignal("Enabled"):Connect(function()
|
|
||||||
self.toggleButton:SetActive(self.dockWidget.Enabled)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function App:startSession(address, port, sessionOptions)
|
|
||||||
Log.trace("Starting new session")
|
|
||||||
|
|
||||||
local baseUrl = ("http://%s:%s"):format(address, port)
|
|
||||||
self.serveSession = ServeSession.new({
|
|
||||||
apiContext = ApiContext.new(baseUrl),
|
|
||||||
openScriptsExternally = sessionOptions.openScriptsExternally,
|
|
||||||
twoWaySync = sessionOptions.twoWaySync,
|
|
||||||
})
|
|
||||||
|
|
||||||
self.serveSession:onStatusChanged(function(status, details)
|
|
||||||
if status == ServeSession.Status.Connecting then
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.Connecting,
|
|
||||||
})
|
|
||||||
elseif status == ServeSession.Status.Connected then
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.Connected,
|
|
||||||
})
|
|
||||||
elseif status == ServeSession.Status.Disconnected then
|
|
||||||
self.serveSession = nil
|
|
||||||
|
|
||||||
-- Details being present indicates that this
|
|
||||||
-- disconnection was from an error.
|
|
||||||
if details ~= nil then
|
|
||||||
Log.warn("Disconnected from an error: {}", details)
|
|
||||||
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.Error,
|
|
||||||
errorMessage = tostring(details),
|
|
||||||
})
|
|
||||||
else
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotStarted,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
self.serveSession:start()
|
|
||||||
end
|
|
||||||
|
|
||||||
function App:render()
|
|
||||||
local children
|
|
||||||
|
|
||||||
if self.state.appStatus == AppStatus.NotStarted then
|
|
||||||
children = {
|
|
||||||
ConnectPanel = e(ConnectPanel, {
|
|
||||||
startSession = function(address, port, settings)
|
|
||||||
self:startSession(address, port, settings)
|
|
||||||
end,
|
|
||||||
openSettings = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.Settings,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
cancel = function()
|
|
||||||
Log.trace("Canceling session configuration")
|
|
||||||
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotStarted,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
elseif self.state.appStatus == AppStatus.Connecting then
|
|
||||||
children = {
|
|
||||||
ConnectingPanel = e(ConnectingPanel),
|
|
||||||
}
|
|
||||||
elseif self.state.appStatus == AppStatus.Connected then
|
|
||||||
children = {
|
|
||||||
ConnectionActivePanel = e(ConnectionActivePanel, {
|
|
||||||
stopSession = function()
|
|
||||||
Log.trace("Disconnecting session")
|
|
||||||
|
|
||||||
self.serveSession:stop()
|
|
||||||
self.serveSession = nil
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotStarted,
|
|
||||||
})
|
|
||||||
|
|
||||||
Log.trace("Session terminated by user")
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
elseif self.state.appStatus == AppStatus.Settings then
|
|
||||||
children = {
|
|
||||||
e(SettingsPanel, {
|
|
||||||
back = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotStarted,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
elseif self.state.appStatus == AppStatus.Error then
|
|
||||||
children = {
|
|
||||||
ErrorPanel = e(ErrorPanel, {
|
|
||||||
errorMessage = self.state.errorMessage,
|
|
||||||
onDismiss = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotStarted,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return e(Roact.Portal, {
|
|
||||||
target = self.dockWidget,
|
|
||||||
}, children)
|
|
||||||
end
|
|
||||||
|
|
||||||
function App:didMount()
|
|
||||||
Log.trace("Rojo {} initializing", self.displayedVersion)
|
|
||||||
|
|
||||||
checkUpgrade(self.props.plugin)
|
|
||||||
preloadAssets()
|
|
||||||
end
|
|
||||||
|
|
||||||
function App:willUnmount()
|
|
||||||
if self.serveSession ~= nil then
|
|
||||||
self.serveSession:stop()
|
|
||||||
self.serveSession = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, signal in pairs(self.signals) do
|
|
||||||
signal:Disconnect()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return App
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local function Checkbox(props)
|
|
||||||
local checked = props.checked
|
|
||||||
local layoutOrder = props.layoutOrder
|
|
||||||
local onChange = props.onChange
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e("ImageButton", {
|
|
||||||
LayoutOrder = layoutOrder,
|
|
||||||
Size = UDim2.new(0, 20, 0, 20),
|
|
||||||
BorderSizePixel = 2,
|
|
||||||
BorderColor3 = theme.Text2,
|
|
||||||
BackgroundColor3 = theme.Background2,
|
|
||||||
|
|
||||||
[Roact.Event.Activated] = function()
|
|
||||||
onChange(not checked)
|
|
||||||
end,
|
|
||||||
}, {
|
|
||||||
Indicator = e("Frame", {
|
|
||||||
Size = UDim2.new(0, 18, 0, 18),
|
|
||||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
|
||||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
|
||||||
BorderSizePixel = 0,
|
|
||||||
BackgroundColor3 = theme.Brand1,
|
|
||||||
BackgroundTransparency = checked and 0 or 1,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Checkbox
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local Config = require(Plugin.Config)
|
|
||||||
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
local Panel = require(Plugin.Components.Panel)
|
|
||||||
local FitList = require(Plugin.Components.FitList)
|
|
||||||
local FitText = require(Plugin.Components.FitText)
|
|
||||||
local FormButton = require(Plugin.Components.FormButton)
|
|
||||||
local FormTextInput = require(Plugin.Components.FormTextInput)
|
|
||||||
local PluginSettings = require(Plugin.Components.PluginSettings)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local ConnectPanel = Roact.Component:extend("ConnectPanel")
|
|
||||||
|
|
||||||
function ConnectPanel:init()
|
|
||||||
self:setState({
|
|
||||||
address = "",
|
|
||||||
port = "",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function ConnectPanel:render()
|
|
||||||
local startSession = self.props.startSession
|
|
||||||
local openSettings = self.props.openSettings
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return PluginSettings.with(function(settings)
|
|
||||||
return e(Panel, nil, {
|
|
||||||
Layout = e("UIListLayout", {
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Inputs = e(FitList, {
|
|
||||||
containerProps = {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
LayoutOrder = 1,
|
|
||||||
},
|
|
||||||
layoutProps = {
|
|
||||||
FillDirection = Enum.FillDirection.Horizontal,
|
|
||||||
Padding = UDim.new(0, 8),
|
|
||||||
},
|
|
||||||
paddingProps = {
|
|
||||||
PaddingTop = UDim.new(0, 20),
|
|
||||||
PaddingBottom = UDim.new(0, 10),
|
|
||||||
PaddingLeft = UDim.new(0, 24),
|
|
||||||
PaddingRight = UDim.new(0, 24),
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Address = e(FitList, {
|
|
||||||
containerProps = {
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
},
|
|
||||||
layoutProps = {
|
|
||||||
Padding = UDim.new(0, 4),
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Label = e(FitText, {
|
|
||||||
Kind = "TextLabel",
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
Font = theme.TitleFont,
|
|
||||||
TextSize = 20,
|
|
||||||
Text = "Address",
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Input = e(FormTextInput, {
|
|
||||||
layoutOrder = 2,
|
|
||||||
width = UDim.new(0, 220),
|
|
||||||
value = self.state.address,
|
|
||||||
placeholderValue = Config.defaultHost,
|
|
||||||
onValueChange = function(newValue)
|
|
||||||
self:setState({
|
|
||||||
address = newValue,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
Port = e(FitList, {
|
|
||||||
containerProps = {
|
|
||||||
LayoutOrder = 2,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
},
|
|
||||||
layoutProps = {
|
|
||||||
Padding = UDim.new(0, 4),
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Label = e(FitText, {
|
|
||||||
Kind = "TextLabel",
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
Font = theme.TitleFont,
|
|
||||||
TextSize = 20,
|
|
||||||
Text = "Port",
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Input = e(FormTextInput, {
|
|
||||||
layoutOrder = 2,
|
|
||||||
width = UDim.new(0, 80),
|
|
||||||
value = self.state.port,
|
|
||||||
placeholderValue = Config.defaultPort,
|
|
||||||
onValueChange = function(newValue)
|
|
||||||
self:setState({
|
|
||||||
port = newValue,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
Buttons = e(FitList, {
|
|
||||||
fitAxes = "Y",
|
|
||||||
containerProps = {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
LayoutOrder = 2,
|
|
||||||
Size = UDim2.new(1, 0, 0, 0),
|
|
||||||
},
|
|
||||||
layoutProps = {
|
|
||||||
FillDirection = Enum.FillDirection.Horizontal,
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
|
||||||
Padding = UDim.new(0, 8),
|
|
||||||
},
|
|
||||||
paddingProps = {
|
|
||||||
PaddingTop = UDim.new(0, 0),
|
|
||||||
PaddingBottom = UDim.new(0, 20),
|
|
||||||
PaddingLeft = UDim.new(0, 24),
|
|
||||||
PaddingRight = UDim.new(0, 24),
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
e(FormButton, {
|
|
||||||
layoutOrder = 1,
|
|
||||||
text = "Settings",
|
|
||||||
secondary = true,
|
|
||||||
onClick = function()
|
|
||||||
if openSettings ~= nil then
|
|
||||||
openSettings()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
e(FormButton, {
|
|
||||||
layoutOrder = 2,
|
|
||||||
text = "Connect",
|
|
||||||
onClick = function()
|
|
||||||
if startSession ~= nil then
|
|
||||||
local address = self.state.address
|
|
||||||
if address:len() == 0 then
|
|
||||||
address = Config.defaultHost
|
|
||||||
end
|
|
||||||
|
|
||||||
local port = self.state.port
|
|
||||||
if port:len() == 0 then
|
|
||||||
port = Config.defaultPort
|
|
||||||
end
|
|
||||||
|
|
||||||
local sessionOptions = {
|
|
||||||
openScriptsExternally = settings:get("openScriptsExternally"),
|
|
||||||
twoWaySync = settings:get("twoWaySync"),
|
|
||||||
}
|
|
||||||
|
|
||||||
startSession(address, port, sessionOptions)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ConnectPanel
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Plugin = script:FindFirstAncestor("Plugin")
|
|
||||||
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
local Panel = require(Plugin.Components.Panel)
|
|
||||||
local FitText = require(Plugin.Components.FitText)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local ConnectingPanel = Roact.Component:extend("ConnectingPanel")
|
|
||||||
|
|
||||||
function ConnectingPanel:render()
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e(Panel, nil, {
|
|
||||||
Layout = Roact.createElement("UIListLayout", {
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
Padding = UDim.new(0, 8),
|
|
||||||
}),
|
|
||||||
|
|
||||||
Text = e(FitText, {
|
|
||||||
Padding = Vector2.new(12, 6),
|
|
||||||
Font = theme.ButtonFont,
|
|
||||||
TextSize = 18,
|
|
||||||
Text = "Connecting...",
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ConnectingPanel
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Plugin = script:FindFirstAncestor("Plugin")
|
|
||||||
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
local Panel = require(Plugin.Components.Panel)
|
|
||||||
local FitText = require(Plugin.Components.FitText)
|
|
||||||
local FormButton = require(Plugin.Components.FormButton)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local ConnectionActivePanel = Roact.Component:extend("ConnectionActivePanel")
|
|
||||||
|
|
||||||
function ConnectionActivePanel:render()
|
|
||||||
local stopSession = self.props.stopSession
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e(Panel, nil, {
|
|
||||||
Layout = Roact.createElement("UIListLayout", {
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
Padding = UDim.new(0, 8),
|
|
||||||
}),
|
|
||||||
|
|
||||||
Text = e(FitText, {
|
|
||||||
Padding = Vector2.new(12, 6),
|
|
||||||
Font = theme.ButtonFont,
|
|
||||||
TextSize = 18,
|
|
||||||
Text = "Connected to Live-Sync Server",
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
DisconnectButton = e(FormButton, {
|
|
||||||
layoutOrder = 2,
|
|
||||||
text = "Disconnect",
|
|
||||||
secondary = true,
|
|
||||||
onClick = function()
|
|
||||||
stopSession()
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ConnectionActivePanel
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Plugin = script:FindFirstAncestor("Plugin")
|
|
||||||
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
local Panel = require(Plugin.Components.Panel)
|
|
||||||
local FitText = require(Plugin.Components.FitText)
|
|
||||||
local FitScrollingFrame = require(Plugin.Components.FitScrollingFrame)
|
|
||||||
local FormButton = require(Plugin.Components.FormButton)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local BUTTON_HEIGHT = 60
|
|
||||||
local HOR_PADDING = 8
|
|
||||||
|
|
||||||
local ErrorPanel = Roact.Component:extend("ErrorPanel")
|
|
||||||
|
|
||||||
function ErrorPanel:render()
|
|
||||||
local errorMessage = self.props.errorMessage
|
|
||||||
local onDismiss = self.props.onDismiss
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e(Panel, nil, {
|
|
||||||
Layout = Roact.createElement("UIListLayout", {
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
Padding = UDim.new(0, 8),
|
|
||||||
}),
|
|
||||||
|
|
||||||
ErrorContainer = e(FitScrollingFrame, {
|
|
||||||
containerProps = {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
BorderSizePixel = 0,
|
|
||||||
Size = UDim2.new(1, -HOR_PADDING * 2, 1, -BUTTON_HEIGHT),
|
|
||||||
Position = UDim2.new(0, HOR_PADDING, 0, 0),
|
|
||||||
ScrollBarImageColor3 = theme.Text1,
|
|
||||||
VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar,
|
|
||||||
ScrollingDirection = Enum.ScrollingDirection.Y,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Text = e(FitText, {
|
|
||||||
Size = UDim2.new(1, 0, 0, 0),
|
|
||||||
|
|
||||||
LayoutOrder = 1,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
TextYAlignment = Enum.TextYAlignment.Top,
|
|
||||||
FitAxis = "Y",
|
|
||||||
Font = theme.ButtonFont,
|
|
||||||
TextSize = 18,
|
|
||||||
Text = errorMessage,
|
|
||||||
TextWrap = true,
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
DismissButton = e(FormButton, {
|
|
||||||
layoutOrder = 2,
|
|
||||||
text = "Dismiss",
|
|
||||||
secondary = true,
|
|
||||||
onClick = function()
|
|
||||||
onDismiss()
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ErrorPanel
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Dictionary = require(script.Parent.Parent.Dictionary)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local FitList = Roact.Component:extend("FitList")
|
|
||||||
|
|
||||||
function FitList:init()
|
|
||||||
self.sizeBinding, self.setSize = Roact.createBinding(UDim2.new())
|
|
||||||
end
|
|
||||||
|
|
||||||
function FitList:render()
|
|
||||||
local containerKind = self.props.containerKind or "Frame"
|
|
||||||
local fitAxes = self.props.fitAxes or "XY"
|
|
||||||
local containerProps = self.props.containerProps
|
|
||||||
local layoutProps = self.props.layoutProps
|
|
||||||
local paddingProps = self.props.paddingProps
|
|
||||||
|
|
||||||
local padding
|
|
||||||
if paddingProps ~= nil then
|
|
||||||
padding = e("UIPadding", paddingProps)
|
|
||||||
end
|
|
||||||
|
|
||||||
local children = Dictionary.merge(self.props[Roact.Children], {
|
|
||||||
["$Layout"] = e("UIListLayout", Dictionary.merge({
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
[Roact.Change.AbsoluteContentSize] = function(instance)
|
|
||||||
local contentSize = instance.AbsoluteContentSize
|
|
||||||
|
|
||||||
if paddingProps ~= nil then
|
|
||||||
contentSize = contentSize + Vector2.new(
|
|
||||||
paddingProps.PaddingLeft.Offset + paddingProps.PaddingRight.Offset,
|
|
||||||
paddingProps.PaddingTop.Offset + paddingProps.PaddingBottom.Offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
local combinedSize
|
|
||||||
|
|
||||||
if fitAxes == "X" then
|
|
||||||
combinedSize = UDim2.new(0, contentSize.X, containerProps.Size.Y.Scale, containerProps.Size.Y.Offset)
|
|
||||||
elseif fitAxes == "Y" then
|
|
||||||
combinedSize = UDim2.new(containerProps.Size.X.Scale, containerProps.Size.X.Offset, 0, contentSize.Y)
|
|
||||||
elseif fitAxes == "XY" then
|
|
||||||
combinedSize = UDim2.new(0, contentSize.X, 0, contentSize.Y)
|
|
||||||
else
|
|
||||||
error("Invalid fitAxes value")
|
|
||||||
end
|
|
||||||
|
|
||||||
self.setSize(combinedSize)
|
|
||||||
end,
|
|
||||||
}, layoutProps)),
|
|
||||||
|
|
||||||
["$Padding"] = padding,
|
|
||||||
})
|
|
||||||
|
|
||||||
local fullContainerProps = Dictionary.merge(containerProps, {
|
|
||||||
Size = self.sizeBinding,
|
|
||||||
})
|
|
||||||
|
|
||||||
return e(containerKind, fullContainerProps, children)
|
|
||||||
end
|
|
||||||
|
|
||||||
return FitList
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Dictionary = require(script.Parent.Parent.Dictionary)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local FitScrollingFrame = Roact.Component:extend("FitScrollingFrame")
|
|
||||||
|
|
||||||
function FitScrollingFrame:init()
|
|
||||||
self.sizeBinding, self.setSize = Roact.createBinding(UDim2.new())
|
|
||||||
end
|
|
||||||
|
|
||||||
function FitScrollingFrame:render()
|
|
||||||
local containerProps = self.props.containerProps
|
|
||||||
local layoutProps = self.props.layoutProps
|
|
||||||
|
|
||||||
local children = Dictionary.merge(self.props[Roact.Children], {
|
|
||||||
["$Layout"] = e("UIListLayout", Dictionary.merge({
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
[Roact.Change.AbsoluteContentSize] = function(instance)
|
|
||||||
self.setSize(UDim2.new(0, 0, 0, instance.AbsoluteContentSize.Y))
|
|
||||||
end,
|
|
||||||
}, layoutProps)),
|
|
||||||
})
|
|
||||||
|
|
||||||
local fullContainerProps = Dictionary.merge(containerProps, {
|
|
||||||
CanvasSize = self.sizeBinding,
|
|
||||||
})
|
|
||||||
|
|
||||||
return e("ScrollingFrame", fullContainerProps, children)
|
|
||||||
end
|
|
||||||
|
|
||||||
return FitScrollingFrame
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
local TextService = game:GetService("TextService")
|
|
||||||
|
|
||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Dictionary = require(script.Parent.Parent.Dictionary)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local FitText = Roact.Component:extend("FitText")
|
|
||||||
|
|
||||||
function FitText:init()
|
|
||||||
self.ref = Roact.createRef()
|
|
||||||
self.sizeBinding, self.setSize = Roact.createBinding(UDim2.new())
|
|
||||||
end
|
|
||||||
|
|
||||||
function FitText:render()
|
|
||||||
local kind = self.props.Kind or "TextLabel"
|
|
||||||
|
|
||||||
local containerProps = Dictionary.merge(self.props, {
|
|
||||||
FitAxis = Dictionary.None,
|
|
||||||
Kind = Dictionary.None,
|
|
||||||
Padding = Dictionary.None,
|
|
||||||
MinSize = Dictionary.None,
|
|
||||||
Size = self.sizeBinding,
|
|
||||||
[Roact.Ref] = self.ref,
|
|
||||||
[Roact.Change.AbsoluteSize] = function()
|
|
||||||
self:updateTextMeasurements()
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
return e(kind, containerProps)
|
|
||||||
end
|
|
||||||
|
|
||||||
function FitText:didMount()
|
|
||||||
self:updateTextMeasurements()
|
|
||||||
end
|
|
||||||
|
|
||||||
function FitText:didUpdate()
|
|
||||||
self:updateTextMeasurements()
|
|
||||||
end
|
|
||||||
|
|
||||||
function FitText:updateTextMeasurements()
|
|
||||||
local minSize = self.props.MinSize or Vector2.new(0, 0)
|
|
||||||
local padding = self.props.Padding or Vector2.new(0, 0)
|
|
||||||
local fitAxis = self.props.FitAxis or "XY"
|
|
||||||
local baseSize = self.props.Size
|
|
||||||
|
|
||||||
local text = self.props.Text or ""
|
|
||||||
local font = self.props.Font or Enum.Font.Legacy
|
|
||||||
local textSize = self.props.TextSize or 12
|
|
||||||
|
|
||||||
local containerSize = self.ref.current.AbsoluteSize
|
|
||||||
|
|
||||||
local textBounds
|
|
||||||
|
|
||||||
if fitAxis == "XY" then
|
|
||||||
textBounds = Vector2.new(9e6, 9e6)
|
|
||||||
elseif fitAxis == "X" then
|
|
||||||
textBounds = Vector2.new(9e6, containerSize.Y - padding.Y * 2)
|
|
||||||
elseif fitAxis == "Y" then
|
|
||||||
textBounds = Vector2.new(containerSize.X - padding.X * 2, 9e6)
|
|
||||||
end
|
|
||||||
|
|
||||||
local measuredText = TextService:GetTextSize(text, textSize, font, textBounds)
|
|
||||||
|
|
||||||
local computedX = math.max(minSize.X, padding.X * 2 + measuredText.X)
|
|
||||||
local computedY = math.max(minSize.Y, padding.Y * 2 + measuredText.Y)
|
|
||||||
|
|
||||||
local totalSize
|
|
||||||
|
|
||||||
if fitAxis == "XY" then
|
|
||||||
totalSize = UDim2.new(
|
|
||||||
0, computedX,
|
|
||||||
0, computedY)
|
|
||||||
elseif fitAxis == "X" then
|
|
||||||
totalSize = UDim2.new(
|
|
||||||
0, computedX,
|
|
||||||
baseSize.Y.Scale, baseSize.Y.Offset)
|
|
||||||
elseif fitAxis == "Y" then
|
|
||||||
totalSize = UDim2.new(
|
|
||||||
baseSize.X.Scale, baseSize.X.Offset,
|
|
||||||
0, computedY)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.setSize(totalSize)
|
|
||||||
end
|
|
||||||
|
|
||||||
return FitText
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
local FitList = require(Plugin.Components.FitList)
|
|
||||||
local FitText = require(Plugin.Components.FitText)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local RoundBox = Assets.Slices.RoundBox
|
|
||||||
|
|
||||||
local function FormButton(props)
|
|
||||||
local text = props.text
|
|
||||||
local layoutOrder = props.layoutOrder
|
|
||||||
local onClick = props.onClick
|
|
||||||
|
|
||||||
local textColor
|
|
||||||
local backgroundColor
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
if props.secondary then
|
|
||||||
textColor = theme.Brand1
|
|
||||||
backgroundColor = theme.Background2
|
|
||||||
else
|
|
||||||
textColor = theme.TextOnAccent
|
|
||||||
backgroundColor = theme.Brand1
|
|
||||||
end
|
|
||||||
|
|
||||||
return e(FitList, {
|
|
||||||
containerKind = "ImageButton",
|
|
||||||
containerProps = {
|
|
||||||
LayoutOrder = layoutOrder,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
Image = RoundBox.asset,
|
|
||||||
ImageRectOffset = RoundBox.offset,
|
|
||||||
ImageRectSize = RoundBox.size,
|
|
||||||
SliceCenter = RoundBox.center,
|
|
||||||
ScaleType = Enum.ScaleType.Slice,
|
|
||||||
ImageColor3 = backgroundColor,
|
|
||||||
|
|
||||||
[Roact.Event.Activated] = function()
|
|
||||||
if onClick ~= nil then
|
|
||||||
onClick()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Text = e(FitText, {
|
|
||||||
Kind = "TextLabel",
|
|
||||||
Text = text,
|
|
||||||
TextSize = 18,
|
|
||||||
TextColor3 = textColor,
|
|
||||||
Font = theme.ButtonFont,
|
|
||||||
Padding = Vector2.new(16, 8),
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return FormButton
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local RoundBox = Assets.Slices.RoundBox
|
|
||||||
|
|
||||||
local TEXT_SIZE = 22
|
|
||||||
local PADDING = 8
|
|
||||||
|
|
||||||
local FormTextInput = Roact.Component:extend("FormTextInput")
|
|
||||||
|
|
||||||
function FormTextInput:init()
|
|
||||||
self:setState({
|
|
||||||
focused = false,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function FormTextInput:render()
|
|
||||||
local value = self.props.value
|
|
||||||
local placeholderValue = self.props.placeholderValue
|
|
||||||
local onValueChange = self.props.onValueChange
|
|
||||||
local layoutOrder = self.props.layoutOrder
|
|
||||||
local width = self.props.width
|
|
||||||
|
|
||||||
local shownPlaceholder
|
|
||||||
if self.state.focused then
|
|
||||||
shownPlaceholder = ""
|
|
||||||
else
|
|
||||||
shownPlaceholder = placeholderValue
|
|
||||||
end
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e("ImageLabel", {
|
|
||||||
LayoutOrder = layoutOrder,
|
|
||||||
Image = RoundBox.asset,
|
|
||||||
ImageRectOffset = RoundBox.offset,
|
|
||||||
ImageRectSize = RoundBox.size,
|
|
||||||
ScaleType = Enum.ScaleType.Slice,
|
|
||||||
SliceCenter = RoundBox.center,
|
|
||||||
ImageColor3 = theme.Background2,
|
|
||||||
Size = UDim2.new(width.Scale, width.Offset, 0, TEXT_SIZE + PADDING * 2),
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}, {
|
|
||||||
InputInner = e("TextBox", {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
Size = UDim2.new(1, -PADDING * 2, 1, -PADDING * 2),
|
|
||||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
|
||||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
|
||||||
Font = theme.InputFont,
|
|
||||||
ClearTextOnFocus = false,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Center,
|
|
||||||
TextSize = TEXT_SIZE,
|
|
||||||
Text = value,
|
|
||||||
PlaceholderText = shownPlaceholder,
|
|
||||||
PlaceholderColor3 = theme.Text2,
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
|
|
||||||
[Roact.Change.Text] = function(rbx)
|
|
||||||
onValueChange(rbx.Text)
|
|
||||||
end,
|
|
||||||
[Roact.Event.Focused] = function()
|
|
||||||
self:setState({
|
|
||||||
focused = true,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
[Roact.Event.FocusLost] = function()
|
|
||||||
self:setState({
|
|
||||||
focused = false,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return FormTextInput
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Plugin = script:FindFirstAncestor("Plugin")
|
|
||||||
|
|
||||||
local RojoFooter = require(Plugin.Components.RojoFooter)
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local Panel = Roact.Component:extend("Panel")
|
|
||||||
|
|
||||||
function Panel:init()
|
|
||||||
self.footerSize, self.setFooterSize = Roact.createBinding(Vector2.new())
|
|
||||||
end
|
|
||||||
|
|
||||||
function Panel:render()
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e("Frame", {
|
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
|
||||||
BackgroundColor3 = theme.Background1,
|
|
||||||
BorderSizePixel = 1,
|
|
||||||
}, {
|
|
||||||
Layout = Roact.createElement("UIListLayout", {
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Body = e("Frame", {
|
|
||||||
Size = UDim2.new(0, 360, 1, -32),
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}, self.props[Roact.Children]),
|
|
||||||
|
|
||||||
Footer = e(RojoFooter),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Panel
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local Config = require(Plugin.Config)
|
|
||||||
local Version = require(Plugin.Version)
|
|
||||||
local Assets = require(Plugin.Assets)
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local RojoFooter = Roact.Component:extend("RojoFooter")
|
|
||||||
|
|
||||||
function RojoFooter:init()
|
|
||||||
self.footerSize, self.setFooterSize = Roact.createBinding(Vector2.new())
|
|
||||||
self.footerVersionSize, self.setFooterVersionSize = Roact.createBinding(Vector2.new())
|
|
||||||
end
|
|
||||||
|
|
||||||
function RojoFooter:render()
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return e("Frame", {
|
|
||||||
LayoutOrder = 3,
|
|
||||||
Size = UDim2.new(1, 0, 0, 32),
|
|
||||||
BackgroundColor3 = theme.Background2,
|
|
||||||
BorderSizePixel = 0,
|
|
||||||
ZIndex = 2,
|
|
||||||
}, {
|
|
||||||
Padding = e("UIPadding", {
|
|
||||||
PaddingTop = UDim.new(0, 4),
|
|
||||||
PaddingBottom = UDim.new(0, 4),
|
|
||||||
PaddingLeft = UDim.new(0, 8),
|
|
||||||
PaddingRight = UDim.new(0, 8),
|
|
||||||
}),
|
|
||||||
|
|
||||||
LogoContainer = e("Frame", {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
|
|
||||||
Size = UDim2.new(0, 0, 0, 32),
|
|
||||||
}, {
|
|
||||||
Logo = e("ImageLabel", {
|
|
||||||
Image = Assets.Images.Logo,
|
|
||||||
Size = UDim2.new(0, 80, 0, 40),
|
|
||||||
ScaleType = Enum.ScaleType.Fit,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
Position = UDim2.new(0, 0, 1, -10),
|
|
||||||
AnchorPoint = Vector2.new(0, 1),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
Version = e("TextLabel", {
|
|
||||||
Position = UDim2.new(1, 0, 0, 0),
|
|
||||||
Size = UDim2.new(0, 0, 1, 0),
|
|
||||||
AnchorPoint = Vector2.new(1, 0),
|
|
||||||
Font = theme.TitleFont,
|
|
||||||
TextSize = 18,
|
|
||||||
Text = Version.display(Config.version),
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Right,
|
|
||||||
TextColor3 = theme.Text2,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
|
|
||||||
[Roact.Change.AbsoluteSize] = function(rbx)
|
|
||||||
self.setFooterVersionSize(rbx.AbsoluteSize)
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return RojoFooter
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
|
||||||
|
|
||||||
local Plugin = script:FindFirstAncestor("Plugin")
|
|
||||||
|
|
||||||
local Checkbox = require(Plugin.Components.Checkbox)
|
|
||||||
local FitList = require(Plugin.Components.FitList)
|
|
||||||
local FitText = require(Plugin.Components.FitText)
|
|
||||||
local FormButton = require(Plugin.Components.FormButton)
|
|
||||||
local Panel = require(Plugin.Components.Panel)
|
|
||||||
local PluginSettings = require(Plugin.Components.PluginSettings)
|
|
||||||
local Theme = require(Plugin.Components.Theme)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local SettingsPanel = Roact.Component:extend("SettingsPanel")
|
|
||||||
|
|
||||||
function SettingsPanel:render()
|
|
||||||
local back = self.props.back
|
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
return PluginSettings.with(function(settings)
|
|
||||||
return e(Panel, nil, {
|
|
||||||
Layout = Roact.createElement("UIListLayout", {
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
Padding = UDim.new(0, 16),
|
|
||||||
}),
|
|
||||||
|
|
||||||
OpenScriptsExternally = e(FitList, {
|
|
||||||
containerProps = {
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
},
|
|
||||||
layoutProps = {
|
|
||||||
Padding = UDim.new(0, 4),
|
|
||||||
FillDirection = Enum.FillDirection.Horizontal,
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Label = e(FitText, {
|
|
||||||
Kind = "TextLabel",
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
Font = theme.MainFont,
|
|
||||||
TextSize = 16,
|
|
||||||
Text = "Open Scripts Externally",
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Padding = e("Frame", {
|
|
||||||
Size = UDim2.new(0, 8, 0, 0),
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
LayoutOrder = 2,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Input = e(Checkbox, {
|
|
||||||
layoutOrder = 3,
|
|
||||||
checked = settings:get("openScriptsExternally"),
|
|
||||||
onChange = function(newValue)
|
|
||||||
settings:set("openScriptsExternally", not settings:get("openScriptsExternally"))
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
TwoWaySync = e(FitList, {
|
|
||||||
containerProps = {
|
|
||||||
LayoutOrder = 2,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
},
|
|
||||||
layoutProps = {
|
|
||||||
Padding = UDim.new(0, 4),
|
|
||||||
FillDirection = Enum.FillDirection.Horizontal,
|
|
||||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Label = e(FitText, {
|
|
||||||
Kind = "TextLabel",
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
Font = theme.MainFont,
|
|
||||||
TextSize = 16,
|
|
||||||
Text = "Two-Way Sync (Experimental!)",
|
|
||||||
TextColor3 = theme.Text1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Padding = e("Frame", {
|
|
||||||
Size = UDim2.new(0, 8, 0, 0),
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
LayoutOrder = 2,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Input = e(Checkbox, {
|
|
||||||
layoutOrder = 3,
|
|
||||||
checked = settings:get("twoWaySync"),
|
|
||||||
onChange = function(newValue)
|
|
||||||
settings:set("twoWaySync", not settings:get("twoWaySync"))
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
BackButton = e(FormButton, {
|
|
||||||
layoutOrder = 4,
|
|
||||||
text = "Okay",
|
|
||||||
secondary = true,
|
|
||||||
onClick = function()
|
|
||||||
back()
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return SettingsPanel
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
--[[
|
|
||||||
Theming system taking advantage of Roact's new context API.
|
|
||||||
|
|
||||||
Doesn't use colors provided by Studio and instead just branches on theme
|
|
||||||
name. This isn't exactly best practice.
|
|
||||||
]]
|
|
||||||
|
|
||||||
-- Studio does not exist outside Roblox Studio, so we'll lazily initialize it
|
|
||||||
-- when possible.
|
|
||||||
local _Studio
|
|
||||||
local function getStudio()
|
|
||||||
if _Studio == nil then
|
|
||||||
_Studio = settings():GetService("Studio")
|
|
||||||
end
|
|
||||||
|
|
||||||
return _Studio
|
|
||||||
end
|
|
||||||
|
|
||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
local Log = require(Rojo.Log)
|
|
||||||
|
|
||||||
local strict = require(script.Parent.Parent.strict)
|
|
||||||
|
|
||||||
local lightTheme = strict("Theme", {
|
|
||||||
ButtonFont = Enum.Font.GothamSemibold,
|
|
||||||
InputFont = Enum.Font.Code,
|
|
||||||
TitleFont = Enum.Font.GothamBold,
|
|
||||||
MainFont = Enum.Font.Gotham,
|
|
||||||
|
|
||||||
Brand1 = Color3.fromRGB(225, 56, 53),
|
|
||||||
|
|
||||||
Text1 = Color3.fromRGB(64, 64, 64),
|
|
||||||
Text2 = Color3.fromRGB(160, 160, 160),
|
|
||||||
TextOnAccent = Color3.fromRGB(235, 235, 235),
|
|
||||||
|
|
||||||
Background1 = Color3.fromRGB(255, 255, 255),
|
|
||||||
Background2 = Color3.fromRGB(235, 235, 235),
|
|
||||||
})
|
|
||||||
|
|
||||||
local darkTheme = strict("Theme", {
|
|
||||||
ButtonFont = Enum.Font.GothamSemibold,
|
|
||||||
InputFont = Enum.Font.Code,
|
|
||||||
TitleFont = Enum.Font.GothamBold,
|
|
||||||
MainFont = Enum.Font.Gotham,
|
|
||||||
|
|
||||||
Brand1 = Color3.fromRGB(225, 56, 53),
|
|
||||||
|
|
||||||
Text1 = Color3.fromRGB(235, 235, 235),
|
|
||||||
Text2 = Color3.fromRGB(200, 200, 200),
|
|
||||||
TextOnAccent = Color3.fromRGB(235, 235, 235),
|
|
||||||
|
|
||||||
Background1 = Color3.fromRGB(48, 48, 48),
|
|
||||||
Background2 = Color3.fromRGB(64, 64, 64),
|
|
||||||
})
|
|
||||||
|
|
||||||
local Context = Roact.createContext(lightTheme)
|
|
||||||
|
|
||||||
local StudioProvider = Roact.Component:extend("StudioProvider")
|
|
||||||
|
|
||||||
-- Pull the current theme from Roblox Studio and update state with it.
|
|
||||||
function StudioProvider:updateTheme()
|
|
||||||
local studioTheme = getStudio().Theme
|
|
||||||
|
|
||||||
if studioTheme.Name == "Light" then
|
|
||||||
self:setState({
|
|
||||||
theme = lightTheme,
|
|
||||||
})
|
|
||||||
elseif studioTheme.Name == "Dark" then
|
|
||||||
self:setState({
|
|
||||||
theme = darkTheme,
|
|
||||||
})
|
|
||||||
else
|
|
||||||
Log.warn("Unexpected theme '{}'' -- falling back to light theme!", studioTheme.Name)
|
|
||||||
|
|
||||||
self:setState({
|
|
||||||
theme = lightTheme,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function StudioProvider:init()
|
|
||||||
self:updateTheme()
|
|
||||||
end
|
|
||||||
|
|
||||||
function StudioProvider:render()
|
|
||||||
return Roact.createElement(Context.Provider, {
|
|
||||||
value = self.state.theme,
|
|
||||||
}, self.props[Roact.Children])
|
|
||||||
end
|
|
||||||
|
|
||||||
function StudioProvider:didMount()
|
|
||||||
self.connection = getStudio().ThemeChanged:Connect(function()
|
|
||||||
self:updateTheme()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function StudioProvider:willUnmount()
|
|
||||||
self.connection:Disconnect()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function with(callback)
|
|
||||||
return Roact.createElement(Context.Consumer, {
|
|
||||||
render = callback,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
StudioProvider = StudioProvider,
|
|
||||||
Consumer = Context.Consumer,
|
|
||||||
with = with,
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,9 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
|||||||
return strict("Config", {
|
return strict("Config", {
|
||||||
isDevBuild = isDevBuild,
|
isDevBuild = isDevBuild,
|
||||||
codename = "Epiphany",
|
codename = "Epiphany",
|
||||||
version = {6, 0, 0, "-rc.3"},
|
version = {7, 0, 0, "-alpha.2"},
|
||||||
expectedServerVersionString = "6.0 or newer",
|
expectedServerVersionString = "7.0 or newer",
|
||||||
protocolVersion = 3,
|
protocolVersion = 4,
|
||||||
defaultHost = "localhost",
|
defaultHost = "localhost",
|
||||||
defaultPort = 34872,
|
defaultPort = 34872,
|
||||||
})
|
})
|
||||||
@@ -52,8 +52,9 @@ local function diff(instanceMap, virtualInstances, rootId)
|
|||||||
invariant("Cannot diff an instance not present in InstanceMap\nID: {}", id)
|
invariant("Cannot diff an instance not present in InstanceMap\nID: {}", id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local changedClassName = nil
|
||||||
if virtualInstance.ClassName ~= instance.ClassName then
|
if virtualInstance.ClassName ~= instance.ClassName then
|
||||||
error("unimplemented: support changing ClassName")
|
changedClassName = virtualInstance.ClassName
|
||||||
end
|
end
|
||||||
|
|
||||||
local changedName = nil
|
local changedName = nil
|
||||||
@@ -89,11 +90,11 @@ local function diff(instanceMap, virtualInstances, rootId)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if changedName ~= nil or not isEmpty(changedProperties) then
|
if changedName ~= nil or changedClassName ~= nil or not isEmpty(changedProperties) then
|
||||||
table.insert(patch.updated, {
|
table.insert(patch.updated, {
|
||||||
id = id,
|
id = id,
|
||||||
changedName = changedName,
|
changedName = changedName,
|
||||||
changedClassName = nil,
|
changedClassName = changedClassName,
|
||||||
changedProperties = changedProperties,
|
changedProperties = changedProperties,
|
||||||
changedMetadata = nil,
|
changedMetadata = nil,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function ServeSession:start()
|
|||||||
|
|
||||||
self.__apiContext:connect()
|
self.__apiContext:connect()
|
||||||
:andThen(function(serverInfo)
|
:andThen(function(serverInfo)
|
||||||
self:__setStatus(Status.Connected)
|
self:__setStatus(Status.Connected, serverInfo.projectName)
|
||||||
|
|
||||||
local rootInstanceId = serverInfo.rootInstanceId
|
local rootInstanceId = serverInfo.rootInstanceId
|
||||||
|
|
||||||
|
|||||||
@@ -13,20 +13,11 @@ end)
|
|||||||
local Roact = require(script.Parent.Roact)
|
local Roact = require(script.Parent.Roact)
|
||||||
|
|
||||||
local Config = require(script.Config)
|
local Config = require(script.Config)
|
||||||
local App = require(script.Components.App)
|
local App = require(script.App)
|
||||||
local Theme = require(script.Components.Theme)
|
|
||||||
local PluginSettings = require(script.Components.PluginSettings)
|
|
||||||
|
|
||||||
local app = Roact.createElement(Theme.StudioProvider, nil, {
|
local app = Roact.createElement(App, {
|
||||||
Roact.createElement(PluginSettings.StudioProvider, {
|
plugin = plugin
|
||||||
plugin = plugin,
|
|
||||||
}, {
|
|
||||||
RojoUI = Roact.createElement(App, {
|
|
||||||
plugin = plugin,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
local tree = Roact.mount(app, nil, "Rojo UI")
|
local tree = Roact.mount(app, nil, "Rojo UI")
|
||||||
|
|
||||||
plugin.Unloading:Connect(function()
|
plugin.Unloading:Connect(function()
|
||||||
|
|||||||
@@ -4,20 +4,23 @@ local Log = require(script.Parent.Parent.Log)
|
|||||||
|
|
||||||
local Assets = require(script.Parent.Assets)
|
local Assets = require(script.Parent.Assets)
|
||||||
|
|
||||||
|
local gatherAssetUrlsRecursive
|
||||||
|
function gatherAssetUrlsRecursive(currentTable, currentUrls)
|
||||||
|
currentUrls = currentUrls or {}
|
||||||
|
|
||||||
|
for _, value in pairs(currentTable) do
|
||||||
|
if typeof(value) == "string" then
|
||||||
|
table.insert(currentUrls, value)
|
||||||
|
elseif typeof(value) == "table" then
|
||||||
|
gatherAssetUrlsRecursive(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return currentUrls
|
||||||
|
end
|
||||||
|
|
||||||
local function preloadAssets()
|
local function preloadAssets()
|
||||||
local contentUrls = {}
|
local contentUrls = gatherAssetUrlsRecursive(Assets)
|
||||||
|
|
||||||
for _, sprite in pairs(Assets.Sprites) do
|
|
||||||
table.insert(contentUrls, sprite.asset)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, slice in pairs(Assets.Slices) do
|
|
||||||
table.insert(contentUrls, slice.asset)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, url in pairs(Assets.Images) do
|
|
||||||
table.insert(contentUrls, url)
|
|
||||||
end
|
|
||||||
|
|
||||||
Log.trace("Preloading assets: {:?}", contentUrls)
|
Log.trace("Preloading assets: {:?}", contentUrls)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ expression: contents
|
|||||||
<Item class="IntValue" referent="1">
|
<Item class="IntValue" referent="1">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">simple-model</string>
|
<string name="Name">simple-model</string>
|
||||||
<int name="Value">5</int>
|
<int64 name="Value">5</int64>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Item class="Folder" referent="2">
|
<Item class="Folder" referent="2">
|
||||||
<Properties>
|
<Properties>
|
||||||
|
|||||||