mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 20:55:50 +00:00
Compare commits
163 Commits
v7.0.0-alp
...
v7.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e17771a6a5 | ||
|
|
bac30ae78b | ||
|
|
c0219922b2 | ||
|
|
b5ed952d5c | ||
|
|
7994bc4909 | ||
|
|
b88d34c639 | ||
|
|
96cb1ee3fd | ||
|
|
003abe86bb | ||
|
|
6ec411a618 | ||
|
|
c7c0903804 | ||
|
|
cdc972a5ce | ||
|
|
17de912608 | ||
|
|
9876508887 | ||
|
|
72d62220e8 | ||
|
|
46ad337fa5 | ||
|
|
7a3ba7721f | ||
|
|
e0198e626b | ||
|
|
142705f386 | ||
|
|
4cb49c7825 | ||
|
|
05adb82dda | ||
|
|
faf7671799 | ||
|
|
d64db329dd | ||
|
|
e34d2339ad | ||
|
|
d196c5091c | ||
|
|
3e83f92532 | ||
|
|
41d7aaf323 | ||
|
|
e110f3726a | ||
|
|
eb5c897ac0 | ||
|
|
e864cf0c7d | ||
|
|
565c12405e | ||
|
|
2a6a8b42a6 | ||
|
|
5cb4cc0d1d | ||
|
|
62eb4f026f | ||
|
|
411d1a89c1 | ||
|
|
6ae0bf366a | ||
|
|
178cdc9dfa | ||
|
|
5bf1f86886 | ||
|
|
e482aba030 | ||
|
|
535e4d42bb | ||
|
|
54398d4c4b | ||
|
|
0987b44e23 | ||
|
|
58098e96d4 | ||
|
|
f649c180cf | ||
|
|
966478b131 | ||
|
|
ca0759a011 | ||
|
|
f1cdf2fe79 | ||
|
|
04fa5e2719 | ||
|
|
eccb95690c | ||
|
|
acf7456371 | ||
|
|
8ea41480b7 | ||
|
|
0d1bc0d7fe | ||
|
|
f9b7774286 | ||
|
|
2e672badf2 | ||
|
|
cd5d6fd15c | ||
|
|
cf76982cfa | ||
|
|
2624ea7d2a | ||
|
|
2e7c4b6dff | ||
|
|
e5dbee1073 | ||
|
|
c06463b61d | ||
|
|
10341e3776 | ||
|
|
824cdc5dcd | ||
|
|
7aa7a35aa5 | ||
|
|
79b57b3359 | ||
|
|
c7aeffe586 | ||
|
|
79c02f2457 | ||
|
|
b9ed68fa9e | ||
|
|
6c6d6c9c8d | ||
|
|
e169d7be68 | ||
|
|
192fd7d4dd | ||
|
|
1f1193e857 | ||
|
|
0a412ade88 | ||
|
|
3cef2fe9aa | ||
|
|
18e53f06fe | ||
|
|
eaac539087 | ||
|
|
57005c4fd5 | ||
|
|
ea58999a2a | ||
|
|
a5a69fd9fc | ||
|
|
f1d0f1c1c9 | ||
|
|
83492d7495 | ||
|
|
10abc2254a | ||
|
|
5d5536a95e | ||
|
|
fe81e55925 | ||
|
|
654690d73e | ||
|
|
256aba4bc1 | ||
|
|
49f8845105 | ||
|
|
12370846b4 | ||
|
|
07637dfe96 | ||
|
|
f389a4a1db | ||
|
|
af077c796c | ||
|
|
1c319f2fa8 | ||
|
|
e8afa03f7b | ||
|
|
9b22545842 | ||
|
|
adc733d25c | ||
|
|
6896257647 | ||
|
|
1d9845a6cb | ||
|
|
8461339e9a | ||
|
|
9904d94e4c | ||
|
|
da25c80d0b | ||
|
|
5fa63733fd | ||
|
|
8b54bf0ba1 | ||
|
|
173dc12cb3 | ||
|
|
e136529ff0 | ||
|
|
75542dacb3 | ||
|
|
07abfbde43 | ||
|
|
96112fe118 | ||
|
|
9d0b313261 | ||
|
|
277ddfa9be | ||
|
|
5d88bdb256 | ||
|
|
8d29b43155 | ||
|
|
cc071a6415 | ||
|
|
8954def25c | ||
|
|
d484098781 | ||
|
|
9f06cbf3a0 | ||
|
|
af4a3ca0af | ||
|
|
43715143e4 | ||
|
|
16aa354d36 | ||
|
|
c739025453 | ||
|
|
f0526d17de | ||
|
|
6cc2e919c0 | ||
|
|
e1f9eaefa9 | ||
|
|
5d62bf9b60 | ||
|
|
4aa5814a0a | ||
|
|
5bca244062 | ||
|
|
7cf57714a4 | ||
|
|
92e6f862ad | ||
|
|
2377f41036 | ||
|
|
26a08f4d9f | ||
|
|
672d207961 | ||
|
|
a3d8e50f26 | ||
|
|
d3abca46a8 | ||
|
|
17fdd18c55 | ||
|
|
e482d038c6 | ||
|
|
d0482a004e | ||
|
|
561a3e3256 | ||
|
|
158dac5e1c | ||
|
|
1413f8c0b6 | ||
|
|
ffb2aa332a | ||
|
|
45e8208e9c | ||
|
|
7f230a8bf4 | ||
|
|
afe26b8c16 | ||
|
|
d153f62b8a | ||
|
|
5c80cd6e50 | ||
|
|
df1aced95d | ||
|
|
5a0a8f5077 | ||
|
|
1c281539e0 | ||
|
|
ef41d14f50 | ||
|
|
aa29397732 | ||
|
|
ca8865a3ce | ||
|
|
7599ef6626 | ||
|
|
43e71f9242 | ||
|
|
aac9b25efe | ||
|
|
532d170585 | ||
|
|
3dcb14013b | ||
|
|
a4c782cd35 | ||
|
|
0779baa0ac | ||
|
|
0849fab644 | ||
|
|
d85bca2e7e | ||
|
|
c7ab6c435c | ||
|
|
98db3b4f08 | ||
|
|
0e7ba839ed | ||
|
|
2c27691e57 | ||
|
|
934506bdfd | ||
|
|
f313fa4ae1 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
patreon: lpghatguy
|
||||||
62
.github/workflows/ci.yml
vendored
62
.github/workflows/ci.yml
vendored
@@ -11,29 +11,67 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: Build and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust_version: [stable, "1.43.1"]
|
rust_version: [stable, 1.69.0]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Install Rust
|
||||||
run: rustup default ${{ matrix.rust_version }}
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust_version }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.3.0
|
||||||
|
with:
|
||||||
|
version: 'v0.2.7'
|
||||||
|
|
||||||
|
- name: Install packages
|
||||||
|
run: |
|
||||||
|
cd plugin
|
||||||
|
wally install
|
||||||
|
cd ..
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --locked --verbose
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
- name: Run tests
|
- name: Test
|
||||||
run: cargo test --locked --verbose
|
run: cargo test --locked --verbose
|
||||||
|
|
||||||
- name: Rustfmt and Clippy
|
lint:
|
||||||
|
name: Rustfmt and Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.3.0
|
||||||
|
with:
|
||||||
|
version: 'v0.2.7'
|
||||||
|
|
||||||
|
- name: Install packages
|
||||||
run: |
|
run: |
|
||||||
cargo fmt -- --check
|
cd plugin
|
||||||
cargo clippy
|
wally install
|
||||||
if: matrix.rust_version == 'stable'
|
cd ..
|
||||||
|
|
||||||
|
- name: Rustfmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
run: cargo clippy
|
||||||
213
.github/workflows/release.yml
vendored
213
.github/workflows/release.yml
vendored
@@ -2,65 +2,166 @@ name: Release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["*"]
|
tags: ["v*"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows:
|
create-release:
|
||||||
runs-on: windows-latest
|
name: Create Release
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: cargo build --verbose --locked --release
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-win64
|
|
||||||
path: target/release/rojo.exe
|
|
||||||
|
|
||||||
macos:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: |
|
|
||||||
source $HOME/.cargo/env
|
|
||||||
cargo build --verbose --locked --release
|
|
||||||
env:
|
|
||||||
OPENSSL_STATIC: 1
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-macos
|
|
||||||
path: target/release/rojo
|
|
||||||
|
|
||||||
linux:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- name: Create Release
|
||||||
with:
|
id: create_release
|
||||||
submodules: true
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ${{ github.ref }}
|
||||||
|
draft: true
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
- name: Build
|
build-plugin:
|
||||||
run: cargo build --locked --verbose --release
|
needs: ["create-release"]
|
||||||
env:
|
name: Build Roblox Studio Plugin
|
||||||
OPENSSL_STATIC: 1
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Setup Aftman
|
||||||
uses: actions/upload-artifact@v1
|
uses: ok-nick/setup-aftman@v0.1.0
|
||||||
with:
|
with:
|
||||||
name: rojo-linux
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
path: target/release/rojo
|
trust-check: false
|
||||||
|
version: 'v0.2.6'
|
||||||
|
|
||||||
|
- name: Install packages
|
||||||
|
run: |
|
||||||
|
cd plugin
|
||||||
|
wally install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
- name: Build Plugin
|
||||||
|
run: rojo build plugin --output Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: Rojo.rbxm
|
||||||
|
asset_name: Rojo.rbxm
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Plugin to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Rojo.rbxm
|
||||||
|
path: Rojo.rbxm
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: ["create-release"]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# https://doc.rust-lang.org/rustc/platform-support.html
|
||||||
|
include:
|
||||||
|
- host: linux
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
label: linux-x86_64
|
||||||
|
|
||||||
|
- host: windows
|
||||||
|
os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
label: windows-x86_64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
label: macos-x86_64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
label: macos-aarch64
|
||||||
|
|
||||||
|
name: Build (${{ matrix.target }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
BIN: rojo
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Get Version from Tag
|
||||||
|
shell: bash
|
||||||
|
# https://github.community/t/how-to-get-just-the-tag-name/16241/7#M1027
|
||||||
|
run: |
|
||||||
|
echo "PROJECT_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
echo "Version is: ${{ env.PROJECT_VERSION }}"
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.1.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
trust-check: false
|
||||||
|
version: 'v0.2.6'
|
||||||
|
|
||||||
|
- name: Install packages
|
||||||
|
run: |
|
||||||
|
cd plugin
|
||||||
|
wally install
|
||||||
|
cd ..
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build Release
|
||||||
|
run: cargo build --release --locked --verbose
|
||||||
|
env:
|
||||||
|
# Build into a known directory so we can find our build artifact more
|
||||||
|
# easily.
|
||||||
|
CARGO_TARGET_DIR: output
|
||||||
|
|
||||||
|
# On platforms that use OpenSSL, ensure it is statically linked to
|
||||||
|
# make binaries more portable.
|
||||||
|
OPENSSL_STATIC: 1
|
||||||
|
|
||||||
|
- name: Create Release Archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir staging
|
||||||
|
|
||||||
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
||||||
|
cp "output/release/$BIN.exe" staging/
|
||||||
|
cd staging
|
||||||
|
7z a ../release.zip *
|
||||||
|
else
|
||||||
|
cp "output/release/$BIN" staging/
|
||||||
|
cd staging
|
||||||
|
zip ../release.zip *
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload Archive to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: release.zip
|
||||||
|
asset_name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Archive to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
path: release.zip
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -13,12 +13,12 @@
|
|||||||
# Test places for the Roblox Studio Plugin
|
# Test places for the Roblox Studio Plugin
|
||||||
/plugin/*.rbxlx
|
/plugin/*.rbxlx
|
||||||
|
|
||||||
|
# Packages for the Roblox Studio Plugin
|
||||||
|
/plugin/*Packages
|
||||||
|
|
||||||
# Roblox Studio holds 'lock' files on places
|
# Roblox Studio holds 'lock' files on places
|
||||||
*.rbxl.lock
|
*.rbxl.lock
|
||||||
*.rbxlx.lock
|
*.rbxlx.lock
|
||||||
|
|
||||||
# Snapshot files from the 'insta' Rust crate
|
# Snapshot files from the 'insta' Rust crate
|
||||||
**/*.snap.new
|
**/*.snap.new
|
||||||
|
|
||||||
# Selene generates a roblox.toml file that should not be checked in.
|
|
||||||
/roblox.toml
|
|
||||||
15
.gitmodules
vendored
15
.gitmodules
vendored
@@ -1,15 +0,0 @@
|
|||||||
[submodule "plugin/modules/roact"]
|
|
||||||
path = plugin/modules/roact
|
|
||||||
url = https://github.com/Roblox/roact.git
|
|
||||||
[submodule "plugin/modules/testez"]
|
|
||||||
path = plugin/modules/testez
|
|
||||||
url = https://github.com/Roblox/testez.git
|
|
||||||
[submodule "plugin/modules/promise"]
|
|
||||||
path = plugin/modules/promise
|
|
||||||
url = https://github.com/LPGhatguy/roblox-lua-promise.git
|
|
||||||
[submodule "plugin/modules/t"]
|
|
||||||
path = plugin/modules/t
|
|
||||||
url = https://github.com/osyrisrblx/t.git
|
|
||||||
[submodule "plugin/modules/flipper"]
|
|
||||||
path = plugin/modules/flipper
|
|
||||||
url = https://github.com/Reselim/Flipper
|
|
||||||
58
.luacheckrc
58
.luacheckrc
@@ -1,58 +0,0 @@
|
|||||||
stds.roblox = {
|
|
||||||
read_globals = {
|
|
||||||
game = {
|
|
||||||
other_fields = true,
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Roblox globals
|
|
||||||
"script",
|
|
||||||
|
|
||||||
-- Extra functions
|
|
||||||
"tick", "warn", "spawn",
|
|
||||||
"wait", "settings", "typeof",
|
|
||||||
|
|
||||||
-- Types
|
|
||||||
"Vector2", "Vector3",
|
|
||||||
"Vector2int16", "Vector3int16",
|
|
||||||
"Color3",
|
|
||||||
"UDim", "UDim2",
|
|
||||||
"Rect",
|
|
||||||
"CFrame",
|
|
||||||
"Enum",
|
|
||||||
"Instance",
|
|
||||||
"DockWidgetPluginGuiInfo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.plugin = {
|
|
||||||
read_globals = {
|
|
||||||
"plugin",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.testez = {
|
|
||||||
read_globals = {
|
|
||||||
"describe",
|
|
||||||
"it", "itFOCUS", "itSKIP", "itFIXME",
|
|
||||||
"FOCUS", "SKIP", "HACK_NO_XPCALL",
|
|
||||||
"expect",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ignore = {
|
|
||||||
"212", -- unused arguments
|
|
||||||
"421", -- shadowing local variable
|
|
||||||
"422", -- shadowing argument
|
|
||||||
"431", -- shadowing upvalue
|
|
||||||
"432", -- shadowing upvalue argument
|
|
||||||
}
|
|
||||||
|
|
||||||
std = "lua51+roblox"
|
|
||||||
|
|
||||||
files["**/*.server.lua"] = {
|
|
||||||
std = "+plugin",
|
|
||||||
}
|
|
||||||
|
|
||||||
files["**/*.spec.lua"] = {
|
|
||||||
std = "+testez",
|
|
||||||
}
|
|
||||||
197
CHANGELOG.md
197
CHANGELOG.md
@@ -2,6 +2,203 @@
|
|||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## [7.3.0] - April 22, 2023
|
||||||
|
* Added `$attributes` to project format. ([#574])
|
||||||
|
* Added `--watch` flag to `rojo sourcemap`. ([#602])
|
||||||
|
* Added support for `init.csv` files. ([#594])
|
||||||
|
* Added real-time sync status to the Studio plugin. ([#569])
|
||||||
|
* Added support for copying error messages to the clipboard. ([#614])
|
||||||
|
* Added sync locking for Team Create. ([#590])
|
||||||
|
* Added support for specifying HTTP or HTTPS protocol in plugin. ([#642])
|
||||||
|
* Added tooltips to buttons in the Studio plugin. ([#637])
|
||||||
|
* Added visual diffs when connecting from the Studio plugin. ([#603])
|
||||||
|
* Host and port are now saved in the Studio plugin. ([#613])
|
||||||
|
* Improved padding on notifications in Studio plugin. ([#589])
|
||||||
|
* Renamed `Common` to `Shared` in the default Rojo project. ([#611])
|
||||||
|
* Reduced the minimum size of the Studio plugin widget. ([#606])
|
||||||
|
* Fixed current directory in `rojo fmt-project`. ([#581])
|
||||||
|
* Fixed errors after a session has already ended. ([#587])
|
||||||
|
* Fixed an uncommon security permission error ([#619])
|
||||||
|
|
||||||
|
[#569]: https://github.com/rojo-rbx/rojo/pull/569
|
||||||
|
[#574]: https://github.com/rojo-rbx/rojo/pull/574
|
||||||
|
[#581]: https://github.com/rojo-rbx/rojo/pull/581
|
||||||
|
[#587]: https://github.com/rojo-rbx/rojo/pull/587
|
||||||
|
[#589]: https://github.com/rojo-rbx/rojo/pull/589
|
||||||
|
[#590]: https://github.com/rojo-rbx/rojo/pull/590
|
||||||
|
[#594]: https://github.com/rojo-rbx/rojo/pull/594
|
||||||
|
[#602]: https://github.com/rojo-rbx/rojo/pull/602
|
||||||
|
[#603]: https://github.com/rojo-rbx/rojo/pull/603
|
||||||
|
[#606]: https://github.com/rojo-rbx/rojo/pull/606
|
||||||
|
[#611]: https://github.com/rojo-rbx/rojo/pull/611
|
||||||
|
[#613]: https://github.com/rojo-rbx/rojo/pull/613
|
||||||
|
[#614]: https://github.com/rojo-rbx/rojo/pull/614
|
||||||
|
[#619]: https://github.com/rojo-rbx/rojo/pull/619
|
||||||
|
[#637]: https://github.com/rojo-rbx/rojo/pull/637
|
||||||
|
[#642]: https://github.com/rojo-rbx/rojo/pull/642
|
||||||
|
[7.3.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.3.0
|
||||||
|
|
||||||
|
## [7.2.1] - July 8, 2022
|
||||||
|
* Fixed notification sound by changing it to a generic sound. ([#566])
|
||||||
|
* Added setting to turn off sound effects. ([#568])
|
||||||
|
|
||||||
|
[#566]: https://github.com/rojo-rbx/rojo/pull/566
|
||||||
|
[#568]: https://github.com/rojo-rbx/rojo/pull/568
|
||||||
|
[7.2.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.2.1
|
||||||
|
|
||||||
|
## [7.2.0] - June 29, 2022
|
||||||
|
* Added support for `.luau` files. ([#552])
|
||||||
|
* Added support for live syncing Attributes and Tags. ([#553])
|
||||||
|
* Added notification popups in the Roblox Studio plugin. ([#540])
|
||||||
|
* Fixed `init.meta.json` when used with `init.lua` and related files. ([#549])
|
||||||
|
* Fixed incorrect output when serving from a non-default address or port ([#556])
|
||||||
|
* Fixed Linux binaries not running on systems with older glibc. ([#561])
|
||||||
|
* Added `camelCase` casing for JSON models, deprecating `PascalCase` names. ([#563])
|
||||||
|
* Switched from structopt to clap for command line argument parsing.
|
||||||
|
* Significantly improved performance of building and serving. ([#548])
|
||||||
|
* Increased minimum supported Rust version to 1.57.0. ([#564])
|
||||||
|
|
||||||
|
[#540]: https://github.com/rojo-rbx/rojo/pull/540
|
||||||
|
[#548]: https://github.com/rojo-rbx/rojo/pull/548
|
||||||
|
[#549]: https://github.com/rojo-rbx/rojo/pull/549
|
||||||
|
[#552]: https://github.com/rojo-rbx/rojo/pull/552
|
||||||
|
[#553]: https://github.com/rojo-rbx/rojo/pull/553
|
||||||
|
[#556]: https://github.com/rojo-rbx/rojo/pull/556
|
||||||
|
[#561]: https://github.com/rojo-rbx/rojo/pull/561
|
||||||
|
[#563]: https://github.com/rojo-rbx/rojo/pull/563
|
||||||
|
[#564]: https://github.com/rojo-rbx/rojo/pull/564
|
||||||
|
[7.2.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.2.0
|
||||||
|
|
||||||
|
## [7.1.1] - May 26, 2022
|
||||||
|
* Fixed sourcemap command not stripping paths correctly ([#544])
|
||||||
|
* Fixed Studio plugin settings not saving correctly.
|
||||||
|
|
||||||
|
[#544]: https://github.com/rojo-rbx/rojo/pull/544
|
||||||
|
[#545]: https://github.com/rojo-rbx/rojo/pull/545
|
||||||
|
[7.1.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.1
|
||||||
|
|
||||||
|
## [7.1.0] - May 22, 2022
|
||||||
|
* Added support for specifying an address to be used by default in project files. ([#507])
|
||||||
|
* Added support for optional paths in project files. ([#472])
|
||||||
|
* Added support for the new Open Cloud API when uploading. ([#504])
|
||||||
|
* Added `sourcemap` command for generating sourcemaps to feed into other tools. ([#530])
|
||||||
|
* Added PluginActions for connecting/disconnecting a session ([#537])
|
||||||
|
* Added changing toolbar icon to indicate state ([#538])
|
||||||
|
|
||||||
|
[#472]: https://github.com/rojo-rbx/rojo/pull/472
|
||||||
|
[#504]: https://github.com/rojo-rbx/rojo/pull/504
|
||||||
|
[#507]: https://github.com/rojo-rbx/rojo/pull/507
|
||||||
|
[#530]: https://github.com/rojo-rbx/rojo/pull/530
|
||||||
|
[#537]: https://github.com/rojo-rbx/rojo/pull/537
|
||||||
|
[#538]: https://github.com/rojo-rbx/rojo/pull/538
|
||||||
|
[7.1.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.0
|
||||||
|
|
||||||
|
## [7.0.0] - December 10, 2021
|
||||||
|
* Fixed Rojo's interactions with properties enabled by FFlags that are not yet enabled. ([#493])
|
||||||
|
* Improved output in Roblox Studio plugin when bad property data is encountered.
|
||||||
|
* Reintroduced support for CFrame shorthand syntax in Rojo project and `.meta.json` files, matching Rojo 6. ([#430])
|
||||||
|
* Connection settings are now remembered when reconnecting in Roblox Studio. ([#500])
|
||||||
|
* Updated reflection database to Roblox v503.
|
||||||
|
|
||||||
|
[#430]: https://github.com/rojo-rbx/rojo/issues/430
|
||||||
|
[#493]: https://github.com/rojo-rbx/rojo/pull/493
|
||||||
|
[#500]: https://github.com/rojo-rbx/rojo/pull/500
|
||||||
|
[7.0.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0
|
||||||
|
|
||||||
|
## [7.0.0-rc.3] - October 19, 2021
|
||||||
|
This is the last release candidate for Rojo 7. In an effort to get Rojo 7 out the door, we'll be freezing features from here on out, something we should've done a couple months ago.
|
||||||
|
|
||||||
|
Expect to see Rojo 7 stable soon!
|
||||||
|
|
||||||
|
* Added support for writing `Tags` in project files, model files, and meta files. ([#484])
|
||||||
|
* Adjusted Studio plugin colors to match Roblox Studio palette. ([#482])
|
||||||
|
* Improved experimental two-way sync feature by batching changes. ([#478])
|
||||||
|
|
||||||
|
[#482]: https://github.com/rojo-rbx/rojo/pull/482
|
||||||
|
[#484]: https://github.com/rojo-rbx/rojo/pull/484
|
||||||
|
[#478]: https://github.com/rojo-rbx/rojo/pull/478
|
||||||
|
[7.0.0-rc.3]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-rc.3
|
||||||
|
|
||||||
|
## 7.0.0-rc.2 - October 19, 2021
|
||||||
|
(Botched release due to Git mishap, oops!)
|
||||||
|
|
||||||
|
## [7.0.0-rc.1] - August 23, 2021
|
||||||
|
In Rojo 6 and previous Rojo 7 alphas, an explicit Vector3 property would be written like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"className": "Part",
|
||||||
|
"properties": {
|
||||||
|
"Position": {
|
||||||
|
"Type": "Vector3",
|
||||||
|
"Value": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For Rojo 7, this will need to be changed to:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"className": "Part",
|
||||||
|
"properties": {
|
||||||
|
"Position": {
|
||||||
|
"Vector3": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The shorthand property format that most users use is not impacted. For reference, it looks like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"className": "Part",
|
||||||
|
"properties": {
|
||||||
|
"Position": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Major breaking change: changed property syntax for project files; shorthand syntax is unchanged.
|
||||||
|
* Added the `fmt-project` subcommand for formatting Rojo project files.
|
||||||
|
* Improved error output for many subcommands.
|
||||||
|
* Updated to stable versions of rbx-dom libraries.
|
||||||
|
* Updated async infrastructure, which should fix a handful of bugs. ([#459])
|
||||||
|
* Fixed syncing refs in the Roblox Studio plugin ([#462], [#466])
|
||||||
|
* Added support for long paths on Windows. ([#464])
|
||||||
|
|
||||||
|
[#459]: https://github.com/rojo-rbx/rojo/pull/459
|
||||||
|
[#462]: https://github.com/rojo-rbx/rojo/pull/462
|
||||||
|
[#464]: https://github.com/rojo-rbx/rojo/pull/464
|
||||||
|
[#466]: https://github.com/rojo-rbx/rojo/pull/466
|
||||||
|
[7.0.0-rc.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-rc.1
|
||||||
|
|
||||||
|
## [7.0.0-alpha.4][7.0.0-alpha.4] (May 5, 2021)
|
||||||
|
* Added the `gameId` and `placeId` optional properties to project files.
|
||||||
|
* When connecting from the Rojo Roblox Studio plugin, Rojo will set the game and place ID of the current place to these values, if set.
|
||||||
|
* This is equivalent to running `game:SetUniverseId(...)` and `game:SetPlaceId(...)` from the command bar in Studio.
|
||||||
|
* Added "EXPERIMENTAL!" label to two-way sync toggle in Rojo's Roblox Studio plugin.
|
||||||
|
* Fixed `Name` and `Parent` properties being allowed in Rojo projects. ([#413][pr-413])
|
||||||
|
* Fixed "Open Scripts Externally" feature crashing Studio. ([#369][issue-369])
|
||||||
|
* Empty `.model.json` files will no longer cause errors. ([#420][pr-420])
|
||||||
|
* When specifying `$path` on a service, Rojo now keeps the correct class name. ([#331][issue-331])
|
||||||
|
* Improved error messages for misconfigured projects.
|
||||||
|
|
||||||
|
[issue-331]: https://github.com/rojo-rbx/rojo/issues/331
|
||||||
|
[issue-369]: https://github.com/rojo-rbx/rojo/issues/369
|
||||||
|
[pr-420]: https://github.com/rojo-rbx/rojo/pull/420
|
||||||
|
[pr-413]: https://github.com/rojo-rbx/rojo/pull/413
|
||||||
|
[7.0.0-alpha.4]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.4
|
||||||
|
|
||||||
|
## [7.0.0-alpha.3][7.0.0-alpha.3] (February 19, 2021)
|
||||||
|
* Updated dependencies, fixing `OptionalCoordinateFrame`-related issues.
|
||||||
|
* Added `--address` flag to `rojo serve` to allow for external connections. ([#403][pr-403])
|
||||||
|
|
||||||
|
[pr-403]: https://github.com/rojo-rbx/rojo/pull/403
|
||||||
|
[7.0.0-alpha.3]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.3
|
||||||
|
|
||||||
## [7.0.0-alpha.2][7.0.0-alpha.2] (February 19, 2021)
|
## [7.0.0-alpha.2][7.0.0-alpha.2] (February 19, 2021)
|
||||||
* Fixed incorrect protocol version between the client and server.
|
* Fixed incorrect protocol version between the client and server.
|
||||||
|
|
||||||
|
|||||||
@@ -29,25 +29,29 @@ Sometimes there's something that Rojo doesn't do that it probably should.
|
|||||||
|
|
||||||
Please file issues and we'll try to help figure out what the best way forward is.
|
Please file issues and we'll try to help figure out what the best way forward is.
|
||||||
|
|
||||||
|
## Local Development Gotchas
|
||||||
|
|
||||||
|
If your build fails with "Error: failed to open file `D:\code\rojo\plugin\modules\roact\src`" you need to update your Git submodules.
|
||||||
|
Run the command and try building again: `git submodule update --init --recursive`.
|
||||||
|
|
||||||
## Pushing a Rojo Release
|
## Pushing a Rojo Release
|
||||||
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
||||||
|
|
||||||
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
||||||
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
||||||
3. Run `cargo test` to update `Cargo.lock` and double-check tests
|
3. Run `cargo test` to update `Cargo.lock` and run tests
|
||||||
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
5. Commit!
|
5. Commit!
|
||||||
* `git add . && git commit -m "Release vX.Y.Z"`
|
* `git add . && git commit -m "Release vX.Y.Z"`
|
||||||
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
|
6. Tag the commit
|
||||||
|
* `git tag vX.Y.Z`
|
||||||
7. Publish the CLI
|
7. Publish the CLI
|
||||||
* `cargo publish`
|
* `cargo publish`
|
||||||
8. Publish the Plugin
|
8. Publish the Plugin
|
||||||
* `rojo publish plugin --asset_id 6415005344`
|
* `cargo run -- upload plugin --asset_id 6415005344`
|
||||||
* `rojo build plugin -o Rojo.rbxm`
|
|
||||||
9. Push commits and tags
|
9. Push commits and tags
|
||||||
* `git push && git push --tags`
|
* `git push && git push --tags`
|
||||||
10. Copy GitHub release content from previous release
|
10. Copy GitHub release content from previous release
|
||||||
* Update the leading text with a summary about the release
|
* Update the leading text with a summary about the release
|
||||||
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
* Write a small summary of each major feature
|
* Write a small summary of each major feature
|
||||||
* Attach release artifacts from GitHub Actions for each platform
|
|
||||||
2609
Cargo.lock
generated
2609
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
114
Cargo.toml
114
Cargo.toml
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "7.0.0-alpha.2"
|
version = "7.3.0"
|
||||||
|
rust-version = "1.68.2"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "Enables professional-grade development tools for Roblox developers"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
@@ -8,7 +9,8 @@ homepage = "https://rojo.space"
|
|||||||
documentation = "https://rojo.space/docs"
|
documentation = "https://rojo.space/docs"
|
||||||
repository = "https://github.com/rojo-rbx/rojo"
|
repository = "https://github.com/rojo-rbx/rojo"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"/test-projects/**",
|
"/test-projects/**",
|
||||||
@@ -26,26 +28,21 @@ default = []
|
|||||||
# Enable this feature to live-reload assets from the web UI.
|
# Enable this feature to live-reload assets from the web UI.
|
||||||
dev_live_assets = []
|
dev_live_assets = []
|
||||||
|
|
||||||
|
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = ["crates/*"]
|
||||||
"rojo-insta-ext",
|
|
||||||
"memofs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "librojo"
|
name = "librojo"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "rojo"
|
|
||||||
path = "src/bin.rs"
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "build"
|
name = "build"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
memofs = { version = "0.1.2", path = "memofs" }
|
memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||||
|
|
||||||
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
||||||
@@ -54,61 +51,60 @@ memofs = { version = "0.1.2", path = "memofs" }
|
|||||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
rbx_binary = "0.6.0-alpha.1"
|
rbx_binary = "0.7.0"
|
||||||
rbx_dom_weak = "2.0.0-alpha.1"
|
rbx_dom_weak = "2.4.0"
|
||||||
rbx_reflection = "4.0.0-alpha.1"
|
rbx_reflection = "4.2.0"
|
||||||
rbx_reflection_database = "0.1.0"
|
rbx_reflection_database = "0.2.6"
|
||||||
rbx_xml = "0.12.0-alpha.1"
|
rbx_xml = "0.13.0"
|
||||||
|
|
||||||
anyhow = "1.0.27"
|
anyhow = "1.0.44"
|
||||||
backtrace = "0.3"
|
backtrace = "0.3.61"
|
||||||
bincode = "1.2.1"
|
bincode = "1.3.3"
|
||||||
crossbeam-channel = "0.4.0"
|
crossbeam-channel = "0.5.1"
|
||||||
csv = "1.1.1"
|
csv = "1.1.6"
|
||||||
env_logger = "0.7.1"
|
env_logger = "0.9.0"
|
||||||
fs-err = "2.2.0"
|
fs-err = "2.6.0"
|
||||||
futures = "0.1.29"
|
futures = "0.3.17"
|
||||||
globset = "0.4.4"
|
globset = "0.4.8"
|
||||||
humantime = "1.3.0"
|
humantime = "2.1.0"
|
||||||
hyper = "0.12.35"
|
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
||||||
jod-thread = "0.1.0"
|
jod-thread = "0.1.2"
|
||||||
lazy_static = "1.4.0"
|
log = "0.4.14"
|
||||||
log = "0.4.8"
|
maplit = "1.0.2"
|
||||||
maplit = "1.0.1"
|
notify = "4.0.17"
|
||||||
notify = "4.0.14"
|
opener = "0.5.0"
|
||||||
opener = "0.4.1"
|
reqwest = { version = "0.11.10", features = ["blocking", "json", "native-tls-vendored"] }
|
||||||
regex = "1.3.1"
|
|
||||||
reqwest = "0.9.20"
|
|
||||||
ritz = "0.1.0"
|
ritz = "0.1.0"
|
||||||
rlua = "0.17.0"
|
roblox_install = "1.0.0"
|
||||||
roblox_install = "0.2.2"
|
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
serde_json = "1.0.68"
|
||||||
serde_json = "1.0"
|
termcolor = "1.1.2"
|
||||||
structopt = "0.3.5"
|
thiserror = "1.0.30"
|
||||||
termcolor = "1.0.5"
|
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
|
||||||
thiserror = "1.0.11"
|
uuid = { version = "1.0.0", features = ["v4", "serde"] }
|
||||||
tokio = "0.1.22"
|
clap = { version = "3.1.18", features = ["derive"] }
|
||||||
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
profiling = "1.0.6"
|
||||||
|
tracy-client = { version = "0.13.2", optional = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.6.2"
|
winreg = "0.10.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
memofs = { version = "0.1.3", path = "memofs" }
|
memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||||
|
|
||||||
anyhow = "1.0.27"
|
embed-resource = "1.6.4"
|
||||||
bincode = "1.2.1"
|
anyhow = "1.0.44"
|
||||||
fs-err = "2.3.0"
|
bincode = "1.3.3"
|
||||||
maplit = "1.0.1"
|
fs-err = "2.6.0"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rojo-insta-ext = { path = "rojo-insta-ext" }
|
rojo-insta-ext = { path = "crates/rojo-insta-ext" }
|
||||||
|
|
||||||
criterion = "0.3"
|
criterion = "0.3.5"
|
||||||
insta = { version = "1.3.0", features = ["redactions"] }
|
insta = { version = "1.8.0", features = ["redactions", "yaml"] }
|
||||||
lazy_static = "1.2"
|
paste = "1.0.5"
|
||||||
paste = "0.1"
|
pretty_assertions = "1.2.1"
|
||||||
pretty_assertions = "0.6.1"
|
serde_yaml = "0.8.21"
|
||||||
serde_yaml = "0.8.9"
|
tempfile = "3.2.0"
|
||||||
tempfile = "3.0"
|
walkdir = "2.3.2"
|
||||||
walkdir = "2.1"
|
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -1,21 +1,14 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://rojo.space">
|
<a href="https://rojo.space"><img src="assets/logo-512.png" alt="Rojo" height="217" /></a>
|
||||||
<img src="assets/logo-512.png" alt="Rojo" height="217" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div> </div>
|
<div> </div>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://github.com/rojo-rbx/rojo/actions">
|
<a href="https://github.com/rojo-rbx/rojo/actions"><img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" /></a>
|
||||||
<img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" />
|
<a href="https://crates.io/crates/rojo"><img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" /></a>
|
||||||
</a>
|
<a href="https://rojo.space/docs"><img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" /></a>
|
||||||
<a href="https://crates.io/crates/rojo">
|
<a href="https://www.patreon.com/lpghatguy"><img src="https://img.shields.io/badge/sponsor-patreon-red" alt="Patreon" /></a>
|
||||||
<img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" />
|
|
||||||
</a>
|
|
||||||
<a href="https://rojo.space/docs">
|
|
||||||
<img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
@@ -48,7 +41,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
|
|||||||
|
|
||||||
Pull requests are welcome!
|
Pull requests are welcome!
|
||||||
|
|
||||||
Rojo supports Rust 1.43.1 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
Rojo supports Rust 1.58.1 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||||
5
aftman.toml
Normal file
5
aftman.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[tools]
|
||||||
|
wally = "UpliftGames/wally@0.3.1"
|
||||||
|
rojo = "rojo-rbx/rojo@7.2.1"
|
||||||
|
selene = "Kampfkarren/selene@0.20.0"
|
||||||
|
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"
|
||||||
BIN
assets/NotificationPop.mp3
Normal file
BIN
assets/NotificationPop.mp3
Normal file
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
"$className": "DataModel",
|
"$className": "DataModel",
|
||||||
|
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"Common": {
|
"Shared": {
|
||||||
"$path": "src/shared"
|
"$path": "src/shared"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
assets/icon-link-32.png
Normal file
BIN
assets/icon-link-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/icon-warn-32.png
Normal file
BIN
assets/icon-warn-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -3,7 +3,7 @@ use std::path::Path;
|
|||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
use librojo::cli::{build, BuildCommand};
|
use librojo::cli::BuildCommand;
|
||||||
|
|
||||||
pub fn benchmark_small_place(c: &mut Criterion) {
|
pub fn benchmark_small_place(c: &mut Criterion) {
|
||||||
bench_build_place(c, "Small Place", "test-projects/benchmark_small_place")
|
bench_build_place(c, "Small Place", "test-projects/benchmark_small_place")
|
||||||
@@ -20,7 +20,7 @@ fn bench_build_place(c: &mut Criterion, name: &str, path: &str) {
|
|||||||
group.bench_function("build", |b| {
|
group.bench_function("build", |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| place_setup(path),
|
|| place_setup(path),
|
||||||
|(_dir, options)| build(options).unwrap(),
|
|(_dir, options)| options.run().unwrap(),
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
watchexec -c -w plugin "sh -c './bin/install-dev-plugin.sh'"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxm"
|
|
||||||
TESTEZ_FILE="$DIR/TestEZ.rbxm"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/testez.project.json -o "$TESTEZ_FILE"
|
|
||||||
remodel bin/mark-plugin-as-dev.lua "$PLUGIN_FILE" "$TESTEZ_FILE" 2>/dev/null
|
|
||||||
|
|
||||||
cp "$PLUGIN_FILE" "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
rojo build plugin -o "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
local pluginPath, testezPath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local testez = remodel.readModelFile(testezPath)[1]
|
|
||||||
|
|
||||||
local marker = Instance.new("Folder")
|
|
||||||
marker.Name = "ROJO_DEV_BUILD"
|
|
||||||
marker.Parent = plugin
|
|
||||||
|
|
||||||
testez.Parent = plugin
|
|
||||||
|
|
||||||
remodel.writeModelFile(plugin, pluginPath)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
local pluginPath, placePath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local place = remodel.readPlaceFile(placePath)
|
|
||||||
|
|
||||||
plugin.Parent = place:GetService("ReplicatedStorage")
|
|
||||||
|
|
||||||
remodel.writePlaceFile(place, placePath)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
./bin/run-cli-tests.sh
|
|
||||||
./bin/run-plugin-tests.sh
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo test --all --locked
|
|
||||||
cargo fmt -- --check
|
|
||||||
|
|
||||||
touch src/lib.rs # Nudge Rust source to make Clippy actually check things
|
|
||||||
cargo clippy
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxmx"
|
|
||||||
PLACE_FILE="$DIR/RojoTestPlace.rbxlx"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/place.project.json -o "$PLACE_FILE"
|
|
||||||
|
|
||||||
remodel bin/put-plugin-in-test-place.lua "$PLUGIN_FILE" "$PLACE_FILE"
|
|
||||||
|
|
||||||
run-in-roblox -s plugin/testBootstrap.server.lua "$PLACE_FILE"
|
|
||||||
|
|
||||||
luacheck plugin/src plugin/log plugin/http
|
|
||||||
23
build.rs
23
build.rs
@@ -21,7 +21,7 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
|
|||||||
|
|
||||||
// We can skip any TestEZ test files since they aren't necessary for
|
// We can skip any TestEZ test files since they aren't necessary for
|
||||||
// the plugin to run.
|
// the plugin to run.
|
||||||
if file_name.ends_with(".spec.lua") {
|
if file_name.ends_with(".spec.lua") || file_name.ends_with(".spec.luau") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +43,6 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
|
let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
|
||||||
let plugin_root = PathBuf::from(root_dir).join("plugin");
|
let plugin_root = PathBuf::from(root_dir).join("plugin");
|
||||||
|
|
||||||
let plugin_modules = plugin_root.join("modules");
|
|
||||||
|
|
||||||
let snapshot = VfsSnapshot::dir(hashmap! {
|
let snapshot = VfsSnapshot::dir(hashmap! {
|
||||||
"default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?,
|
"default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?,
|
||||||
"fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?,
|
"fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?,
|
||||||
@@ -52,20 +50,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
"log" => snapshot_from_fs_path(&plugin_root.join("log"))?,
|
"log" => snapshot_from_fs_path(&plugin_root.join("log"))?,
|
||||||
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?,
|
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?,
|
||||||
"src" => snapshot_from_fs_path(&plugin_root.join("src"))?,
|
"src" => snapshot_from_fs_path(&plugin_root.join("src"))?,
|
||||||
"modules" => VfsSnapshot::dir(hashmap! {
|
"Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?,
|
||||||
"roact" => VfsSnapshot::dir(hashmap! {
|
|
||||||
"src" => snapshot_from_fs_path(&plugin_modules.join("roact").join("src"))?
|
|
||||||
}),
|
|
||||||
"promise" => VfsSnapshot::dir(hashmap! {
|
|
||||||
"lib" => snapshot_from_fs_path(&plugin_modules.join("promise").join("lib"))?
|
|
||||||
}),
|
|
||||||
"t" => VfsSnapshot::dir(hashmap! {
|
|
||||||
"lib" => snapshot_from_fs_path(&plugin_modules.join("t").join("lib"))?
|
|
||||||
}),
|
|
||||||
"flipper" => VfsSnapshot::dir(hashmap! {
|
|
||||||
"src" => snapshot_from_fs_path(&plugin_modules.join("flipper").join("src"))?
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let out_path = Path::new(&out_dir).join("plugin.bincode");
|
let out_path = Path::new(&out_dir).join("plugin.bincode");
|
||||||
@@ -73,5 +58,9 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
|
|
||||||
bincode::serialize_into(out_file, &snapshot)?;
|
bincode::serialize_into(out_file, &snapshot)?;
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
||||||
|
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
||||||
|
embed_resource::compile("build/windows/rojo-manifest.rc");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
2
build/windows/rojo-manifest.rc
Normal file
2
build/windows/rojo-manifest.rc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define RT_MANIFEST 24
|
||||||
|
1 RT_MANIFEST "rojo.manifest"
|
||||||
8
build/windows/rojo.manifest
Normal file
8
build/windows/rojo.manifest
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||||
|
<ws2:longPathAware>true</ws2:longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## 0.2.0 (2021-08-23)
|
||||||
|
* Updated to `crossbeam-channel` 0.5.1.
|
||||||
|
|
||||||
## 0.1.3 (2020-11-19)
|
## 0.1.3 (2020-11-19)
|
||||||
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.
|
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "memofs"
|
name = "memofs"
|
||||||
description = "Virtual filesystem with configurable backends."
|
description = "Virtual filesystem with configurable backends."
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -11,7 +11,7 @@ homepage = "https://github.com/rojo-rbx/rojo/tree/master/memofs"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = "0.4.0"
|
crossbeam-channel = "0.5.1"
|
||||||
fs-err = "2.3.0"
|
fs-err = "2.3.0"
|
||||||
notify = "4.0.15"
|
notify = "4.0.15"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[tools]
|
|
||||||
rojo = { source = "rojo-rbx/rojo", version = "6.0.0-rc.3" }
|
|
||||||
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
|
||||||
@@ -1,33 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "Rojo",
|
"name": "Rojo",
|
||||||
"tree": {
|
"tree": {
|
||||||
"$className": "Folder",
|
"$className": "Folder",
|
||||||
"Plugin": {
|
"Plugin": {
|
||||||
"$path": "src"
|
"$path": "src"
|
||||||
},
|
},
|
||||||
"Log": {
|
"Packages": {
|
||||||
"$path": "log"
|
"$path": "Packages",
|
||||||
},
|
|
||||||
"Http": {
|
"Log": {
|
||||||
"$path": "http"
|
"$path": "log"
|
||||||
},
|
},
|
||||||
"Fmt": {
|
"Http": {
|
||||||
"$path": "fmt"
|
"$path": "http"
|
||||||
},
|
},
|
||||||
"RbxDom": {
|
"Fmt": {
|
||||||
"$path": "rbx_dom_lua"
|
"$path": "fmt"
|
||||||
},
|
},
|
||||||
"Roact": {
|
"RbxDom": {
|
||||||
"$path": "modules/roact/src"
|
"$path": "rbx_dom_lua"
|
||||||
},
|
}
|
||||||
"Promise": {
|
}
|
||||||
"$path": "modules/promise/lib"
|
}
|
||||||
},
|
}
|
||||||
"t": {
|
|
||||||
"$path": "modules/t/lib"
|
|
||||||
},
|
|
||||||
"Flipper": {
|
|
||||||
"$path": "modules/flipper/src"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Submodule plugin/modules/flipper deleted from 4cf7a03cb6
Submodule plugin/modules/promise deleted from 7fb09d103f
Submodule plugin/modules/roact deleted from f7d2f1ce1d
Submodule plugin/modules/t deleted from f643b50682
Submodule plugin/modules/testez deleted from 6e9157db3c
@@ -1,44 +0,0 @@
|
|||||||
stds.roblox = {
|
|
||||||
read_globals = {
|
|
||||||
game = {
|
|
||||||
other_fields = true,
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Roblox globals
|
|
||||||
"script",
|
|
||||||
|
|
||||||
-- Extra functions
|
|
||||||
"tick", "warn",
|
|
||||||
"wait", "typeof",
|
|
||||||
|
|
||||||
-- Types
|
|
||||||
"CFrame",
|
|
||||||
"Color3",
|
|
||||||
"Enum",
|
|
||||||
"Instance",
|
|
||||||
"NumberRange",
|
|
||||||
"Rect",
|
|
||||||
"UDim", "UDim2",
|
|
||||||
"Vector2", "Vector3",
|
|
||||||
"Vector2int16", "Vector3int16",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.testez = {
|
|
||||||
read_globals = {
|
|
||||||
"describe",
|
|
||||||
"it", "itFOCUS", "itSKIP",
|
|
||||||
"FOCUS", "SKIP", "HACK_NO_XPCALL",
|
|
||||||
"expect",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ignore = {
|
|
||||||
"212", -- unused arguments
|
|
||||||
}
|
|
||||||
|
|
||||||
std = "lua51+roblox"
|
|
||||||
|
|
||||||
files["**/*.spec.lua"] = {
|
|
||||||
std = "+testez",
|
|
||||||
}
|
|
||||||
@@ -23,8 +23,45 @@ end
|
|||||||
local ALL_AXES = {"X", "Y", "Z"}
|
local ALL_AXES = {"X", "Y", "Z"}
|
||||||
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
|
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
|
||||||
|
|
||||||
|
local EncodedValue = {}
|
||||||
|
|
||||||
local types
|
local types
|
||||||
types = {
|
types = {
|
||||||
|
Attributes = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
for key, value in pairs(pod) do
|
||||||
|
local ok, result = EncodedValue.decode(value)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
output[key] = result
|
||||||
|
else
|
||||||
|
local warning = ("Could not decode attribute value of type %q: %s"):format(typeof(value), tostring(result))
|
||||||
|
warn(warning)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end,
|
||||||
|
toPod = function(roblox)
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
for key, value in pairs(roblox) do
|
||||||
|
local ok, result = EncodedValue.encodeNaive(value)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
output[key] = result
|
||||||
|
else
|
||||||
|
local warning = ("Could not encode attribute value of type %q: %s"):format(typeof(value), tostring(result))
|
||||||
|
warn(warning)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
Axes = {
|
Axes = {
|
||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local axes = {}
|
local axes = {}
|
||||||
@@ -71,8 +108,8 @@ types = {
|
|||||||
|
|
||||||
CFrame = {
|
CFrame = {
|
||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local pos = pod.Position
|
local pos = pod.position
|
||||||
local orient = pod.Orientation
|
local orient = pod.orientation
|
||||||
|
|
||||||
return CFrame.new(
|
return CFrame.new(
|
||||||
pos[1], pos[2], pos[3],
|
pos[1], pos[2], pos[3],
|
||||||
@@ -89,8 +126,8 @@ types = {
|
|||||||
r20, r21, r22 = roblox:GetComponents()
|
r20, r21, r22 = roblox:GetComponents()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Position = {x, y, z},
|
position = {x, y, z},
|
||||||
Orientation = {
|
orientation = {
|
||||||
{r00, r01, r02},
|
{r00, r01, r02},
|
||||||
{r10, r11, r12},
|
{r10, r11, r12},
|
||||||
{r20, r21, r22},
|
{r20, r21, r22},
|
||||||
@@ -123,10 +160,10 @@ types = {
|
|||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local keypoints = {}
|
local keypoints = {}
|
||||||
|
|
||||||
for index, keypoint in ipairs(pod.Keypoints) do
|
for index, keypoint in ipairs(pod.keypoints) do
|
||||||
keypoints[index] = ColorSequenceKeypoint.new(
|
keypoints[index] = ColorSequenceKeypoint.new(
|
||||||
keypoint.Time,
|
keypoint.time,
|
||||||
types.Color3.fromPod(keypoint.Color)
|
types.Color3.fromPod(keypoint.color)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -138,13 +175,13 @@ types = {
|
|||||||
|
|
||||||
for index, keypoint in ipairs(roblox.Keypoints) do
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
keypoints[index] = {
|
keypoints[index] = {
|
||||||
Time = keypoint.Time,
|
time = keypoint.Time,
|
||||||
Color = types.Color3.toPod(keypoint.Value),
|
color = types.Color3.toPod(keypoint.Value),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Keypoints = keypoints,
|
keypoints = keypoints,
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@@ -201,6 +238,23 @@ types = {
|
|||||||
toPod = serializeFloat,
|
toPod = serializeFloat,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Font = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Font.new(
|
||||||
|
pod.family,
|
||||||
|
if pod.weight ~= nil then Enum.FontWeight[pod.weight] else nil,
|
||||||
|
if pod.style ~= nil then Enum.FontStyle[pod.style] else nil
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
family = roblox.Family,
|
||||||
|
weight = roblox.Weight.Name,
|
||||||
|
style = roblox.Style.Name,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
Int32 = {
|
Int32 = {
|
||||||
fromPod = identity,
|
fromPod = identity,
|
||||||
toPod = identity,
|
toPod = identity,
|
||||||
@@ -223,11 +277,11 @@ types = {
|
|||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local keypoints = {}
|
local keypoints = {}
|
||||||
|
|
||||||
for index, keypoint in ipairs(pod.Keypoints) do
|
for index, keypoint in ipairs(pod.keypoints) do
|
||||||
keypoints[index] = NumberSequenceKeypoint.new(
|
keypoints[index] = NumberSequenceKeypoint.new(
|
||||||
keypoint.Time,
|
keypoint.time,
|
||||||
keypoint.Value,
|
keypoint.value,
|
||||||
keypoint.Envelope
|
keypoint.envelope
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -239,14 +293,14 @@ types = {
|
|||||||
|
|
||||||
for index, keypoint in ipairs(roblox.Keypoints) do
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
keypoints[index] = {
|
keypoints[index] = {
|
||||||
Time = keypoint.Time,
|
time = keypoint.Time,
|
||||||
Value = keypoint.Value,
|
value = keypoint.Value,
|
||||||
Envelope = keypoint.Envelope,
|
envelope = keypoint.Envelope,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Keypoints = keypoints,
|
keypoints = keypoints,
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@@ -257,11 +311,11 @@ types = {
|
|||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
return PhysicalProperties.new(
|
return PhysicalProperties.new(
|
||||||
pod.Density,
|
pod.density,
|
||||||
pod.Friction,
|
pod.friction,
|
||||||
pod.Elasticity,
|
pod.elasticity,
|
||||||
pod.FrictionWeight,
|
pod.frictionWeight,
|
||||||
pod.ElasticityWeight
|
pod.elasticityWeight
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -271,11 +325,11 @@ types = {
|
|||||||
return "Default"
|
return "Default"
|
||||||
else
|
else
|
||||||
return {
|
return {
|
||||||
Density = roblox.Density,
|
density = roblox.Density,
|
||||||
Friction = roblox.Friction,
|
friction = roblox.Friction,
|
||||||
Elasticity = roblox.Elasticity,
|
elasticity = roblox.Elasticity,
|
||||||
FrictionWeight = roblox.FrictionWeight,
|
frictionWeight = roblox.FrictionWeight,
|
||||||
ElasticityWeight = roblox.ElasticityWeight,
|
elasticityWeight = roblox.ElasticityWeight,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -284,15 +338,15 @@ types = {
|
|||||||
Ray = {
|
Ray = {
|
||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
return Ray.new(
|
return Ray.new(
|
||||||
types.Vector3.fromPod(pod.Origin),
|
types.Vector3.fromPod(pod.origin),
|
||||||
types.Vector3.fromPod(pod.Direction)
|
types.Vector3.fromPod(pod.direction)
|
||||||
)
|
)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
toPod = function(roblox)
|
toPod = function(roblox)
|
||||||
return {
|
return {
|
||||||
Origin = types.Vector3.toPod(roblox.Origin),
|
origin = types.Vector3.toPod(roblox.Origin),
|
||||||
Direction = types.Vector3.toPod(roblox.Direction),
|
direction = types.Vector3.toPod(roblox.Direction),
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@@ -388,6 +442,11 @@ types = {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Tags = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
Vector2 = {
|
Vector2 = {
|
||||||
fromPod = unpackDecoder(Vector2.new),
|
fromPod = unpackDecoder(Vector2.new),
|
||||||
|
|
||||||
@@ -428,15 +487,15 @@ types = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local EncodedValue = {}
|
|
||||||
|
|
||||||
function EncodedValue.decode(encodedValue)
|
function EncodedValue.decode(encodedValue)
|
||||||
local typeImpl = types[encodedValue.Type]
|
local ty, value = next(encodedValue)
|
||||||
|
|
||||||
|
local typeImpl = types[ty]
|
||||||
if typeImpl == nil then
|
if typeImpl == nil then
|
||||||
return false, "Couldn't decode value " .. tostring(encodedValue.Type)
|
return false, "Couldn't decode value " .. tostring(ty)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, typeImpl.fromPod(encodedValue.Value)
|
return true, typeImpl.fromPod(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
function EncodedValue.encode(rbxValue, propertyType)
|
function EncodedValue.encode(rbxValue, propertyType)
|
||||||
@@ -448,9 +507,23 @@ function EncodedValue.encode(rbxValue, propertyType)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return true, {
|
return true, {
|
||||||
Type = propertyType,
|
[propertyType] = typeImpl.toPod(rbxValue),
|
||||||
Value = typeImpl.toPod(rbxValue),
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local propertyTypeRenames = {
|
||||||
|
number = "Float64",
|
||||||
|
boolean = "Bool",
|
||||||
|
string = "String",
|
||||||
|
}
|
||||||
|
|
||||||
|
function EncodedValue.encodeNaive(rbxValue)
|
||||||
|
local propertyType = typeof(rbxValue)
|
||||||
|
if propertyTypeRenames[propertyType] ~= nil then
|
||||||
|
propertyType = propertyTypeRenames[propertyType]
|
||||||
|
end
|
||||||
|
|
||||||
|
return EncodedValue.encode(rbxValue, propertyType)
|
||||||
|
end
|
||||||
|
|
||||||
return EncodedValue
|
return EncodedValue
|
||||||
@@ -20,7 +20,21 @@ local function set(container, key, value)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
||||||
|
local key, value = next(data.DataType)
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
|
-- The meanings of the key and value in DataType differ when the type of
|
||||||
|
-- the property is Enum. When the property is of type Enum, the key is
|
||||||
|
-- the name of the type:
|
||||||
|
--
|
||||||
|
-- { Enum = "<name of enum>" }
|
||||||
|
--
|
||||||
|
-- When the property is not of type Enum, the value is the name of the
|
||||||
|
-- type:
|
||||||
|
--
|
||||||
|
-- { Value = "<data type>" }
|
||||||
|
dataType = key == "Enum" and key or value,
|
||||||
|
|
||||||
scriptability = data.Scriptability,
|
scriptability = data.Scriptability,
|
||||||
className = className,
|
className = className,
|
||||||
name = propertyName,
|
name = propertyName,
|
||||||
@@ -39,6 +53,11 @@ function PropertyDescriptor:read(instance)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.scriptability == "Custom" then
|
if self.scriptability == "Custom" then
|
||||||
|
if customProperties[self.className] == nil then
|
||||||
|
local fullName = ("%s.%s"):format(instance.className, self.name)
|
||||||
|
return false, Error.new(Error.Kind.PropertyNotReadable, fullName)
|
||||||
|
end
|
||||||
|
|
||||||
local interface = customProperties[self.className][self.name]
|
local interface = customProperties[self.className][self.name]
|
||||||
|
|
||||||
return interface.read(instance, self.name)
|
return interface.read(instance, self.name)
|
||||||
@@ -65,6 +84,11 @@ function PropertyDescriptor:write(instance, value)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.scriptability == "Custom" then
|
if self.scriptability == "Custom" then
|
||||||
|
if customProperties[self.className] == nil then
|
||||||
|
local fullName = ("%s.%s"):format(instance.className, self.name)
|
||||||
|
return false, Error.new(Error.Kind.PropertyNotWritable, fullName)
|
||||||
|
end
|
||||||
|
|
||||||
local interface = customProperties[self.className][self.name]
|
local interface = customProperties[self.className][self.name]
|
||||||
|
|
||||||
return interface.write(instance, self.name, value)
|
return interface.write(instance, self.name, value)
|
||||||
@@ -77,4 +101,4 @@ function PropertyDescriptor:write(instance, value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return PropertyDescriptor
|
return PropertyDescriptor
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# rbx_dom_lua
|
|
||||||
Roblox Lua implementation of rbx-dom mechanisms, intended to work with rbx_dom_weak and friends.
|
|
||||||
@@ -1,8 +1,76 @@
|
|||||||
{
|
{
|
||||||
|
"Attributes": {
|
||||||
|
"value": {
|
||||||
|
"Attributes": {
|
||||||
|
"TestBool": {
|
||||||
|
"Bool": true
|
||||||
|
},
|
||||||
|
"TestBrickColor": {
|
||||||
|
"BrickColor": 24
|
||||||
|
},
|
||||||
|
"TestColor3": {
|
||||||
|
"Color3": [
|
||||||
|
1.0,
|
||||||
|
0.5,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestNumber": {
|
||||||
|
"Float64": 1337.0
|
||||||
|
},
|
||||||
|
"TestRect": {
|
||||||
|
"Rect": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.0,
|
||||||
|
4.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestString": {
|
||||||
|
"String": "Test"
|
||||||
|
},
|
||||||
|
"TestUDim": {
|
||||||
|
"UDim": [
|
||||||
|
1.0,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestUDim2": {
|
||||||
|
"UDim2": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.0,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestVector2": {
|
||||||
|
"Vector2": [
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestVector3": {
|
||||||
|
"Vector3": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Attributes"
|
||||||
|
},
|
||||||
"Axes": {
|
"Axes": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Axes",
|
"Axes": [
|
||||||
"Value": [
|
|
||||||
"X",
|
"X",
|
||||||
"Y",
|
"Y",
|
||||||
"Z"
|
"Z"
|
||||||
@@ -12,35 +80,31 @@
|
|||||||
},
|
},
|
||||||
"BinaryString": {
|
"BinaryString": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "BinaryString",
|
"BinaryString": "SGVsbG8h"
|
||||||
"Value": "SGVsbG8h"
|
|
||||||
},
|
},
|
||||||
"ty": "BinaryString"
|
"ty": "BinaryString"
|
||||||
},
|
},
|
||||||
"Bool": {
|
"Bool": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Bool",
|
"Bool": true
|
||||||
"Value": true
|
|
||||||
},
|
},
|
||||||
"ty": "Bool"
|
"ty": "Bool"
|
||||||
},
|
},
|
||||||
"BrickColor": {
|
"BrickColor": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "BrickColor",
|
"BrickColor": 1004
|
||||||
"Value": 1004
|
|
||||||
},
|
},
|
||||||
"ty": "BrickColor"
|
"ty": "BrickColor"
|
||||||
},
|
},
|
||||||
"CFrame": {
|
"CFrame": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "CFrame",
|
"CFrame": {
|
||||||
"Value": {
|
"position": [
|
||||||
"Position": [
|
|
||||||
1.0,
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
3.0
|
3.0
|
||||||
],
|
],
|
||||||
"Orientation": [
|
"orientation": [
|
||||||
[
|
[
|
||||||
4.0,
|
4.0,
|
||||||
5.0,
|
5.0,
|
||||||
@@ -63,8 +127,7 @@
|
|||||||
},
|
},
|
||||||
"Color3": {
|
"Color3": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Color3",
|
"Color3": [
|
||||||
"Value": [
|
|
||||||
1.0,
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
3.0
|
3.0
|
||||||
@@ -74,8 +137,7 @@
|
|||||||
},
|
},
|
||||||
"Color3uint8": {
|
"Color3uint8": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Color3uint8",
|
"Color3uint8": [
|
||||||
"Value": [
|
|
||||||
0,
|
0,
|
||||||
128,
|
128,
|
||||||
255
|
255
|
||||||
@@ -85,20 +147,19 @@
|
|||||||
},
|
},
|
||||||
"ColorSequence": {
|
"ColorSequence": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "ColorSequence",
|
"ColorSequence": {
|
||||||
"Value": {
|
"keypoints": [
|
||||||
"Keypoints": [
|
|
||||||
{
|
{
|
||||||
"Time": 0.0,
|
"time": 0.0,
|
||||||
"Color": [
|
"color": [
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
0.5
|
0.5
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Time": 1.0,
|
"time": 1.0,
|
||||||
"Color": [
|
"color": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
@@ -111,22 +172,19 @@
|
|||||||
},
|
},
|
||||||
"Content": {
|
"Content": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Content",
|
"Content": "rbxassetid://12345"
|
||||||
"Value": "rbxassetid://12345"
|
|
||||||
},
|
},
|
||||||
"ty": "Content"
|
"ty": "Content"
|
||||||
},
|
},
|
||||||
"Enum": {
|
"Enum": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Enum",
|
"Enum": 1234
|
||||||
"Value": 1234
|
|
||||||
},
|
},
|
||||||
"ty": "Enum"
|
"ty": "Enum"
|
||||||
},
|
},
|
||||||
"Faces": {
|
"Faces": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Faces",
|
"Faces": [
|
||||||
"Value": [
|
|
||||||
"Right",
|
"Right",
|
||||||
"Top",
|
"Top",
|
||||||
"Back",
|
"Back",
|
||||||
@@ -139,36 +197,42 @@
|
|||||||
},
|
},
|
||||||
"Float32": {
|
"Float32": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Float32",
|
"Float32": 15.0
|
||||||
"Value": 15.0
|
|
||||||
},
|
},
|
||||||
"ty": "Float32"
|
"ty": "Float32"
|
||||||
},
|
},
|
||||||
"Float64": {
|
"Float64": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Float64",
|
"Float64": 15123.0
|
||||||
"Value": 15123.0
|
|
||||||
},
|
},
|
||||||
"ty": "Float64"
|
"ty": "Float64"
|
||||||
},
|
},
|
||||||
|
"Font": {
|
||||||
|
"value": {
|
||||||
|
"Font": {
|
||||||
|
"family": "rbxasset://fonts/families/SourceSansPro.json",
|
||||||
|
"weight": "Regular",
|
||||||
|
"style": "Normal",
|
||||||
|
"cachedFaceId": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Font"
|
||||||
|
},
|
||||||
"Int32": {
|
"Int32": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Int32",
|
"Int32": 6014
|
||||||
"Value": 6014
|
|
||||||
},
|
},
|
||||||
"ty": "Int32"
|
"ty": "Int32"
|
||||||
},
|
},
|
||||||
"Int64": {
|
"Int64": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Int64",
|
"Int64": 23491023
|
||||||
"Value": 23491023
|
|
||||||
},
|
},
|
||||||
"ty": "Int64"
|
"ty": "Int64"
|
||||||
},
|
},
|
||||||
"NumberRange": {
|
"NumberRange": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "NumberRange",
|
"NumberRange": [
|
||||||
"Value": [
|
|
||||||
-36.0,
|
-36.0,
|
||||||
94.0
|
94.0
|
||||||
]
|
]
|
||||||
@@ -177,18 +241,17 @@
|
|||||||
},
|
},
|
||||||
"NumberSequence": {
|
"NumberSequence": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "NumberSequence",
|
"NumberSequence": {
|
||||||
"Value": {
|
"keypoints": [
|
||||||
"Keypoints": [
|
|
||||||
{
|
{
|
||||||
"Time": 0.0,
|
"time": 0.0,
|
||||||
"Value": 5.0,
|
"value": 5.0,
|
||||||
"Envelope": 2.0
|
"envelope": 2.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Time": 1.0,
|
"time": 1.0,
|
||||||
"Value": 22.0,
|
"value": 22.0,
|
||||||
"Envelope": 0.0
|
"envelope": 0.0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -197,34 +260,31 @@
|
|||||||
},
|
},
|
||||||
"PhysicalProperties-Custom": {
|
"PhysicalProperties-Custom": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "PhysicalProperties",
|
"PhysicalProperties": {
|
||||||
"Value": {
|
"density": 0.5,
|
||||||
"Density": 0.5,
|
"friction": 1.0,
|
||||||
"Friction": 1.0,
|
"elasticity": 0.0,
|
||||||
"Elasticity": 0.0,
|
"frictionWeight": 50.0,
|
||||||
"FrictionWeight": 50.0,
|
"elasticityWeight": 25.0
|
||||||
"ElasticityWeight": 25.0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ty": "PhysicalProperties"
|
"ty": "PhysicalProperties"
|
||||||
},
|
},
|
||||||
"PhysicalProperties-Default": {
|
"PhysicalProperties-Default": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "PhysicalProperties",
|
"PhysicalProperties": "Default"
|
||||||
"Value": "Default"
|
|
||||||
},
|
},
|
||||||
"ty": "PhysicalProperties"
|
"ty": "PhysicalProperties"
|
||||||
},
|
},
|
||||||
"Ray": {
|
"Ray": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Ray",
|
"Ray": {
|
||||||
"Value": {
|
"origin": [
|
||||||
"Origin": [
|
|
||||||
1.0,
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
3.0
|
3.0
|
||||||
],
|
],
|
||||||
"Direction": [
|
"direction": [
|
||||||
4.0,
|
4.0,
|
||||||
5.0,
|
5.0,
|
||||||
6.0
|
6.0
|
||||||
@@ -235,8 +295,7 @@
|
|||||||
},
|
},
|
||||||
"Rect": {
|
"Rect": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Rect",
|
"Rect": [
|
||||||
"Value": [
|
|
||||||
[
|
[
|
||||||
0.0,
|
0.0,
|
||||||
5.0
|
5.0
|
||||||
@@ -251,8 +310,7 @@
|
|||||||
},
|
},
|
||||||
"Region3int16": {
|
"Region3int16": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Region3int16",
|
"Region3int16": [
|
||||||
"Value": [
|
|
||||||
[
|
[
|
||||||
-10,
|
-10,
|
||||||
-5,
|
-5,
|
||||||
@@ -269,15 +327,23 @@
|
|||||||
},
|
},
|
||||||
"String": {
|
"String": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "String",
|
"String": "Hello, world!"
|
||||||
"Value": "Hello, world!"
|
|
||||||
},
|
},
|
||||||
"ty": "String"
|
"ty": "String"
|
||||||
},
|
},
|
||||||
|
"Tags": {
|
||||||
|
"value": {
|
||||||
|
"Tags": [
|
||||||
|
"foo",
|
||||||
|
"con'fusion?!",
|
||||||
|
"bar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Tags"
|
||||||
|
},
|
||||||
"UDim": {
|
"UDim": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "UDim",
|
"UDim": [
|
||||||
"Value": [
|
|
||||||
1.0,
|
1.0,
|
||||||
32
|
32
|
||||||
]
|
]
|
||||||
@@ -286,8 +352,7 @@
|
|||||||
},
|
},
|
||||||
"UDim2": {
|
"UDim2": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "UDim2",
|
"UDim2": [
|
||||||
"Value": [
|
|
||||||
[
|
[
|
||||||
-1.0,
|
-1.0,
|
||||||
100
|
100
|
||||||
@@ -302,8 +367,7 @@
|
|||||||
},
|
},
|
||||||
"Vector2": {
|
"Vector2": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector2",
|
"Vector2": [
|
||||||
"Value": [
|
|
||||||
-50.0,
|
-50.0,
|
||||||
50.0
|
50.0
|
||||||
]
|
]
|
||||||
@@ -312,8 +376,7 @@
|
|||||||
},
|
},
|
||||||
"Vector2int16": {
|
"Vector2int16": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector2int16",
|
"Vector2int16": [
|
||||||
"Value": [
|
|
||||||
-300,
|
-300,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
@@ -322,8 +385,7 @@
|
|||||||
},
|
},
|
||||||
"Vector3": {
|
"Vector3": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector3",
|
"Vector3": [
|
||||||
"Value": [
|
|
||||||
-300.0,
|
-300.0,
|
||||||
0.0,
|
0.0,
|
||||||
1500.0
|
1500.0
|
||||||
@@ -333,8 +395,7 @@
|
|||||||
},
|
},
|
||||||
"Vector3int16": {
|
"Vector3int16": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector3int16",
|
"Vector3int16": [
|
||||||
"Value": [
|
|
||||||
60,
|
60,
|
||||||
37,
|
37,
|
||||||
-450
|
-450
|
||||||
@@ -5,13 +5,31 @@ local CollectionService = game:GetService("CollectionService")
|
|||||||
-- The reflection database refers to these as having scriptability = "Custom"
|
-- The reflection database refers to these as having scriptability = "Custom"
|
||||||
return {
|
return {
|
||||||
Instance = {
|
Instance = {
|
||||||
Tags = {
|
Attributes = {
|
||||||
read = function(instance, key)
|
read = function(instance)
|
||||||
local tagList = CollectionService:GetTags(instance)
|
return true, instance:GetAttributes()
|
||||||
|
|
||||||
return true, table.concat(tagList, "\0")
|
|
||||||
end,
|
end,
|
||||||
write = function(instance, key, value)
|
write = function(instance, _, value)
|
||||||
|
local existing = instance:GetAttributes()
|
||||||
|
|
||||||
|
for key, attr in pairs(value) do
|
||||||
|
instance:SetAttribute(key, attr)
|
||||||
|
end
|
||||||
|
|
||||||
|
for key in pairs(existing) do
|
||||||
|
if value[key] == nil then
|
||||||
|
instance:SetAttribute(key, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
Tags = {
|
||||||
|
read = function(instance)
|
||||||
|
return true, CollectionService:GetTags(instance)
|
||||||
|
end,
|
||||||
|
write = function(instance, _, value)
|
||||||
local existingTags = CollectionService:GetTags(instance)
|
local existingTags = CollectionService:GetTags(instance)
|
||||||
|
|
||||||
local unseenTags = {}
|
local unseenTags = {}
|
||||||
@@ -19,8 +37,7 @@ return {
|
|||||||
unseenTags[tag] = true
|
unseenTags[tag] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local tagList = string.split(value, "\0")
|
for _, tag in ipairs(value) do
|
||||||
for _, tag in ipairs(tagList) do
|
|
||||||
unseenTags[tag] = nil
|
unseenTags[tag] = nil
|
||||||
CollectionService:AddTag(instance, tag)
|
CollectionService:AddTag(instance, tag)
|
||||||
end
|
end
|
||||||
@@ -44,4 +61,14 @@ return {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
Model = {
|
||||||
|
Scale = {
|
||||||
|
read = function(instance, _, _)
|
||||||
|
return true, instance:GetScale()
|
||||||
|
end,
|
||||||
|
write = function(instance, _, value)
|
||||||
|
return true, instance:ScaleTo(value)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ local function findCanonicalPropertyDescriptor(className, propertyName)
|
|||||||
local aliasData = propertyData.Kind.Alias
|
local aliasData = propertyData.Kind.Alias
|
||||||
if aliasData ~= nil then
|
if aliasData ~= nil then
|
||||||
return PropertyDescriptor.fromRaw(
|
return PropertyDescriptor.fromRaw(
|
||||||
currentClass.properties[aliasData.AliasFor],
|
currentClass.Properties[aliasData.AliasFor],
|
||||||
currentClassName,
|
currentClassName,
|
||||||
aliasData.AliasFor)
|
aliasData.AliasFor)
|
||||||
end
|
end
|
||||||
@@ -66,4 +66,4 @@ return {
|
|||||||
findCanonicalPropertyDescriptor = findCanonicalPropertyDescriptor,
|
findCanonicalPropertyDescriptor = findCanonicalPropertyDescriptor,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
EncodedValue = require(script.EncodedValue),
|
EncodedValue = require(script.EncodedValue),
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
require(game.ReplicatedStorage.TestEZ).TestBootstrap:run({game.ReplicatedStorage.RbxDom})
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
rojo build test-place.project.json -o TestPlace.rbxlx
|
|
||||||
run-in-roblox --script run-tests.lua --place TestPlace.rbxlx
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "rbx_dom_lua test place",
|
|
||||||
"tree": {
|
|
||||||
"$className": "DataModel",
|
|
||||||
"ReplicatedStorage": {
|
|
||||||
"$className": "ReplicatedStorage",
|
|
||||||
|
|
||||||
"RbxDom": {
|
|
||||||
"$path": "src"
|
|
||||||
},
|
|
||||||
"TestEZ": {
|
|
||||||
"$path": "modules/testez/lib"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ServerScriptService": {
|
|
||||||
"$className": "ServerScriptService",
|
|
||||||
|
|
||||||
"Run Tests": {
|
|
||||||
"$path": "run-tests.lua"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Players": {
|
|
||||||
"$className": "Players",
|
|
||||||
"$properties": {
|
|
||||||
"CharacterAutoLoads": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"HttpService": {
|
|
||||||
"$className": "HttpService",
|
|
||||||
"$properties": {
|
|
||||||
"HttpEnabled": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,11 @@
|
|||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
local TestEZ = require(ReplicatedStorage.TestEZ)
|
local TestEZ = require(ReplicatedStorage.Packages.TestEZ)
|
||||||
|
|
||||||
local Rojo = ReplicatedStorage.Rojo
|
local Rojo = ReplicatedStorage.Rojo
|
||||||
|
|
||||||
local DevSettings = require(Rojo.Plugin.DevSettings)
|
local Settings = require(Rojo.Plugin.Settings)
|
||||||
|
Settings:set("logLevel", "Trace")
|
||||||
local setDevSettings = not DevSettings:hasChangedValues()
|
Settings:set("typecheckingEnabled", true)
|
||||||
|
|
||||||
if setDevSettings then
|
|
||||||
DevSettings:createTestSettings()
|
|
||||||
end
|
|
||||||
|
|
||||||
require(Rojo.Plugin.runTests)(TestEZ)
|
require(Rojo.Plugin.runTests)(TestEZ)
|
||||||
|
|
||||||
if setDevSettings then
|
|
||||||
DevSettings:resetValues()
|
|
||||||
end
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
local Http = require(script.Parent.Parent.Http)
|
local Packages = script.Parent.Parent.Packages
|
||||||
local Log = require(script.Parent.Parent.Log)
|
local Http = require(Packages.Http)
|
||||||
local Promise = require(script.Parent.Parent.Promise)
|
local Log = require(Packages.Log)
|
||||||
|
local Promise = require(Packages.Promise)
|
||||||
|
|
||||||
local Config = require(script.Parent.Config)
|
local Config = require(script.Parent.Config)
|
||||||
local Types = require(script.Parent.Types)
|
local Types = require(script.Parent.Types)
|
||||||
@@ -85,7 +86,7 @@ local ApiContext = {}
|
|||||||
ApiContext.__index = ApiContext
|
ApiContext.__index = ApiContext
|
||||||
|
|
||||||
function ApiContext.new(baseUrl)
|
function ApiContext.new(baseUrl)
|
||||||
assert(type(baseUrl) == "string")
|
assert(type(baseUrl) == "string", "baseUrl must be a string")
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
__baseUrl = baseUrl,
|
__baseUrl = baseUrl,
|
||||||
@@ -248,4 +249,4 @@ function ApiContext:open(id)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return ApiContext
|
return ApiContext
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
@@ -23,8 +24,10 @@ local function BorderedContainer(props)
|
|||||||
layoutOrder = props.layoutOrder,
|
layoutOrder = props.layoutOrder,
|
||||||
}, {
|
}, {
|
||||||
Content = e("Frame", {
|
Content = e("Frame", {
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
Size = UDim2.new(1, -2, 1, -2),
|
||||||
|
Position = UDim2.new(0, 1, 0, 1),
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
ZIndex = 2,
|
||||||
}, props[Roact.Children]),
|
}, props[Roact.Children]),
|
||||||
|
|
||||||
Border = e(SlicedImage, {
|
Border = e(SlicedImage, {
|
||||||
@@ -38,4 +41,4 @@ local function BorderedContainer(props)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return BorderedContainer
|
return BorderedContainer
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
local Flipper = require(Rojo.Flipper)
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
local bindingUtil = require(Plugin.App.bindingUtil)
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
local SlicedImage = require(script.Parent.SlicedImage)
|
local SlicedImage = require(script.Parent.SlicedImage)
|
||||||
|
local Tooltip = require(script.Parent.Tooltip)
|
||||||
|
|
||||||
local e = Roact.createElement
|
local e = Roact.createElement
|
||||||
|
|
||||||
@@ -51,6 +53,10 @@ function Checkbox:render()
|
|||||||
|
|
||||||
[Roact.Event.Activated] = self.props.onClick,
|
[Roact.Event.Activated] = self.props.onClick,
|
||||||
}, {
|
}, {
|
||||||
|
StateTip = e(Tooltip.Trigger, {
|
||||||
|
text = if self.props.active then "Enabled" else "Disabled",
|
||||||
|
}),
|
||||||
|
|
||||||
Active = e(SlicedImage, {
|
Active = e(SlicedImage, {
|
||||||
slice = Assets.Slices.RoundedBackground,
|
slice = Assets.Slices.RoundedBackground,
|
||||||
color = theme.Active.BackgroundColor,
|
color = theme.Active.BackgroundColor,
|
||||||
@@ -93,4 +99,4 @@ function Checkbox:render()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return Checkbox
|
return Checkbox
|
||||||
|
|||||||
169
plugin/src/App/Components/Dropdown.lua
Normal file
169
plugin/src/App/Components/Dropdown.lua
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local SlicedImage = require(script.Parent.SlicedImage)
|
||||||
|
local ScrollingFrame = require(script.Parent.ScrollingFrame)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local Dropdown = Roact.Component:extend("Dropdown")
|
||||||
|
|
||||||
|
function Dropdown:init()
|
||||||
|
self.openMotor = Flipper.SingleMotor.new(0)
|
||||||
|
self.openBinding = bindingUtil.fromMotor(self.openMotor)
|
||||||
|
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
open = false,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dropdown:didUpdate()
|
||||||
|
self.openMotor:setGoal(
|
||||||
|
Flipper.Spring.new(self.state.open and 1 or 0, {
|
||||||
|
frequency = 6,
|
||||||
|
dampingRatio = 1.1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dropdown:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
theme = 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
|
||||||
|
end
|
||||||
|
|
||||||
|
optionButtons[text] = e("TextButton", {
|
||||||
|
Text = text,
|
||||||
|
LayoutOrder = i,
|
||||||
|
Size = UDim2.new(1, 0, 0, 24),
|
||||||
|
BackgroundColor3 = theme.BackgroundColor,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
BackgroundTransparency = self.props.transparency,
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
TextColor3 = theme.TextColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextSize = 15,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = function()
|
||||||
|
self:setState({
|
||||||
|
open = false,
|
||||||
|
})
|
||||||
|
self.props.onClick(option)
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 6),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return e("ImageButton", {
|
||||||
|
Size = UDim2.new(0, width+50, 0, 28),
|
||||||
|
Position = self.props.position,
|
||||||
|
AnchorPoint = self.props.anchorPoint,
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
ZIndex = self.props.zIndex,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = function()
|
||||||
|
self:setState({
|
||||||
|
open = not self.state.open,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Border = e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBorder,
|
||||||
|
color = theme.BorderColor,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
}, {
|
||||||
|
DropArrow = e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Dropdown.Arrow,
|
||||||
|
ImageColor3 = self.openBinding:map(function(a)
|
||||||
|
return theme.Closed.IconColor:Lerp(theme.Open.IconColor, a)
|
||||||
|
end),
|
||||||
|
ImageTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, 18, 0, 18),
|
||||||
|
Position = UDim2.new(1, -6, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(1, 0.5),
|
||||||
|
Rotation = self.openBinding:map(function(a)
|
||||||
|
return a * 180
|
||||||
|
end),
|
||||||
|
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
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,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Options = if self.state.open then e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBackground,
|
||||||
|
color = theme.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)
|
||||||
|
end),
|
||||||
|
anchorPoint = Vector2.new(1, 0),
|
||||||
|
}, {
|
||||||
|
Border = e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBorder,
|
||||||
|
color = theme.BorderColor,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
}),
|
||||||
|
ScrollingFrame = e(ScrollingFrame, {
|
||||||
|
size = UDim2.new(1, -4, 1, -4),
|
||||||
|
position = UDim2.new(0, 2, 0, 2),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
contentSize = self.contentSize,
|
||||||
|
}, {
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 0),
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteContentSize] = function(object)
|
||||||
|
self.setContentSize(object.AbsoluteContentSize)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
Roact.createFragment(optionButtons),
|
||||||
|
}),
|
||||||
|
}) else nil,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Dropdown
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
@@ -52,4 +53,4 @@ local function Header(props)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return Header
|
return Header
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
local Flipper = require(Rojo.Flipper)
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
local bindingUtil = require(Plugin.App.bindingUtil)
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
@@ -29,6 +30,7 @@ function IconButton:render()
|
|||||||
Position = self.props.position,
|
Position = self.props.position,
|
||||||
AnchorPoint = self.props.anchorPoint,
|
AnchorPoint = self.props.anchorPoint,
|
||||||
|
|
||||||
|
Visible = self.props.visible,
|
||||||
LayoutOrder = self.props.layoutOrder,
|
LayoutOrder = self.props.layoutOrder,
|
||||||
ZIndex = self.props.zIndex,
|
ZIndex = self.props.zIndex,
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
@@ -73,7 +75,9 @@ function IconButton:render()
|
|||||||
|
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
Children = Roact.createFragment(self.props[Roact.Children]),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return IconButton
|
return IconButton
|
||||||
|
|||||||
181
plugin/src/App/Components/PatchVisualizer/ChangeList.lua
Normal file
181
plugin/src/App/Components/PatchVisualizer/ChangeList.lua
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
||||||
|
local DisplayValue = require(script.Parent.DisplayValue)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local ChangeList = Roact.Component:extend("ChangeList")
|
||||||
|
|
||||||
|
function ChangeList:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeList:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
local props = self.props
|
||||||
|
local changes = props.changes
|
||||||
|
|
||||||
|
-- Color alternating rows for readability
|
||||||
|
local rowTransparency = props.transparency:map(function(t)
|
||||||
|
return 0.93 + (0.07 * t)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local columnVisibility = props.columnVisibility
|
||||||
|
|
||||||
|
local rows = {}
|
||||||
|
local pad = {
|
||||||
|
PaddingLeft = UDim.new(0, 5),
|
||||||
|
PaddingRight = UDim.new(0, 5),
|
||||||
|
}
|
||||||
|
|
||||||
|
local headers = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 30),
|
||||||
|
BackgroundTransparency = rowTransparency,
|
||||||
|
BackgroundColor3 = theme.Diff.Row,
|
||||||
|
LayoutOrder = 0,
|
||||||
|
}, {
|
||||||
|
Padding = e("UIPadding", pad),
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
}),
|
||||||
|
A = e("TextLabel", {
|
||||||
|
Visible = columnVisibility[1],
|
||||||
|
Text = tostring(changes[1][1]),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamBold,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(0.3, 0, 1, 0),
|
||||||
|
LayoutOrder = 1,
|
||||||
|
}),
|
||||||
|
B = e("TextLabel", {
|
||||||
|
Visible = columnVisibility[2],
|
||||||
|
Text = tostring(changes[1][2]),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamBold,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(0.35, 0, 1, 0),
|
||||||
|
LayoutOrder = 2,
|
||||||
|
}),
|
||||||
|
C = e("TextLabel", {
|
||||||
|
Visible = columnVisibility[3],
|
||||||
|
Text = tostring(changes[1][3]),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamBold,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(0.35, 0, 1, 0),
|
||||||
|
LayoutOrder = 3,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
for row, values in changes do
|
||||||
|
if row == 1 then
|
||||||
|
continue -- Skip headers, already handled above
|
||||||
|
end
|
||||||
|
|
||||||
|
rows[row] = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 30),
|
||||||
|
BackgroundTransparency = row % 2 ~= 0 and rowTransparency or 1,
|
||||||
|
BackgroundColor3 = theme.Diff.Row,
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
LayoutOrder = row,
|
||||||
|
}, {
|
||||||
|
Padding = e("UIPadding", pad),
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
}),
|
||||||
|
A = e("TextLabel", {
|
||||||
|
Visible = columnVisibility[1],
|
||||||
|
Text = tostring(values[1]),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(0.3, 0, 1, 0),
|
||||||
|
LayoutOrder = 1,
|
||||||
|
}),
|
||||||
|
B = e(
|
||||||
|
"Frame",
|
||||||
|
{
|
||||||
|
Visible = columnVisibility[2],
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(0.35, 0, 1, 0),
|
||||||
|
LayoutOrder = 2,
|
||||||
|
},
|
||||||
|
e(DisplayValue, {
|
||||||
|
value = values[2],
|
||||||
|
transparency = props.transparency,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
C = e(
|
||||||
|
"Frame",
|
||||||
|
{
|
||||||
|
Visible = columnVisibility[3],
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(0.35, 0, 1, 0),
|
||||||
|
LayoutOrder = 3,
|
||||||
|
},
|
||||||
|
e(DisplayValue, {
|
||||||
|
value = values[3],
|
||||||
|
transparency = props.transparency,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(
|
||||||
|
rows,
|
||||||
|
e("UIListLayout", {
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteContentSize] = function(object)
|
||||||
|
self.setContentSize(object.AbsoluteContentSize)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Headers = headers,
|
||||||
|
Values = e(ScrollingFrame, {
|
||||||
|
size = UDim2.new(1, 0, 1, -30),
|
||||||
|
position = UDim2.new(0, 0, 0, 30),
|
||||||
|
contentSize = self.contentSize,
|
||||||
|
transparency = props.transparency,
|
||||||
|
}, rows),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ChangeList
|
||||||
107
plugin/src/App/Components/PatchVisualizer/DisplayValue.lua
Normal file
107
plugin/src/App/Components/PatchVisualizer/DisplayValue.lua
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function DisplayValue(props)
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
local t = typeof(props.value)
|
||||||
|
if t == "Color3" then
|
||||||
|
-- Colors get a blot that shows the color
|
||||||
|
return Roact.createFragment({
|
||||||
|
Blot = e("Frame", {
|
||||||
|
BackgroundTransparency = props.transparency,
|
||||||
|
BackgroundColor3 = props.value,
|
||||||
|
Size = UDim2.new(0, 20, 0, 20),
|
||||||
|
Position = UDim2.new(0, 0, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(0, 0.5),
|
||||||
|
}, {
|
||||||
|
Corner = e("UICorner", {
|
||||||
|
CornerRadius = UDim.new(0, 4),
|
||||||
|
}),
|
||||||
|
Stroke = e("UIStroke", {
|
||||||
|
Color = theme.BorderedContainer.BorderColor,
|
||||||
|
Transparency = props.transparency,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
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,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(1, -25, 1, 0),
|
||||||
|
Position = UDim2.new(0, 25, 0, 0),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
elseif t == "table" then
|
||||||
|
-- Showing a memory address for tables is useless, so we want to show the best we can
|
||||||
|
local textRepresentation = nil
|
||||||
|
|
||||||
|
local meta = getmetatable(props.value)
|
||||||
|
if meta and meta.__tostring then
|
||||||
|
-- If the table has a tostring metamethod, use that
|
||||||
|
textRepresentation = tostring(props.value)
|
||||||
|
elseif next(props.value) == nil then
|
||||||
|
-- If it's empty, show empty braces
|
||||||
|
textRepresentation = "{}"
|
||||||
|
else
|
||||||
|
-- If it has children, list them out
|
||||||
|
local out, i = {}, 0
|
||||||
|
for k, v in pairs(props.value) do
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
-- Wrap strings in quotes
|
||||||
|
if type(k) == "string" then
|
||||||
|
k = "\"" .. k .. "\""
|
||||||
|
end
|
||||||
|
if type(v) == "string" then
|
||||||
|
v = "\"" .. v .. "\""
|
||||||
|
end
|
||||||
|
|
||||||
|
out[i] = string.format("[%s] = %s", tostring(k), tostring(v))
|
||||||
|
end
|
||||||
|
textRepresentation = "{ " .. table.concat(out, ", ") .. " }"
|
||||||
|
end
|
||||||
|
|
||||||
|
return e("TextLabel", {
|
||||||
|
Text = textRepresentation,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: Maybe add visualizations to other datatypes?
|
||||||
|
-- Or special text handling tostring for some?
|
||||||
|
-- Will add as needed, let's see what cases arise.
|
||||||
|
|
||||||
|
return e("TextLabel", {
|
||||||
|
Text = string.gsub(tostring(props.value), "%s", " "),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return DisplayValue
|
||||||
180
plugin/src/App/Components/PatchVisualizer/DomLabel.lua
Normal file
180
plugin/src/App/Components/PatchVisualizer/DomLabel.lua
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
local StudioService = game:GetService("StudioService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local ChangeList = require(script.Parent.ChangeList)
|
||||||
|
|
||||||
|
local Expansion = Roact.Component:extend("Expansion")
|
||||||
|
|
||||||
|
function Expansion:render()
|
||||||
|
local props = self.props
|
||||||
|
|
||||||
|
if not props.rendered then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(1, -props.indent, 1, -30),
|
||||||
|
Position = UDim2.new(0, props.indent, 0, 30),
|
||||||
|
}, {
|
||||||
|
ChangeList = e(ChangeList, {
|
||||||
|
changes = props.changeList,
|
||||||
|
transparency = props.transparency,
|
||||||
|
columnVisibility = props.columnVisibility,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local DomLabel = Roact.Component:extend("DomLabel")
|
||||||
|
|
||||||
|
function DomLabel:init()
|
||||||
|
self.maxElementHeight = 0
|
||||||
|
if self.props.changeList then
|
||||||
|
self.maxElementHeight = math.clamp(#self.props.changeList * 30, 30, 30 * 6)
|
||||||
|
end
|
||||||
|
|
||||||
|
local initHeight = self.props.elementHeight:getValue()
|
||||||
|
self.expanded = initHeight > 30
|
||||||
|
|
||||||
|
self.motor = Flipper.SingleMotor.new(initHeight)
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
renderExpansion = self.expanded,
|
||||||
|
})
|
||||||
|
self.motor:onStep(function(value)
|
||||||
|
local renderExpansion = value > 30
|
||||||
|
|
||||||
|
self.props.setElementHeight(value)
|
||||||
|
if self.props.updateEvent then
|
||||||
|
self.props.updateEvent:Fire()
|
||||||
|
end
|
||||||
|
|
||||||
|
self:setState(function(state)
|
||||||
|
if state.renderExpansion == renderExpansion then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
renderExpansion = renderExpansion,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function DomLabel:render()
|
||||||
|
local props = self.props
|
||||||
|
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
local iconProps = StudioService:GetClassIcon(props.className)
|
||||||
|
local indent = (props.depth or 0) * 20 + 25
|
||||||
|
|
||||||
|
-- Line guides help indent depth remain readable
|
||||||
|
local lineGuides = {}
|
||||||
|
for i = 1, props.depth or 0 do
|
||||||
|
table.insert(
|
||||||
|
lineGuides,
|
||||||
|
e("Frame", {
|
||||||
|
Name = "Line_" .. i,
|
||||||
|
Size = UDim2.new(0, 2, 1, 2),
|
||||||
|
Position = UDim2.new(0, (20 * i) + 15, 0, -1),
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
BackgroundTransparency = props.transparency,
|
||||||
|
BackgroundColor3 = theme.BorderedContainer.BorderColor,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
Name = "Change",
|
||||||
|
ClipsDescendants = true,
|
||||||
|
BackgroundColor3 = if props.patchType then theme.Diff[props.patchType] else nil,
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
BackgroundTransparency = props.patchType and props.transparency or 1,
|
||||||
|
Size = self.binding:map(function(expand)
|
||||||
|
return UDim2.new(1, 0, 0, expand)
|
||||||
|
end),
|
||||||
|
}, {
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 10),
|
||||||
|
PaddingRight = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
ExpandButton = if props.changeList
|
||||||
|
then e("TextButton", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Text = "",
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
[Roact.Event.Activated] = function()
|
||||||
|
self.expanded = not self.expanded
|
||||||
|
self.motor:setGoal(Flipper.Spring.new((self.expanded and self.maxElementHeight or 0) + 30, {
|
||||||
|
frequency = 5,
|
||||||
|
dampingRatio = 1,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
|
Expansion = if props.changeList
|
||||||
|
then e(Expansion, {
|
||||||
|
rendered = self.state.renderExpansion,
|
||||||
|
indent = indent,
|
||||||
|
transparency = props.transparency,
|
||||||
|
changeList = props.changeList,
|
||||||
|
columnVisibility = props.columnVisibility,
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
|
DiffIcon = if props.patchType
|
||||||
|
then e("ImageLabel", {
|
||||||
|
Image = Assets.Images.Diff[props.patchType],
|
||||||
|
ImageColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(0, 20, 0, 20),
|
||||||
|
Position = UDim2.new(0, 0, 0, 15),
|
||||||
|
AnchorPoint = Vector2.new(0, 0.5),
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
|
ClassIcon = e("ImageLabel", {
|
||||||
|
Image = iconProps.Image,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
ImageRectOffset = iconProps.ImageRectOffset,
|
||||||
|
ImageRectSize = iconProps.ImageRectSize,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(0, 20, 0, 20),
|
||||||
|
Position = UDim2.new(0, indent, 0, 15),
|
||||||
|
AnchorPoint = Vector2.new(0, 0.5),
|
||||||
|
}),
|
||||||
|
InstanceName = e("TextLabel", {
|
||||||
|
Text = props.name .. (props.hint and string.format(
|
||||||
|
' <font color="#%s">%s</font>',
|
||||||
|
theme.AddressEntry.PlaceholderColor:ToHex(),
|
||||||
|
props.hint
|
||||||
|
) or ""),
|
||||||
|
RichText = true,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
Size = UDim2.new(1, -indent - 50, 0, 30),
|
||||||
|
Position = UDim2.new(0, indent + 30, 0, 0),
|
||||||
|
}),
|
||||||
|
LineGuides = e("Folder", nil, lineGuides),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return DomLabel
|
||||||
402
plugin/src/App/Components/PatchVisualizer/init.lua
Normal file
402
plugin/src/App/Components/PatchVisualizer/init.lua
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
local HttpService = game:GetService("HttpService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
local Log = require(Packages.Log)
|
||||||
|
|
||||||
|
local PatchSet = require(Plugin.PatchSet)
|
||||||
|
local decodeValue = require(Plugin.Reconciler.decodeValue)
|
||||||
|
local getProperty = require(Plugin.Reconciler.getProperty)
|
||||||
|
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
local VirtualScroller = require(Plugin.App.Components.VirtualScroller)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function alphabeticalNext(t, state)
|
||||||
|
-- Equivalent of the next function, but returns the keys in the alphabetic
|
||||||
|
-- order of node names. We use a temporary ordered key table that is stored in the
|
||||||
|
-- table being iterated.
|
||||||
|
|
||||||
|
local key = nil
|
||||||
|
if state == nil then
|
||||||
|
-- First iteration, generate the index
|
||||||
|
local orderedIndex, i = table.create(5), 0
|
||||||
|
for k in t do
|
||||||
|
i += 1
|
||||||
|
orderedIndex[i] = k
|
||||||
|
end
|
||||||
|
table.sort(orderedIndex, function(a, b)
|
||||||
|
local nodeA, nodeB = t[a], t[b]
|
||||||
|
return (nodeA.name or "") < (nodeB.name or "")
|
||||||
|
end)
|
||||||
|
|
||||||
|
t.__orderedIndex = orderedIndex
|
||||||
|
key = orderedIndex[1]
|
||||||
|
else
|
||||||
|
-- Fetch the next value
|
||||||
|
for i, orderedState in t.__orderedIndex do
|
||||||
|
if orderedState == state then
|
||||||
|
key = t.__orderedIndex[i + 1]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if key then
|
||||||
|
return key, t[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No more value to return, cleanup
|
||||||
|
t.__orderedIndex = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local function alphabeticalPairs(t)
|
||||||
|
-- Equivalent of the pairs() iterator, but sorted
|
||||||
|
return alphabeticalNext, t, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Tree()
|
||||||
|
local tree = {
|
||||||
|
idToNode = {},
|
||||||
|
ROOT = {
|
||||||
|
className = "DataModel",
|
||||||
|
name = "ROOT",
|
||||||
|
children = {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
-- Add ROOT to idToNode or it won't be found by getNode since that searches *within* ROOT
|
||||||
|
tree.idToNode["ROOT"] = tree.ROOT
|
||||||
|
|
||||||
|
function tree:getNode(id, target)
|
||||||
|
if self.idToNode[id] then
|
||||||
|
return self.idToNode[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
for nodeId, node in target or tree.ROOT.children do
|
||||||
|
if nodeId == id then
|
||||||
|
self.idToNode[id] = node
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
local descendant = self:getNode(id, node.children)
|
||||||
|
if descendant then
|
||||||
|
return descendant
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function tree:addNode(parent, props)
|
||||||
|
parent = parent or "ROOT"
|
||||||
|
|
||||||
|
local node = self:getNode(props.id)
|
||||||
|
if node then
|
||||||
|
for k, v in props do
|
||||||
|
node[k] = v
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
node = table.clone(props)
|
||||||
|
node.children = {}
|
||||||
|
|
||||||
|
local parentNode = self:getNode(parent)
|
||||||
|
if not parentNode then
|
||||||
|
Log.warn("Failed to create node since parent doesnt exist: {}, {}", parent, props)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
parentNode.children[node.id] = node
|
||||||
|
self.idToNode[node.id] = node
|
||||||
|
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
function tree:buildAncestryNodes(ancestry, patch, instanceMap)
|
||||||
|
-- Build nodes for ancestry by going up the tree
|
||||||
|
local previousId = "ROOT"
|
||||||
|
for _, ancestorId in ancestry do
|
||||||
|
local value = instanceMap.fromIds[ancestorId] or patch.added[ancestorId]
|
||||||
|
if not value then
|
||||||
|
Log.warn("Failed to find ancestor object for " .. ancestorId)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
self:addNode(previousId, {
|
||||||
|
id = ancestorId,
|
||||||
|
className = value.ClassName,
|
||||||
|
name = value.Name,
|
||||||
|
})
|
||||||
|
previousId = ancestorId
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return tree
|
||||||
|
end
|
||||||
|
|
||||||
|
local DomLabel = require(script.DomLabel)
|
||||||
|
|
||||||
|
local PatchVisualizer = Roact.Component:extend("PatchVisualizer")
|
||||||
|
|
||||||
|
function PatchVisualizer:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
|
||||||
|
self.updateEvent = Instance.new("BindableEvent")
|
||||||
|
end
|
||||||
|
|
||||||
|
function PatchVisualizer:willUnmount()
|
||||||
|
self.updateEvent:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
function PatchVisualizer:shouldUpdate(nextProps)
|
||||||
|
local currentPatch, nextPatch = self.props.patch, nextProps.patch
|
||||||
|
|
||||||
|
return not PatchSet.isEqual(currentPatch, nextPatch)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PatchVisualizer:buildTree(patch, instanceMap)
|
||||||
|
local tree = Tree()
|
||||||
|
|
||||||
|
for _, change in patch.updated do
|
||||||
|
local instance = instanceMap.fromIds[change.id]
|
||||||
|
if not instance then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Gather ancestors from existing DOM
|
||||||
|
local ancestry = {}
|
||||||
|
local parentObject = instance.Parent
|
||||||
|
local parentId = instanceMap.fromInstances[parentObject]
|
||||||
|
while parentObject do
|
||||||
|
table.insert(ancestry, 1, parentId)
|
||||||
|
parentObject = parentObject.Parent
|
||||||
|
parentId = instanceMap.fromInstances[parentObject]
|
||||||
|
end
|
||||||
|
|
||||||
|
tree:buildAncestryNodes(ancestry, patch, instanceMap)
|
||||||
|
|
||||||
|
-- Gather detail text
|
||||||
|
local changeList, hint = nil, nil
|
||||||
|
if next(change.changedProperties) or change.changedName then
|
||||||
|
changeList = {}
|
||||||
|
|
||||||
|
local hintBuffer, i = {}, 0
|
||||||
|
local function addProp(prop: string, current: any?, incoming: any?)
|
||||||
|
i += 1
|
||||||
|
hintBuffer[i] = prop
|
||||||
|
changeList[i] = { prop, current, incoming }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Gather the changes
|
||||||
|
|
||||||
|
if change.changedName then
|
||||||
|
addProp("Name", instance.Name, change.changedName)
|
||||||
|
end
|
||||||
|
|
||||||
|
for prop, incoming in change.changedProperties do
|
||||||
|
local incomingSuccess, incomingValue = decodeValue(incoming, instanceMap)
|
||||||
|
local currentSuccess, currentValue = getProperty(instance, prop)
|
||||||
|
|
||||||
|
addProp(
|
||||||
|
prop,
|
||||||
|
if currentSuccess then currentValue else "[Error]",
|
||||||
|
if incomingSuccess then incomingValue else next(incoming)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Finalize detail values
|
||||||
|
|
||||||
|
-- Trim hint to top 3
|
||||||
|
table.sort(hintBuffer)
|
||||||
|
if #hintBuffer > 3 then
|
||||||
|
hintBuffer = {
|
||||||
|
hintBuffer[1],
|
||||||
|
hintBuffer[2],
|
||||||
|
hintBuffer[3],
|
||||||
|
i - 3 .. " more",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
hint = table.concat(hintBuffer, ", ")
|
||||||
|
|
||||||
|
-- Sort changes and add header
|
||||||
|
table.sort(changeList, function(a, b)
|
||||||
|
return a[1] < b[1]
|
||||||
|
end)
|
||||||
|
table.insert(changeList, 1, { "Property", "Current", "Incoming" })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add this node to tree
|
||||||
|
tree:addNode(instanceMap.fromInstances[instance.Parent], {
|
||||||
|
id = change.id,
|
||||||
|
patchType = "Edit",
|
||||||
|
className = instance.ClassName,
|
||||||
|
name = instance.Name,
|
||||||
|
hint = hint,
|
||||||
|
changeList = changeList,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, instance in patch.removed do
|
||||||
|
-- Gather ancestors from existing DOM
|
||||||
|
-- (note that they may have no ID if they're being removed as unknown)
|
||||||
|
local ancestry = {}
|
||||||
|
local parentObject = instance.Parent
|
||||||
|
local parentId = instanceMap.fromInstances[parentObject] or HttpService:GenerateGUID(false)
|
||||||
|
while parentObject do
|
||||||
|
instanceMap:insert(parentId, parentObject)
|
||||||
|
table.insert(ancestry, 1, parentId)
|
||||||
|
parentObject = parentObject.Parent
|
||||||
|
parentId = instanceMap.fromInstances[parentObject] or HttpService:GenerateGUID(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
tree:buildAncestryNodes(ancestry, patch, instanceMap)
|
||||||
|
|
||||||
|
-- Add this node to tree
|
||||||
|
local nodeId = instanceMap.fromInstances[instance] or HttpService:GenerateGUID(false)
|
||||||
|
instanceMap:insert(nodeId, instance)
|
||||||
|
tree:addNode(instanceMap.fromInstances[instance.Parent], {
|
||||||
|
id = nodeId,
|
||||||
|
patchType = "Remove",
|
||||||
|
className = instance.ClassName,
|
||||||
|
name = instance.Name,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, change in patch.added do
|
||||||
|
-- Gather ancestors from existing DOM or future additions
|
||||||
|
local ancestry = {}
|
||||||
|
local parentId = change.Parent
|
||||||
|
local parentData = patch.added[parentId]
|
||||||
|
local parentObject = instanceMap.fromIds[parentId]
|
||||||
|
while parentId do
|
||||||
|
table.insert(ancestry, 1, parentId)
|
||||||
|
parentId = nil
|
||||||
|
|
||||||
|
if parentData then
|
||||||
|
parentId = parentData.Parent
|
||||||
|
parentData = patch.added[parentId]
|
||||||
|
parentObject = instanceMap.fromIds[parentId]
|
||||||
|
elseif parentObject then
|
||||||
|
parentObject = parentObject.Parent
|
||||||
|
parentId = instanceMap.fromInstances[parentObject]
|
||||||
|
parentData = patch.added[parentId]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tree:buildAncestryNodes(ancestry, patch, instanceMap)
|
||||||
|
|
||||||
|
-- Gather detail text
|
||||||
|
local changeList, hint = nil, nil
|
||||||
|
if next(change.Properties) then
|
||||||
|
changeList = {}
|
||||||
|
|
||||||
|
local hintBuffer, i = {}, 0
|
||||||
|
for prop, incoming in change.Properties do
|
||||||
|
i += 1
|
||||||
|
hintBuffer[i] = prop
|
||||||
|
|
||||||
|
local success, incomingValue = decodeValue(incoming, instanceMap)
|
||||||
|
if success then
|
||||||
|
table.insert(changeList, { prop, "N/A", incomingValue })
|
||||||
|
else
|
||||||
|
table.insert(changeList, { prop, "N/A", next(incoming) })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Finalize detail values
|
||||||
|
|
||||||
|
-- Trim hint to top 3
|
||||||
|
table.sort(hintBuffer)
|
||||||
|
if #hintBuffer > 3 then
|
||||||
|
hintBuffer = {
|
||||||
|
hintBuffer[1],
|
||||||
|
hintBuffer[2],
|
||||||
|
hintBuffer[3],
|
||||||
|
i - 3 .. " more",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
hint = table.concat(hintBuffer, ", ")
|
||||||
|
|
||||||
|
-- Sort changes and add header
|
||||||
|
table.sort(changeList, function(a, b)
|
||||||
|
return a[1] < b[1]
|
||||||
|
end)
|
||||||
|
table.insert(changeList, 1, { "Property", "Current", "Incoming" })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add this node to tree
|
||||||
|
tree:addNode(change.Parent, {
|
||||||
|
id = change.Id,
|
||||||
|
patchType = "Add",
|
||||||
|
className = change.ClassName,
|
||||||
|
name = change.Name,
|
||||||
|
hint = hint,
|
||||||
|
changeList = changeList,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return tree
|
||||||
|
end
|
||||||
|
|
||||||
|
function PatchVisualizer:render()
|
||||||
|
local patch = self.props.patch
|
||||||
|
local instanceMap = self.props.instanceMap
|
||||||
|
|
||||||
|
local tree = self:buildTree(patch, instanceMap)
|
||||||
|
|
||||||
|
-- Recusively draw tree
|
||||||
|
local scrollElements, elementHeights = {}, {}
|
||||||
|
local function drawNode(node, depth)
|
||||||
|
local elementHeight, setElementHeight = Roact.createBinding(30)
|
||||||
|
table.insert(elementHeights, elementHeight)
|
||||||
|
table.insert(
|
||||||
|
scrollElements,
|
||||||
|
e(DomLabel, {
|
||||||
|
columnVisibility = self.props.columnVisibility,
|
||||||
|
updateEvent = self.updateEvent,
|
||||||
|
elementHeight = elementHeight,
|
||||||
|
setElementHeight = setElementHeight,
|
||||||
|
patchType = node.patchType,
|
||||||
|
className = node.className,
|
||||||
|
name = node.name,
|
||||||
|
hint = node.hint,
|
||||||
|
changeList = node.changeList,
|
||||||
|
depth = depth,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, childNode in alphabeticalPairs(node.children) do
|
||||||
|
drawNode(childNode, depth + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, node in alphabeticalPairs(tree.ROOT.children) do
|
||||||
|
drawNode(node, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
return e(BorderedContainer, {
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
size = self.props.size,
|
||||||
|
position = self.props.position,
|
||||||
|
layoutOrder = self.props.layoutOrder,
|
||||||
|
}, {
|
||||||
|
VirtualScroller = e(VirtualScroller, {
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
count = #scrollElements,
|
||||||
|
updateEvent = self.updateEvent.Event,
|
||||||
|
render = function(i)
|
||||||
|
return scrollElements[i]
|
||||||
|
end,
|
||||||
|
getHeightBinding = function(i)
|
||||||
|
return elementHeights[i]
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return PatchVisualizer
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
@@ -39,4 +40,4 @@ local function ScrollingFrame(props)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return ScrollingFrame
|
return ScrollingFrame
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local e = Roact.createElement
|
local e = Roact.createElement
|
||||||
|
|
||||||
@@ -26,4 +27,4 @@ local function SlicedImage(props)
|
|||||||
}, props[Roact.Children])
|
}, props[Roact.Children])
|
||||||
end
|
end
|
||||||
|
|
||||||
return SlicedImage
|
return SlicedImage
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ local RunService = game:GetService("RunService")
|
|||||||
|
|
||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
@@ -63,4 +64,4 @@ function Spinner:willUnmount()
|
|||||||
self.stepper:Disconnect()
|
self.stepper:Disconnect()
|
||||||
end
|
end
|
||||||
|
|
||||||
return Spinner
|
return Spinner
|
||||||
|
|||||||
41
plugin/src/App/Components/Studio/StudioPluginAction.lua
Normal file
41
plugin/src/App/Components/Studio/StudioPluginAction.lua
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
|
local StudioPluginContext = require(script.Parent.StudioPluginContext)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local StudioPluginAction = Roact.Component:extend("StudioPluginAction")
|
||||||
|
|
||||||
|
function StudioPluginAction:init()
|
||||||
|
self.pluginAction = self.props.plugin:CreatePluginAction(
|
||||||
|
self.props.name, self.props.title, self.props.description, self.props.icon, self.props.bindable
|
||||||
|
)
|
||||||
|
|
||||||
|
self.pluginAction.Triggered:Connect(self.props.onTriggered)
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginAction:render()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginAction:willUnmount()
|
||||||
|
self.pluginAction:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function StudioPluginActionWrapper(props)
|
||||||
|
return e(StudioPluginContext.Consumer, {
|
||||||
|
render = function(plugin)
|
||||||
|
return e(StudioPluginAction, Dictionary.merge(props, {
|
||||||
|
plugin = plugin,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return StudioPluginActionWrapper
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local StudioPluginContext = Roact.createContext(nil)
|
local StudioPluginContext = Roact.createContext(nil)
|
||||||
|
|
||||||
return StudioPluginContext
|
return StudioPluginContext
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Dictionary = require(Plugin.Dictionary)
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
|
||||||
local StudioPluginContext = require(script.Parent.StudioPluginContext)
|
local StudioPluginContext = require(script.Parent.StudioPluginContext)
|
||||||
|
|
||||||
@@ -28,8 +30,10 @@ function StudioPluginGui:init()
|
|||||||
self.props.initDockState,
|
self.props.initDockState,
|
||||||
self.props.active,
|
self.props.active,
|
||||||
self.props.overridePreviousState,
|
self.props.overridePreviousState,
|
||||||
floatingSize.X, floatingSize.Y,
|
floatingSize.X,
|
||||||
minimumSize.X, minimumSize.Y
|
floatingSize.Y,
|
||||||
|
minimumSize.X,
|
||||||
|
minimumSize.Y
|
||||||
)
|
)
|
||||||
|
|
||||||
local pluginGui = self.props.plugin:CreateDockWidgetPluginGui(self.props.id, dockWidgetPluginGuiInfo)
|
local pluginGui = self.props.plugin:CreateDockWidgetPluginGui(self.props.id, dockWidgetPluginGuiInfo)
|
||||||
@@ -56,7 +60,16 @@ end
|
|||||||
function StudioPluginGui:render()
|
function StudioPluginGui:render()
|
||||||
return e(Roact.Portal, {
|
return e(Roact.Portal, {
|
||||||
target = self.pluginGui,
|
target = self.pluginGui,
|
||||||
}, self.props[Roact.Children])
|
}, {
|
||||||
|
Background = Theme.with(function(theme)
|
||||||
|
return e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundColor3 = theme.BackgroundColor,
|
||||||
|
ZIndex = 0,
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
}, self.props[Roact.Children])
|
||||||
|
end),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function StudioPluginGui:didUpdate(lastProps)
|
function StudioPluginGui:didUpdate(lastProps)
|
||||||
@@ -74,11 +87,14 @@ end
|
|||||||
local function StudioPluginGuiWrapper(props)
|
local function StudioPluginGuiWrapper(props)
|
||||||
return e(StudioPluginContext.Consumer, {
|
return e(StudioPluginContext.Consumer, {
|
||||||
render = function(plugin)
|
render = function(plugin)
|
||||||
return e(StudioPluginGui, Dictionary.merge(props, {
|
return e(
|
||||||
plugin = plugin,
|
StudioPluginGui,
|
||||||
}))
|
Dictionary.merge(props, {
|
||||||
|
plugin = plugin,
|
||||||
|
})
|
||||||
|
)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return StudioPluginGuiWrapper
|
return StudioPluginGuiWrapper
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Dictionary = require(Plugin.Dictionary)
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
@@ -44,6 +45,10 @@ function StudioToggleButton:didUpdate(lastProps)
|
|||||||
self.button.Enabled = self.props.enabled
|
self.button.Enabled = self.props.enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.props.icon ~= lastProps.icon then
|
||||||
|
self.button.Icon = self.props.icon
|
||||||
|
end
|
||||||
|
|
||||||
if self.props.active ~= lastProps.active then
|
if self.props.active ~= lastProps.active then
|
||||||
self.button:SetActive(self.props.active)
|
self.button:SetActive(self.props.active)
|
||||||
end
|
end
|
||||||
@@ -63,4 +68,4 @@ local function StudioToggleButtonWrapper(props)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return StudioToggleButtonWrapper
|
return StudioToggleButtonWrapper
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Dictionary = require(Plugin.Dictionary)
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
@@ -42,4 +43,4 @@ local function StudioToolbarWrapper(props)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return StudioToolbarWrapper
|
return StudioToolbarWrapper
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local StudioToolbarContext = Roact.createContext(nil)
|
local StudioToolbarContext = Roact.createContext(nil)
|
||||||
|
|
||||||
return StudioToolbarContext
|
return StudioToolbarContext
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ local TextService = game:GetService("TextService")
|
|||||||
|
|
||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
local Flipper = require(Rojo.Flipper)
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
@@ -130,8 +131,10 @@ function TextButton:render()
|
|||||||
|
|
||||||
zIndex = -2,
|
zIndex = -2,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
Children = Roact.createFragment(self.props[Roact.Children]),
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return TextButton
|
return TextButton
|
||||||
|
|||||||
226
plugin/src/App/Components/Tooltip.lua
Normal file
226
plugin/src/App/Components/Tooltip.lua
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
local HttpService = game:GetService("HttpService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local DELAY = 0.75 -- How long to hover before a popup is shown (seconds)
|
||||||
|
local TEXT_PADDING = Vector2.new(8 * 2, 6 * 2) -- Padding for the popup text containers
|
||||||
|
local TAIL_SIZE = 16 -- Size of the triangle tail piece
|
||||||
|
local X_OFFSET = 30 -- How far right (from left) the tail will be (assuming enough space)
|
||||||
|
local Y_OVERLAP = 10 -- Let the triangle tail piece overlap the target a bit to help "connect" it
|
||||||
|
|
||||||
|
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)
|
||||||
|
return e(BorderedContainer, {
|
||||||
|
position = UDim2.fromOffset(X, Y),
|
||||||
|
size = UDim2.fromOffset(textSize.X, textSize.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),
|
||||||
|
Text = props.Text,
|
||||||
|
TextSize = 16,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextWrapped = true,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextColor3 = theme.Button.Bordered.Enabled.TextColor,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
}),
|
||||||
|
|
||||||
|
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
|
||||||
|
),
|
||||||
|
Size = UDim2.fromOffset(TAIL_SIZE, TAIL_SIZE),
|
||||||
|
AnchorPoint = Vector2.new(0.5, 0),
|
||||||
|
Rotation = if displayAbove then 180 else 0,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Image = "rbxassetid://10983945016",
|
||||||
|
ImageColor3 = theme.BorderedContainer.BackgroundColor,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
}, {
|
||||||
|
Border = e("ImageLabel", {
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Image = "rbxassetid://10983946430",
|
||||||
|
ImageColor3 = theme.BorderedContainer.BorderColor,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Provider = Roact.Component:extend("TooltipManager")
|
||||||
|
|
||||||
|
function Provider:init()
|
||||||
|
self:setState({
|
||||||
|
tips = {},
|
||||||
|
addTip = function(id: string, data: { Text: string, Position: Vector2, Trigger: any })
|
||||||
|
self:setState(function(state)
|
||||||
|
state.tips[id] = data
|
||||||
|
return state
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
removeTip = function(id: string)
|
||||||
|
self:setState(function(state)
|
||||||
|
state.tips[id] = nil
|
||||||
|
return state
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Provider:render()
|
||||||
|
return Roact.createElement(TooltipContext.Provider, {
|
||||||
|
value = self.state,
|
||||||
|
}, self.props[Roact.Children])
|
||||||
|
end
|
||||||
|
|
||||||
|
local Container = Roact.Component:extend("TooltipContainer")
|
||||||
|
|
||||||
|
function Container:init()
|
||||||
|
self:setState({
|
||||||
|
size = Vector2.new(200, 100),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Container:render()
|
||||||
|
return Roact.createElement(TooltipContext.Consumer, {
|
||||||
|
render = function(context)
|
||||||
|
local tips = context.tips
|
||||||
|
local popups = {}
|
||||||
|
|
||||||
|
for key, value in tips do
|
||||||
|
popups[key] = e(Popup, {
|
||||||
|
Text = value.Text or "",
|
||||||
|
Position = value.Position or Vector2.zero,
|
||||||
|
Trigger = value.Trigger,
|
||||||
|
|
||||||
|
parentSize = self.state.size,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
[Roact.Change.AbsoluteSize] = function(rbx)
|
||||||
|
self:setState({
|
||||||
|
size = rbx.AbsoluteSize,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
ZIndex = 100,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
}, popups)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local Trigger = Roact.Component:extend("TooltipTrigger")
|
||||||
|
|
||||||
|
function Trigger:init()
|
||||||
|
self.id = HttpService:GenerateGUID(false)
|
||||||
|
self.ref = Roact.createRef()
|
||||||
|
self.mousePos = Vector2.zero
|
||||||
|
|
||||||
|
self.destroy = function()
|
||||||
|
self.props.context.removeTip(self.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Trigger:willUnmount()
|
||||||
|
if self.showDelayThread then
|
||||||
|
task.cancel(self.showDelayThread)
|
||||||
|
end
|
||||||
|
if self.destroy then
|
||||||
|
self.destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Trigger:render()
|
||||||
|
return e("Frame", {
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
ZIndex = self.props.zIndex or 100,
|
||||||
|
[Roact.Ref] = self.ref,
|
||||||
|
|
||||||
|
[Roact.Event.MouseMoved] = function(_rbx, x, y)
|
||||||
|
self.mousePos = Vector2.new(x, y)
|
||||||
|
end,
|
||||||
|
[Roact.Event.MouseEnter] = function()
|
||||||
|
self.showDelayThread = task.delay(DELAY, function()
|
||||||
|
self.props.context.addTip(self.id, {
|
||||||
|
Text = self.props.text,
|
||||||
|
Position = self.mousePos,
|
||||||
|
Trigger = self.ref,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
[Roact.Event.MouseLeave] = function()
|
||||||
|
if self.showDelayThread then
|
||||||
|
task.cancel(self.showDelayThread)
|
||||||
|
end
|
||||||
|
self.props.context.removeTip(self.id)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function TriggerConsumer(props)
|
||||||
|
return Roact.createElement(TooltipContext.Consumer, {
|
||||||
|
render = function(context)
|
||||||
|
local innerProps = table.clone(props)
|
||||||
|
innerProps.context = context
|
||||||
|
|
||||||
|
return e(Trigger, innerProps)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
Provider = Provider,
|
||||||
|
Container = Container,
|
||||||
|
Trigger = TriggerConsumer,
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
local Flipper = require(Rojo.Flipper)
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
local bindingUtil = require(Plugin.App.bindingUtil)
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
@@ -142,4 +143,4 @@ function TouchRipple:render()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return TouchRipple
|
return TouchRipple
|
||||||
|
|||||||
156
plugin/src/App/Components/VirtualScroller.lua
Normal file
156
plugin/src/App/Components/VirtualScroller.lua
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local VirtualScroller = Roact.Component:extend("VirtualScroller")
|
||||||
|
|
||||||
|
function VirtualScroller:init()
|
||||||
|
self.scrollFrameRef = Roact.createRef()
|
||||||
|
self:setState({
|
||||||
|
WindowSize = Vector2.new(),
|
||||||
|
CanvasPosition = Vector2.new(),
|
||||||
|
})
|
||||||
|
|
||||||
|
self.totalCanvas, self.setTotalCanvas = Roact.createBinding(0)
|
||||||
|
self.padding, self.setPadding = Roact.createBinding(0)
|
||||||
|
|
||||||
|
self:refresh()
|
||||||
|
if self.props.updateEvent then
|
||||||
|
self.connection = self.props.updateEvent:Connect(function()
|
||||||
|
self:refresh()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualScroller:didMount()
|
||||||
|
local rbx = self.scrollFrameRef:getValue()
|
||||||
|
|
||||||
|
local windowSizeSignal = rbx:GetPropertyChangedSignal("AbsoluteWindowSize")
|
||||||
|
self.windowSizeChanged = windowSizeSignal:Connect(function()
|
||||||
|
self:setState({ WindowSize = rbx.AbsoluteWindowSize })
|
||||||
|
self:refresh()
|
||||||
|
end)
|
||||||
|
|
||||||
|
local canvasPositionSignal = rbx:GetPropertyChangedSignal("CanvasPosition")
|
||||||
|
self.canvasPositionChanged = canvasPositionSignal:Connect(function()
|
||||||
|
if math.abs(rbx.CanvasPosition.Y - self.state.CanvasPosition.Y) > 5 then
|
||||||
|
self:setState({ CanvasPosition = rbx.CanvasPosition })
|
||||||
|
self:refresh()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
self:refresh()
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualScroller:willUnmount()
|
||||||
|
self.windowSizeChanged:Disconnect()
|
||||||
|
self.canvasPositionChanged:Disconnect()
|
||||||
|
if self.connection then
|
||||||
|
self.connection:Disconnect()
|
||||||
|
self.connection = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualScroller:refresh()
|
||||||
|
local props = self.props
|
||||||
|
local state = self.state
|
||||||
|
|
||||||
|
local count = props.count
|
||||||
|
local windowSize, canvasPosition = state.WindowSize.Y, state.CanvasPosition.Y
|
||||||
|
local bottom = canvasPosition + windowSize
|
||||||
|
|
||||||
|
local minIndex, maxIndex = 1, count
|
||||||
|
local padding, canvasSize = 0, 0
|
||||||
|
|
||||||
|
local pos = 0
|
||||||
|
for i = 1, count do
|
||||||
|
local height = props.getHeightBinding(i):getValue()
|
||||||
|
canvasSize += height
|
||||||
|
|
||||||
|
if pos > bottom then
|
||||||
|
-- Below window
|
||||||
|
if maxIndex > i then
|
||||||
|
maxIndex = i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
pos += height
|
||||||
|
|
||||||
|
if pos < canvasPosition then
|
||||||
|
-- Above window
|
||||||
|
minIndex = i
|
||||||
|
padding = pos - height
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.setPadding(padding)
|
||||||
|
self.setTotalCanvas(canvasSize)
|
||||||
|
self:setState({
|
||||||
|
Start = minIndex,
|
||||||
|
End = maxIndex,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualScroller:render()
|
||||||
|
local props, state = self.props, self.state
|
||||||
|
|
||||||
|
local items = {}
|
||||||
|
for i = state.Start, state.End do
|
||||||
|
items["Item" .. i] = e("Frame", {
|
||||||
|
LayoutOrder = i,
|
||||||
|
Size = props.getHeightBinding(i):map(function(height)
|
||||||
|
return UDim2.new(1, 0, 0, height)
|
||||||
|
end),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, props.render(i))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("ScrollingFrame", {
|
||||||
|
Size = props.size,
|
||||||
|
Position = props.position,
|
||||||
|
AnchorPoint = props.anchorPoint,
|
||||||
|
BackgroundTransparency = props.backgroundTransparency or 1,
|
||||||
|
BackgroundColor3 = props.backgroundColor3,
|
||||||
|
BorderColor3 = props.borderColor3,
|
||||||
|
CanvasSize = self.totalCanvas:map(function(s)
|
||||||
|
return UDim2.fromOffset(0, s)
|
||||||
|
end),
|
||||||
|
ScrollBarThickness = 9,
|
||||||
|
ScrollBarImageColor3 = theme.ScrollBarColor,
|
||||||
|
ScrollBarImageTransparency = props.transparency:map(function(value)
|
||||||
|
return bindingUtil.blendAlpha({ 0.65, value })
|
||||||
|
end),
|
||||||
|
TopImage = Assets.Images.ScrollBar.Top,
|
||||||
|
MidImage = Assets.Images.ScrollBar.Middle,
|
||||||
|
BottomImage = Assets.Images.ScrollBar.Bottom,
|
||||||
|
|
||||||
|
ElasticBehavior = Enum.ElasticBehavior.Always,
|
||||||
|
ScrollingDirection = Enum.ScrollingDirection.Y,
|
||||||
|
VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar,
|
||||||
|
[Roact.Ref] = self.scrollFrameRef,
|
||||||
|
}, {
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
Padding = UDim.new(0, 0),
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
}),
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingTop = self.padding:map(function(p)
|
||||||
|
return UDim.new(0, p)
|
||||||
|
end),
|
||||||
|
}),
|
||||||
|
Content = Roact.createFragment(items),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return VirtualScroller
|
||||||
199
plugin/src/App/Notifications.lua
Normal file
199
plugin/src/App/Notifications.lua
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
local StudioService = game:GetService("StudioService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
|
local bindingUtil = require(script.Parent.bindingUtil)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
|
||||||
|
local baseClock = DateTime.now().UnixTimestampMillis
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local Notification = Roact.Component:extend("Notification")
|
||||||
|
|
||||||
|
function Notification:init()
|
||||||
|
self.motor = Flipper.SingleMotor.new(0)
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
|
||||||
|
self.lifetime = self.props.timeout
|
||||||
|
|
||||||
|
self.motor:onStep(function(value)
|
||||||
|
if value <= 0 then
|
||||||
|
if self.props.onClose then
|
||||||
|
self.props.onClose()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:dismiss()
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(0, {
|
||||||
|
frequency = 5,
|
||||||
|
dampingRatio = 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:didMount()
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(1, {
|
||||||
|
frequency = 3,
|
||||||
|
dampingRatio = 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.props.soundPlayer:play(Assets.Sounds.Notification)
|
||||||
|
|
||||||
|
self.timeout = task.spawn(function()
|
||||||
|
local clock = os.clock()
|
||||||
|
local seen = false
|
||||||
|
while task.wait(1/10) do
|
||||||
|
local now = os.clock()
|
||||||
|
local dt = now - clock
|
||||||
|
clock = now
|
||||||
|
|
||||||
|
if not seen then
|
||||||
|
seen = StudioService.ActiveScript == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not seen then
|
||||||
|
-- Don't run down timer before being viewed
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
self.lifetime -= dt
|
||||||
|
if self.lifetime <= 0 then
|
||||||
|
self:dismiss()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:willUnmount()
|
||||||
|
task.cancel(self.timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:render()
|
||||||
|
local time = DateTime.fromUnixTimestampMillis(self.props.timestamp)
|
||||||
|
|
||||||
|
local textBounds = TextService:GetTextSize(
|
||||||
|
self.props.text,
|
||||||
|
15,
|
||||||
|
Enum.Font.GothamSemibold,
|
||||||
|
Vector2.new(350, 700)
|
||||||
|
)
|
||||||
|
|
||||||
|
local transparency = self.binding:map(function(value)
|
||||||
|
return 1 - value
|
||||||
|
end)
|
||||||
|
|
||||||
|
local size = self.binding:map(function(value)
|
||||||
|
return UDim2.fromOffset(
|
||||||
|
(35+40+textBounds.X)*value,
|
||||||
|
math.max(14+20+textBounds.Y, 32+20)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("TextButton", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = size,
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
Text = "",
|
||||||
|
ClipsDescendants = true,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = function()
|
||||||
|
self:dismiss()
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
e(BorderedContainer, {
|
||||||
|
transparency = transparency,
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
}, {
|
||||||
|
TextContainer = e("Frame", {
|
||||||
|
Size = UDim2.new(0, 35+textBounds.X, 1, -20),
|
||||||
|
Position = UDim2.new(0, 0, 0, 10),
|
||||||
|
BackgroundTransparency = 1
|
||||||
|
}, {
|
||||||
|
Logo = e("ImageLabel", {
|
||||||
|
ImageTransparency = transparency,
|
||||||
|
Image = Assets.Images.PluginButton,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(0, 32, 0, 32),
|
||||||
|
Position = UDim2.new(0, 0, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(0, 0.5),
|
||||||
|
}),
|
||||||
|
Info = e("TextLabel", {
|
||||||
|
Text = self.props.text,
|
||||||
|
Font = Enum.Font.GothamSemibold,
|
||||||
|
TextSize = 15,
|
||||||
|
TextColor3 = theme.Notification.InfoColor,
|
||||||
|
TextTransparency = transparency,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextWrapped = true,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, textBounds.X, 0, textBounds.Y),
|
||||||
|
Position = UDim2.fromOffset(35, 0),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
Time = e("TextLabel", {
|
||||||
|
Text = time:FormatLocalTime("LTS", "en-us"),
|
||||||
|
Font = Enum.Font.Code,
|
||||||
|
TextSize = 12,
|
||||||
|
TextColor3 = theme.Notification.InfoColor,
|
||||||
|
TextTransparency = transparency,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, -35, 0, 14),
|
||||||
|
Position = UDim2.new(0, 35, 1, -14),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 17),
|
||||||
|
PaddingRight = UDim.new(0, 15),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Notifications = Roact.Component:extend("Notifications")
|
||||||
|
|
||||||
|
function Notifications:render()
|
||||||
|
local notifs = {}
|
||||||
|
|
||||||
|
for index, notif in ipairs(self.props.notifications) do
|
||||||
|
notifs[notif] = e(Notification, {
|
||||||
|
soundPlayer = self.props.soundPlayer,
|
||||||
|
text = notif.text,
|
||||||
|
timestamp = notif.timestamp,
|
||||||
|
timeout = notif.timeout,
|
||||||
|
layoutOrder = (notif.timestamp - baseClock),
|
||||||
|
onClose = function()
|
||||||
|
self.props.onClose(index)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return Roact.createFragment(notifs)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Notifications
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
local Flipper = require(Rojo.Flipper)
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
local Dictionary = require(Plugin.Dictionary)
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
@@ -67,4 +68,4 @@ function Page:didUpdate(lastProps)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Page
|
return Page
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
--[[
|
|
||||||
Persistent plugin settings that can be accessed via Roact context.
|
|
||||||
]]
|
|
||||||
|
|
||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local defaultSettings = {
|
|
||||||
openScriptsExternally = false,
|
|
||||||
twoWaySync = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
local Settings = {}
|
|
||||||
Settings.__index = Settings
|
|
||||||
|
|
||||||
function Settings.fromPlugin(plugin)
|
|
||||||
local values = {}
|
|
||||||
|
|
||||||
for name, defaultValue in pairs(defaultSettings) do
|
|
||||||
local savedValue = plugin:GetSetting("Rojo_" .. name)
|
|
||||||
|
|
||||||
if savedValue == nil then
|
|
||||||
plugin:SetSetting("Rojo_" .. name, defaultValue)
|
|
||||||
values[name] = defaultValue
|
|
||||||
else
|
|
||||||
values[name] = savedValue
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable({
|
|
||||||
__values = values,
|
|
||||||
__plugin = plugin,
|
|
||||||
__updateListeners = {},
|
|
||||||
}, Settings)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Settings:get(name)
|
|
||||||
if defaultSettings[name] == nil then
|
|
||||||
error("Invalid setings name " .. tostring(name), 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self.__values[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
function Settings:set(name, value)
|
|
||||||
self.__plugin:SetSetting("Rojo_" .. name, value)
|
|
||||||
self.__values[name] = value
|
|
||||||
|
|
||||||
for callback in pairs(self.__updateListeners) do
|
|
||||||
callback(name, value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Settings:onUpdate(newCallback)
|
|
||||||
local newListeners = {}
|
|
||||||
for callback in pairs(self.__updateListeners) do
|
|
||||||
newListeners[callback] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
newListeners[newCallback] = true
|
|
||||||
self.__updateListeners = newListeners
|
|
||||||
|
|
||||||
return function()
|
|
||||||
local newListeners = {}
|
|
||||||
for callback in pairs(self.__updateListeners) do
|
|
||||||
if callback ~= newCallback then
|
|
||||||
newListeners[callback] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.__updateListeners = newListeners
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local Context = Roact.createContext(nil)
|
|
||||||
|
|
||||||
local StudioProvider = Roact.Component:extend("StudioProvider")
|
|
||||||
|
|
||||||
function StudioProvider:init()
|
|
||||||
self.settings = Settings.fromPlugin(self.props.plugin)
|
|
||||||
end
|
|
||||||
|
|
||||||
function StudioProvider:render()
|
|
||||||
return Roact.createElement(Context.Provider, {
|
|
||||||
value = self.settings,
|
|
||||||
}, self.props[Roact.Children])
|
|
||||||
end
|
|
||||||
|
|
||||||
local InternalConsumer = Roact.Component:extend("InternalConsumer")
|
|
||||||
|
|
||||||
function InternalConsumer:render()
|
|
||||||
return self.props.render(self.props.settings)
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternalConsumer:didMount()
|
|
||||||
self.disconnect = self.props.settings:onUpdate(function()
|
|
||||||
-- Trigger a dummy state update to update the settings consumer.
|
|
||||||
self:setState({})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternalConsumer:willUnmount()
|
|
||||||
self.disconnect()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function with(callback)
|
|
||||||
return Roact.createElement(Context.Consumer, {
|
|
||||||
render = function(settings)
|
|
||||||
return Roact.createElement(InternalConsumer, {
|
|
||||||
settings = settings,
|
|
||||||
render = callback,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
StudioProvider = StudioProvider,
|
|
||||||
with = with,
|
|
||||||
}
|
|
||||||
156
plugin/src/App/StatusPages/Confirming.lua
Normal file
156
plugin/src/App/StatusPages/Confirming.lua
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
|
local Settings = require(Plugin.Settings)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local TextButton = require(Plugin.App.Components.TextButton)
|
||||||
|
local Header = require(Plugin.App.Components.Header)
|
||||||
|
local StudioPluginGui = require(Plugin.App.Components.Studio.StudioPluginGui)
|
||||||
|
local Tooltip = require(Plugin.App.Components.Tooltip)
|
||||||
|
local PatchVisualizer = require(Plugin.App.Components.PatchVisualizer)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local ConfirmingPage = Roact.Component:extend("ConfirmingPage")
|
||||||
|
|
||||||
|
function ConfirmingPage:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(0)
|
||||||
|
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
function ConfirmingPage:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
local pageContent = Roact.createFragment({
|
||||||
|
Header = e(Header, {
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Title = e("TextLabel", {
|
||||||
|
Text = string.format(
|
||||||
|
"Sync changes for project '%s':",
|
||||||
|
self.props.confirmData.serverInfo.projectName or "UNKNOWN"
|
||||||
|
),
|
||||||
|
LayoutOrder = 2,
|
||||||
|
Font = Enum.Font.Gotham,
|
||||||
|
LineHeight = 1.2,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Settings.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
Size = UDim2.new(1, 0, 0, 20),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
PatchVisualizer = e(PatchVisualizer, {
|
||||||
|
size = UDim2.new(1, 0, 1, -150),
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 3,
|
||||||
|
|
||||||
|
columnVisibility = {true, true, true},
|
||||||
|
patch = self.props.confirmData.patch,
|
||||||
|
instanceMap = self.props.confirmData.instanceMap,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Buttons = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 0, 34),
|
||||||
|
LayoutOrder = 4,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Abort = e(TextButton, {
|
||||||
|
text = "Abort",
|
||||||
|
style = "Bordered",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
onClick = self.props.onAbort,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Stop the connection process"
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Reject = if Settings:get("twoWaySync")
|
||||||
|
then e(TextButton, {
|
||||||
|
text = "Reject",
|
||||||
|
style = "Bordered",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
onClick = self.props.onReject,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Push Studio changes to the Rojo server"
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
|
|
||||||
|
Accept = e(TextButton, {
|
||||||
|
text = "Accept",
|
||||||
|
style = "Solid",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 3,
|
||||||
|
onClick = self.props.onAccept,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Pull Rojo server changes to Studio"
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 20),
|
||||||
|
PaddingRight = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.props.createPopup then
|
||||||
|
return e(StudioPluginGui, {
|
||||||
|
id = "Rojo_DiffSync",
|
||||||
|
title = string.format(
|
||||||
|
"Confirm sync for project '%s':",
|
||||||
|
self.props.confirmData.serverInfo.projectName or "UNKNOWN"
|
||||||
|
),
|
||||||
|
active = true,
|
||||||
|
|
||||||
|
initDockState = Enum.InitialDockState.Float,
|
||||||
|
initEnabled = true,
|
||||||
|
overridePreviousState = true,
|
||||||
|
floatingSize = Vector2.new(500, 350),
|
||||||
|
minimumSize = Vector2.new(400, 250),
|
||||||
|
|
||||||
|
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||||
|
|
||||||
|
onClose = self.props.onAbort,
|
||||||
|
}, {
|
||||||
|
Tooltips = e(Tooltip.Container, nil),
|
||||||
|
Content = e("Frame", {
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, pageContent),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return pageContent
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ConfirmingPage
|
||||||
@@ -1,17 +1,87 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
local Flipper = require(Packages.Flipper)
|
||||||
|
|
||||||
|
local bindingUtil = require(Plugin.App.bindingUtil)
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
local Assets = require(Plugin.Assets)
|
local Assets = require(Plugin.Assets)
|
||||||
|
local PatchSet = require(Plugin.PatchSet)
|
||||||
|
|
||||||
local Header = require(Plugin.App.Components.Header)
|
local Header = require(Plugin.App.Components.Header)
|
||||||
local IconButton = require(Plugin.App.Components.IconButton)
|
local IconButton = require(Plugin.App.Components.IconButton)
|
||||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
local Tooltip = require(Plugin.App.Components.Tooltip)
|
||||||
|
local PatchVisualizer = require(Plugin.App.Components.PatchVisualizer)
|
||||||
|
|
||||||
local e = Roact.createElement
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local AGE_UNITS = { {31556909, "year"}, {2629743, "month"}, {604800, "week"}, {86400, "day"}, {3600, "hour"}, {60, "minute"}, }
|
||||||
|
function timeSinceText(elapsed: number): string
|
||||||
|
if elapsed < 3 then
|
||||||
|
return "just now"
|
||||||
|
end
|
||||||
|
|
||||||
|
local ageText = string.format("%d seconds ago", elapsed)
|
||||||
|
|
||||||
|
for _,UnitData in ipairs(AGE_UNITS) do
|
||||||
|
local UnitSeconds, UnitName = UnitData[1], UnitData[2]
|
||||||
|
if elapsed > UnitSeconds then
|
||||||
|
local c = math.floor(elapsed/UnitSeconds)
|
||||||
|
ageText = string.format("%d %s%s ago", c, UnitName, c>1 and "s" or "")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ageText
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ChangesDrawer(props)
|
||||||
|
if props.rendered == false then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e(BorderedContainer, {
|
||||||
|
transparency = props.transparency,
|
||||||
|
size = props.height:map(function(y)
|
||||||
|
return UDim2.new(1, 0, y, -180 * y)
|
||||||
|
end),
|
||||||
|
position = UDim2.new(0, 0, 1, 0),
|
||||||
|
anchorPoint = Vector2.new(0, 1),
|
||||||
|
layoutOrder = props.layoutOrder,
|
||||||
|
}, {
|
||||||
|
Close = e(IconButton, {
|
||||||
|
icon = Assets.Images.Icons.Close,
|
||||||
|
iconSize = 24,
|
||||||
|
color = theme.ConnectionDetails.DisconnectColor,
|
||||||
|
transparency = props.transparency,
|
||||||
|
|
||||||
|
position = UDim2.new(1, 0, 0, 0),
|
||||||
|
anchorPoint = Vector2.new(1, 0),
|
||||||
|
|
||||||
|
onClick = props.onClose,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Close the patch visualizer"
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
PatchVisualizer = e(PatchVisualizer, {
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
transparency = props.transparency,
|
||||||
|
layoutOrder = 3,
|
||||||
|
|
||||||
|
columnVisibility = {true, false, true},
|
||||||
|
patch = props.patchInfo:getValue().patch,
|
||||||
|
instanceMap = props.serveSession.__instanceMap,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local function ConnectionDetails(props)
|
local function ConnectionDetails(props)
|
||||||
return Theme.with(function(theme)
|
return Theme.with(function(theme)
|
||||||
return e(BorderedContainer, {
|
return e(BorderedContainer, {
|
||||||
@@ -69,6 +139,10 @@ local function ConnectionDetails(props)
|
|||||||
anchorPoint = Vector2.new(1, 0.5),
|
anchorPoint = Vector2.new(1, 0.5),
|
||||||
|
|
||||||
onClick = props.onDisconnect,
|
onClick = props.onDisconnect,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Disconnect from the Rojo sync server"
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Padding = e("UIPadding", {
|
Padding = e("UIPadding", {
|
||||||
@@ -81,36 +155,116 @@ end
|
|||||||
|
|
||||||
local ConnectedPage = Roact.Component:extend("ConnectedPage")
|
local ConnectedPage = Roact.Component:extend("ConnectedPage")
|
||||||
|
|
||||||
function ConnectedPage:render()
|
function ConnectedPage:init()
|
||||||
return Roact.createFragment({
|
self.changeDrawerMotor = Flipper.SingleMotor.new(0)
|
||||||
Header = e(Header, {
|
self.changeDrawerHeight = bindingUtil.fromMotor(self.changeDrawerMotor)
|
||||||
transparency = self.props.transparency,
|
|
||||||
layoutOrder = 1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
ConnectionDetails = e(ConnectionDetails, {
|
self.changeDrawerMotor:onStep(function(value)
|
||||||
projectName = self.state.projectName,
|
local renderChanges = value > 0.05
|
||||||
address = self.state.address,
|
|
||||||
transparency = self.props.transparency,
|
|
||||||
layoutOrder = 2,
|
|
||||||
|
|
||||||
onDisconnect = self.props.onDisconnect,
|
self:setState(function(state)
|
||||||
}),
|
if state.renderChanges == renderChanges then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
return {
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
renderChanges = renderChanges,
|
||||||
FillDirection = Enum.FillDirection.Vertical,
|
}
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
end)
|
||||||
Padding = UDim.new(0, 10),
|
end)
|
||||||
}),
|
|
||||||
|
|
||||||
Padding = e("UIPadding", {
|
self:setState({
|
||||||
PaddingLeft = UDim.new(0, 20),
|
renderChanges = false,
|
||||||
PaddingRight = UDim.new(0, 20),
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ConnectedPage:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return Roact.createFragment({
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 20),
|
||||||
|
PaddingRight = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Header = e(Header, {
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ConnectionDetails = e(ConnectionDetails, {
|
||||||
|
projectName = self.state.projectName,
|
||||||
|
address = self.state.address,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
|
||||||
|
onDisconnect = self.props.onDisconnect,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ChangeInfo = e("TextButton", {
|
||||||
|
Text = self.props.patchInfo:map(function(info)
|
||||||
|
local changes = PatchSet.countChanges(info.patch)
|
||||||
|
return string.format(
|
||||||
|
"<i>Synced %d change%s %s</i>",
|
||||||
|
changes,
|
||||||
|
changes == 1 and "" or "s",
|
||||||
|
timeSinceText(os.time() - info.timestamp)
|
||||||
|
)
|
||||||
|
end),
|
||||||
|
Font = Enum.Font.Gotham,
|
||||||
|
TextSize = 14,
|
||||||
|
TextWrapped = true,
|
||||||
|
RichText = true,
|
||||||
|
TextColor3 = theme.Header.VersionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextYAlignment = Enum.TextYAlignment.Top,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 0, 28),
|
||||||
|
|
||||||
|
LayoutOrder = 3,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = function()
|
||||||
|
if self.state.renderChanges then
|
||||||
|
self.changeDrawerMotor:setGoal(Flipper.Spring.new(0, {
|
||||||
|
frequency = 4,
|
||||||
|
dampingRatio = 1,
|
||||||
|
}))
|
||||||
|
else
|
||||||
|
self.changeDrawerMotor:setGoal(Flipper.Spring.new(1, {
|
||||||
|
frequency = 3,
|
||||||
|
dampingRatio = 1,
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ChangesDrawer = e(ChangesDrawer, {
|
||||||
|
rendered = self.state.renderChanges,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
patchInfo = self.props.patchInfo,
|
||||||
|
serveSession = self.props.serveSession,
|
||||||
|
height = self.changeDrawerHeight,
|
||||||
|
layoutOrder = 4,
|
||||||
|
|
||||||
|
onClose = function()
|
||||||
|
self.changeDrawerMotor:setGoal(Flipper.Spring.new(0, {
|
||||||
|
frequency = 4,
|
||||||
|
dampingRatio = 1,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
function ConnectedPage.getDerivedStateFromProps(props)
|
function ConnectedPage.getDerivedStateFromProps(props)
|
||||||
-- If projectName or address ever get removed from props, make sure we still have
|
-- If projectName or address ever get removed from props, make sure we still have
|
||||||
-- the properties! The component still needs to have its data for it to be properly
|
-- the properties! The component still needs to have its data for it to be properly
|
||||||
@@ -122,4 +276,4 @@ function ConnectedPage.getDerivedStateFromProps(props)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return ConnectedPage
|
return ConnectedPage
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Spinner = require(Plugin.App.Components.Spinner)
|
local Spinner = require(Plugin.App.Components.Spinner)
|
||||||
|
|
||||||
@@ -17,4 +18,4 @@ function ConnectingPage:render()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return ConnectingPage
|
return ConnectingPage
|
||||||
|
|||||||
@@ -2,14 +2,16 @@ local TextService = game:GetService("TextService")
|
|||||||
|
|
||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Theme = require(Plugin.App.Theme)
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
|
||||||
local TextButton = require(Plugin.App.Components.TextButton)
|
local TextButton = require(Plugin.App.Components.TextButton)
|
||||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
||||||
|
local Tooltip = require(Plugin.App.Components.Tooltip)
|
||||||
|
|
||||||
local e = Roact.createElement
|
local e = Roact.createElement
|
||||||
|
|
||||||
@@ -56,8 +58,16 @@ function Error:render()
|
|||||||
end,
|
end,
|
||||||
}, {
|
}, {
|
||||||
ErrorMessage = Theme.with(function(theme)
|
ErrorMessage = Theme.with(function(theme)
|
||||||
return e("TextLabel", {
|
return e("TextBox", {
|
||||||
|
[Roact.Event.InputBegan] = function(rbx, input)
|
||||||
|
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
|
||||||
|
rbx.SelectionStart = 0
|
||||||
|
rbx.CursorPosition = #rbx.Text+1
|
||||||
|
end,
|
||||||
|
|
||||||
|
|
||||||
Text = self.props.errorMessage,
|
Text = self.props.errorMessage,
|
||||||
|
TextEditable = false,
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 16,
|
TextSize = 16,
|
||||||
TextColor3 = theme.ErrorColor,
|
TextColor3 = theme.ErrorColor,
|
||||||
@@ -65,10 +75,9 @@ function Error:render()
|
|||||||
TextYAlignment = Enum.TextYAlignment.Top,
|
TextYAlignment = Enum.TextYAlignment.Top,
|
||||||
TextTransparency = self.props.transparency,
|
TextTransparency = self.props.transparency,
|
||||||
TextWrapped = true,
|
TextWrapped = true,
|
||||||
|
ClearTextOnFocus = false,
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
|
||||||
|
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
})
|
})
|
||||||
end),
|
end),
|
||||||
|
|
||||||
@@ -115,6 +124,10 @@ function ErrorPage:render()
|
|||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 1,
|
layoutOrder = 1,
|
||||||
onClick = self.props.onClose,
|
onClick = self.props.onClose,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Dismiss message"
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
Layout = e("UIListLayout", {
|
||||||
@@ -150,4 +163,4 @@ function ErrorPage.getDerivedStateFromProps(props)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return ErrorPage
|
return ErrorPage
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
local Plugin = Rojo.Plugin
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
local Config = require(Plugin.Config)
|
local Config = require(Plugin.Config)
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ local Theme = require(Plugin.App.Theme)
|
|||||||
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
local TextButton = require(Plugin.App.Components.TextButton)
|
local TextButton = require(Plugin.App.Components.TextButton)
|
||||||
local Header = require(Plugin.App.Components.Header)
|
local Header = require(Plugin.App.Components.Header)
|
||||||
|
local Tooltip = require(Plugin.App.Components.Tooltip)
|
||||||
|
|
||||||
local PORT_WIDTH = 74
|
local PORT_WIDTH = 74
|
||||||
local DIVIDER_WIDTH = 1
|
local DIVIDER_WIDTH = 1
|
||||||
@@ -24,7 +26,7 @@ local function AddressEntry(props)
|
|||||||
layoutOrder = props.layoutOrder,
|
layoutOrder = props.layoutOrder,
|
||||||
}, {
|
}, {
|
||||||
Host = e("TextBox", {
|
Host = e("TextBox", {
|
||||||
Text = "",
|
Text = props.host or "",
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
TextColor3 = theme.AddressEntry.TextColor,
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
@@ -32,6 +34,7 @@ local function AddressEntry(props)
|
|||||||
TextTransparency = props.transparency,
|
TextTransparency = props.transparency,
|
||||||
PlaceholderText = Config.defaultHost,
|
PlaceholderText = Config.defaultHost,
|
||||||
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
ClearTextOnFocus = false,
|
||||||
|
|
||||||
Size = UDim2.new(1, -(HOST_OFFSET + DIVIDER_WIDTH + PORT_WIDTH), 1, 0),
|
Size = UDim2.new(1, -(HOST_OFFSET + DIVIDER_WIDTH + PORT_WIDTH), 1, 0),
|
||||||
Position = UDim2.new(0, HOST_OFFSET, 0, 0),
|
Position = UDim2.new(0, HOST_OFFSET, 0, 0),
|
||||||
@@ -39,17 +42,22 @@ local function AddressEntry(props)
|
|||||||
ClipsDescendants = true,
|
ClipsDescendants = true,
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
[Roact.Ref] = props.hostRef,
|
[Roact.Change.Text] = function(object)
|
||||||
|
if props.onHostChange ~= nil then
|
||||||
|
props.onHostChange(object.Text)
|
||||||
|
end
|
||||||
|
end
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Port = e("TextBox", {
|
Port = e("TextBox", {
|
||||||
Text = "",
|
Text = props.port or "",
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
TextColor3 = theme.AddressEntry.TextColor,
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
TextTransparency = props.transparency,
|
TextTransparency = props.transparency,
|
||||||
PlaceholderText = Config.defaultPort,
|
PlaceholderText = Config.defaultPort,
|
||||||
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
ClearTextOnFocus = false,
|
||||||
|
|
||||||
Size = UDim2.new(0, PORT_WIDTH, 1, 0),
|
Size = UDim2.new(0, PORT_WIDTH, 1, 0),
|
||||||
Position = UDim2.new(1, 0, 0, 0),
|
Position = UDim2.new(1, 0, 0, 0),
|
||||||
@@ -58,12 +66,14 @@ local function AddressEntry(props)
|
|||||||
ClipsDescendants = true,
|
ClipsDescendants = true,
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
[Roact.Ref] = props.portRef,
|
|
||||||
|
|
||||||
[Roact.Change.Text] = function(object)
|
[Roact.Change.Text] = function(object)
|
||||||
local text = object.Text
|
local text = object.Text
|
||||||
text = text:gsub("%D", "")
|
text = text:gsub("%D", "")
|
||||||
object.Text = text
|
object.Text = text
|
||||||
|
|
||||||
|
if props.onPortChange ~= nil then
|
||||||
|
props.onPortChange(text)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
}, {
|
}, {
|
||||||
Divider = e("Frame", {
|
Divider = e("Frame", {
|
||||||
@@ -80,11 +90,6 @@ end
|
|||||||
|
|
||||||
local NotConnectedPage = Roact.Component:extend("NotConnectedPage")
|
local NotConnectedPage = Roact.Component:extend("NotConnectedPage")
|
||||||
|
|
||||||
function NotConnectedPage:init()
|
|
||||||
self.hostRef = Roact.createRef()
|
|
||||||
self.portRef = Roact.createRef()
|
|
||||||
end
|
|
||||||
|
|
||||||
function NotConnectedPage:render()
|
function NotConnectedPage:render()
|
||||||
return Roact.createFragment({
|
return Roact.createFragment({
|
||||||
Header = e(Header, {
|
Header = e(Header, {
|
||||||
@@ -93,8 +98,10 @@ function NotConnectedPage:render()
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
AddressEntry = e(AddressEntry, {
|
AddressEntry = e(AddressEntry, {
|
||||||
hostRef = self.hostRef,
|
host = self.props.host,
|
||||||
portRef = self.portRef,
|
port = self.props.port,
|
||||||
|
onHostChange = self.props.onHostChange,
|
||||||
|
onPortChange = self.props.onPortChange,
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 2,
|
layoutOrder = 2,
|
||||||
}),
|
}),
|
||||||
@@ -103,6 +110,7 @@ function NotConnectedPage:render()
|
|||||||
Size = UDim2.new(1, 0, 0, 34),
|
Size = UDim2.new(1, 0, 0, 34),
|
||||||
LayoutOrder = 3,
|
LayoutOrder = 3,
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
ZIndex = 2,
|
||||||
}, {
|
}, {
|
||||||
Settings = e(TextButton, {
|
Settings = e(TextButton, {
|
||||||
text = "Settings",
|
text = "Settings",
|
||||||
@@ -110,6 +118,10 @@ function NotConnectedPage:render()
|
|||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 1,
|
layoutOrder = 1,
|
||||||
onClick = self.props.onNavigateSettings,
|
onClick = self.props.onNavigateSettings,
|
||||||
|
}, {
|
||||||
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "View and modify plugin settings"
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Connect = e(TextButton, {
|
Connect = e(TextButton, {
|
||||||
@@ -117,15 +129,11 @@ function NotConnectedPage:render()
|
|||||||
style = "Solid",
|
style = "Solid",
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 2,
|
layoutOrder = 2,
|
||||||
onClick = function()
|
onClick = self.props.onConnect,
|
||||||
local hostText = self.hostRef.current.Text
|
}, {
|
||||||
local portText = self.portRef.current.Text
|
Tip = e(Tooltip.Trigger, {
|
||||||
|
text = "Connect to a Rojo sync server"
|
||||||
self.props.onConnect(
|
}),
|
||||||
#hostText > 0 and hostText or Config.defaultHost,
|
|
||||||
#portText > 0 and portText or Config.defaultPort
|
|
||||||
)
|
|
||||||
end,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
Layout = e("UIListLayout", {
|
||||||
@@ -151,4 +159,4 @@ function NotConnectedPage:render()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return NotConnectedPage
|
return NotConnectedPage
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
local TextService = game:GetService("TextService")
|
|
||||||
|
|
||||||
local Rojo = script:FindFirstAncestor("Rojo")
|
|
||||||
local Plugin = Rojo.Plugin
|
|
||||||
|
|
||||||
local Roact = require(Rojo.Roact)
|
|
||||||
|
|
||||||
local Assets = require(Plugin.Assets)
|
|
||||||
local Theme = require(Plugin.App.Theme)
|
|
||||||
local PluginSettings = require(Plugin.App.PluginSettings)
|
|
||||||
|
|
||||||
local Checkbox = require(Plugin.App.Components.Checkbox)
|
|
||||||
local IconButton = require(Plugin.App.Components.IconButton)
|
|
||||||
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
|
||||||
|
|
||||||
local e = Roact.createElement
|
|
||||||
|
|
||||||
local DIVIDER_FADE_SIZE = 0.1
|
|
||||||
|
|
||||||
local function getTextBounds(text, textSize, font, lineHeight, bounds)
|
|
||||||
local textBounds = TextService:GetTextSize(text, textSize, font, bounds)
|
|
||||||
|
|
||||||
local lineCount = textBounds.Y / textSize
|
|
||||||
local lineHeightAbsolute = textSize * lineHeight
|
|
||||||
|
|
||||||
return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function Navbar(props)
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
theme = theme.Settings.Navbar
|
|
||||||
|
|
||||||
return e("Frame", {
|
|
||||||
Size = UDim2.new(1, 0, 0, 46),
|
|
||||||
LayoutOrder = props.layoutOrder,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}, {
|
|
||||||
Back = e(IconButton, {
|
|
||||||
icon = Assets.Images.Icons.Back,
|
|
||||||
iconSize = 24,
|
|
||||||
color = theme.BackButtonColor,
|
|
||||||
transparency = props.transparency,
|
|
||||||
|
|
||||||
position = UDim2.new(0, 0, 0.5, 0),
|
|
||||||
anchorPoint = Vector2.new(0, 0.5),
|
|
||||||
|
|
||||||
onClick = props.onBack,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Text = e("TextLabel", {
|
|
||||||
Text = "Settings",
|
|
||||||
Font = Enum.Font.Gotham,
|
|
||||||
TextSize = 18,
|
|
||||||
TextColor3 = theme.TextColor,
|
|
||||||
TextTransparency = props.transparency,
|
|
||||||
|
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
|
||||||
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local Setting = Roact.Component:extend("Setting")
|
|
||||||
|
|
||||||
function Setting:init()
|
|
||||||
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
|
||||||
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
|
|
||||||
end
|
|
||||||
|
|
||||||
function Setting:render()
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
theme = theme.Settings
|
|
||||||
|
|
||||||
return PluginSettings.with(function(settings)
|
|
||||||
return e("Frame", {
|
|
||||||
Size = self.contentSize:map(function(value)
|
|
||||||
return UDim2.new(1, 0, 0, 20 + value.Y + 20)
|
|
||||||
end),
|
|
||||||
LayoutOrder = self.props.layoutOrder,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
|
|
||||||
[Roact.Change.AbsoluteSize] = function(object)
|
|
||||||
self.setContainerSize(object.AbsoluteSize)
|
|
||||||
end,
|
|
||||||
}, {
|
|
||||||
Checkbox = e(Checkbox, {
|
|
||||||
active = settings:get(self.props.id),
|
|
||||||
transparency = self.props.transparency,
|
|
||||||
position = UDim2.new(1, 0, 0.5, 0),
|
|
||||||
anchorPoint = Vector2.new(1, 0.5),
|
|
||||||
onClick = function()
|
|
||||||
local currentValue = settings:get(self.props.id)
|
|
||||||
settings:set(self.props.id, not currentValue)
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Text = e("Frame", {
|
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}, {
|
|
||||||
Name = e("TextLabel", {
|
|
||||||
Text = self.props.name,
|
|
||||||
Font = Enum.Font.GothamBold,
|
|
||||||
TextSize = 17,
|
|
||||||
TextColor3 = theme.Setting.NameColor,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
TextTransparency = self.props.transparency,
|
|
||||||
|
|
||||||
Size = UDim2.new(1, 0, 0, 17),
|
|
||||||
|
|
||||||
LayoutOrder = 1,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Description = e("TextLabel", {
|
|
||||||
Text = self.props.description,
|
|
||||||
Font = Enum.Font.Gotham,
|
|
||||||
LineHeight = 1.2,
|
|
||||||
TextSize = 14,
|
|
||||||
TextColor3 = theme.Setting.DescriptionColor,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
TextTransparency = self.props.transparency,
|
|
||||||
TextWrapped = true,
|
|
||||||
|
|
||||||
Size = self.containerSize:map(function(value)
|
|
||||||
local textBounds = getTextBounds(
|
|
||||||
self.props.description, 14, Enum.Font.Gotham, 1.2,
|
|
||||||
Vector2.new(value.X - 50, math.huge)
|
|
||||||
)
|
|
||||||
return UDim2.new(1, -50, 0, textBounds.Y)
|
|
||||||
end),
|
|
||||||
|
|
||||||
LayoutOrder = 2,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
|
||||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
|
||||||
FillDirection = Enum.FillDirection.Vertical,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
Padding = UDim.new(0, 6),
|
|
||||||
|
|
||||||
[Roact.Change.AbsoluteContentSize] = function(object)
|
|
||||||
self.setContentSize(object.AbsoluteContentSize)
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Padding = e("UIPadding", {
|
|
||||||
PaddingTop = UDim.new(0, 20),
|
|
||||||
PaddingBottom = UDim.new(0, 20),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
Divider = e("Frame", {
|
|
||||||
BackgroundColor3 = theme.DividerColor,
|
|
||||||
BackgroundTransparency = self.props.transparency,
|
|
||||||
Size = UDim2.new(1, 0, 0, 1),
|
|
||||||
BorderSizePixel = 0,
|
|
||||||
}, {
|
|
||||||
Gradient = e("UIGradient", {
|
|
||||||
Transparency = NumberSequence.new({
|
|
||||||
NumberSequenceKeypoint.new(0, 1),
|
|
||||||
NumberSequenceKeypoint.new(DIVIDER_FADE_SIZE, 0),
|
|
||||||
NumberSequenceKeypoint.new(1 - DIVIDER_FADE_SIZE, 0),
|
|
||||||
NumberSequenceKeypoint.new(1, 1),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local SettingsPage = Roact.Component:extend("SettingsPage")
|
|
||||||
|
|
||||||
function SettingsPage:init()
|
|
||||||
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
|
||||||
end
|
|
||||||
|
|
||||||
function SettingsPage:render()
|
|
||||||
return Theme.with(function(theme)
|
|
||||||
theme = theme.Settings
|
|
||||||
|
|
||||||
return e(ScrollingFrame, {
|
|
||||||
size = UDim2.new(1, 0, 1, 0),
|
|
||||||
contentSize = self.contentSize,
|
|
||||||
transparency = self.props.transparency,
|
|
||||||
}, {
|
|
||||||
Navbar = e(Navbar, {
|
|
||||||
onBack = self.props.onBack,
|
|
||||||
transparency = self.props.transparency,
|
|
||||||
layoutOrder = 0,
|
|
||||||
}),
|
|
||||||
|
|
||||||
OpenScriptsExternally = e(Setting, {
|
|
||||||
id = "openScriptsExternally",
|
|
||||||
name = "Open Scripts Externally",
|
|
||||||
description = "Attempt to open scripts in an external editor",
|
|
||||||
transparency = self.props.transparency,
|
|
||||||
layoutOrder = 1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
TwoWaySync = e(Setting, {
|
|
||||||
id = "twoWaySync",
|
|
||||||
name = "Two-Way Sync",
|
|
||||||
description = "Editing files in Studio will sync them into the filesystem",
|
|
||||||
transparency = self.props.transparency,
|
|
||||||
layoutOrder = 2,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
|
||||||
FillDirection = Enum.FillDirection.Vertical,
|
|
||||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
||||||
|
|
||||||
[Roact.Change.AbsoluteContentSize] = function(object)
|
|
||||||
self.setContentSize(object.AbsoluteContentSize)
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Padding = e("UIPadding", {
|
|
||||||
PaddingLeft = UDim.new(0, 20),
|
|
||||||
PaddingRight = UDim.new(0, 20),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return SettingsPage
|
|
||||||
180
plugin/src/App/StatusPages/Settings/Setting.lua
Normal file
180
plugin/src/App/StatusPages/Settings/Setting.lua
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
|
local Roact = require(Packages.Roact)
|
||||||
|
|
||||||
|
local Settings = require(Plugin.Settings)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
|
||||||
|
local Checkbox = require(Plugin.App.Components.Checkbox)
|
||||||
|
local Dropdown = require(Plugin.App.Components.Dropdown)
|
||||||
|
local IconButton = require(Plugin.App.Components.IconButton)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local DIVIDER_FADE_SIZE = 0.1
|
||||||
|
|
||||||
|
local function getTextBounds(text, textSize, font, lineHeight, bounds)
|
||||||
|
local textBounds = TextService:GetTextSize(text, textSize, font, bounds)
|
||||||
|
|
||||||
|
local lineCount = textBounds.Y / textSize
|
||||||
|
local lineHeightAbsolute = textSize * lineHeight
|
||||||
|
|
||||||
|
return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize))
|
||||||
|
end
|
||||||
|
|
||||||
|
local Setting = Roact.Component:extend("Setting")
|
||||||
|
|
||||||
|
function Setting:init()
|
||||||
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
setting = Settings:get(self.props.id),
|
||||||
|
})
|
||||||
|
|
||||||
|
self.changedCleanup = Settings:onChanged(self.props.id, function(value)
|
||||||
|
self:setState({
|
||||||
|
setting = value,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Setting:willUnmount()
|
||||||
|
self.changedCleanup()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Setting:render()
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
theme = theme.Settings
|
||||||
|
|
||||||
|
return e("Frame", {
|
||||||
|
Size = self.contentSize:map(function(value)
|
||||||
|
return UDim2.new(1, 0, 0, 20 + value.Y + 20)
|
||||||
|
end),
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
ZIndex = -self.props.layoutOrder,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteSize] = function(object)
|
||||||
|
self.setContainerSize(object.AbsoluteSize)
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
Input = if self.props.options ~= nil then
|
||||||
|
e(Dropdown, {
|
||||||
|
options = self.props.options,
|
||||||
|
active = self.state.setting,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
position = UDim2.new(1, 0, 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(1, 0.5),
|
||||||
|
onClick = function(option)
|
||||||
|
Settings:set(self.props.id, option)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
else
|
||||||
|
e(Checkbox, {
|
||||||
|
active = self.state.setting,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
position = UDim2.new(1, 0, 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(1, 0.5),
|
||||||
|
onClick = function()
|
||||||
|
local currentValue = Settings:get(self.props.id)
|
||||||
|
Settings:set(self.props.id, not currentValue)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Reset = if self.props.onReset then e(IconButton, {
|
||||||
|
icon = Assets.Images.Icons.Reset,
|
||||||
|
iconSize = 24,
|
||||||
|
color = theme.BackButtonColor,
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
visible = self.props.showReset,
|
||||||
|
|
||||||
|
position = UDim2.new(1, -32 - (self.props.options ~= nil and 120 or 40), 0.5, 0),
|
||||||
|
anchorPoint = Vector2.new(0, 0.5),
|
||||||
|
|
||||||
|
onClick = self.props.onReset,
|
||||||
|
}) else nil,
|
||||||
|
|
||||||
|
Text = e("Frame", {
|
||||||
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}, {
|
||||||
|
Name = e("TextLabel", {
|
||||||
|
Text = self.props.name,
|
||||||
|
Font = Enum.Font.GothamBold,
|
||||||
|
TextSize = 17,
|
||||||
|
TextColor3 = theme.Setting.NameColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, 0, 0, 17),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Description = e("TextLabel", {
|
||||||
|
Text = self.props.description,
|
||||||
|
Font = Enum.Font.Gotham,
|
||||||
|
LineHeight = 1.2,
|
||||||
|
TextSize = 14,
|
||||||
|
TextColor3 = theme.Setting.DescriptionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = self.props.transparency,
|
||||||
|
TextWrapped = true,
|
||||||
|
|
||||||
|
Size = self.containerSize:map(function(value)
|
||||||
|
local offset = (self.props.onReset and 34 or 0) + (self.props.options ~= nil and 120 or 40)
|
||||||
|
local textBounds = getTextBounds(
|
||||||
|
self.props.description, 14, Enum.Font.Gotham, 1.2,
|
||||||
|
Vector2.new(value.X - offset, math.huge)
|
||||||
|
)
|
||||||
|
return UDim2.new(1, -offset, 0, textBounds.Y)
|
||||||
|
end),
|
||||||
|
|
||||||
|
LayoutOrder = 2,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Layout = e("UIListLayout", {
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
FillDirection = Enum.FillDirection.Vertical,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, 6),
|
||||||
|
|
||||||
|
[Roact.Change.AbsoluteContentSize] = function(object)
|
||||||
|
self.setContentSize(object.AbsoluteContentSize)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingTop = UDim.new(0, 20),
|
||||||
|
PaddingBottom = UDim.new(0, 20),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Divider = e("Frame", {
|
||||||
|
BackgroundColor3 = theme.DividerColor,
|
||||||
|
BackgroundTransparency = self.props.transparency,
|
||||||
|
Size = UDim2.new(1, 0, 0, 1),
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
}, {
|
||||||
|
Gradient = e("UIGradient", {
|
||||||
|
Transparency = NumberSequence.new({
|
||||||
|
NumberSequenceKeypoint.new(0, 1),
|
||||||
|
NumberSequenceKeypoint.new(DIVIDER_FADE_SIZE, 0),
|
||||||
|
NumberSequenceKeypoint.new(1 - DIVIDER_FADE_SIZE, 0),
|
||||||
|
NumberSequenceKeypoint.new(1, 1),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Setting
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user