mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 20:55:50 +00:00
Compare commits
24 Commits
aarch-wind
...
v7.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ddbefa88f | ||
|
|
d935115591 | ||
|
|
bd2ea42732 | ||
|
|
3bac38ee34 | ||
|
|
a7a4f6d8f2 | ||
|
|
80b6facbd3 | ||
|
|
7dee898400 | ||
|
|
4c4b2dbe17 | ||
|
|
73ed5ae697 | ||
|
|
833320de64 | ||
|
|
0d6ff8ef8a | ||
|
|
55a207a275 | ||
|
|
f33d1f1cc4 | ||
|
|
19ca2b12fc | ||
|
|
b7d3394464 | ||
|
|
8c33100d7a | ||
|
|
80c406f196 | ||
|
|
bc2c76e5e2 | ||
|
|
4a7bddbc09 | ||
|
|
e316fdbaef | ||
|
|
34106f470f | ||
|
|
d9ab0e7de8 | ||
|
|
5ca1573e2e | ||
|
|
c9ce996626 |
2
.dir-locals.el
Normal file
2
.dir-locals.el
Normal file
@@ -0,0 +1,2 @@
|
||||
((nil . ((eglot-luau-rojo-project-path . "plugin.project.json")
|
||||
(eglot-luau-rojo-sourcemap-enabled . 't))))
|
||||
6
.github/workflows/changelog.yml
vendored
6
.github/workflows/changelog.yml
vendored
@@ -11,12 +11,12 @@ jobs:
|
||||
name: Check Actions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Changelog check
|
||||
uses: Zomzog/changelog-checker@v1.3.0
|
||||
with:
|
||||
fileName: CHANGELOG.md
|
||||
fileName: CHANGELOG.md
|
||||
noChangelogLabel: skip changelog
|
||||
checkNotification: Simple
|
||||
env:
|
||||
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -30,9 +30,9 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.3.0
|
||||
uses: ok-nick/setup-aftman@v0.4.2
|
||||
with:
|
||||
version: 'v0.2.7'
|
||||
version: 'v0.3.0'
|
||||
|
||||
- name: Build
|
||||
run: cargo build --locked --verbose
|
||||
@@ -56,9 +56,9 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.3.0
|
||||
uses: ok-nick/setup-aftman@v0.4.2
|
||||
with:
|
||||
version: 'v0.2.7'
|
||||
version: 'v0.3.0'
|
||||
|
||||
- name: Build
|
||||
run: cargo build --locked --verbose
|
||||
@@ -81,9 +81,9 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.3.0
|
||||
uses: ok-nick/setup-aftman@v0.4.2
|
||||
with:
|
||||
version: 'v0.2.7'
|
||||
version: 'v0.3.0'
|
||||
|
||||
- name: Stylua
|
||||
run: stylua --check plugin/src
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -26,14 +26,12 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.1.0
|
||||
uses: ok-nick/setup-aftman@v0.4.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
trust-check: false
|
||||
version: 'v0.2.6'
|
||||
version: 'v0.3.0'
|
||||
|
||||
- name: Build Plugin
|
||||
run: rojo build plugin --output Rojo.rbxm
|
||||
run: rojo build plugin.project.json --output Rojo.rbxm
|
||||
|
||||
- name: Upload Plugin to Release
|
||||
env:
|
||||
@@ -55,7 +53,7 @@ jobs:
|
||||
# https://doc.rust-lang.org/rustc/platform-support.html
|
||||
include:
|
||||
- host: linux
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
label: linux-x86_64
|
||||
|
||||
@@ -89,11 +87,9 @@ jobs:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.1.0
|
||||
uses: ok-nick/setup-aftman@v0.4.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
trust-check: false
|
||||
version: 'v0.2.6'
|
||||
version: 'v0.3.0'
|
||||
|
||||
- name: Build Release
|
||||
run: cargo build --release --locked --verbose --target ${{ matrix.target }}
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -10,8 +10,8 @@
|
||||
/*.rbxl
|
||||
/*.rbxlx
|
||||
|
||||
# Test places for the Roblox Studio Plugin
|
||||
/plugin/*.rbxlx
|
||||
# Sourcemap for the Rojo plugin (for better intellisense)
|
||||
/sourcemap.json
|
||||
|
||||
# Roblox Studio holds 'lock' files on places
|
||||
*.rbxl.lock
|
||||
@@ -19,3 +19,7 @@
|
||||
|
||||
# Snapshot files from the 'insta' Rust crate
|
||||
**/*.snap.new
|
||||
|
||||
# Macos file system junk
|
||||
._*
|
||||
.DS_STORE
|
||||
|
||||
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"JohnnyMorganz.luau-lsp",
|
||||
"JohnnyMorganz.stylua",
|
||||
"Kampfkarren.selene-vscode",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"luau-lsp.sourcemap.rojoProjectFile": "plugin.project.json",
|
||||
"luau-lsp.sourcemap.autogenerate": true
|
||||
}
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,6 +1,14 @@
|
||||
# Rojo Changelog
|
||||
|
||||
## Unreleased Changes
|
||||
## 7.5.0 - April 25th, 2025
|
||||
* Fixed an edge case that caused model pivots to not be built correctly in some cases ([#1027])
|
||||
* Add `blockedPlaceIds` project config field to allow blocking place ids from being live synced ([#1021])
|
||||
* Adds support for `.plugin.lua(u)` files - this applies the `Plugin` RunContext. ([#1008])
|
||||
* Added support for Roblox's `Content` type. This replaces the old `Content` type with `ContentId` to reflect Roblox's change.
|
||||
If you were previously using the fully-qualified syntax for `Content` you will need to switch it to `ContentId`.
|
||||
* Added support for `Enum` attributes
|
||||
* Significantly improved performance of `.rbxm` parsing
|
||||
* Support for a `$schema` field in all special JSON files (`.project.json`, `.model.json`, and `.meta.json`) ([#974])
|
||||
* Projects may now manually link `Ref` properties together using `Attributes`. ([#843])
|
||||
This has two parts: using `id` or `$id` in JSON files or a `Rojo_Target` attribute, an Instance
|
||||
is given an ID. Then, that ID may be used elsewhere in the project to point to an Instance
|
||||
@@ -28,6 +36,8 @@
|
||||
* Added experimental setting for Auto Connect in playtests ([#840])
|
||||
* Improved settings UI ([#886])
|
||||
* `Open Scripts Externally` option can now be changed while syncing ([#911])
|
||||
* The sync reminder notification will now tell you what was last synced and when ([#987])
|
||||
* Fixed notification and tooltip text sometimes getting cut off ([#988])
|
||||
* Projects may now specify rules for syncing files as if they had a different file extension. ([#813])
|
||||
This is specified via a new field on project files, `syncRules`:
|
||||
|
||||
@@ -56,7 +66,7 @@
|
||||
|
||||
Additionally, the `exclude` field allows files to be excluded from the sync rule if they match a pattern specified by it. If it's not present, all files that match `pattern` will be modified using the sync rule.
|
||||
|
||||
The `use` field corresponds to one of the potential file type that Rojo will currently include in a project. Files that match the provided pattern will be treated as if they had the file extension for that file type. A full list is below:
|
||||
The `use` field corresponds to one of the potential file type that Rojo will currently include in a project. Files that match the provided pattern will be treated as if they had the file extension for that file type.
|
||||
|
||||
| `use` value | file extension |
|
||||
|:---------------|:----------------|
|
||||
@@ -73,6 +83,16 @@
|
||||
| `project` | `.project.json` |
|
||||
| `ignore` | None! |
|
||||
|
||||
Additionally, there are `use` values for specific script types ([#909]):
|
||||
|
||||
| `use` value | script type |
|
||||
|:-------------------------|:---------------------------------------|
|
||||
| `legacyServerScript` | `Script` with `Enum.RunContext.Legacy` |
|
||||
| `legacyClientScript` | `LocalScript` |
|
||||
| `runContextServerScript` | `Script` with `Enum.RunContext.Server` |
|
||||
| `runContextClientScript` | `Script` with `Enum.RunContext.Client` |
|
||||
| `pluginScript` | `Script` with `Enum.RunContext.Plugin` |
|
||||
|
||||
**All** sync rules are reset between project files, so they must be specified in each one when nesting them. This is to ensure that nothing can break other projects by changing how files are synced!
|
||||
|
||||
[#813]: https://github.com/rojo-rbx/rojo/pull/813
|
||||
@@ -83,8 +103,19 @@
|
||||
[#843]: https://github.com/rojo-rbx/rojo/pull/843
|
||||
[#883]: https://github.com/rojo-rbx/rojo/pull/883
|
||||
[#886]: https://github.com/rojo-rbx/rojo/pull/886
|
||||
[#909]: https://github.com/rojo-rbx/rojo/pull/909
|
||||
[#911]: https://github.com/rojo-rbx/rojo/pull/911
|
||||
[#915]: https://github.com/rojo-rbx/rojo/pull/915
|
||||
[#974]: https://github.com/rojo-rbx/rojo/pull/974
|
||||
[#987]: https://github.com/rojo-rbx/rojo/pull/987
|
||||
[#988]: https://github.com/rojo-rbx/rojo/pull/988
|
||||
[#1008]: https://github.com/rojo-rbx/rojo/pull/1008
|
||||
[#1021]: https://github.com/rojo-rbx/rojo/pull/1021
|
||||
[#1027]: https://github.com/rojo-rbx/rojo/pull/1027
|
||||
|
||||
## [7.4.4] - August 22nd, 2024
|
||||
* Fixed issue with reading attributes from `Lighting` in new place files
|
||||
* `Instance.Archivable` will now default to `true` when building a project into a binary (`rbxm`/`rbxl`) file rather than `false`.
|
||||
|
||||
## [7.4.3] - August 6th, 2024
|
||||
* Fixed issue with building binary files introduced in 7.4.2
|
||||
|
||||
@@ -16,6 +16,23 @@ You'll want these tools to work on Rojo:
|
||||
* Latest stable Rust compiler
|
||||
* Latest stable [Rojo](https://github.com/rojo-rbx/rojo)
|
||||
* [Foreman](https://github.com/Roblox/foreman)
|
||||
* [Luau Language Server](https://github.com/JohnnyMorganz/luau-lsp) (Only needed if working on the Studio plugin.)
|
||||
|
||||
When working on the Studio plugin, we recommend using this command to automatically rebuild the plugin when you save a change:
|
||||
|
||||
*(Make sure you've enabled the Studio setting to reload plugins on file change!)*
|
||||
|
||||
```bash
|
||||
bash scripts/watch-build-plugin.sh
|
||||
```
|
||||
|
||||
You can also run the plugin's unit tests with the following:
|
||||
|
||||
*(Make sure you have `run-in-roblox` installed first!)*
|
||||
|
||||
```bash
|
||||
bash scripts/unit-test-plugin.sh
|
||||
```
|
||||
|
||||
## Documentation
|
||||
Documentation impacts way more people than the individual lines of code we write.
|
||||
|
||||
180
Cargo.lock
generated
180
Cargo.lock
generated
@@ -17,6 +17,19 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
@@ -171,6 +184,10 @@ name = "cc"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -492,7 +509,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.4.1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -935,6 +952,15 @@ version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jod-thread"
|
||||
version = "0.1.2"
|
||||
@@ -962,9 +988,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
@@ -986,7 +1012,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1001,6 +1027,16 @@ version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
@@ -1241,6 +1277,29 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.5.10",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
@@ -1310,6 +1369,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.5"
|
||||
@@ -1498,10 +1563,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_binary"
|
||||
version = "0.7.7"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b85057e8ff75a1ce99248200c4b3c7b481a3d52f921f1053ecd67921dcc7930"
|
||||
checksum = "9573fee5e073d7b303f475c285197fdc8179468de66ca60ee115a58fbac99296"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"log",
|
||||
"lz4",
|
||||
"profiling",
|
||||
@@ -1509,23 +1575,26 @@ dependencies = [
|
||||
"rbx_reflection",
|
||||
"rbx_reflection_database",
|
||||
"thiserror",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rbx_dom_weak"
|
||||
version = "2.9.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcd2a17d09e46af0805f8b311a926402172b97e8d9388745c9adf8f448901841"
|
||||
checksum = "04425cf6e9376e5486f4fb35906c120d1b1b45618a490318cf563fab1fa230a9"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"rbx_types",
|
||||
"serde",
|
||||
"ustr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rbx_reflection"
|
||||
version = "4.7.0"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8118ac6021d700e8debe324af6b40ecfd2cef270a00247849dbdfeebb0802677"
|
||||
checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e"
|
||||
dependencies = [
|
||||
"rbx_types",
|
||||
"serde",
|
||||
@@ -1534,9 +1603,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_reflection_database"
|
||||
version = "0.2.12+roblox-638"
|
||||
version = "1.0.2+roblox-670"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e29381d675420e841f8c02db5755cbb2545ed3e13f56c539546dc58702b512a"
|
||||
checksum = "5349b19e5e94fbcaba7a52175263ab64011e0a13f17ff57729f2f560ccdec615"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rbx_reflection",
|
||||
@@ -1546,9 +1615,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_types"
|
||||
version = "1.10.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e30f49b2a3bb667e4074ba73c2dfb8ca0873f610b448ccf318a240acfdec6c73"
|
||||
checksum = "78e4fdde46493def107e5f923d82e813dec9b3eef52c2f75fbad3a716023eda2"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bitflags 1.3.2",
|
||||
@@ -1561,10 +1630,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_xml"
|
||||
version = "0.13.5"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b14b3027bc9ccd82e2fc854c8bcd25ed58318e570c355bf2cf63df9cdbd5ba8"
|
||||
checksum = "bb623833c31cc43bbdaeb32f5e91db8ecd63fc46e438d0d268baf9e61539cf1c"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.13.1",
|
||||
"log",
|
||||
"rbx_dom_weak",
|
||||
@@ -1582,6 +1652,15 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.4"
|
||||
@@ -1752,7 +1831,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rojo"
|
||||
version = "7.4.0"
|
||||
version = "7.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"backtrace",
|
||||
@@ -1896,6 +1975,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.1"
|
||||
@@ -2393,6 +2478,19 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ustr"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.7.0"
|
||||
@@ -2784,3 +2882,51 @@ name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.78",
|
||||
"quote 1.0.35",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rojo"
|
||||
version = "7.4.0"
|
||||
version = "7.5.0"
|
||||
rust-version = "1.70.0"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
description = "Enables professional-grade development tools for Roblox developers"
|
||||
@@ -51,11 +51,11 @@ memofs = { version = "0.3.0", path = "crates/memofs" }
|
||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||
|
||||
rbx_binary = "0.7.7"
|
||||
rbx_dom_weak = "2.9.0"
|
||||
rbx_reflection = "4.7.0"
|
||||
rbx_reflection_database = "0.2.12"
|
||||
rbx_xml = "0.13.5"
|
||||
rbx_binary = "1.0.0"
|
||||
rbx_dom_weak = "3.0.0"
|
||||
rbx_reflection = "5.0.0"
|
||||
rbx_reflection_database = "1.0.2"
|
||||
rbx_xml = "1.0.0"
|
||||
|
||||
anyhow = "1.0.80"
|
||||
backtrace = "0.3.69"
|
||||
@@ -70,7 +70,6 @@ humantime = "2.1.0"
|
||||
hyper = { version = "0.14.28", features = ["server", "tcp", "http1"] }
|
||||
jod-thread = "0.1.2"
|
||||
log = "0.4.21"
|
||||
maplit = "1.0.2"
|
||||
num_cpus = "1.16.0"
|
||||
opener = "0.5.2"
|
||||
rayon = "1.9.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[tools]
|
||||
rojo = "rojo-rbx/rojo@7.3.0"
|
||||
selene = "Kampfkarren/selene@0.26.1"
|
||||
stylua = "JohnnyMorganz/stylua@0.18.2"
|
||||
rojo = "rojo-rbx/rojo@7.4.1"
|
||||
selene = "Kampfkarren/selene@0.27.1"
|
||||
stylua = "JohnnyMorganz/stylua@0.20.0"
|
||||
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"
|
||||
|
||||
28
build.rs
28
build.rs
@@ -20,6 +20,10 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
|
||||
|
||||
let file_name = entry.file_name().to_str().unwrap().to_owned();
|
||||
|
||||
if file_name.starts_with(".git") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can skip any TestEZ test files since they aren't necessary for
|
||||
// the plugin to run.
|
||||
if file_name.ends_with(".spec.lua") || file_name.ends_with(".spec.luau") {
|
||||
@@ -41,12 +45,12 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
|
||||
let plugin_root = PathBuf::from(root_dir).join("plugin");
|
||||
let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
let plugin_dir = root_dir.join("plugin");
|
||||
|
||||
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
||||
let plugin_version =
|
||||
Version::parse(fs::read_to_string(plugin_root.join("Version.txt"))?.trim())?;
|
||||
Version::parse(fs::read_to_string(plugin_dir.join("Version.txt"))?.trim())?;
|
||||
|
||||
assert_eq!(
|
||||
our_version, plugin_version,
|
||||
@@ -54,14 +58,16 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
);
|
||||
|
||||
let snapshot = VfsSnapshot::dir(hashmap! {
|
||||
"default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?,
|
||||
"fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?,
|
||||
"http" => snapshot_from_fs_path(&plugin_root.join("http"))?,
|
||||
"log" => snapshot_from_fs_path(&plugin_root.join("log"))?,
|
||||
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?,
|
||||
"src" => snapshot_from_fs_path(&plugin_root.join("src"))?,
|
||||
"Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?,
|
||||
"Version.txt" => snapshot_from_fs_path(&plugin_root.join("Version.txt"))?,
|
||||
"default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?,
|
||||
"plugin" => VfsSnapshot::dir(hashmap! {
|
||||
"fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?,
|
||||
"http" => snapshot_from_fs_path(&plugin_dir.join("http"))?,
|
||||
"log" => snapshot_from_fs_path(&plugin_dir.join("log"))?,
|
||||
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_dir.join("rbx_dom_lua"))?,
|
||||
"src" => snapshot_from_fs_path(&plugin_dir.join("src"))?,
|
||||
"Packages" => snapshot_from_fs_path(&plugin_dir.join("Packages"))?,
|
||||
"Version.txt" => snapshot_from_fs_path(&plugin_dir.join("Version.txt"))?,
|
||||
}),
|
||||
});
|
||||
|
||||
let out_path = Path::new(&out_dir).join("plugin.bincode");
|
||||
|
||||
@@ -5,19 +5,13 @@ use serde::Serialize;
|
||||
/// Enables redacting any value that serializes as a string.
|
||||
///
|
||||
/// Used for transforming Rojo instance IDs into something deterministic.
|
||||
#[derive(Default)]
|
||||
pub struct RedactionMap {
|
||||
ids: HashMap<String, usize>,
|
||||
last_id: usize,
|
||||
}
|
||||
|
||||
impl RedactionMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ids: HashMap::new(),
|
||||
last_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_redacted_value(&self, id: impl ToString) -> Option<String> {
|
||||
let id = id.to_string();
|
||||
|
||||
|
||||
@@ -3,25 +3,25 @@
|
||||
"tree": {
|
||||
"$className": "Folder",
|
||||
"Plugin": {
|
||||
"$path": "src"
|
||||
"$path": "plugin/src"
|
||||
},
|
||||
"Packages": {
|
||||
"$path": "Packages",
|
||||
"$path": "plugin/Packages",
|
||||
"Log": {
|
||||
"$path": "log"
|
||||
"$path": "plugin/log"
|
||||
},
|
||||
"Http": {
|
||||
"$path": "http"
|
||||
"$path": "plugin/http"
|
||||
},
|
||||
"Fmt": {
|
||||
"$path": "fmt"
|
||||
"$path": "plugin/fmt"
|
||||
},
|
||||
"RbxDom": {
|
||||
"$path": "rbx_dom_lua"
|
||||
"$path": "plugin/rbx_dom_lua"
|
||||
}
|
||||
},
|
||||
"Version": {
|
||||
"$path": "Version.txt"
|
||||
"$path": "plugin/Version.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
7.4.0
|
||||
7.5.0
|
||||
@@ -70,7 +70,7 @@ local function debugImpl(buffer, value, extendedForm)
|
||||
elseif valueType == "table" then
|
||||
local valueMeta = getmetatable(value)
|
||||
|
||||
if valueMeta ~= nil and valueMeta.__fmtDebug ~= nil then
|
||||
if valueMeta ~= nil and valueMeta.__fmtDebug ~= nil then
|
||||
-- This type implement's the metamethod we made up to line up with
|
||||
-- Rust's 'Debug' trait.
|
||||
|
||||
@@ -242,4 +242,4 @@ return {
|
||||
debugOutputBuffer = debugOutputBuffer,
|
||||
fmt = fmt,
|
||||
debugify = debugify,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,4 @@ function Response:json()
|
||||
return HttpService:JSONDecode(self.body)
|
||||
end
|
||||
|
||||
return Response
|
||||
return Response
|
||||
|
||||
@@ -2,4 +2,4 @@ return function()
|
||||
it("should load", function()
|
||||
require(script.Parent)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -57,4 +57,4 @@ function Log.error(template, ...)
|
||||
error(Fmt.fmt(template, ...))
|
||||
end
|
||||
|
||||
return Log
|
||||
return Log
|
||||
|
||||
@@ -2,4 +2,4 @@ return function()
|
||||
it("should load", function()
|
||||
require(script.Parent)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -188,6 +188,38 @@ types = {
|
||||
},
|
||||
|
||||
Content = {
|
||||
fromPod = function(pod): Content
|
||||
if type(pod) == "string" then
|
||||
if pod == "None" then
|
||||
return Content.none
|
||||
else
|
||||
error(`unexpected Content value '{pod}'`)
|
||||
end
|
||||
else
|
||||
local ty, value = next(pod)
|
||||
if ty == "Uri" then
|
||||
return Content.fromUri(value)
|
||||
elseif ty == "Object" then
|
||||
error("Object deserializing is not currently implemented")
|
||||
else
|
||||
error(`Unknown Content type '{ty}' (could not deserialize)`)
|
||||
end
|
||||
end
|
||||
end,
|
||||
toPod = function(roblox: Content)
|
||||
if roblox.SourceType == Enum.ContentSourceType.None then
|
||||
return "None"
|
||||
elseif roblox.SourceType == Enum.ContentSourceType.Uri then
|
||||
return { Uri = roblox.Uri }
|
||||
elseif roblox.SourceType == Enum.ContentSourceType.Object then
|
||||
error("Object serializing is not currently implemented")
|
||||
else
|
||||
error(`Unknown Content type '{roblox.SourceType} (could not serialize)`)
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
ContentId = {
|
||||
fromPod = identity,
|
||||
toPod = identity,
|
||||
},
|
||||
@@ -205,6 +237,19 @@ types = {
|
||||
end,
|
||||
},
|
||||
|
||||
EnumItem = {
|
||||
fromPod = function(pod)
|
||||
return Enum[pod.type]:FromValue(pod.value)
|
||||
end,
|
||||
|
||||
toPod = function(roblox)
|
||||
return {
|
||||
type = tostring(roblox.EnumType),
|
||||
value = roblox.Value,
|
||||
}
|
||||
end,
|
||||
},
|
||||
|
||||
Faces = {
|
||||
fromPod = function(pod)
|
||||
local faces = {}
|
||||
@@ -300,7 +345,12 @@ types = {
|
||||
local keypoints = {}
|
||||
|
||||
for index, keypoint in ipairs(pod.keypoints) do
|
||||
keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, keypoint.value, keypoint.envelope)
|
||||
-- TODO: Add a test for NaN or Infinity values and envelopes
|
||||
-- Right now it isn't possible because it'd fail the roundtrip.
|
||||
-- It's more important that it works right now, though.
|
||||
local value = keypoint.value or 0
|
||||
local envelope = keypoint.envelope or 0
|
||||
keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, value, envelope)
|
||||
end
|
||||
|
||||
return NumberSequence.new(keypoints)
|
||||
|
||||
@@ -5,6 +5,7 @@ Error.Kind = {
|
||||
UnknownProperty = "UnknownProperty",
|
||||
PropertyNotReadable = "PropertyNotReadable",
|
||||
PropertyNotWritable = "PropertyNotWritable",
|
||||
CannotParseBinaryString = "CannotParseBinaryString",
|
||||
Roblox = "Roblox",
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"TestEnumItem": {
|
||||
"EnumItem": {
|
||||
"type": "Material",
|
||||
"value": 256
|
||||
}
|
||||
},
|
||||
"TestNumber": {
|
||||
"Float64": 1337.0
|
||||
},
|
||||
@@ -170,9 +176,23 @@
|
||||
},
|
||||
"ty": "ColorSequence"
|
||||
},
|
||||
"Content": {
|
||||
"ContentId": {
|
||||
"value": {
|
||||
"Content": "rbxassetid://12345"
|
||||
"ContentId": "rbxassetid://12345"
|
||||
},
|
||||
"ty": "ContentId"
|
||||
},
|
||||
"Content_None": {
|
||||
"value": {
|
||||
"Content": "None"
|
||||
},
|
||||
"ty": "Content"
|
||||
},
|
||||
"Content_Uri": {
|
||||
"value": {
|
||||
"Content": {
|
||||
"Uri": "rbxasset://abc/123.rojo"
|
||||
}
|
||||
},
|
||||
"ty": "Content"
|
||||
},
|
||||
@@ -182,6 +202,15 @@
|
||||
},
|
||||
"ty": "Enum"
|
||||
},
|
||||
"EnumItem": {
|
||||
"value": {
|
||||
"EnumItem": {
|
||||
"type": "Material",
|
||||
"value": 256
|
||||
}
|
||||
},
|
||||
"ty": "EnumItem"
|
||||
},
|
||||
"Faces": {
|
||||
"value": {
|
||||
"Faces": [
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
local CollectionService = game:GetService("CollectionService")
|
||||
local ScriptEditorService = game:GetService("ScriptEditorService")
|
||||
|
||||
local Error = require(script.Parent.Error)
|
||||
|
||||
--- A list of `Enum.Material` values that are used for Terrain.MaterialColors
|
||||
local TERRAIN_MATERIAL_COLORS = {
|
||||
Enum.Material.Grass,
|
||||
@@ -51,6 +53,10 @@ return {
|
||||
return true, instance:GetAttributes()
|
||||
end,
|
||||
write = function(instance, _, value)
|
||||
if typeof(value) ~= "table" then
|
||||
return false, Error.new(Error.Kind.CannotParseBinaryString)
|
||||
end
|
||||
|
||||
local existing = instance:GetAttributes()
|
||||
local didAllWritesSucceed = true
|
||||
|
||||
@@ -160,9 +166,14 @@ return {
|
||||
return true, colors
|
||||
end,
|
||||
write = function(instance: Terrain, _, value: { [Enum.Material]: Color3 })
|
||||
if typeof(value) ~= "table" then
|
||||
return false, Error.new(Error.Kind.CannotParseBinaryString)
|
||||
end
|
||||
|
||||
for material, color in value do
|
||||
instance:SetMaterialColor(material, color)
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local TestEZ = require(ReplicatedStorage.Packages.TestEZ)
|
||||
local TestEZ = require(ReplicatedStorage.Packages:WaitForChild("TestEZ", 10))
|
||||
|
||||
local Rojo = ReplicatedStorage.Rojo
|
||||
|
||||
|
||||
@@ -45,14 +45,7 @@ end
|
||||
|
||||
local function rejectWrongPlaceId(infoResponseBody)
|
||||
if infoResponseBody.expectedPlaceIds ~= nil then
|
||||
local foundId = false
|
||||
|
||||
for _, id in ipairs(infoResponseBody.expectedPlaceIds) do
|
||||
if id == game.PlaceId then
|
||||
foundId = true
|
||||
break
|
||||
end
|
||||
end
|
||||
local foundId = table.find(infoResponseBody.expectedPlaceIds, game.PlaceId)
|
||||
|
||||
if not foundId then
|
||||
local idList = {}
|
||||
@@ -62,10 +55,30 @@ local function rejectWrongPlaceId(infoResponseBody)
|
||||
|
||||
local message = (
|
||||
"Found a Rojo server, but its project is set to only be used with a specific list of places."
|
||||
.. "\nYour place ID is %s, but needs to be one of these:"
|
||||
.. "\nYour place ID is %u, but needs to be one of these:"
|
||||
.. "\n%s"
|
||||
.. "\n\nTo change this list, edit 'servePlaceIds' in your .project.json file."
|
||||
):format(tostring(game.PlaceId), table.concat(idList, "\n"))
|
||||
):format(game.PlaceId, table.concat(idList, "\n"))
|
||||
|
||||
return Promise.reject(message)
|
||||
end
|
||||
end
|
||||
|
||||
if infoResponseBody.unexpectedPlaceIds ~= nil then
|
||||
local foundId = table.find(infoResponseBody.unexpectedPlaceIds, game.PlaceId)
|
||||
|
||||
if foundId then
|
||||
local idList = {}
|
||||
for _, id in ipairs(infoResponseBody.unexpectedPlaceIds) do
|
||||
table.insert(idList, "- " .. tostring(id))
|
||||
end
|
||||
|
||||
local message = (
|
||||
"Found a Rojo server, but its project is set to not be used with a specific list of places."
|
||||
.. "\nYour place ID is %u, but needs to not be one of these:"
|
||||
.. "\n%s"
|
||||
.. "\n\nTo change this list, edit 'blockedPlaceIds' in your .project.json file."
|
||||
):format(game.PlaceId, table.concat(idList, "\n"))
|
||||
|
||||
return Promise.reject(message)
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ end
|
||||
|
||||
function Checkbox:render()
|
||||
return Theme.with(function(theme)
|
||||
theme = theme.Checkbox
|
||||
local checkboxTheme = theme.Checkbox
|
||||
|
||||
local activeTransparency = Roact.joinBindings({
|
||||
self.binding:map(function(value)
|
||||
@@ -57,20 +57,21 @@ function Checkbox:render()
|
||||
end,
|
||||
}, {
|
||||
StateTip = e(Tooltip.Trigger, {
|
||||
text = (if self.props.locked then "[LOCKED] " else "")
|
||||
.. (if self.props.active then "Enabled" else "Disabled"),
|
||||
text = (if self.props.locked
|
||||
then (self.props.lockedTooltip or "(Cannot be changed right now)") .. "\n"
|
||||
else "") .. (if self.props.active then "Enabled" else "Disabled"),
|
||||
}),
|
||||
|
||||
Active = e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = theme.Active.BackgroundColor,
|
||||
color = checkboxTheme.Active.BackgroundColor,
|
||||
transparency = activeTransparency,
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
zIndex = 2,
|
||||
}, {
|
||||
Icon = e("ImageLabel", {
|
||||
Image = if self.props.locked then Assets.Images.Checkbox.Locked else Assets.Images.Checkbox.Active,
|
||||
ImageColor3 = theme.Active.IconColor,
|
||||
ImageColor3 = checkboxTheme.Active.IconColor,
|
||||
ImageTransparency = activeTransparency,
|
||||
|
||||
Size = UDim2.new(0, 16, 0, 16),
|
||||
@@ -83,7 +84,7 @@ function Checkbox:render()
|
||||
|
||||
Inactive = e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBorder,
|
||||
color = theme.Inactive.BorderColor,
|
||||
color = checkboxTheme.Inactive.BorderColor,
|
||||
transparency = self.props.transparency,
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
@@ -91,7 +92,7 @@ function Checkbox:render()
|
||||
Image = if self.props.locked
|
||||
then Assets.Images.Checkbox.Locked
|
||||
else Assets.Images.Checkbox.Inactive,
|
||||
ImageColor3 = theme.Inactive.IconColor,
|
||||
ImageColor3 = checkboxTheme.Inactive.IconColor,
|
||||
ImageTransparency = self.props.transparency,
|
||||
|
||||
Size = UDim2.new(0, 16, 0, 16),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
local Packages = Rojo.Packages
|
||||
|
||||
local Roact = require(Packages.Roact)
|
||||
@@ -7,6 +8,8 @@ Highlighter.matchStudioSettings()
|
||||
|
||||
local e = Roact.createElement
|
||||
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
|
||||
local CodeLabel = Roact.PureComponent:extend("CodeLabel")
|
||||
|
||||
function CodeLabel:init()
|
||||
@@ -40,22 +43,24 @@ function CodeLabel:updateHighlights()
|
||||
end
|
||||
|
||||
function CodeLabel:render()
|
||||
return e("TextLabel", {
|
||||
Size = self.props.size,
|
||||
Position = self.props.position,
|
||||
Text = self.props.text,
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.RobotoMono,
|
||||
TextSize = 16,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
TextColor3 = Color3.fromRGB(255, 255, 255),
|
||||
[Roact.Ref] = self.labelRef,
|
||||
}, {
|
||||
SyntaxHighlights = e("Folder", {
|
||||
[Roact.Ref] = self.highlightsRef,
|
||||
}),
|
||||
})
|
||||
return Theme.with(function(theme)
|
||||
return e("TextLabel", {
|
||||
Size = self.props.size,
|
||||
Position = self.props.position,
|
||||
Text = self.props.text,
|
||||
BackgroundTransparency = 1,
|
||||
FontFace = theme.Font.Code,
|
||||
TextSize = theme.TextSize.Code,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
TextColor3 = Color3.fromRGB(255, 255, 255),
|
||||
[Roact.Ref] = self.labelRef,
|
||||
}, {
|
||||
SyntaxHighlights = e("Folder", {
|
||||
[Roact.Ref] = self.highlightsRef,
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
return CodeLabel
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
local Packages = Rojo.Packages
|
||||
@@ -10,9 +8,11 @@ local Flipper = require(Packages.Flipper)
|
||||
local Assets = require(Plugin.Assets)
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local SlicedImage = require(script.Parent.SlicedImage)
|
||||
local ScrollingFrame = require(script.Parent.ScrollingFrame)
|
||||
local Tooltip = require(script.Parent.Tooltip)
|
||||
|
||||
local e = Roact.createElement
|
||||
|
||||
@@ -44,29 +44,29 @@ end
|
||||
|
||||
function Dropdown:render()
|
||||
return Theme.with(function(theme)
|
||||
theme = theme.Dropdown
|
||||
local dropdownTheme = theme.Dropdown
|
||||
|
||||
local optionButtons = {}
|
||||
local width = -1
|
||||
for i, option in self.props.options do
|
||||
local text = tostring(option or "")
|
||||
local textSize = TextService:GetTextSize(text, 15, Enum.Font.GothamMedium, Vector2.new(math.huge, 20))
|
||||
if textSize.X > width then
|
||||
width = textSize.X
|
||||
local textBounds = getTextBoundsAsync(text, theme.Font.Main, theme.TextSize.Body, math.huge)
|
||||
if textBounds.X > width then
|
||||
width = textBounds.X
|
||||
end
|
||||
|
||||
optionButtons[text] = e("TextButton", {
|
||||
Text = text,
|
||||
LayoutOrder = i,
|
||||
Size = UDim2.new(1, 0, 0, 24),
|
||||
BackgroundColor3 = theme.BackgroundColor,
|
||||
BackgroundColor3 = dropdownTheme.BackgroundColor,
|
||||
TextTransparency = self.props.transparency,
|
||||
BackgroundTransparency = self.props.transparency,
|
||||
BorderSizePixel = 0,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextColor3 = dropdownTheme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextSize = 15,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = theme.TextSize.Body,
|
||||
FontFace = theme.Font.Main,
|
||||
|
||||
[Roact.Event.Activated] = function()
|
||||
if self.props.locked then
|
||||
@@ -103,13 +103,13 @@ function Dropdown:render()
|
||||
}, {
|
||||
Border = e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBorder,
|
||||
color = theme.BorderColor,
|
||||
color = dropdownTheme.BorderColor,
|
||||
transparency = self.props.transparency,
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
DropArrow = e("ImageLabel", {
|
||||
Image = if self.props.locked then Assets.Images.Dropdown.Locked else Assets.Images.Dropdown.Arrow,
|
||||
ImageColor3 = theme.IconColor,
|
||||
ImageColor3 = dropdownTheme.IconColor,
|
||||
ImageTransparency = self.props.transparency,
|
||||
|
||||
Size = UDim2.new(0, 18, 0, 18),
|
||||
@@ -120,15 +120,21 @@ function Dropdown:render()
|
||||
end),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
}, {
|
||||
StateTip = if self.props.locked
|
||||
then e(Tooltip.Trigger, {
|
||||
text = self.props.lockedTooltip or "(Cannot be changed right now)",
|
||||
})
|
||||
else nil,
|
||||
}),
|
||||
Active = e("TextLabel", {
|
||||
Size = UDim2.new(1, -30, 1, 0),
|
||||
Position = UDim2.new(0, 6, 0, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Text = self.props.active,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 15,
|
||||
TextColor3 = theme.TextColor,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = dropdownTheme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = self.props.transparency,
|
||||
}),
|
||||
@@ -136,7 +142,7 @@ function Dropdown:render()
|
||||
Options = if self.state.open
|
||||
then e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = theme.BackgroundColor,
|
||||
color = dropdownTheme.BackgroundColor,
|
||||
position = UDim2.new(1, 0, 1, 3),
|
||||
size = self.openBinding:map(function(a)
|
||||
return UDim2.new(1, 0, a * math.min(3, #self.props.options), 0)
|
||||
@@ -145,7 +151,7 @@ function Dropdown:render()
|
||||
}, {
|
||||
Border = e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBorder,
|
||||
color = theme.BorderColor,
|
||||
color = dropdownTheme.BorderColor,
|
||||
transparency = self.props.transparency,
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
}),
|
||||
|
||||
@@ -31,13 +31,13 @@ local function Header(props)
|
||||
|
||||
Version = e("TextLabel", {
|
||||
Text = Version.display(Config.version),
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Header.VersionColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
|
||||
Size = UDim2.new(1, 0, 0, 14),
|
||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Body),
|
||||
|
||||
LayoutOrder = 2,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
@@ -39,8 +39,8 @@ local function ViewDiffButton(props)
|
||||
Label = e("TextLabel", {
|
||||
Text = "View Diff",
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -170,8 +170,8 @@ function ChangeList:render()
|
||||
ColumnA = e("TextLabel", {
|
||||
Text = tostring(headerRow[1]),
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -182,8 +182,8 @@ function ChangeList:render()
|
||||
ColumnB = e("TextLabel", {
|
||||
Text = tostring(headerRow[2]),
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -194,8 +194,8 @@ function ChangeList:render()
|
||||
ColumnC = e("TextLabel", {
|
||||
Text = tostring(headerRow[3]),
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -230,8 +230,8 @@ function ChangeList:render()
|
||||
ColumnA = e("TextLabel", {
|
||||
Text = (if isWarning then "⚠ " else "") .. tostring(values[1]),
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = if isWarning then theme.Diff.Warning else theme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
|
||||
@@ -32,8 +32,8 @@ local function DisplayValue(props)
|
||||
Label = e("TextLabel", {
|
||||
Text = string.format("%d, %d, %d", props.value.R * 255, props.value.G * 255, props.value.B * 255),
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = props.textColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -90,8 +90,8 @@ local function DisplayValue(props)
|
||||
return e("TextLabel", {
|
||||
Text = textRepresentation,
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = props.textColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -112,8 +112,8 @@ local function DisplayValue(props)
|
||||
return e("TextLabel", {
|
||||
Text = textRepresentation,
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = props.textColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
|
||||
@@ -225,8 +225,8 @@ function DomLabel:render()
|
||||
Text = (if props.isWarning then "⚠ " else "") .. props.name,
|
||||
RichText = true,
|
||||
BackgroundTransparency = 1,
|
||||
Font = if props.patchType then Enum.Font.GothamBold else Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = if props.patchType then theme.Font.Bold else theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = color,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -251,11 +251,11 @@ function DomLabel:render()
|
||||
then e("TextLabel", {
|
||||
Text = props.changeInfo.edits .. if props.changeInfo.failed then "," else "",
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.SubTextColor,
|
||||
TextTransparency = props.transparency,
|
||||
Size = UDim2.new(0, 0, 0, 16),
|
||||
Size = UDim2.new(0, 0, 0, theme.TextSize.Body),
|
||||
AutomaticSize = Enum.AutomaticSize.X,
|
||||
LayoutOrder = 2,
|
||||
})
|
||||
@@ -264,11 +264,11 @@ function DomLabel:render()
|
||||
then e("TextLabel", {
|
||||
Text = props.changeInfo.failed,
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Diff.Warning,
|
||||
TextTransparency = props.transparency,
|
||||
Size = UDim2.new(0, 0, 0, 16),
|
||||
Size = UDim2.new(0, 0, 0, theme.TextSize.Body),
|
||||
AutomaticSize = Enum.AutomaticSize.X,
|
||||
LayoutOrder = 6,
|
||||
})
|
||||
|
||||
@@ -124,8 +124,8 @@ function PatchVisualizer:render()
|
||||
CleanMerge = e("TextLabel", {
|
||||
Visible = #scrollElements == 0,
|
||||
Text = "No changes to sync, project is up to date.",
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Medium,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextWrapped = true,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
local Packages = Rojo.Packages
|
||||
@@ -11,6 +9,7 @@ local StringDiff = require(script:FindFirstChild("StringDiff"))
|
||||
|
||||
local Timer = require(Plugin.Timer)
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local CodeLabel = require(Plugin.App.Components.CodeLabel)
|
||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||
@@ -32,7 +31,6 @@ function StringDiffVisualizer:init()
|
||||
end)
|
||||
end)
|
||||
|
||||
self:calculateContentSize()
|
||||
self:updateScriptBackground()
|
||||
|
||||
self:setState({
|
||||
@@ -54,7 +52,6 @@ end
|
||||
|
||||
function StringDiffVisualizer:didUpdate(previousProps)
|
||||
if previousProps.oldString ~= self.props.oldString or previousProps.newString ~= self.props.newString then
|
||||
self:calculateContentSize()
|
||||
local add, remove = self:calculateDiffLines()
|
||||
self:setState({
|
||||
add = add,
|
||||
@@ -63,11 +60,11 @@ function StringDiffVisualizer:didUpdate(previousProps)
|
||||
end
|
||||
end
|
||||
|
||||
function StringDiffVisualizer:calculateContentSize()
|
||||
function StringDiffVisualizer:calculateContentSize(theme)
|
||||
local oldString, newString = self.props.oldString, self.props.newString
|
||||
|
||||
local oldStringBounds = TextService:GetTextSize(oldString, 16, Enum.Font.RobotoMono, Vector2.new(99999, 99999))
|
||||
local newStringBounds = TextService:GetTextSize(newString, 16, Enum.Font.RobotoMono, Vector2.new(99999, 99999))
|
||||
local oldStringBounds = getTextBoundsAsync(oldString, theme.Font.Code, theme.TextSize.Code, math.huge)
|
||||
local newStringBounds = getTextBoundsAsync(newString, theme.Font.Code, theme.TextSize.Code, math.huge)
|
||||
|
||||
self.setContentSize(
|
||||
Vector2.new(math.max(oldStringBounds.X, newStringBounds.X), math.max(oldStringBounds.Y, newStringBounds.Y))
|
||||
@@ -143,6 +140,8 @@ function StringDiffVisualizer:render()
|
||||
local oldString, newString = self.props.oldString, self.props.newString
|
||||
|
||||
return Theme.with(function(theme)
|
||||
self:calculateContentSize(theme)
|
||||
|
||||
return e(BorderedContainer, {
|
||||
size = self.props.size,
|
||||
position = self.props.position,
|
||||
|
||||
@@ -152,8 +152,8 @@ function Array:render()
|
||||
BackgroundTransparency = 1,
|
||||
Text = "Old",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
}),
|
||||
@@ -163,8 +163,8 @@ function Array:render()
|
||||
BackgroundTransparency = 1,
|
||||
Text = "New",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
}),
|
||||
|
||||
@@ -112,8 +112,8 @@ function Dictionary:render()
|
||||
BackgroundTransparency = 1,
|
||||
Text = key,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
}),
|
||||
@@ -157,8 +157,8 @@ function Dictionary:render()
|
||||
BackgroundTransparency = 1,
|
||||
Text = "Key",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
}),
|
||||
@@ -168,8 +168,8 @@ function Dictionary:render()
|
||||
BackgroundTransparency = 1,
|
||||
Text = "Old",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
}),
|
||||
@@ -179,8 +179,8 @@ function Dictionary:render()
|
||||
BackgroundTransparency = 1,
|
||||
Text = "New",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 14,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
}),
|
||||
|
||||
@@ -4,6 +4,7 @@ local Packages = Rojo.Packages
|
||||
|
||||
local Roact = require(Packages.Roact)
|
||||
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local Assets = require(Plugin.Assets)
|
||||
|
||||
local SlicedImage = require(Plugin.App.Components.SlicedImage)
|
||||
@@ -11,46 +12,48 @@ local SlicedImage = require(Plugin.App.Components.SlicedImage)
|
||||
local e = Roact.createElement
|
||||
|
||||
return function(props)
|
||||
return e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = props.color,
|
||||
transparency = props.transparency:map(function(transparency)
|
||||
return 0.9 + (0.1 * transparency)
|
||||
end),
|
||||
layoutOrder = props.layoutOrder,
|
||||
position = props.position,
|
||||
anchorPoint = props.anchorPoint,
|
||||
size = UDim2.new(0, 0, 0, 16),
|
||||
automaticSize = Enum.AutomaticSize.X,
|
||||
}, {
|
||||
Padding = e("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 4),
|
||||
PaddingRight = UDim.new(0, 4),
|
||||
PaddingTop = UDim.new(0, 2),
|
||||
PaddingBottom = UDim.new(0, 2),
|
||||
}),
|
||||
Icon = if props.icon
|
||||
then e("ImageLabel", {
|
||||
Size = UDim2.new(0, 12, 0, 12),
|
||||
Position = UDim2.new(0, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Image = props.icon,
|
||||
return Theme.with(function(theme)
|
||||
return e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = props.color,
|
||||
transparency = props.transparency:map(function(transparency)
|
||||
return 0.9 + (0.1 * transparency)
|
||||
end),
|
||||
layoutOrder = props.layoutOrder,
|
||||
position = props.position,
|
||||
anchorPoint = props.anchorPoint,
|
||||
size = UDim2.new(0, 0, 0, theme.TextSize.Medium),
|
||||
automaticSize = Enum.AutomaticSize.X,
|
||||
}, {
|
||||
Padding = e("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 4),
|
||||
PaddingRight = UDim.new(0, 4),
|
||||
PaddingTop = UDim.new(0, 2),
|
||||
PaddingBottom = UDim.new(0, 2),
|
||||
}),
|
||||
Icon = if props.icon
|
||||
then e("ImageLabel", {
|
||||
Size = UDim2.new(0, 12, 0, 12),
|
||||
Position = UDim2.new(0, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Image = props.icon,
|
||||
BackgroundTransparency = 1,
|
||||
ImageColor3 = props.color,
|
||||
ImageTransparency = props.transparency,
|
||||
})
|
||||
else nil,
|
||||
Text = e("TextLabel", {
|
||||
Text = props.text,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Small,
|
||||
TextColor3 = props.color,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextTransparency = props.transparency,
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
Position = UDim2.new(0, if props.icon then 15 else 0, 0, 0),
|
||||
AutomaticSize = Enum.AutomaticSize.X,
|
||||
BackgroundTransparency = 1,
|
||||
ImageColor3 = props.color,
|
||||
ImageTransparency = props.transparency,
|
||||
})
|
||||
else nil,
|
||||
Text = e("TextLabel", {
|
||||
Text = props.text,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 12,
|
||||
TextColor3 = props.color,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextTransparency = props.transparency,
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
Position = UDim2.new(0, if props.icon then 15 else 0, 0, 0),
|
||||
AutomaticSize = Enum.AutomaticSize.X,
|
||||
BackgroundTransparency = 1,
|
||||
}),
|
||||
})
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
local Packages = Rojo.Packages
|
||||
@@ -10,6 +8,7 @@ local Flipper = require(Packages.Flipper)
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local Assets = require(Plugin.Assets)
|
||||
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local SlicedImage = require(script.Parent.SlicedImage)
|
||||
local TouchRipple = require(script.Parent.TouchRipple)
|
||||
@@ -41,18 +40,17 @@ end
|
||||
|
||||
function TextButton:render()
|
||||
return Theme.with(function(theme)
|
||||
local textSize =
|
||||
TextService:GetTextSize(self.props.text, 18, Enum.Font.GothamMedium, Vector2.new(math.huge, math.huge))
|
||||
local textBounds = getTextBoundsAsync(self.props.text, theme.Font.Main, theme.TextSize.Large, math.huge)
|
||||
|
||||
local style = self.props.style
|
||||
|
||||
theme = theme.Button[style]
|
||||
local buttonTheme = 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),
|
||||
Size = UDim2.new(0, (theme.TextSize.Body * 2) + textBounds.X, 0, 34),
|
||||
Position = self.props.position,
|
||||
AnchorPoint = self.props.anchorPoint,
|
||||
|
||||
@@ -74,18 +72,22 @@ function TextButton:render()
|
||||
end,
|
||||
}, {
|
||||
TouchRipple = e(TouchRipple, {
|
||||
color = theme.ActionFillColor,
|
||||
color = buttonTheme.ActionFillColor,
|
||||
transparency = self.props.transparency:map(function(value)
|
||||
return bindingUtil.blendAlpha({ theme.ActionFillTransparency, value })
|
||||
return bindingUtil.blendAlpha({ buttonTheme.ActionFillTransparency, value })
|
||||
end),
|
||||
zIndex = 2,
|
||||
}),
|
||||
|
||||
Text = e("TextLabel", {
|
||||
Text = self.props.text,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 18,
|
||||
TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.TextColor, theme.Disabled.TextColor),
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextColor3 = bindingUtil.mapLerp(
|
||||
bindingEnabled,
|
||||
buttonTheme.Enabled.TextColor,
|
||||
buttonTheme.Disabled.TextColor
|
||||
),
|
||||
TextTransparency = self.props.transparency,
|
||||
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
@@ -95,7 +97,11 @@ function TextButton:render()
|
||||
|
||||
Border = style == "Bordered" and e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBorder,
|
||||
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor),
|
||||
color = bindingUtil.mapLerp(
|
||||
bindingEnabled,
|
||||
buttonTheme.Enabled.BorderColor,
|
||||
buttonTheme.Disabled.BorderColor
|
||||
),
|
||||
transparency = self.props.transparency,
|
||||
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
@@ -105,14 +111,18 @@ function TextButton:render()
|
||||
|
||||
HoverOverlay = e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = theme.ActionFillColor,
|
||||
color = buttonTheme.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 })
|
||||
return bindingUtil.blendAlpha({
|
||||
buttonTheme.ActionFillTransparency,
|
||||
values.hover,
|
||||
values.transparency,
|
||||
})
|
||||
end),
|
||||
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
@@ -124,8 +134,8 @@ function TextButton:render()
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = bindingUtil.mapLerp(
|
||||
bindingEnabled,
|
||||
theme.Enabled.BackgroundColor,
|
||||
theme.Disabled.BackgroundColor
|
||||
buttonTheme.Enabled.BackgroundColor,
|
||||
buttonTheme.Disabled.BackgroundColor
|
||||
),
|
||||
transparency = self.props.transparency,
|
||||
|
||||
|
||||
@@ -38,14 +38,18 @@ end
|
||||
|
||||
function TextInput:render()
|
||||
return Theme.with(function(theme)
|
||||
theme = theme.TextInput
|
||||
local textInputTheme = theme.TextInput
|
||||
|
||||
local bindingHover = bindingUtil.deriveProperty(self.binding, "hover")
|
||||
local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled")
|
||||
|
||||
return e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBorder,
|
||||
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor),
|
||||
color = bindingUtil.mapLerp(
|
||||
bindingEnabled,
|
||||
textInputTheme.Enabled.BorderColor,
|
||||
textInputTheme.Disabled.BorderColor
|
||||
),
|
||||
transparency = self.props.transparency,
|
||||
|
||||
size = self.props.size or UDim2.new(1, 0, 1, 0),
|
||||
@@ -55,14 +59,18 @@ function TextInput:render()
|
||||
}, {
|
||||
HoverOverlay = e(SlicedImage, {
|
||||
slice = Assets.Slices.RoundedBackground,
|
||||
color = theme.ActionFillColor,
|
||||
color = textInputTheme.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 })
|
||||
return bindingUtil.blendAlpha({
|
||||
textInputTheme.ActionFillTransparency,
|
||||
values.hover,
|
||||
values.transparency,
|
||||
})
|
||||
end),
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
zIndex = -1,
|
||||
@@ -72,14 +80,18 @@ function TextInput:render()
|
||||
Size = UDim2.fromScale(1, 1),
|
||||
Text = self.props.text,
|
||||
PlaceholderText = self.props.placeholder,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Disabled.TextColor, theme.Enabled.TextColor),
|
||||
FontFace = theme.Font.Main,
|
||||
TextColor3 = bindingUtil.mapLerp(
|
||||
bindingEnabled,
|
||||
textInputTheme.Disabled.TextColor,
|
||||
textInputTheme.Enabled.TextColor
|
||||
),
|
||||
PlaceholderColor3 = bindingUtil.mapLerp(
|
||||
bindingEnabled,
|
||||
theme.Disabled.PlaceholderColor,
|
||||
theme.Enabled.PlaceholderColor
|
||||
textInputTheme.Disabled.PlaceholderColor,
|
||||
textInputTheme.Enabled.PlaceholderColor
|
||||
),
|
||||
TextSize = 18,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextEditable = self.props.enabled,
|
||||
ClearTextOnFocus = self.props.clearTextOnFocus,
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
local HttpService = game:GetService("HttpService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
@@ -8,6 +7,8 @@ local Packages = Rojo.Packages
|
||||
local Roact = require(Packages.Roact)
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||
|
||||
local e = Roact.createElement
|
||||
@@ -21,50 +22,48 @@ local Y_OVERLAP = 10 -- Let the triangle tail piece overlap the target a bit to
|
||||
local TooltipContext = Roact.createContext({})
|
||||
|
||||
local function Popup(props)
|
||||
local textSize = TextService:GetTextSize(
|
||||
props.Text,
|
||||
16,
|
||||
Enum.Font.GothamMedium,
|
||||
Vector2.new(math.min(props.parentSize.X, 160), math.huge)
|
||||
) + TEXT_PADDING + (Vector2.one * 2)
|
||||
|
||||
local trigger = props.Trigger:getValue()
|
||||
|
||||
local spaceBelow = props.parentSize.Y
|
||||
- (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE)
|
||||
local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE
|
||||
|
||||
-- If there's not enough space below, and there's more space above, then show the tooltip above the trigger
|
||||
local displayAbove = spaceBelow < textSize.Y and spaceAbove > spaceBelow
|
||||
|
||||
local X = math.clamp(props.Position.X - X_OFFSET, 0, props.parentSize.X - textSize.X)
|
||||
local Y = 0
|
||||
|
||||
if displayAbove then
|
||||
Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - textSize.Y + Y_OVERLAP, 0)
|
||||
else
|
||||
Y = math.min(
|
||||
trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP,
|
||||
props.parentSize.Y - textSize.Y
|
||||
)
|
||||
end
|
||||
|
||||
return Theme.with(function(theme)
|
||||
local textXSpace = math.min(props.parentSize.X, 250) - TEXT_PADDING.X
|
||||
local textBounds = getTextBoundsAsync(props.Text, theme.Font.Main, theme.TextSize.Medium, textXSpace)
|
||||
local contentSize = textBounds + TEXT_PADDING + (Vector2.one * 2)
|
||||
|
||||
local trigger = props.Trigger:getValue()
|
||||
|
||||
local spaceBelow = props.parentSize.Y
|
||||
- (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE)
|
||||
local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE
|
||||
|
||||
-- If there's not enough space below, and there's more space above, then show the tooltip above the trigger
|
||||
local displayAbove = spaceBelow < contentSize.Y and spaceAbove > spaceBelow
|
||||
|
||||
local X = math.clamp(props.Position.X - X_OFFSET, 0, math.max(props.parentSize.X - contentSize.X, 1))
|
||||
local Y = 0
|
||||
|
||||
if displayAbove then
|
||||
Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - contentSize.Y + Y_OVERLAP, 0)
|
||||
else
|
||||
Y = math.min(
|
||||
trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP,
|
||||
props.parentSize.Y - contentSize.Y
|
||||
)
|
||||
end
|
||||
|
||||
return e(BorderedContainer, {
|
||||
position = UDim2.fromOffset(X, Y),
|
||||
size = UDim2.fromOffset(textSize.X, textSize.Y),
|
||||
size = UDim2.fromOffset(contentSize.X, contentSize.Y),
|
||||
transparency = props.transparency,
|
||||
}, {
|
||||
Label = e("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.fromScale(0.5, 0.5),
|
||||
Size = UDim2.new(1, -TEXT_PADDING.X, 1, -TEXT_PADDING.Y),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Size = UDim2.fromOffset(textBounds.X, textBounds.Y),
|
||||
Text = props.Text,
|
||||
TextSize = 16,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = theme.TextSize.Medium,
|
||||
FontFace = theme.Font.Main,
|
||||
TextWrapped = true,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
TextColor3 = theme.Button.Bordered.Enabled.TextColor,
|
||||
TextTransparency = props.transparency,
|
||||
}),
|
||||
@@ -72,8 +71,8 @@ local function Popup(props)
|
||||
Tail = e("ImageLabel", {
|
||||
ZIndex = 100,
|
||||
Position = if displayAbove
|
||||
then UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 1, -1)
|
||||
else UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 0, -TAIL_SIZE + 1),
|
||||
then UDim2.new(0, math.clamp(props.Position.X - X, 6, contentSize.X - 6), 1, -1)
|
||||
else UDim2.new(0, math.clamp(props.Position.X - X, 6, contentSize.X - 6), 0, -TAIL_SIZE + 1),
|
||||
Size = UDim2.fromOffset(TAIL_SIZE, TAIL_SIZE),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Rotation = if displayAbove then 180 else 0,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
local StudioService = game:GetService("StudioService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
@@ -9,10 +8,10 @@ local Roact = require(Packages.Roact)
|
||||
local Flipper = require(Packages.Flipper)
|
||||
local Log = require(Packages.Log)
|
||||
|
||||
local bindingUtil = require(script.Parent.bindingUtil)
|
||||
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local Assets = require(Plugin.Assets)
|
||||
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||
local TextButton = require(Plugin.App.Components.TextButton)
|
||||
@@ -86,51 +85,46 @@ function Notification:render()
|
||||
return 1 - value
|
||||
end)
|
||||
|
||||
local textBounds = TextService:GetTextSize(self.props.text, 15, Enum.Font.GothamMedium, Vector2.new(350, 700))
|
||||
return Theme.with(function(theme)
|
||||
local actionButtons = {}
|
||||
local buttonsX = 0
|
||||
if self.props.actions then
|
||||
local count = 0
|
||||
for key, action in self.props.actions do
|
||||
actionButtons[key] = e(TextButton, {
|
||||
text = action.text,
|
||||
style = action.style,
|
||||
onClick = function()
|
||||
local success, err = pcall(action.onClick, self)
|
||||
if not success then
|
||||
Log.warn("Error in notification action: " .. tostring(err))
|
||||
end
|
||||
end,
|
||||
layoutOrder = -action.layoutOrder,
|
||||
transparency = transparency,
|
||||
})
|
||||
|
||||
local actionButtons = {}
|
||||
local buttonsX = 0
|
||||
if self.props.actions then
|
||||
local count = 0
|
||||
for key, action in self.props.actions do
|
||||
actionButtons[key] = e(TextButton, {
|
||||
text = action.text,
|
||||
style = action.style,
|
||||
onClick = function()
|
||||
local success, err = pcall(action.onClick, self)
|
||||
if not success then
|
||||
Log.warn("Error in notification action: " .. tostring(err))
|
||||
end
|
||||
end,
|
||||
layoutOrder = -action.layoutOrder,
|
||||
transparency = transparency,
|
||||
})
|
||||
buttonsX += getTextBoundsAsync(action.text, theme.Font.Main, theme.TextSize.Large, math.huge).X + (theme.TextSize.Body * 2)
|
||||
|
||||
buttonsX += TextService:GetTextSize(
|
||||
action.text,
|
||||
18,
|
||||
Enum.Font.GothamMedium,
|
||||
Vector2.new(math.huge, math.huge)
|
||||
).X + 30
|
||||
count += 1
|
||||
end
|
||||
|
||||
count += 1
|
||||
buttonsX += (count - 1) * 5
|
||||
end
|
||||
|
||||
buttonsX += (count - 1) * 5
|
||||
end
|
||||
local paddingY, logoSize = 20, 32
|
||||
local actionsY = if self.props.actions then 37 else 0
|
||||
local textXSpace = math.max(250, buttonsX) + 35
|
||||
local textBounds = getTextBoundsAsync(self.props.text, theme.Font.Main, theme.TextSize.Body, textXSpace)
|
||||
local contentX = math.max(textBounds.X, buttonsX)
|
||||
|
||||
local paddingY, logoSize = 20, 32
|
||||
local actionsY = if self.props.actions then 35 else 0
|
||||
local contentX = math.max(textBounds.X, buttonsX)
|
||||
local size = self.binding:map(function(value)
|
||||
return UDim2.fromOffset(
|
||||
(35 + 40 + contentX) * value,
|
||||
5 + actionsY + paddingY + math.max(logoSize, textBounds.Y)
|
||||
)
|
||||
end)
|
||||
|
||||
local size = self.binding:map(function(value)
|
||||
return UDim2.fromOffset(
|
||||
(35 + 40 + contentX) * value,
|
||||
5 + actionsY + paddingY + math.max(logoSize, textBounds.Y)
|
||||
)
|
||||
end)
|
||||
|
||||
return Theme.with(function(theme)
|
||||
return e("TextButton", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = size,
|
||||
@@ -147,8 +141,7 @@ function Notification:render()
|
||||
size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
Contents = e("Frame", {
|
||||
Size = UDim2.new(0, 35 + contentX, 1, -paddingY),
|
||||
Position = UDim2.new(0, 0, 0, paddingY / 2),
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
}, {
|
||||
Logo = e("ImageLabel", {
|
||||
@@ -161,14 +154,15 @@ function Notification:render()
|
||||
}),
|
||||
Info = e("TextLabel", {
|
||||
Text = self.props.text,
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Notification.InfoColor,
|
||||
TextTransparency = transparency,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
TextWrapped = true,
|
||||
|
||||
Size = UDim2.new(0, textBounds.X, 0, textBounds.Y),
|
||||
Size = UDim2.new(0, textBounds.X, 1, -actionsY),
|
||||
Position = UDim2.fromOffset(35, 0),
|
||||
|
||||
LayoutOrder = 1,
|
||||
@@ -176,7 +170,7 @@ function Notification:render()
|
||||
}),
|
||||
Actions = if self.props.actions
|
||||
then e("Frame", {
|
||||
Size = UDim2.new(1, -40, 0, 35),
|
||||
Size = UDim2.new(1, -40, 0, actionsY),
|
||||
Position = UDim2.new(1, 0, 1, 0),
|
||||
AnchorPoint = Vector2.new(1, 1),
|
||||
BackgroundTransparency = 1,
|
||||
@@ -196,6 +190,8 @@ function Notification:render()
|
||||
Padding = e("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 17),
|
||||
PaddingRight = UDim.new(0, 15),
|
||||
PaddingTop = UDim.new(0, paddingY / 2),
|
||||
PaddingBottom = UDim.new(0, paddingY / 2),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -64,13 +64,13 @@ function ConfirmingPage:render()
|
||||
"Sync changes for project '%s':",
|
||||
self.props.confirmData.serverInfo.projectName or "UNKNOWN"
|
||||
),
|
||||
Font = Enum.Font.Gotham,
|
||||
FontFace = theme.Font.Thin,
|
||||
LineHeight = 1.2,
|
||||
TextSize = 14,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = self.props.transparency,
|
||||
Size = UDim2.new(1, 0, 0, 20),
|
||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Large + 2),
|
||||
BackgroundTransparency = 1,
|
||||
}),
|
||||
|
||||
|
||||
@@ -61,12 +61,12 @@ function ChangesViewer:render()
|
||||
|
||||
Title = e("TextLabel", {
|
||||
Text = "Sync",
|
||||
Font = Enum.Font.GothamMedium,
|
||||
TextSize = 17,
|
||||
FontFace = theme.Font.Main,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextTransparency = self.props.transparency,
|
||||
Size = UDim2.new(1, -40, 0, 20),
|
||||
Size = UDim2.new(1, -40, 0, theme.TextSize.Large + 2),
|
||||
Position = UDim2.new(0, 40, 0, 0),
|
||||
BackgroundTransparency = 1,
|
||||
}),
|
||||
@@ -74,13 +74,13 @@ function ChangesViewer:render()
|
||||
Subtitle = e("TextLabel", {
|
||||
Text = DateTime.fromUnixTimestamp(self.props.patchData.timestamp):FormatLocalTime("LTS", "en-us"),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Medium,
|
||||
TextColor3 = theme.SubTextColor,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
TextTransparency = self.props.transparency,
|
||||
Size = UDim2.new(1, -40, 0, 16),
|
||||
Position = UDim2.new(0, 40, 0, 20),
|
||||
Size = UDim2.new(1, -40, 0, theme.TextSize.Medium),
|
||||
Position = UDim2.new(0, 40, 0, theme.TextSize.Large + 2),
|
||||
BackgroundTransparency = 1,
|
||||
}),
|
||||
|
||||
@@ -131,8 +131,8 @@ function ChangesViewer:render()
|
||||
}),
|
||||
AppliedText = e("TextLabel", {
|
||||
Text = applied,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.TextColor,
|
||||
TextTransparency = self.props.transparency,
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
@@ -156,8 +156,8 @@ function ChangesViewer:render()
|
||||
}),
|
||||
UnappliedText = e("TextLabel", {
|
||||
Text = unapplied,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = theme.Diff.Warning,
|
||||
TextTransparency = self.props.transparency,
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
@@ -217,13 +217,13 @@ local function ConnectionDetails(props)
|
||||
}, {
|
||||
ProjectName = e("TextLabel", {
|
||||
Text = props.projectName,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 20,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextColor3 = theme.ConnectionDetails.ProjectNameColor,
|
||||
TextTransparency = props.transparency,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
|
||||
Size = UDim2.new(1, 0, 0, 20),
|
||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Large),
|
||||
|
||||
LayoutOrder = 1,
|
||||
BackgroundTransparency = 1,
|
||||
@@ -231,13 +231,13 @@ local function ConnectionDetails(props)
|
||||
|
||||
Address = e("TextLabel", {
|
||||
Text = props.address,
|
||||
Font = Enum.Font.Code,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Code,
|
||||
TextSize = theme.TextSize.Medium,
|
||||
TextColor3 = theme.ConnectionDetails.AddressColor,
|
||||
TextTransparency = props.transparency,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
|
||||
Size = UDim2.new(1, 0, 0, 15),
|
||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Medium),
|
||||
|
||||
LayoutOrder = 2,
|
||||
BackgroundTransparency = 1,
|
||||
@@ -410,8 +410,8 @@ function ConnectedPage:render()
|
||||
Text = e("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Text = self.changeInfoText,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 15,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor,
|
||||
TextTransparency = self.props.transparency,
|
||||
TextXAlignment = Enum.TextXAlignment.Right,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
local Packages = Rojo.Packages
|
||||
@@ -7,6 +5,7 @@ local Packages = Rojo.Packages
|
||||
local Roact = require(Packages.Roact)
|
||||
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local TextButton = require(Plugin.App.Components.TextButton)
|
||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||
@@ -24,43 +23,43 @@ function Error:init()
|
||||
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
|
||||
return Theme.with(function(theme)
|
||||
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
|
||||
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
|
||||
return UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y))
|
||||
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("TextBox", {
|
||||
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 = getTextBoundsAsync(
|
||||
self.props.errorMessage,
|
||||
theme.Font.Code,
|
||||
theme.TextSize.Code,
|
||||
containerSize.X
|
||||
)
|
||||
|
||||
self.setContentSize(Vector2.new(containerSize.X, textBounds.Y))
|
||||
end,
|
||||
}, {
|
||||
ErrorMessage = e("TextBox", {
|
||||
[Roact.Event.InputBegan] = function(rbx, input)
|
||||
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then
|
||||
return
|
||||
@@ -71,8 +70,8 @@ function Error:render()
|
||||
|
||||
Text = self.props.errorMessage,
|
||||
TextEditable = false,
|
||||
Font = Enum.Font.Code,
|
||||
TextSize = 16,
|
||||
FontFace = theme.Font.Code,
|
||||
TextSize = theme.TextSize.Code,
|
||||
TextColor3 = theme.ErrorColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
@@ -81,17 +80,17 @@ function Error:render()
|
||||
ClearTextOnFocus = false,
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
})
|
||||
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),
|
||||
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)
|
||||
end
|
||||
|
||||
local ErrorPage = Roact.Component:extend("ErrorPage")
|
||||
|
||||
@@ -27,8 +27,8 @@ local function AddressEntry(props)
|
||||
}, {
|
||||
Host = e("TextBox", {
|
||||
Text = props.host or "",
|
||||
Font = Enum.Font.Code,
|
||||
TextSize = 18,
|
||||
FontFace = theme.Font.Code,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextColor3 = theme.AddressEntry.TextColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = props.transparency,
|
||||
@@ -51,8 +51,8 @@ local function AddressEntry(props)
|
||||
|
||||
Port = e("TextBox", {
|
||||
Text = props.port or "",
|
||||
Font = Enum.Font.Code,
|
||||
TextSize = 18,
|
||||
FontFace = theme.Font.Code,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextColor3 = theme.AddressEntry.TextColor,
|
||||
TextTransparency = props.transparency,
|
||||
PlaceholderText = Config.defaultPort,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
local Packages = Rojo.Packages
|
||||
@@ -9,6 +7,7 @@ local Roact = require(Packages.Roact)
|
||||
local Settings = require(Plugin.Settings)
|
||||
local Assets = require(Plugin.Assets)
|
||||
local Theme = require(Plugin.App.Theme)
|
||||
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
||||
|
||||
local Checkbox = require(Plugin.App.Components.Checkbox)
|
||||
local Dropdown = require(Plugin.App.Components.Dropdown)
|
||||
@@ -31,10 +30,16 @@ local TAG_TYPES = {
|
||||
},
|
||||
}
|
||||
|
||||
local function getTextBounds(text, textSize, font, lineHeight, bounds)
|
||||
local textBounds = TextService:GetTextSize(text, textSize, font, bounds)
|
||||
local function getTextBoundsWithLineHeight(
|
||||
text: string,
|
||||
font: Font,
|
||||
textSize: number,
|
||||
width: number,
|
||||
lineHeight: number
|
||||
)
|
||||
local textBounds = getTextBoundsAsync(text, font, textSize, width)
|
||||
|
||||
local lineCount = textBounds.Y / textSize
|
||||
local lineCount = math.ceil(textBounds.Y / textSize)
|
||||
local lineHeightAbsolute = textSize * lineHeight
|
||||
|
||||
return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize))
|
||||
@@ -109,6 +114,7 @@ function Setting:render()
|
||||
then self.props.input
|
||||
elseif self.props.options ~= nil then e(Dropdown, {
|
||||
locked = self.props.locked,
|
||||
lockedTooltip = self.props.lockedTooltip,
|
||||
options = self.props.options,
|
||||
active = self.state.setting,
|
||||
transparency = self.props.transparency,
|
||||
@@ -118,6 +124,7 @@ function Setting:render()
|
||||
})
|
||||
else e(Checkbox, {
|
||||
locked = self.props.locked,
|
||||
lockedTooltip = self.props.lockedTooltip,
|
||||
active = self.state.setting,
|
||||
transparency = self.props.transparency,
|
||||
onClick = function()
|
||||
@@ -145,7 +152,7 @@ function Setting:render()
|
||||
BackgroundTransparency = 1,
|
||||
}, {
|
||||
Heading = e("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, 16),
|
||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Medium),
|
||||
BackgroundTransparency = 1,
|
||||
}, {
|
||||
Layout = e("UIListLayout", {
|
||||
@@ -165,8 +172,8 @@ function Setting:render()
|
||||
else nil,
|
||||
Name = e("TextLabel", {
|
||||
Text = self.props.name,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextSize = 16,
|
||||
FontFace = theme.Font.Bold,
|
||||
TextSize = theme.TextSize.Medium,
|
||||
TextColor3 = if self.props.tag and TAG_TYPES[self.props.tag]
|
||||
then getThemeColorFromPath(theme, TAG_TYPES[self.props.tag].color)
|
||||
else settingsTheme.Setting.NameColor,
|
||||
@@ -174,7 +181,7 @@ function Setting:render()
|
||||
TextTransparency = self.props.transparency,
|
||||
RichText = true,
|
||||
|
||||
Size = UDim2.new(1, 0, 0, 16),
|
||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Medium),
|
||||
|
||||
LayoutOrder = 2,
|
||||
BackgroundTransparency = 1,
|
||||
@@ -183,9 +190,9 @@ function Setting:render()
|
||||
|
||||
Description = e("TextLabel", {
|
||||
Text = self.props.description,
|
||||
Font = Enum.Font.Gotham,
|
||||
FontFace = theme.Font.Main,
|
||||
LineHeight = 1.2,
|
||||
TextSize = 14,
|
||||
TextSize = theme.TextSize.Body,
|
||||
TextColor3 = settingsTheme.Setting.DescriptionColor,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = self.props.transparency,
|
||||
@@ -197,12 +204,12 @@ function Setting:render()
|
||||
inputSize = self.inputSize,
|
||||
}):map(function(values)
|
||||
local offset = values.inputSize.X + 5
|
||||
local textBounds = getTextBounds(
|
||||
local textBounds = getTextBoundsWithLineHeight(
|
||||
self.props.description,
|
||||
14,
|
||||
Enum.Font.Gotham,
|
||||
1.2,
|
||||
Vector2.new(values.containerSize.X - offset, math.huge)
|
||||
theme.Font.Main,
|
||||
theme.TextSize.Body,
|
||||
values.containerSize.X - offset,
|
||||
1.2
|
||||
)
|
||||
return UDim2.new(1, -offset, 0, textBounds.Y)
|
||||
end),
|
||||
|
||||
@@ -30,7 +30,7 @@ local confirmationBehaviors = { "Initial", "Always", "Large Changes", "Unlisted
|
||||
|
||||
local function Navbar(props)
|
||||
return Theme.with(function(theme)
|
||||
theme = theme.Settings.Navbar
|
||||
local navbarTheme = theme.Settings.Navbar
|
||||
|
||||
return e("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, 46),
|
||||
@@ -40,7 +40,7 @@ local function Navbar(props)
|
||||
Back = e(IconButton, {
|
||||
icon = Assets.Images.Icons.Back,
|
||||
iconSize = 24,
|
||||
color = theme.BackButtonColor,
|
||||
color = navbarTheme.BackButtonColor,
|
||||
transparency = props.transparency,
|
||||
|
||||
position = UDim2.new(0, 0, 0.5, 0),
|
||||
@@ -55,9 +55,9 @@ local function Navbar(props)
|
||||
|
||||
Text = e("TextLabel", {
|
||||
Text = "Settings",
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = 18,
|
||||
TextColor3 = theme.TextColor,
|
||||
FontFace = theme.Font.Thin,
|
||||
TextSize = theme.TextSize.Large,
|
||||
TextColor3 = navbarTheme.TextColor,
|
||||
TextTransparency = props.transparency,
|
||||
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
@@ -81,185 +81,182 @@ function SettingsPage:render()
|
||||
return layoutOrder
|
||||
end
|
||||
|
||||
return Theme.with(function(theme)
|
||||
theme = theme.Settings
|
||||
|
||||
return Roact.createFragment({
|
||||
Navbar = e(Navbar, {
|
||||
onBack = self.props.onBack,
|
||||
return Roact.createFragment({
|
||||
Navbar = e(Navbar, {
|
||||
onBack = self.props.onBack,
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
Content = e(ScrollingFrame, {
|
||||
size = UDim2.new(1, 0, 1, -47),
|
||||
position = UDim2.new(0, 0, 0, 47),
|
||||
contentSize = self.contentSize,
|
||||
transparency = self.props.transparency,
|
||||
}, {
|
||||
ShowNotifications = e(Setting, {
|
||||
id = "showNotifications",
|
||||
name = "Show Notifications",
|
||||
description = "Popup notifications in viewport",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
Content = e(ScrollingFrame, {
|
||||
size = UDim2.new(1, 0, 1, -47),
|
||||
position = UDim2.new(0, 0, 0, 47),
|
||||
contentSize = self.contentSize,
|
||||
|
||||
SyncReminder = e(Setting, {
|
||||
id = "syncReminder",
|
||||
name = "Sync Reminder",
|
||||
description = "Notify to sync when opening a place that has previously been synced",
|
||||
transparency = self.props.transparency,
|
||||
}, {
|
||||
ShowNotifications = e(Setting, {
|
||||
id = "showNotifications",
|
||||
name = "Show Notifications",
|
||||
description = "Popup notifications in viewport",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
visible = Settings:getBinding("showNotifications"),
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
SyncReminder = e(Setting, {
|
||||
id = "syncReminder",
|
||||
name = "Sync Reminder",
|
||||
description = "Notify to sync when opening a place that has previously been synced",
|
||||
transparency = self.props.transparency,
|
||||
visible = Settings:getBinding("showNotifications"),
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
ConfirmationBehavior = e(Setting, {
|
||||
id = "confirmationBehavior",
|
||||
name = "Confirmation Behavior",
|
||||
description = "When to prompt for confirmation before syncing",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
|
||||
ConfirmationBehavior = e(Setting, {
|
||||
id = "confirmationBehavior",
|
||||
name = "Confirmation Behavior",
|
||||
description = "When to prompt for confirmation before syncing",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
options = confirmationBehaviors,
|
||||
}),
|
||||
|
||||
options = confirmationBehaviors,
|
||||
}),
|
||||
|
||||
LargeChangesConfirmationThreshold = e(Setting, {
|
||||
id = "largeChangesConfirmationThreshold",
|
||||
name = "Confirmation Threshold",
|
||||
description = "How many modified instances to be considered a large change",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
visible = Settings:getBinding("confirmationBehavior"):map(function(value)
|
||||
return value == "Large Changes"
|
||||
LargeChangesConfirmationThreshold = e(Setting, {
|
||||
id = "largeChangesConfirmationThreshold",
|
||||
name = "Confirmation Threshold",
|
||||
description = "How many modified instances to be considered a large change",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
visible = Settings:getBinding("confirmationBehavior"):map(function(value)
|
||||
return value == "Large Changes"
|
||||
end),
|
||||
input = e(TextInput, {
|
||||
size = UDim2.new(0, 40, 0, 28),
|
||||
text = Settings:getBinding("largeChangesConfirmationThreshold"):map(function(value)
|
||||
return tostring(value)
|
||||
end),
|
||||
input = e(TextInput, {
|
||||
size = UDim2.new(0, 40, 0, 28),
|
||||
text = Settings:getBinding("largeChangesConfirmationThreshold"):map(function(value)
|
||||
return tostring(value)
|
||||
end),
|
||||
transparency = self.props.transparency,
|
||||
enabled = true,
|
||||
onEntered = function(text)
|
||||
local number = tonumber(string.match(text, "%d+"))
|
||||
if number then
|
||||
Settings:set("largeChangesConfirmationThreshold", math.clamp(number, 1, 999))
|
||||
else
|
||||
-- Force text back to last valid value
|
||||
Settings:set(
|
||||
"largeChangesConfirmationThreshold",
|
||||
Settings:get("largeChangesConfirmationThreshold")
|
||||
)
|
||||
end
|
||||
end,
|
||||
}),
|
||||
}),
|
||||
|
||||
PlaySounds = e(Setting, {
|
||||
id = "playSounds",
|
||||
name = "Play Sounds",
|
||||
description = "Toggle sound effects",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
CheckForUpdates = e(Setting, {
|
||||
id = "checkForUpdates",
|
||||
name = "Check For Updates",
|
||||
description = "Notify about newer compatible Rojo releases",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
CheckForPreleases = e(Setting, {
|
||||
id = "checkForPrereleases",
|
||||
name = "Include Prerelease Updates",
|
||||
description = "Include prereleases when checking for updates",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
visible = if string.find(debug.traceback(), "\n[^\n]-user_.-$") == nil
|
||||
then false -- Must be a local install to allow prerelease checks
|
||||
else Settings:getBinding("checkForUpdates"),
|
||||
}),
|
||||
|
||||
AutoConnectPlaytestServer = e(Setting, {
|
||||
id = "autoConnectPlaytestServer",
|
||||
name = "Auto Connect Playtest Server",
|
||||
description = "Automatically connect game server to Rojo when playtesting while connected in Edit",
|
||||
tag = "unstable",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
OpenScriptsExternally = e(Setting, {
|
||||
id = "openScriptsExternally",
|
||||
name = "Open Scripts Externally",
|
||||
description = "Attempt to open scripts in an external editor",
|
||||
tag = "unstable",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
TwoWaySync = e(Setting, {
|
||||
id = "twoWaySync",
|
||||
name = "Two-Way Sync",
|
||||
description = "Editing files in Studio will sync them into the filesystem",
|
||||
locked = self.props.syncActive,
|
||||
tag = "unstable",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
LogLevel = e(Setting, {
|
||||
id = "logLevel",
|
||||
name = "Log Level",
|
||||
description = "Plugin output verbosity level",
|
||||
tag = "debug",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
|
||||
options = invertedLevels,
|
||||
showReset = Settings:getBinding("logLevel"):map(function(value)
|
||||
return value ~= "Info"
|
||||
end),
|
||||
onReset = function()
|
||||
Settings:set("logLevel", "Info")
|
||||
enabled = true,
|
||||
onEntered = function(text)
|
||||
local number = tonumber(string.match(text, "%d+"))
|
||||
if number then
|
||||
Settings:set("largeChangesConfirmationThreshold", math.clamp(number, 1, 999))
|
||||
else
|
||||
-- Force text back to last valid value
|
||||
Settings:set(
|
||||
"largeChangesConfirmationThreshold",
|
||||
Settings:get("largeChangesConfirmationThreshold")
|
||||
)
|
||||
end
|
||||
end,
|
||||
}),
|
||||
|
||||
TypecheckingEnabled = e(Setting, {
|
||||
id = "typecheckingEnabled",
|
||||
name = "Typechecking",
|
||||
description = "Toggle typechecking on the API surface",
|
||||
tag = "debug",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
TimingLogsEnabled = e(Setting, {
|
||||
id = "timingLogsEnabled",
|
||||
name = "Timing Logs",
|
||||
description = "Toggle logging timing of internal actions for benchmarking Rojo performance",
|
||||
tag = "debug",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
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)
|
||||
|
||||
PlaySounds = e(Setting, {
|
||||
id = "playSounds",
|
||||
name = "Play Sounds",
|
||||
description = "Toggle sound effects",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
CheckForUpdates = e(Setting, {
|
||||
id = "checkForUpdates",
|
||||
name = "Check For Updates",
|
||||
description = "Notify about newer compatible Rojo releases",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
CheckForPreleases = e(Setting, {
|
||||
id = "checkForPrereleases",
|
||||
name = "Include Prerelease Updates",
|
||||
description = "Include prereleases when checking for updates",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
visible = if string.find(debug.traceback(), "\n[^\n]-user_.-$") == nil
|
||||
then false -- Must be a local install to allow prerelease checks
|
||||
else Settings:getBinding("checkForUpdates"),
|
||||
}),
|
||||
|
||||
AutoConnectPlaytestServer = e(Setting, {
|
||||
id = "autoConnectPlaytestServer",
|
||||
name = "Auto Connect Playtest Server",
|
||||
description = "Automatically connect game server to Rojo when playtesting while connected in Edit",
|
||||
tag = "unstable",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
OpenScriptsExternally = e(Setting, {
|
||||
id = "openScriptsExternally",
|
||||
name = "Open Scripts Externally",
|
||||
description = "Attempt to open scripts in an external editor",
|
||||
tag = "unstable",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
TwoWaySync = e(Setting, {
|
||||
id = "twoWaySync",
|
||||
name = "Two-Way Sync",
|
||||
description = "Editing files in Studio will sync them into the filesystem",
|
||||
locked = self.props.syncActive,
|
||||
lockedTooltip = "(Cannot change while currently syncing. Disconnect first.)",
|
||||
tag = "unstable",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
LogLevel = e(Setting, {
|
||||
id = "logLevel",
|
||||
name = "Log Level",
|
||||
description = "Plugin output verbosity level",
|
||||
tag = "debug",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
|
||||
options = invertedLevels,
|
||||
showReset = Settings:getBinding("logLevel"):map(function(value)
|
||||
return value ~= "Info"
|
||||
end),
|
||||
onReset = function()
|
||||
Settings:set("logLevel", "Info")
|
||||
end,
|
||||
}),
|
||||
|
||||
TypecheckingEnabled = e(Setting, {
|
||||
id = "typecheckingEnabled",
|
||||
name = "Typechecking",
|
||||
description = "Toggle typechecking on the API surface",
|
||||
tag = "debug",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
TimingLogsEnabled = e(Setting, {
|
||||
id = "timingLogsEnabled",
|
||||
name = "Timing Logs",
|
||||
description = "Toggle logging timing of internal actions for benchmarking Rojo performance",
|
||||
tag = "debug",
|
||||
transparency = self.props.transparency,
|
||||
layoutOrder = layoutIncrement(),
|
||||
}),
|
||||
|
||||
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
|
||||
|
||||
return SettingsPage
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
--[[
|
||||
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.
|
||||
Theming system provided through Roact's context.
|
||||
Uses Studio colors when possible.
|
||||
]]
|
||||
|
||||
-- Studio does not exist outside Roblox Studio, so we'll lazily initialize it
|
||||
@@ -15,6 +14,8 @@ local function getStudio()
|
||||
return _Studio
|
||||
end
|
||||
|
||||
local ContentProvider = game:GetService("ContentProvider")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Packages = Rojo.Packages
|
||||
|
||||
@@ -35,6 +36,27 @@ function StudioProvider:updateTheme()
|
||||
local isDark = studioTheme.Name == "Dark"
|
||||
|
||||
local theme = strict(studioTheme.Name .. "Theme", {
|
||||
Font = {
|
||||
Main = Font.new("rbxasset://fonts/families/Montserrat.json", Enum.FontWeight.Medium, Enum.FontStyle.Normal),
|
||||
Bold = Font.new("rbxasset://fonts/families/Montserrat.json", Enum.FontWeight.Bold, Enum.FontStyle.Normal),
|
||||
Thin = Font.new(
|
||||
"rbxasset://fonts/families/Montserrat.json",
|
||||
Enum.FontWeight.Regular,
|
||||
Enum.FontStyle.Normal
|
||||
),
|
||||
Code = Font.new(
|
||||
"rbxasset://fonts/families/Inconsolata.json",
|
||||
Enum.FontWeight.Regular,
|
||||
Enum.FontStyle.Normal
|
||||
),
|
||||
},
|
||||
TextSize = {
|
||||
Body = 15,
|
||||
Small = 13,
|
||||
Medium = 16,
|
||||
Large = 18,
|
||||
Code = 16,
|
||||
},
|
||||
BrandColor = BRAND_COLOR,
|
||||
BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground),
|
||||
TextColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainText),
|
||||
@@ -190,6 +212,13 @@ end
|
||||
|
||||
function StudioProvider:init()
|
||||
self:updateTheme()
|
||||
|
||||
-- Preload the Fonts so that getTextBoundsAsync won't yield
|
||||
local fontAssetIds = {}
|
||||
for _, font in self.state.theme.Font do
|
||||
table.insert(fontAssetIds, font.Family)
|
||||
end
|
||||
pcall(ContentProvider.PreloadAsync, ContentProvider, fontAssetIds)
|
||||
end
|
||||
|
||||
function StudioProvider:render()
|
||||
|
||||
41
plugin/src/App/getTextBoundsAsync.lua
Normal file
41
plugin/src/App/getTextBoundsAsync.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Packages = Rojo.Packages
|
||||
|
||||
local Log = require(Packages.Log)
|
||||
|
||||
local params = Instance.new("GetTextBoundsParams")
|
||||
|
||||
local function getTextBoundsAsync(
|
||||
text: string,
|
||||
font: Font,
|
||||
textSize: number,
|
||||
width: number,
|
||||
richText: boolean?
|
||||
): Vector2
|
||||
if type(text) ~= "string" then
|
||||
Log.warn(`Invalid text. Expected string, received {type(text)} instead`)
|
||||
return Vector2.zero
|
||||
end
|
||||
if #text >= 200_000 then
|
||||
Log.warn(`Invalid text. Exceeds the 199,999 character limit`)
|
||||
return Vector2.zero
|
||||
end
|
||||
|
||||
params.Text = text
|
||||
params.Font = font
|
||||
params.Size = textSize
|
||||
params.Width = width
|
||||
params.RichText = not not richText
|
||||
|
||||
local success, bounds = pcall(TextService.GetTextBoundsAsync, TextService, params)
|
||||
if not success then
|
||||
Log.warn(`Failed to get text bounds: {bounds}`)
|
||||
return Vector2.zero
|
||||
end
|
||||
|
||||
return bounds
|
||||
end
|
||||
|
||||
return getTextBoundsAsync
|
||||
@@ -52,9 +52,9 @@ local App = Roact.Component:extend("App")
|
||||
function App:init()
|
||||
preloadAssets()
|
||||
|
||||
local priorHost, priorPort = self:getPriorEndpoint()
|
||||
self.host, self.setHost = Roact.createBinding(priorHost or "")
|
||||
self.port, self.setPort = Roact.createBinding(priorPort or "")
|
||||
local priorSyncInfo = self:getPriorSyncInfo()
|
||||
self.host, self.setHost = Roact.createBinding(priorSyncInfo.host or "")
|
||||
self.port, self.setPort = Roact.createBinding(priorSyncInfo.port or "")
|
||||
|
||||
self.confirmationBindable = Instance.new("BindableEvent")
|
||||
self.confirmationEvent = self.confirmationBindable.Event
|
||||
@@ -145,28 +145,38 @@ function App:init()
|
||||
if
|
||||
Settings:get("syncReminder")
|
||||
and self.serveSession == nil
|
||||
and self:getLastSyncTimestamp()
|
||||
and self:getPriorSyncInfo().timestamp ~= nil
|
||||
and (self:isSyncLockAvailable())
|
||||
then
|
||||
self:addNotification("You've previously synced this place. Would you like to reconnect?", 300, {
|
||||
Connect = {
|
||||
text = "Connect",
|
||||
style = "Solid",
|
||||
layoutOrder = 1,
|
||||
onClick = function(notification)
|
||||
notification:dismiss()
|
||||
self:startSession()
|
||||
end,
|
||||
},
|
||||
Dismiss = {
|
||||
text = "Dismiss",
|
||||
style = "Bordered",
|
||||
layoutOrder = 2,
|
||||
onClick = function(notification)
|
||||
notification:dismiss()
|
||||
end,
|
||||
},
|
||||
})
|
||||
local syncInfo = self:getPriorSyncInfo()
|
||||
local timeSinceSync = timeUtil.elapsedToText(os.time() - syncInfo.timestamp)
|
||||
local syncDetail = if syncInfo.projectName
|
||||
then `project '{syncInfo.projectName}'`
|
||||
else `{syncInfo.host or Config.defaultHost}:{syncInfo.port or Config.defaultPort}`
|
||||
|
||||
self:addNotification(
|
||||
`You synced {syncDetail} to this place {timeSinceSync}. Would you like to reconnect?`,
|
||||
300,
|
||||
{
|
||||
Connect = {
|
||||
text = "Connect",
|
||||
style = "Solid",
|
||||
layoutOrder = 1,
|
||||
onClick = function(notification)
|
||||
notification:dismiss()
|
||||
self:startSession()
|
||||
end,
|
||||
},
|
||||
Dismiss = {
|
||||
text = "Dismiss",
|
||||
style = "Bordered",
|
||||
layoutOrder = 2,
|
||||
onClick = function(notification)
|
||||
notification:dismiss()
|
||||
end,
|
||||
},
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -274,54 +284,32 @@ function App:checkForUpdates()
|
||||
)
|
||||
end
|
||||
|
||||
function App:getPriorEndpoint()
|
||||
local priorEndpoints = Settings:get("priorEndpoints")
|
||||
if not priorEndpoints then
|
||||
return
|
||||
function App:getPriorSyncInfo(): { host: string?, port: string?, projectName: string?, timestamp: number? }
|
||||
local priorSyncInfos = Settings:get("priorEndpoints")
|
||||
if not priorSyncInfos then
|
||||
return {}
|
||||
end
|
||||
|
||||
local id = tostring(game.PlaceId)
|
||||
if ignorePlaceIds[id] then
|
||||
return
|
||||
return {}
|
||||
end
|
||||
|
||||
local place = priorEndpoints[id]
|
||||
if not place then
|
||||
return
|
||||
end
|
||||
|
||||
return place.host, place.port
|
||||
return priorSyncInfos[id] or {}
|
||||
end
|
||||
|
||||
function App:getLastSyncTimestamp()
|
||||
local priorEndpoints = Settings:get("priorEndpoints")
|
||||
if not priorEndpoints then
|
||||
return
|
||||
function App:setPriorSyncInfo(host: string, port: string, projectName: string)
|
||||
local priorSyncInfos = Settings:get("priorEndpoints")
|
||||
if not priorSyncInfos then
|
||||
priorSyncInfos = {}
|
||||
end
|
||||
|
||||
local id = tostring(game.PlaceId)
|
||||
if ignorePlaceIds[id] then
|
||||
return
|
||||
end
|
||||
|
||||
local place = priorEndpoints[id]
|
||||
if not place then
|
||||
return
|
||||
end
|
||||
|
||||
return place.timestamp
|
||||
end
|
||||
|
||||
function App:setPriorEndpoint(host: string, port: string)
|
||||
local priorEndpoints = Settings:get("priorEndpoints")
|
||||
if not priorEndpoints then
|
||||
priorEndpoints = {}
|
||||
end
|
||||
local now = os.time()
|
||||
|
||||
-- Clear any stale saves to avoid disc bloat
|
||||
for placeId, endpoint in priorEndpoints do
|
||||
if os.time() - endpoint.timestamp > 12_960_000 then
|
||||
priorEndpoints[placeId] = nil
|
||||
for placeId, syncInfo in priorSyncInfos do
|
||||
if now - (syncInfo.timestamp or now) > 12_960_000 then
|
||||
priorSyncInfos[placeId] = nil
|
||||
Log.trace("Cleared stale saved endpoint for {}", placeId)
|
||||
end
|
||||
end
|
||||
@@ -331,14 +319,15 @@ function App:setPriorEndpoint(host: string, port: string)
|
||||
return
|
||||
end
|
||||
|
||||
priorEndpoints[id] = {
|
||||
priorSyncInfos[id] = {
|
||||
host = if host ~= Config.defaultHost then host else nil,
|
||||
port = if port ~= Config.defaultPort then port else nil,
|
||||
timestamp = os.time(),
|
||||
projectName = projectName,
|
||||
timestamp = now,
|
||||
}
|
||||
Log.trace("Saved last used endpoint for {}", game.PlaceId)
|
||||
|
||||
Settings:set("priorEndpoints", priorEndpoints)
|
||||
Settings:set("priorEndpoints", priorSyncInfos)
|
||||
end
|
||||
|
||||
function App:getHostAndPort()
|
||||
@@ -533,8 +522,6 @@ function App:startSession()
|
||||
|
||||
serveSession:onStatusChanged(function(status, details)
|
||||
if status == ServeSession.Status.Connecting then
|
||||
self:setPriorEndpoint(host, port)
|
||||
|
||||
self:setState({
|
||||
appStatus = AppStatus.Connecting,
|
||||
toolbarIcon = Assets.Images.PluginButton,
|
||||
@@ -542,6 +529,7 @@ function App:startSession()
|
||||
self:addNotification("Connecting to session...")
|
||||
elseif status == ServeSession.Status.Connected then
|
||||
self.knownProjects[details] = true
|
||||
self:setPriorSyncInfo(host, port, details)
|
||||
self:setRunningConnectionInfo(baseUrl)
|
||||
|
||||
local address = ("%s:%s"):format(host, port)
|
||||
|
||||
@@ -16,6 +16,7 @@ local invariant = require(script.Parent.Parent.invariant)
|
||||
|
||||
local decodeValue = require(script.Parent.decodeValue)
|
||||
local reify = require(script.Parent.reify)
|
||||
local reifyInstance, applyDeferredRefs = reify.reifyInstance, reify.applyDeferredRefs
|
||||
local setProperty = require(script.Parent.setProperty)
|
||||
|
||||
local function applyPatch(instanceMap, patch)
|
||||
@@ -29,6 +30,11 @@ local function applyPatch(instanceMap, patch)
|
||||
-- Tracks any portions of the patch that could not be applied to the DOM.
|
||||
local unappliedPatch = PatchSet.newEmpty()
|
||||
|
||||
-- Contains a list of all of the ref properties that we'll need to assign.
|
||||
-- It is imperative that refs are assigned after all instances are created
|
||||
-- to ensure that referents can be mapped to instances correctly.
|
||||
local deferredRefs = {}
|
||||
|
||||
for _, removedIdOrInstance in ipairs(patch.removed) do
|
||||
local removeInstanceSuccess = pcall(function()
|
||||
if Types.RbxId(removedIdOrInstance) then
|
||||
@@ -78,7 +84,7 @@ local function applyPatch(instanceMap, patch)
|
||||
)
|
||||
end
|
||||
|
||||
local failedToReify = reify(instanceMap, patch.added, id, parentInstance)
|
||||
local failedToReify = reifyInstance(deferredRefs, instanceMap, patch.added, id, parentInstance)
|
||||
|
||||
if not PatchSet.isEmpty(failedToReify) then
|
||||
Log.debug("Failed to reify as part of applying a patch: {:#?}", failedToReify)
|
||||
@@ -143,7 +149,7 @@ local function applyPatch(instanceMap, patch)
|
||||
[update.id] = mockVirtualInstance,
|
||||
}
|
||||
|
||||
local failedToReify = reify(instanceMap, mockAdded, update.id, instance.Parent)
|
||||
local failedToReify = reifyInstance(deferredRefs, instanceMap, mockAdded, update.id, instance.Parent)
|
||||
|
||||
local newInstance = instanceMap.fromIds[update.id]
|
||||
|
||||
@@ -206,6 +212,18 @@ local function applyPatch(instanceMap, patch)
|
||||
|
||||
if update.changedProperties ~= nil then
|
||||
for propertyName, propertyValue in pairs(update.changedProperties) do
|
||||
-- Because refs may refer to instances that we haven't constructed yet,
|
||||
-- we defer applying any ref properties until all instances are created.
|
||||
if next(propertyValue) == "Ref" then
|
||||
table.insert(deferredRefs, {
|
||||
id = update.id,
|
||||
instance = instance,
|
||||
propertyName = propertyName,
|
||||
virtualValue = propertyValue,
|
||||
})
|
||||
continue
|
||||
end
|
||||
|
||||
local decodeSuccess, decodedValue = decodeValue(propertyValue, instanceMap)
|
||||
if not decodeSuccess then
|
||||
unappliedUpdate.changedProperties[propertyName] = propertyValue
|
||||
@@ -230,6 +248,8 @@ local function applyPatch(instanceMap, patch)
|
||||
ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit)
|
||||
end
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
return unappliedPatch
|
||||
end
|
||||
|
||||
|
||||
@@ -151,7 +151,24 @@ local function diff(instanceMap, virtualInstances, rootId)
|
||||
|
||||
if getProperySuccess then
|
||||
local existingValue = existingValueOrErr
|
||||
local decodeSuccess, decodedValue = decodeValue(virtualValue, instanceMap)
|
||||
local decodeSuccess, decodedValue
|
||||
|
||||
-- If `virtualValue` is a ref then instead of decoding it to an instance,
|
||||
-- we change `existingValue` to be a ref. This is because `virtualValue`
|
||||
-- may point to an Instance which doesn't exist yet and therefore
|
||||
-- decoding it may throw an error.
|
||||
if next(virtualValue) == "Ref" then
|
||||
decodeSuccess, decodedValue = true, virtualValue
|
||||
|
||||
if existingValue and typeof(existingValue) == "Instance" then
|
||||
local existingValueRef = instanceMap.fromInstances[existingValue]
|
||||
if existingValueRef then
|
||||
existingValue = { Ref = existingValueRef }
|
||||
end
|
||||
end
|
||||
else
|
||||
decodeSuccess, decodedValue = decodeValue(virtualValue, instanceMap)
|
||||
end
|
||||
|
||||
if decodeSuccess then
|
||||
if not trueEquals(existingValue, decodedValue) then
|
||||
|
||||
@@ -7,26 +7,6 @@ local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||
local setProperty = require(script.Parent.setProperty)
|
||||
local decodeValue = require(script.Parent.decodeValue)
|
||||
|
||||
local reifyInner, applyDeferredRefs
|
||||
|
||||
local function reify(instanceMap, virtualInstances, rootId, parentInstance)
|
||||
-- Create an empty patch that will be populated with any parts of this reify
|
||||
-- that could not happen, like instances that couldn't be created and
|
||||
-- properties that could not be assigned.
|
||||
local unappliedPatch = PatchSet.newEmpty()
|
||||
|
||||
-- Contains a list of all of the ref properties that we'll need to assign
|
||||
-- after all instances are created. We apply refs in a second pass, after
|
||||
-- we create as many instances as we can, so that we ensure that referents
|
||||
-- can be mapped to instances correctly.
|
||||
local deferredRefs = {}
|
||||
|
||||
reifyInner(instanceMap, virtualInstances, rootId, parentInstance, unappliedPatch, deferredRefs)
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
return unappliedPatch
|
||||
end
|
||||
|
||||
--[[
|
||||
Add the given ID and all of its descendants in virtualInstances to the given
|
||||
PatchSet, marked for addition.
|
||||
@@ -40,10 +20,21 @@ local function addAllToPatch(patchSet, virtualInstances, id)
|
||||
end
|
||||
end
|
||||
|
||||
function reifyInstance(deferredRefs, instanceMap, virtualInstances, rootId, parentInstance)
|
||||
-- Create an empty patch that will be populated with any parts of this reify
|
||||
-- that could not happen, like instances that couldn't be created and
|
||||
-- properties that could not be assigned.
|
||||
local unappliedPatch = PatchSet.newEmpty()
|
||||
|
||||
reifyInstanceInner(unappliedPatch, deferredRefs, instanceMap, virtualInstances, rootId, parentInstance)
|
||||
|
||||
return unappliedPatch
|
||||
end
|
||||
|
||||
--[[
|
||||
Inner function that defines the core routine.
|
||||
]]
|
||||
function reifyInner(instanceMap, virtualInstances, id, parentInstance, unappliedPatch, deferredRefs)
|
||||
function reifyInstanceInner(unappliedPatch, deferredRefs, instanceMap, virtualInstances, id, parentInstance)
|
||||
local virtualInstance = virtualInstances[id]
|
||||
|
||||
if virtualInstance == nil then
|
||||
@@ -102,7 +93,7 @@ function reifyInner(instanceMap, virtualInstances, id, parentInstance, unapplied
|
||||
end
|
||||
|
||||
for _, childId in ipairs(virtualInstance.Children) do
|
||||
reifyInner(instanceMap, virtualInstances, childId, instance, unappliedPatch, deferredRefs)
|
||||
reifyInstanceInner(unappliedPatch, deferredRefs, instanceMap, virtualInstances, childId, instance)
|
||||
end
|
||||
|
||||
instance.Parent = parentInstance
|
||||
@@ -143,6 +134,7 @@ function applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
end
|
||||
|
||||
local targetInstance = instanceMap.fromIds[refId]
|
||||
|
||||
if targetInstance == nil then
|
||||
markFailed(entry.id, entry.propertyName, entry.virtualValue)
|
||||
continue
|
||||
@@ -155,4 +147,7 @@ function applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
end
|
||||
end
|
||||
|
||||
return reify
|
||||
return {
|
||||
reifyInstance = reifyInstance,
|
||||
applyDeferredRefs = applyDeferredRefs,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
return function()
|
||||
local reify = require(script.Parent.reify)
|
||||
local reifyInstance, applyDeferredRefs = reify.reifyInstance, reify.applyDeferredRefs
|
||||
|
||||
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||
local InstanceMap = require(script.Parent.Parent.InstanceMap)
|
||||
@@ -20,7 +21,11 @@ return function()
|
||||
|
||||
it("should throw when given a bogus ID", function()
|
||||
expect(function()
|
||||
reify(InstanceMap.new(), {}, "Hi, mom!", game)
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, {}, "Hi, mom!", game)
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
end).to.throw()
|
||||
end)
|
||||
|
||||
@@ -34,8 +39,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT", nil)
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT", nil)
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(instanceMap:size() == 0, "expected instanceMap to be empty")
|
||||
|
||||
@@ -60,8 +68,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(PatchSet.isEmpty(unappliedPatch), "expected remaining patch to be empty")
|
||||
|
||||
@@ -90,8 +101,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(PatchSet.isEmpty(unappliedPatch), "expected remaining patch to be empty")
|
||||
|
||||
@@ -122,8 +136,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
expect(size(unappliedPatch.added)).to.equal(1)
|
||||
expect(unappliedPatch.added["CHILD"]).to.equal(virtualInstances["CHILD"])
|
||||
@@ -153,8 +170,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
local instance = instanceMap.fromIds["ROOT"]
|
||||
expect(instance.ClassName).to.equal("StringValue")
|
||||
@@ -196,8 +216,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(PatchSet.isEmpty(unappliedPatch), "expected remaining patch to be empty")
|
||||
|
||||
@@ -223,13 +246,16 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local existing = Instance.new("Folder")
|
||||
existing.Name = "Existing"
|
||||
instanceMap:insert("EXISTING", existing)
|
||||
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(PatchSet.isEmpty(unappliedPatch), "expected remaining patch to be empty")
|
||||
|
||||
@@ -268,8 +294,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(PatchSet.isEmpty(unappliedPatch), "expected remaining patch to be empty")
|
||||
|
||||
@@ -307,8 +336,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(PatchSet.isEmpty(unappliedPatch), "expected remaining patch to be empty")
|
||||
|
||||
@@ -332,8 +364,11 @@ return function()
|
||||
},
|
||||
}
|
||||
|
||||
local deferredRefs = {}
|
||||
local instanceMap = InstanceMap.new()
|
||||
local unappliedPatch = reify(instanceMap, virtualInstances, "ROOT")
|
||||
local unappliedPatch = reifyInstance(deferredRefs, instanceMap, virtualInstances, "ROOT")
|
||||
|
||||
applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
||||
|
||||
assert(not PatchSet.hasRemoves(unappliedPatch), "expected no removes")
|
||||
assert(not PatchSet.hasAdditions(unappliedPatch), "expected no additions")
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
rojo build test-place.project.json -o TestPlace.rbxlx
|
||||
run-in-roblox --script run-tests.server.lua --place TestPlace.rbxlx
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
"ReplicatedStorage": {
|
||||
"Rojo": {
|
||||
"$path": "default.project.json"
|
||||
"$path": "../plugin.project.json"
|
||||
},
|
||||
|
||||
"Packages": {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Continously build the rojo plugin into the local plugin directory on Windows
|
||||
rojo build plugin/default.project.json -o $LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm --watch
|
||||
@@ -6,6 +6,7 @@ expression: contents
|
||||
<Item class="Model" referent="0">
|
||||
<Properties>
|
||||
<string name="Name">init_meta_class_name</string>
|
||||
<bool name="NeedsPivotMigration">false</bool>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/build.rs
|
||||
expression: contents
|
||||
|
||||
---
|
||||
<roblox version="4">
|
||||
<Item class="Folder" referent="0">
|
||||
@@ -25,6 +24,7 @@ expression: contents
|
||||
<R21>0</R21>
|
||||
<R22>1</R22>
|
||||
</CoordinateFrame>
|
||||
<bool name="NeedsPivotMigration">false</bool>
|
||||
<Ref name="PrimaryPart">null</Ref>
|
||||
<BinaryString name="Tags"></BinaryString>
|
||||
</Properties>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/build.rs
|
||||
expression: contents
|
||||
|
||||
---
|
||||
<roblox version="4">
|
||||
<Item class="DataModel" referent="0">
|
||||
@@ -22,6 +21,7 @@ expression: contents
|
||||
<Item class="Workspace" referent="2">
|
||||
<Properties>
|
||||
<string name="Name">Workspace</string>
|
||||
<bool name="NeedsPivotMigration">false</bool>
|
||||
</Properties>
|
||||
<Item class="BoolValue" referent="3">
|
||||
<Properties>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||
|
||||
---
|
||||
instances:
|
||||
id-2:
|
||||
@@ -22,7 +21,8 @@ instances:
|
||||
ignoreUnknownInstances: false
|
||||
Name: test
|
||||
Parent: id-2
|
||||
Properties: {}
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
messageCursor: 1
|
||||
sessionId: id-1
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||
|
||||
---
|
||||
messageCursor: 1
|
||||
messages:
|
||||
@@ -14,8 +13,9 @@ messages:
|
||||
ignoreUnknownInstances: false
|
||||
Name: test
|
||||
Parent: id-2
|
||||
Properties: {}
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
removed: []
|
||||
updated: []
|
||||
sessionId: id-1
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||
---
|
||||
instances:
|
||||
id-2:
|
||||
Children:
|
||||
- id-3
|
||||
ClassName: DataModel
|
||||
Id: id-2
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: pivot_migration
|
||||
Parent: "00000000000000000000000000000000"
|
||||
Properties: {}
|
||||
id-3:
|
||||
Children:
|
||||
- id-4
|
||||
- id-5
|
||||
- id-6
|
||||
ClassName: Workspace
|
||||
Id: id-3
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Workspace
|
||||
Parent: id-2
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-4:
|
||||
Children: []
|
||||
ClassName: Model
|
||||
Id: id-4
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Model
|
||||
Parent: id-3
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-5:
|
||||
Children: []
|
||||
ClassName: Tool
|
||||
Id: id-5
|
||||
Metadata:
|
||||
ignoreUnknownInstances: false
|
||||
Name: Tool
|
||||
Parent: id-3
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-6:
|
||||
Children: []
|
||||
ClassName: Actor
|
||||
Id: id-6
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Actor
|
||||
Parent: id-3
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
messageCursor: 1
|
||||
sessionId: id-1
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||
---
|
||||
messageCursor: 1
|
||||
messages:
|
||||
- added:
|
||||
id-6:
|
||||
Children: []
|
||||
ClassName: Actor
|
||||
Id: id-6
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Actor
|
||||
Parent: id-3
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
removed: []
|
||||
updated:
|
||||
- changedClassName: ~
|
||||
changedMetadata:
|
||||
ignoreUnknownInstances: true
|
||||
changedName: ~
|
||||
changedProperties: {}
|
||||
id: id-3
|
||||
sessionId: id-1
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 316
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 335
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 351
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||
---
|
||||
instances:
|
||||
id-2:
|
||||
Children:
|
||||
- id-3
|
||||
ClassName: DataModel
|
||||
Id: id-2
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: pivot_migration
|
||||
Parent: "00000000000000000000000000000000"
|
||||
Properties: {}
|
||||
id-3:
|
||||
Children:
|
||||
- id-4
|
||||
- id-5
|
||||
ClassName: Workspace
|
||||
Id: id-3
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Workspace
|
||||
Parent: id-2
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-4:
|
||||
Children: []
|
||||
ClassName: Model
|
||||
Id: id-4
|
||||
Metadata:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Model
|
||||
Parent: id-3
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-5:
|
||||
Children: []
|
||||
ClassName: Tool
|
||||
Id: id-5
|
||||
Metadata:
|
||||
ignoreUnknownInstances: false
|
||||
Name: Tool
|
||||
Parent: id-3
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
messageCursor: 0
|
||||
sessionId: id-1
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
placeId: ~
|
||||
projectName: pivot_migration
|
||||
protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
unexpectedPlaceIds: ~
|
||||
@@ -31,6 +31,8 @@ instances:
|
||||
Attributes:
|
||||
Rojo_Target_PrimaryPart:
|
||||
String: project target
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
PrimaryPart:
|
||||
Ref: id-9
|
||||
id-2:
|
||||
@@ -55,7 +57,9 @@ instances:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Workspace
|
||||
Parent: id-2
|
||||
Properties: {}
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-4:
|
||||
Children: []
|
||||
ClassName: ObjectValue
|
||||
@@ -124,6 +128,8 @@ instances:
|
||||
Attributes:
|
||||
Rojo_Target_PrimaryPart:
|
||||
String: model target 2
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
PrimaryPart:
|
||||
Ref: id-7
|
||||
id-9:
|
||||
@@ -138,4 +144,3 @@ instances:
|
||||
Properties: {}
|
||||
messageCursor: 1
|
||||
sessionId: id-1
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ instances:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Workspace
|
||||
Parent: id-2
|
||||
Properties: {}
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-4:
|
||||
Children: []
|
||||
ClassName: ObjectValue
|
||||
@@ -104,6 +106,8 @@ instances:
|
||||
Attributes:
|
||||
Rojo_Target_PrimaryPart:
|
||||
String: model target
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
PrimaryPart:
|
||||
Ref: id-7
|
||||
id-9:
|
||||
@@ -118,4 +122,3 @@ instances:
|
||||
Properties: {}
|
||||
messageCursor: 0
|
||||
sessionId: id-1
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -40,7 +40,9 @@ instances:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Workspace
|
||||
Parent: id-2
|
||||
Properties: {}
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-4:
|
||||
Children: []
|
||||
ClassName: ObjectValue
|
||||
@@ -104,8 +106,12 @@ instances:
|
||||
Attributes:
|
||||
Rojo_Target_PrimaryPart:
|
||||
String: model target
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
PrimaryPart:
|
||||
Ref: id-7
|
||||
Scale:
|
||||
Float32: 1
|
||||
id-9:
|
||||
Children:
|
||||
- id-10
|
||||
@@ -116,5 +122,5 @@ instances:
|
||||
Name: ProjectTarget
|
||||
Parent: id-3
|
||||
Properties: {}
|
||||
messageCursor: 0
|
||||
messageCursor: 1
|
||||
sessionId: id-1
|
||||
|
||||
@@ -40,7 +40,9 @@ instances:
|
||||
ignoreUnknownInstances: true
|
||||
Name: Workspace
|
||||
Parent: id-2
|
||||
Properties: {}
|
||||
Properties:
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
id-4:
|
||||
Children: []
|
||||
ClassName: ObjectValue
|
||||
@@ -104,6 +106,8 @@ instances:
|
||||
Attributes:
|
||||
Rojo_Target_PrimaryPart:
|
||||
String: model target
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
PrimaryPart:
|
||||
Ref: id-7
|
||||
id-9:
|
||||
|
||||
@@ -10,3 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||
---
|
||||
messageCursor: 1
|
||||
messages:
|
||||
- added: {}
|
||||
removed: []
|
||||
updated:
|
||||
- changedClassName: ~
|
||||
changedMetadata: ~
|
||||
changedName: ~
|
||||
changedProperties:
|
||||
Scale:
|
||||
Float32: 1
|
||||
id: id-8
|
||||
sessionId: id-1
|
||||
@@ -10,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -18,6 +18,8 @@ messages:
|
||||
Attributes:
|
||||
Rojo_Target_PrimaryPart:
|
||||
String: project target
|
||||
NeedsPivotMigration:
|
||||
Bool: false
|
||||
PrimaryPart:
|
||||
Ref: id-9
|
||||
removed: []
|
||||
@@ -43,4 +45,3 @@ messages:
|
||||
PrimaryPart: ~
|
||||
id: id-8
|
||||
sessionId: id-1
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
expression: redactions.redacted_yaml(info)
|
||||
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
gameId: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 265
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 281
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 297
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,4 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tests/tests/serve.rs
|
||||
assertion_line: 383
|
||||
expression: redactions.redacted_yaml(info)
|
||||
---
|
||||
expectedPlaceIds: ~
|
||||
@@ -11,3 +10,4 @@ protocolVersion: 4
|
||||
rootInstanceId: id-2
|
||||
serverVersion: "[server-version]"
|
||||
sessionId: id-1
|
||||
unexpectedPlaceIds: ~
|
||||
|
||||
3
rojo-test/serve-tests/pivot_migration/Tool.model.json
Normal file
3
rojo-test/serve-tests/pivot_migration/Tool.model.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"className": "Tool"
|
||||
}
|
||||
14
rojo-test/serve-tests/pivot_migration/default.project.json
Normal file
14
rojo-test/serve-tests/pivot_migration/default.project.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "pivot_migration",
|
||||
"tree": {
|
||||
"$className": "DataModel",
|
||||
"Workspace": {
|
||||
"Model": {
|
||||
"$className": "Model"
|
||||
},
|
||||
"Tool": {
|
||||
"$path": "Tool.model.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
scripts/unit-test-plugin.sh
Normal file
2
scripts/unit-test-plugin.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
rojo build plugin/test-place.project.json -o TestPlace.rbxl
|
||||
run-in-roblox --script plugin/run-tests.server.lua --place TestPlace.rbxl
|
||||
1
scripts/watch-build-plugin.sh
Normal file
1
scripts/watch-build-plugin.sh
Normal file
@@ -0,0 +1 @@
|
||||
rojo build plugin.project.json --plugin Rojo.rbxm --watch
|
||||
@@ -136,7 +136,7 @@ impl JobThreadContext {
|
||||
// created all at once.
|
||||
let mut current_path = path.as_path();
|
||||
let affected_ids = loop {
|
||||
let ids = tree.get_ids_at_path(¤t_path);
|
||||
let ids = tree.get_ids_at_path(current_path);
|
||||
|
||||
log::trace!("Path {} affects IDs {:?}", current_path.display(), ids);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const UNKNOWN_PLUGIN_KIND_ERR: &str = "Could not detect what kind of file to bui
|
||||
/// Generates a model or place file from the Rojo project.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct BuildCommand {
|
||||
/// Path to the project to serve. Defaults to the current directory.
|
||||
/// Path to the project to build. Defaults to the current directory.
|
||||
#[clap(default_value = "")]
|
||||
pub project: PathBuf,
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use clap::Parser;
|
||||
use fs_err::File;
|
||||
use memofs::Vfs;
|
||||
use rayon::prelude::*;
|
||||
use rbx_dom_weak::types::Ref;
|
||||
use rbx_dom_weak::{types::Ref, Ustr};
|
||||
use serde::Serialize;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
@@ -26,7 +26,7 @@ const PATH_STRIP_FAILED_ERR: &str = "Failed to create relative paths for project
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SourcemapNode<'a> {
|
||||
name: &'a str,
|
||||
class_name: &'a str,
|
||||
class_name: Ustr,
|
||||
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
file_paths: Vec<PathBuf>,
|
||||
@@ -67,7 +67,7 @@ impl SourcemapCommand {
|
||||
let vfs = Vfs::new_default();
|
||||
vfs.set_watch_enabled(self.watch);
|
||||
|
||||
let session = ServeSession::new(vfs, &project_path)?;
|
||||
let session = ServeSession::new(vfs, project_path)?;
|
||||
let mut cursor = session.message_queue().cursor();
|
||||
|
||||
let filter = if self.include_non_scripts {
|
||||
@@ -113,7 +113,7 @@ fn filter_nothing(_instance: &InstanceWithMeta) -> bool {
|
||||
|
||||
fn filter_non_scripts(instance: &InstanceWithMeta) -> bool {
|
||||
matches!(
|
||||
instance.class_name(),
|
||||
instance.class_name().as_str(),
|
||||
"Script" | "LocalScript" | "ModuleScript"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ trait FmtLua {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct DisplayLua<T>(T);
|
||||
|
||||
impl<T> fmt::Display for DisplayLua<T>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user