From 494ed6244654f0eb7886177eff2f6c86520cb742 Mon Sep 17 00:00:00 2001 From: James <119134081+2jammers@users.noreply.github.com> Date: Sat, 14 Dec 2024 23:23:46 -0600 Subject: [PATCH] release --- .github/ISSUE_TEMPLATE/bug.yml | 34 + .github/ISSUE_TEMPLATE/enhancement.yml | 26 + .github/pull_request_template.md | 6 +- .github/workflows/release.yml | 102 +-- .vscode/settings.json | 3 - README.md | 6 +- docs/changelog.md | 8 +- pesde.toml | 14 +- src/Action.luau | 12 +- src/Apply.luau | 6 +- src/Change.luau | 22 +- src/Clean.luau | 28 +- src/Defaults.luau | 50 +- src/Event.luau | 18 +- src/Logs.luau | 16 +- src/New.luau | 2 +- src/Spr.luau | 853 ------------------------- src/Spring.luau | 5 +- src/State.luau | 20 +- src/Tags.luau | 16 +- src/Types.luau | 12 +- src/init.luau | 18 +- standalone.project.json | 4 +- 23 files changed, 211 insertions(+), 1070 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/enhancement.yml delete mode 100644 .vscode/settings.json delete mode 100644 src/Spr.luau diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..2c61f3a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,34 @@ +name: Bug Report +description: Report an issue you come across in this project +labels: ["bug", "triage", "review"] +body: + - type: checkboxes + id: check + attributes: + label: Original + description: Be sure that a change related to this issue isn't already pending, and a similar issue does not exist. + options: + - label: My issue is original + required: true + - type: textarea + id: description + attributes: + label: Description + description: Give a detailed description on the bug you are experiencing. + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduction + description: A bullet-pointed list in the order to reproduce the bug. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Output + description: Copy the exact error, and omit the date + time prefixes if any. + render: shell + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 0000000..b7c1348 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,26 @@ +name: Enhancement +description: Request a feature that you think would be nice to include in this project +labels: ["enhancement", "triage", "review"] +body: + - type: checkboxes + id: check + attributes: + label: Original + description: Be sure that a change related to this issue isn't already pending, and a similar issue does not exist. + options: + - label: My issue is original + required: true + - type: textarea + id: description + attributes: + label: Description + description: Give a detailed description on the feature you're proposing. + validations: + required: true + - type: textarea + id: repro + attributes: + label: Objective + description: What should this feature achieve, and why should it be added? + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 442e902..e273f2b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -16,6 +16,6 @@ By submitting this pull request for maintainer review, you agree to the followin -- [ ] I give full permission for maintainers and contributors of this repository to change my code in any way. -- [ ] I am open to this pull request being a part of a completely open source repository which can be modified at any time, and I abide by the license terms. -- [ ] To the best of my knowledge, there are no bugs present in this pull request. +- [ ] I give permission to maintainers to modify my code +- [ ] My code abides by the license selected for this repository +- [ ] To the best of my knowledge, there are no bugs present in this pull request diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e32eb6..c31c8fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Draft +name: Rekease on: push: @@ -20,54 +20,41 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: "main" + ref: 'main' - name: Update changelog id: update-changelog uses: thomaseizinger/keep-a-changelog-new-release@3.1.0 with: tag: ${{ github.ref_name }} - changelogPath: "docs/changelog.md" - - - name: Bump Wally version - id: version-bump - uses: DervexDev/file-version-bumper@v1 - with: - path: ./wally.toml + changelogPath: 'docs/changelog.md' - name: Commit and push uses: EndBug/add-and-commit@v9 - if: ${{ github.ref_name != steps.version-bump.outputs.old_version }} with: message: Bump version to ${{ github.ref_name }} default_author: github_actions - - name: Update tag - if: ${{ github.ref_name != steps.version-bump.outputs.old_version }} - run: | - git tag -f ${{ github.ref_name }} - git push -f --tags - publish-build: - name: Publish Assets + name: Publish Build runs-on: ubuntu-latest needs: bump steps: - name: Checkout Repository uses: actions/checkout@v4 - - name: Setup Rokit - uses: CompeyDev/setup-rokit@v0.1.0 + - name: Setup Pesde + uses: 2jammers/setup-pesde@v0.3.0 + with: + cache: 'true' + pesde-version: 'v0.5.1+registry.0.1.0' - - name: Install Wally dependencies - run: wally install + - name: Install dependencies + run: pesde install - name: Generate sourcemap run: rojo sourcemap standalone.project.json --output sourcemap.json - - name: Generate package types - run: wally-package-types --sourcemap sourcemap.json Packages - - name: Format code run: stylua src/ @@ -95,67 +82,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup Rokit - uses: CompeyDev/setup-rokit@v0.1.0 - - - name: Log In - env: - WALLY_AUTH: ${{ secrets.WALLY_AUTH_TOKEN }} - run: | - mkdir ~/.wally - printenv WALLY_AUTH > ~/.wally/auth.toml + - name: Setup Pesde + uses: 2jammers/setup-pesde@v0.3.0 + with: + pesde-version: 'v0.5.1+registry.0.1.0' + token: '${{ secrets.PESDE_TOKEN }}' - name: Publish run: | - wally publish - - publish-announcement: - name: Publish Announcement - runs-on: ubuntu-latest - needs: [publish-package, publish-build] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Get project information - id: project_info - run: | - echo "REPO_NAME=${{ github.repository }}" >> $GITHUB_ENV - echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV - - - - name: Send webhook - uses: actions/github-script@v6 - with: - script: | - const net = require('node-fetch'); - const projectRole = "1298443968794722334"; - const webhookUrl = process.env.WEBHOOK_URL; - const projectName = process.env.REPO_NAME; - const formattedProjectName = projectName.charAt(0).toUpperCase() + projectName.slice(1); - const tag = process.env.RELEASE_TAG; - - const body = { - content: `<@&${projectRole}>`, - embeds: [ - { - title: `Release ${tag} · ${formattedProjectName}`, - description: `Release notification for the latest version of ${formattedProjectName}`, - url: `https://github.com/luminlabsdev/${projectName}/releases/tag/${tag}`, - color: 7506646, - author: { - name: "GitHub", - }, - }, - ], - }; - - const response = await net(webhookUrl, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - - if (!response.ok) { - throw new Error(`${response.status}: ${response.statusText}`); - } + pesde publish -y diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 1bdfda2..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "stylua.targetReleaseVersion": "latest" -} diff --git a/README.md b/README.md index 5d9b624..5dc4d38 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ [![ci](https://img.shields.io/github/actions/workflow/status/lumin-org/ui/release.yml?style=plastic&logo=github&logoColor=FFFFFF&label=ci)](https://github.com/lumin-org/ui/blob/main/.github/workflows/release.yml) [![discord](https://img.shields.io/discord/1105688855375511642?logo=discord&logoColor=white&label=chat&color=4d3dff&style=plastic)](https://lumin-org.github.io/to/discord) -A small, fast, and efficient UI framework that has a small learning curve. +A light, fast, and efficient UI framework that has a small learning curve. ## Prerequisites In order to use **lumin/ui** you must have the following dependencies installed: -* [`pesde@v0.5.0-rc.18`](https://github.com/pesde-pkg/pesde) -* [`rojo@v7.4.4`](https://github.com/rojo-rbx/rojo) +* [`pesde@v0.5.1+registry.0.1.0^`](https://github.com/pesde-pkg/pesde) +* [`rojo@v7.4.4^`](https://github.com/rojo-rbx/rojo) ## Usage diff --git a/docs/changelog.md b/docs/changelog.md index d177e60..f407113 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,13 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Changed - -- Migrates to new github actions and changelog format - ### Added -- Added Rokit as the default package manager +- Complete rewrite ## [0.2.3] - 2024-07-25 @@ -34,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixed infinite error on spring destruction bug ( [#5 by @dazscripts](https://github.com/lumin-dev/Aegis/pull/5) ) +- Fixed infinite error on spring destruction bug ( [#5 by @dazscripts](https://github.com/lumin-org/ui/pull/5) ) ## [0.2.1] - 2024-07-06 diff --git a/pesde.toml b/pesde.toml index 07b4588..e12b2c8 100644 --- a/pesde.toml +++ b/pesde.toml @@ -1,12 +1,20 @@ name = "lumin/ui" version = "0.3.0-rc1" -description = "A lightweight and embeddable UI framework" +description = "A small, fast, and efficient UI framework that has a small learning curve" authors = ["2jammers"] -repository = "https://github.com/luminlabsdev/ui" +repository = "https://github.com/lumin-org/ui" license = "MIT" +includes = [ + "pesde.toml", + "README.md", + "LICENSE", + "src", +] [target] environment = "roblox" +lib = "src/init.luau" +build_files = ["src"] [indices] default = "https://github.com/daimond113/pesde-index" @@ -18,6 +26,8 @@ sourcemap_generator = ".pesde/scripts/sourcemap_generator.luau" [dev_dependencies] scripts = { name = "pesde/scripts_rojo", version = "^0.1.0", target = "lune" } rojo = { name = "pesde/rojo", version = "^7.4.4", target = "lune" } +stylua = { name = "pesde/stylua", version = "^2.0.1", target = "lune" } [dependencies] debugger = { name = "lumin/debugger", version = "^0.5.0" } +spr = { name = "2jammers/spr", version = "^2.1.0" } diff --git a/src/Action.luau b/src/Action.luau index 5ebaa74..866dadb 100644 --- a/src/Action.luau +++ b/src/Action.luau @@ -1,7 +1,7 @@ -- Variables local Root = script.Parent local Types = require(Root.Types) -local Actions: {[string]: Types.Action} = {} +local Actions: { [string]: Types.Action } = {} -- Functions @@ -11,12 +11,12 @@ local Actions: {[string]: Types.Action} = {} [Open Documentation](https://lumin-org.github.io/ui/api/#action) ]=] local function New(name: string, apply: (Instance) -> ()) - if not Actions[name] then -- Cache the action so a new one is created each time - Actions[name] = apply - end + if not Actions[name] then -- Cache the action so a new one is created each time + Actions[name] = apply + end end return { - New = New, - List = Actions, + New = New, + List = Actions, } diff --git a/src/Apply.luau b/src/Apply.luau index 047ac28..28497fe 100644 --- a/src/Apply.luau +++ b/src/Apply.luau @@ -5,14 +5,14 @@ local Types = require(Root.Types) -- Module return function(instance: Instance, property: any, value: any) - local PropType = type(property) - local ValueType = type(value) + local PropType = type(property) + local ValueType = type(value) if PropType == "number" then -- If the property is a modifier if ValueType == "function" then -- If the value is an action (value :: Types.Action)(instance) else -- If the value is an instance value.Parent = instance - value.Name = property + value.Name = property end elseif PropType == "string" then -- If the property is a string if ValueType == "table" then -- If the value is a constructor diff --git a/src/Change.luau b/src/Change.luau index 5573e62..586edc4 100644 --- a/src/Change.luau +++ b/src/Change.luau @@ -12,16 +12,16 @@ local Debugger = require(Root.Parent.roblox_packages.debugger) [Open Documentation](https://lumin-org.github.io/ui/api/actions/#change) ]=] return function(prop: string, callback: (changed: any) -> ()): Types.Action - if not Action.List["Changed"] then - Action.New("Changed", function(instance) - local Success, Event = pcall(instance.GetPropertyChangedSignal, instance :: any, prop :: any) -- Ensure prop exists - if not Success or type(callback) ~= "function" then - Debugger.Fatal("InvalidPropOrEvent", prop) - end - Event:Connect(function() - callback((instance :: any)[prop]) -- Pass new value through callback - end) - end) - end + if not Action.List["Changed"] then + Action.New("Changed", function(instance) + local Success, Event = pcall(instance.GetPropertyChangedSignal, instance :: any, prop :: any) -- Ensure prop exists + if not Success or type(callback) ~= "function" then + Debugger.Fatal("InvalidPropOrEvent", prop) + end + Event:Connect(function() + callback((instance :: any)[prop]) -- Pass new value through callback + end) + end) + end return Action.List["Changed"] end diff --git a/src/Clean.luau b/src/Clean.luau index d496cca..ce001e7 100644 --- a/src/Clean.luau +++ b/src/Clean.luau @@ -12,19 +12,19 @@ local Debugger = require(Root.Parent.roblox_packages.debugger) [Open Documentation](https://lumin-org.github.io/ui/api/keys/#clean) ]=] return function(values: { any }): Types.Action - if not Action.List["Clean"] then - Action.New("Clean", function(instance: Instance) - Debugger.Assert(type(values) == "table", "InvalidType", "table", type(values)) - instance.Destroying:Once(function() - for _, value in values do - if (type(value) == "table" and value._Bind) or typeof(value) == "Instance" then - value:Destroy() - elseif typeof(value) == "RBXScriptConnection" then - value:Disconnect() - end - end - end) - end) - end + if not Action.List["Clean"] then + Action.New("Clean", function(instance: Instance) + Debugger.Assert(type(values) == "table", "InvalidType", "table", type(values)) + instance.Destroying:Once(function() + for _, value in values do + if (type(value) == "table" and value._Bind) or typeof(value) == "Instance" then + value:Destroy() + elseif typeof(value) == "RBXScriptConnection" then + value:Disconnect() + end + end + end) + end) + end return Action.List["Clean"] end diff --git a/src/Defaults.luau b/src/Defaults.luau index 9e70a0c..7c76213 100644 --- a/src/Defaults.luau +++ b/src/Defaults.luau @@ -1,15 +1,15 @@ -- Stores default properties for common items -- Forked from Fusion 0.3 return { - ScreenGui = { + ScreenGui = { ResetOnSpawn = false, - ZIndexBehavior = Enum.ZIndexBehavior.Sibling + ZIndexBehavior = Enum.ZIndexBehavior.Sibling, }, BillboardGui = { ResetOnSpawn = false, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, - Active = true + Active = true, }, SurfaceGui = { @@ -17,13 +17,13 @@ return { ZIndexBehavior = Enum.ZIndexBehavior.Sibling, SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud, - PixelsPerStud = 50 + PixelsPerStud = 50, }, Frame = { BackgroundColor3 = Color3.new(1, 1, 1), BorderColor3 = Color3.new(0, 0, 0), - BorderSizePixel = 0 + BorderSizePixel = 0, }, ScrollingFrame = { @@ -31,7 +31,7 @@ return { BorderColor3 = Color3.new(0, 0, 0), BorderSizePixel = 0, - ScrollBarImageColor3 = Color3.new(0, 0, 0) + ScrollBarImageColor3 = Color3.new(0, 0, 0), }, TextLabel = { @@ -42,7 +42,7 @@ return { Font = Enum.Font.SourceSans, Text = "", TextColor3 = Color3.new(0, 0, 0), - TextSize = 14 + TextSize = 14, }, TextButton = { @@ -55,7 +55,7 @@ return { Font = Enum.Font.SourceSans, Text = "", TextColor3 = Color3.new(0, 0, 0), - TextSize = 14 + TextSize = 14, }, TextBox = { @@ -68,13 +68,13 @@ return { Font = Enum.Font.SourceSans, Text = "", TextColor3 = Color3.new(0, 0, 0), - TextSize = 14 + TextSize = 14, }, ImageLabel = { BackgroundColor3 = Color3.new(1, 1, 1), BorderColor3 = Color3.new(0, 0, 0), - BorderSizePixel = 0 + BorderSizePixel = 0, }, ImageButton = { @@ -82,53 +82,53 @@ return { BorderColor3 = Color3.new(0, 0, 0), BorderSizePixel = 0, - AutoButtonColor = false + AutoButtonColor = false, }, ViewportFrame = { BackgroundColor3 = Color3.new(1, 1, 1), BorderColor3 = Color3.new(0, 0, 0), - BorderSizePixel = 0 + BorderSizePixel = 0, }, VideoFrame = { BackgroundColor3 = Color3.new(1, 1, 1), BorderColor3 = Color3.new(0, 0, 0), - BorderSizePixel = 0 + BorderSizePixel = 0, }, - + CanvasGroup = { BackgroundColor3 = Color3.new(1, 1, 1), BorderColor3 = Color3.new(0, 0, 0), - BorderSizePixel = 0 + BorderSizePixel = 0, }, SpawnLocation = { - Duration = 0 + Duration = 0, }, BoxHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, ConeHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, CylinderHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, ImageHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, LineHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, SphereHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, WireframeHandleAdornment = { - ZIndex = 0 + ZIndex = 0, }, - + Part = { Anchored = true, Size = Vector3.one, @@ -139,7 +139,7 @@ return { TopSurface = Enum.SurfaceType.Smooth, BottomSurface = Enum.SurfaceType.Smooth, }, - + TrussPart = { Anchored = true, Size = Vector3.one * 2, diff --git a/src/Event.luau b/src/Event.luau index 2cedb5e..d44b43e 100644 --- a/src/Event.luau +++ b/src/Event.luau @@ -17,14 +17,14 @@ end [Open Documentation](https://lumin-org.github.io/ui/api/keys/#event) ]=] return function(event: string, callback: (...any) -> ()): Types.Action - if not Action.List["Event"] then - Action.New("Event", function(instance: Instance) - local Success, Event = pcall(Find, instance :: any, event :: any) -- Ensure event exists - if not Success or type(callback) ~= "function" then - Debugger.Fatal("InvalidPropOrEvent", event) - end - Event:Connect(callback) - end) - end + if not Action.List["Event"] then + Action.New("Event", function(instance: Instance) + local Success, Event = pcall(Find, instance :: any, event :: any) -- Ensure event exists + if not Success or type(callback) ~= "function" then + Debugger.Fatal("InvalidPropOrEvent", event) + end + Event:Connect(callback) + end) + end return Action.List["Event"] end diff --git a/src/Logs.luau b/src/Logs.luau index 2a5a25a..9400e39 100644 --- a/src/Logs.luau +++ b/src/Logs.luau @@ -1,11 +1,11 @@ return { - IncompatibleType = "Data type '%s' is incompatible", - NotAnimatable = "%s is not an animatable object type", - FailedCreation = "%s could not be created; '%s'", - AlreadyExists = "Item '%s' already exists", + IncompatibleType = "Data type '%s' is incompatible", + NotAnimatable = "%s is not an animatable object type", + FailedCreation = "%s could not be created; '%s'", + AlreadyExists = "Item '%s' already exists", - InvalidType = "Expected type %s; got '%s'", - InvalidKey = "Expected valid key type; got '%s'", - InvalidClass = "'%s' is not an instance class name", - InvalidPropOrEvent = "'%s' is not a property/event of the %s class" + InvalidType = "Expected type %s; got '%s'", + InvalidKey = "Expected valid key type; got '%s'", + InvalidClass = "'%s' is not an instance class name", + InvalidPropOrEvent = "'%s' is not a property/event of the %s class", } diff --git a/src/New.luau b/src/New.luau index 5c3e886..cc12416 100644 --- a/src/New.luau +++ b/src/New.luau @@ -17,7 +17,7 @@ return function(class: string) local Success, New = pcall(Instance.new, class) if Success then - -- Apply default properties + -- Apply default properties if Defaults[class] then for prop, value in Defaults[class] do (New :: any)[prop] = value diff --git a/src/Spr.luau b/src/Spr.luau deleted file mode 100644 index d623bda..0000000 --- a/src/Spr.luau +++ /dev/null @@ -1,853 +0,0 @@ ---!strict ---!native ---------------------------------------------------------------------- --- spr - Spring-driven motion library --- --- Copyright (c) 2024 Fractality. All rights reserved. --- Released under the MIT license. --- --- Docs & license can be found at https://github.com/Fraktality/spr --- --- API Summary: --- --- spr.target( --- Instance obj, --- number dampingRatio, --- number undampedFrequency, --- dict targetProperties) --- --- Animates the given properties towardes the target values, --- given damping ratio and undamped frequency. --- --- --- spr.stop( --- Instance obj[, --- string property]) --- --- Stops the specified property on an Instance from animating. --- If no property is specified, all properties of the Instance --- will stop animating. --- --- Visualizer: https://www.desmos.com/calculator/rzvw27ljh9 ---------------------------------------------------------------------- - -local STRICT_RUNTIME_TYPES = true -- assert on parameter and property type mismatch -local SLEEP_OFFSET_SQ_LIMIT = (1/3840)^2 -- square of the offset sleep limit -local SLEEP_VELOCITY_SQ_LIMIT = 1e-2^2 -- square of the velocity sleep limit -local SLEEP_ROTATION_DIFF = math.rad(0.01) -- rad -local SLEEP_ROTATION_VELOCITY = math.rad(0.1) -- rad/s -local EPS = 1e-5 -- epsilon for stability checks around pathological frequency/damping values - -local RunService: RunService = game:GetService("RunService") - -local pi = math.pi -local exp = math.exp -local sin = math.sin -local cos = math.cos -local min = math.min -local max = math.max -local sqrt = math.sqrt -local atan2 = math.atan2 -local round = math.round - -local function magnitudeSq(vec: {number}) - local out = 0 - for _, v in vec do - out += v^2 - end - return out -end - -local function distanceSq(vec0: {number}, vec1: {number}) - local out = 0 - for i0, v0 in vec0 do - out += (vec1[i0] - v0)^2 - end - return out -end - -type TypeMetadata = { - springType: (dampingRatio: number, frequency: number, pos: number, typedat: TypeMetadata, rawTarget: T) -> LinearSpring, - toIntermediate: (T) -> {number}, - fromIntermediate: ({number}) -> T, -} - --- Spring for an array of linear values -local LinearSpring = {} - -type LinearSpring = typeof(setmetatable({} :: { - d: number, - f: number, - g: {number}, - p: {number}, - v: {number}, - typedat: TypeMetadata, - rawTarget: T, -}, LinearSpring)) - -do - LinearSpring.__index = LinearSpring - - function LinearSpring.new(dampingRatio: number, frequency: number, pos: T, rawGoal: T, typedat) - local linearPos = typedat.toIntermediate(pos) - return setmetatable( - { - d = dampingRatio, - f = frequency, - g = linearPos, - p = linearPos, - v = table.create(#linearPos, 0), - typedat = typedat, - rawGoal = rawGoal - }, - LinearSpring - ) - end - - function LinearSpring.setGoal(self, goal: T) - self.rawGoal = goal - self.g = self.typedat.toIntermediate(goal) - end - - function LinearSpring.setDampingRatio(self: LinearSpring, dampingRatio: number) - self.d = dampingRatio - end - - function LinearSpring.setFrequency(self: LinearSpring, frequency: number) - self.f = frequency - end - - function LinearSpring.canSleep(self) - if magnitudeSq(self.v) > SLEEP_VELOCITY_SQ_LIMIT then - return false - end - - if distanceSq(self.p, self.g) > SLEEP_OFFSET_SQ_LIMIT then - return false - end - - return true - end - - function LinearSpring.step(self: LinearSpring, dt: number) - -- Advance the spring simulation by dt seconds. - -- Take the damped harmonic oscillator ODE: - -- f^2*(X[t] - g) + 2*d*f*X'[t] + X''[t] = 0 - -- Where X[t] is position at time t, g is target position, - -- f is undamped angular frequency, and d is damping ratio. - -- Apply constant initial conditions: - -- X[0] = p0 - -- X'[0] = v0 - -- Solve the IVP to get analytic expressions for X[t] and X'[t]. - -- The solution takes one of three forms for 0<=d<1, d=1, and d>1 - - local d = self.d - local f = self.f*(2*pi) -- Hz -> Rad/s - local g = self.g - local p = self.p - local v = self.v - - if d == 1 then -- critically damped - local q = exp(-f*dt) - local w = dt*q - - local c0 = q + w*f - local c2 = q - w*f - local c3 = w*f*f - - for idx = 1, #p do - local o = p[idx] - g[idx] - p[idx] = o*c0 + v[idx]*w + g[idx] - v[idx] = v[idx]*c2 - o*c3 - end - - elseif d < 1 then -- underdamped - local q = exp(-d*f*dt) - local c = sqrt(1 - d*d) - - local i = cos(dt*f*c) - local j = sin(dt*f*c) - - -- Damping ratios approaching 1 can cause division by very small numbers. - -- To mitigate that, group terms around z=j/c and find an approximation for z. - -- Start with the definition of z: - -- z = sin(dt*f*c)/c - -- Substitute a=dt*f: - -- z = sin(a*c)/c - -- Take the Maclaurin expansion of z with respect to c: - -- z = a - (a^3*c^2)/6 + (a^5*c^4)/120 + O(c^6) - -- z ≈ a - (a^3*c^2)/6 + (a^5*c^4)/120 - -- Rewrite in Horner form: - -- z ≈ a + ((a*a)*(c*c)*(c*c)/20 - c*c)*(a*a*a)/6 - - local z - if c > EPS then - z = j/c - else - local a = dt*f - z = a + ((a*a)*(c*c)*(c*c)/20 - c*c)*(a*a*a)/6 - end - - -- Frequencies approaching 0 present a similar problem. - -- We want an approximation for y as f approaches 0, where: - -- y = sin(dt*f*c)/(f*c) - -- Substitute b=dt*c: - -- y = sin(b*c)/b - -- Now reapply the process from z. - - local y - if f*c > EPS then - y = j/(f*c) - else - local b = f*c - y = dt + ((dt*dt)*(b*b)*(b*b)/20 - b*b)*(dt*dt*dt)/6 - end - - for idx = 1, #p do - local o = p[idx] - g[idx] - p[idx] = (o*(i + z*d) + v[idx]*y)*q + g[idx] - v[idx] = (v[idx]*(i - z*d) - o*(z*f))*q - end - - else -- overdamped - local c = sqrt(d*d - 1) - - local r1 = -f*(d + c) - local r2 = -f*(d - c) - - local ec1 = exp(r1*dt) - local ec2 = exp(r2*dt) - - for idx = 1, #p do - local o = p[idx] - g[idx] - local co2 = (v[idx] - o*r1)/(2*f*c) - local co1 = ec1*(o - co2) - - p[idx] = co1 + co2*ec2 + g[idx] - v[idx] = co1*r1 + co2*ec2*r2 - end - end - - return self.typedat.fromIntermediate(self.p) - end -end - -local RotationSpring = {} - -type RotationSpring = typeof(setmetatable({} :: { - d: number, - f: number, - g: CFrame, - p: CFrame, - v: Vector3, -}, RotationSpring)) - -do - RotationSpring.__index = RotationSpring - - function RotationSpring.new(d: number, f: number, p: CFrame, g: CFrame) - return setmetatable( - { - d = d, - f = f, - g = g:Orthonormalize(), - p = p:Orthonormalize(), - v = Vector3.zero - }, - RotationSpring - ) - end - - function RotationSpring.setGoal(self: RotationSpring, value: CFrame) - self.g = value:Orthonormalize() - end - - function RotationSpring.setDampingRatio(self: RotationSpring, dampingRatio: number) - self.d = dampingRatio - end - - function RotationSpring.setFrequency(self: RotationSpring, frequency: number) - self.f = frequency - end - - -- evaluate dot products in high precision - local function dot(v0: Vector3, v1: Vector3) - return v0.X*v1.X + v0.Y*v1.Y + v0.Z*v1.Z - end - - local function areRotationsClose(c0: CFrame, c1: CFrame) - local rx = dot(c0.XVector, c1.XVector) - local ry = dot(c0.YVector, c1.YVector) - local rz = dot(c0.ZVector, c1.ZVector) - local trace = rx + ry + rz - return trace > 1 + 2*cos(SLEEP_ROTATION_DIFF) - end - - local function angleDiff(c0: CFrame, c1: CFrame) - local x = dot(c0.XVector, c1.XVector) - local y = dot(c0.YVector, c1.YVector) - local z = dot(c0.ZVector, c1.ZVector) - local w = x + y + z - 1 - return atan2(sqrt(max(0, 1 - w*w*0.25)), w*0.5) - end - - -- gives approx. 21% accuracy improvement over CFrame.fromAxisAngle near poles - local function fromAxisAngle(axis: Vector3, angle: number) - local c = cos(angle) - local s = sin(angle) - local x, y, z = axis.X, axis.Y, axis.Z - - local mxy = x*y*(1 - c) - local myz = y*z*(1 - c) - local mzx = z*x*(1 - c) - - local rx = Vector3.new(x*x*(1 - c) + c, mxy + z*s, mzx - y*s) - local ry = Vector3.new(mxy - z*s, y*y*(1 - c) + c, myz + x*s) - local rz = Vector3.new(mzx + y*s, myz - x*s, z*z*(1 - c) + c) - - return CFrame.fromMatrix(Vector3.zero, rx, ry, rz):Orthonormalize() - end - - local function rotateAxis(r0: Vector3, c1: CFrame) - local c0 = CFrame.identity - local mag = r0.Magnitude - if mag > 1e-6 then - c0 = fromAxisAngle(r0.Unit, mag) - end - return c0 * c1 - end - - -- axis*angle difference between two cframes - local function axisAngleDiff(c0: CFrame, c1: CFrame) - -- use native axis (stable enough) - local axis = (c0*c1:Inverse()):ToAxisAngle() - - -- use full-precision angle calculation to minimize truncation - local angle = angleDiff(c0, c1) - return axis.Unit*angle - end - - function RotationSpring.canSleep(self: RotationSpring) - local sleepP = areRotationsClose(self.p, self.g) - local sleepV = self.v.Magnitude < SLEEP_ROTATION_VELOCITY - return sleepP and sleepV - end - - function RotationSpring.step(self: RotationSpring, dt: number): CFrame - local d = self.d - local f = self.f*(2*pi) - local g = self.g - local p0 = self.p - local v0 = self.v - - local offset = axisAngleDiff(p0, g) - local decay = exp(-d*f*dt) - - local pt: CFrame - local vt: Vector3 - - if d == 1 then -- critically damped - pt = rotateAxis((offset*(1 + f*dt) + v0*dt)*decay, g) - vt = (v0*(1 - dt*f) - offset*(dt*f*f))*decay - - elseif d < 1 then -- underdamped - local c = sqrt(1 - d*d) - - local i = cos(dt*f*c) - local j = sin(dt*f*c) - - local y = j/(f*c) - local z = j/c - - pt = rotateAxis((offset*(i + z*d) + v0*y)*decay, g) - vt = (v0*(i - z*d) - offset*(z*f))*decay - - else -- overdamped - local c = sqrt(d*d - 1) - - local r1 = -f*(d + c) - local r2 = -f*(d - c) - - local co2 = (v0 - offset*r1)/(2*f*c) - local co1 = offset - co2 - - local e1 = co1*exp(r1*dt) - local e2 = co2*exp(r2*dt) - - pt = rotateAxis(e1 + e2, g) - vt = e1*r1 + e2*r2 - end - - self.p = pt - self.v = vt - - return pt - end -end - --- Defined early to be used by CFrameSpring -local typeMetadata_Vector3 = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value.X, value.Y, value.Z} - end, - - fromIntermediate = function(value: {number}) - return Vector3.new(value[1], value[2], value[3]) - end, -} - --- Encapsulates a CFrame - Separates translation from rotation -local CFrameSpring = {} -do - CFrameSpring.__index = CFrameSpring - - function CFrameSpring.new( - dampingRatio: number, - frequency: number, - valueCurrent: CFrame, - valueGoal: CFrame, - _: any - ) - return setmetatable( - { - rawGoal = valueGoal, - _position = LinearSpring.new(dampingRatio, frequency, valueCurrent.Position, valueGoal.Position, typeMetadata_Vector3), - _rotation = RotationSpring.new(dampingRatio, frequency, valueCurrent.Rotation, valueGoal.Rotation) - }, - CFrameSpring - ) - end - - function CFrameSpring:setGoal(value: CFrame) - self.rawGoal = value - self._position:setGoal(value.Position) - self._rotation:setGoal(value.Rotation) - end - - function CFrameSpring:setDampingRatio(value: number) - self._position.d = value - self._rotation.d = value - end - - function CFrameSpring:setFrequency(value: number) - self._position.f = value - self._rotation.f = value - end - - function CFrameSpring:canSleep() - return self._position:canSleep() and self._rotation:canSleep() - end - - function CFrameSpring:step(dt): CFrame - local p: Vector3 = self._position:step(dt) - local r: CFrame = self._rotation:step(dt) - return r + p - end -end - --- Color conversions -local rgbToLuv -local luvToRgb -do - local function inverseGammaCorrectD65(c) - return c < 0.0404482362771076 and c/12.92 or 0.87941546140213*(c + 0.055)^2.4 - end - - local function gammaCorrectD65(c) - return c < 3.1306684425e-3 and 12.92*c or 1.055*c^(1/2.4) - 0.055 - end - - function rgbToLuv(value: Color3): {number} - -- convert RGB to a variant of cieluv space - local r, g, b = value.R, value.G, value.B - - -- D65 sRGB inverse gamma correction - r = inverseGammaCorrectD65(r) - g = inverseGammaCorrectD65(g) - b = inverseGammaCorrectD65(b) - - -- sRGB -> xyz - local x = 0.9257063972951867*r - 0.8333736323779866*g - 0.09209820666085898*b - local y = 0.2125862307855956*r + 0.71517030370341085*g + 0.0722004986433362*b - local z = 3.6590806972265883*r + 11.4426895800574232*g + 4.1149915024264843*b - - -- xyz -> scaled cieluv - local l = y > 0.008856451679035631 and 116*y^(1/3) - 16 or 903.296296296296*y - - local u, v - if z > 1e-14 then - u = l*x/z - v = l*(9*y/z - 0.46832) - else - u = -0.19783*l - v = -0.46832*l - end - - return {l, u, v} - end - - function luvToRgb(value: {number}): Color3 - -- convert back from modified cieluv to rgb space - local l = value[1] - if l < 0.0197955 then - return Color3.new(0, 0, 0) - end - local u = value[2]/l + 0.19783 - local v = value[3]/l + 0.46832 - - -- cieluv -> xyz - local y = (l + 16)/116 - y = y > 0.206896551724137931 and y*y*y or 0.12841854934601665*y - 0.01771290335807126 - local x = y*u/v - local z = y*((3 - 0.75*u)/v - 5) - - -- xyz -> D65 sRGB - local r = 7.2914074*x - 1.5372080*y - 0.4986286*z - local g = -2.1800940*x + 1.8757561*y + 0.0415175*z - local b = 0.1253477*x - 0.2040211*y + 1.0569959*z - - -- clamp minimum sRGB component - if r < 0 and r < g and r < b then - r, g, b = 0, g - r, b - r - elseif g < 0 and g < b then - r, g, b = r - g, 0, b - g - elseif b < 0 then - r, g, b = r - b, g - b, 0 - end - - -- gamma correction from D65 - -- clamp to avoid undesirable overflow wrapping behavior on certain properties (e.g. BasePart.Color) - return Color3.new( - min(gammaCorrectD65(r), 1), - min(gammaCorrectD65(g), 1), - min(gammaCorrectD65(b), 1) - ) - end -end - --- Type definitions --- Transforms Roblox types into intermediate types, converting --- between spaces as necessary to preserve perceptual linearity -local typeMetadata = { - boolean = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value and 1 or 0} - end, - - fromIntermediate = function(value) - return value[1] >= 0.5 - end, - }, - - number = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value} - end, - - fromIntermediate = function(value) - return value[1] - end, - }, - - NumberRange = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value.Min, value.Max} - end, - - fromIntermediate = function(value) - return NumberRange.new(value[1], value[2]) - end, - }, - - UDim = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value.Scale, value.Offset} - end, - - fromIntermediate = function(value: {number}) - return UDim.new(value[1], round(value[2])) - end, - }, - - UDim2 = { - springType = LinearSpring.new, - - toIntermediate = function(value) - local x = value.X - local y = value.Y - return {x.Scale, x.Offset, y.Scale, y.Offset} - end, - - fromIntermediate = function(value: {number}) - return UDim2.new(value[1], round(value[2]), value[3], round(value[4])) - end, - }, - - Vector2 = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value.X, value.Y} - end, - - fromIntermediate = function(value: {number}) - return Vector2.new(value[1], value[2]) - end, - }, - - Vector3 = typeMetadata_Vector3, - - Color3 = { - springType = LinearSpring.new, - toIntermediate = rgbToLuv, - fromIntermediate = luvToRgb, - }, - - -- Only interpolates start and end keypoints - ColorSequence = { - springType = LinearSpring.new, - - toIntermediate = function(value) - local keypoints = value.Keypoints - - local luv0 = rgbToLuv(keypoints[1].Value) - local luv1 = rgbToLuv(keypoints[#keypoints].Value) - - return { - luv0[1], luv0[2], luv0[3], - luv1[1], luv1[2], luv1[3], - } - end, - - fromIntermediate = function(value: {}) - return ColorSequence.new( - luvToRgb{value[1], value[2], value[3]}, - luvToRgb{value[4], value[5], value[6]} - ) - end, - }, - - NumberSequence = { - springType = LinearSpring.new, - - toIntermediate = function(value) - return {value.Keypoints[1].Value, value.Keypoints[2].Value} - end, - - fromIntermediate = function(value) - return NumberSequence.new({ - NumberSequenceKeypoint.new(0, value[1]), - NumberSequenceKeypoint.new(1, value[2]), - }) - end, - }, - - CFrame = { - springType = CFrameSpring.new, - toIntermediate = error, -- custom (CFrameSpring) - fromIntermediate = error, -- custom (CFrameSpring) - } -} - -type PropertyOverride = { - [string]: { - class: string, - get: (any)->(), - set: (any, any)->(), - } -} - -local PSEUDO_PROPERTIES: PropertyOverride = { - Pivot = { - class = "PVInstance", - get = function(inst: PVInstance) - return inst:GetPivot() - end, - set = function(inst: PVInstance, value: CFrame) - inst:PivotTo(value) - end - }, - Scale = { - class = "Model", - get = function(inst: Model) - return inst:GetScale() - end, - set = function(inst: Model, value: number) - local FLOAT_MANTISSA_MIN = 1.402e-45 - local FLOAT_MANTISSA_MAX = 2^24 - value = math.clamp(value, FLOAT_MANTISSA_MIN, FLOAT_MANTISSA_MAX) - inst:ScaleTo(value) - end - } -} - -local function getProperty(instance: Instance, property: string): any - local override = PSEUDO_PROPERTIES[property] - if override and instance:IsA(override.class) then - return override.get(instance) - else - return (instance :: any)[property] - end -end - -local function setProperty(instance: Instance, property: string, value: unknown) - local override = PSEUDO_PROPERTIES[property] - if override and instance:IsA(override.class) then - override.set(instance, value) - else - (instance :: any)[property] = value - end -end - --- Frame loop -local springStates_other: {[Instance]: {[string]: any}} = {} -- {[instance] = {[property] = spring} -local springStates_render: {[Instance]: {[string]: any}} = {} -- {[instance] = {[property] = spring} -local completedCallbacks: {[Instance]: {()->()}} = {} - -local function processSprings(springStates: typeof(springStates_other), dt: number) - for instance, state in springStates do - for propName, spring in state do - if spring:canSleep() then - state[propName] = nil - setProperty(instance, propName, spring.rawGoal) - else - setProperty(instance, propName, spring:step(dt)) - end - end - - if not next(state) then - springStates[instance] = nil - - -- trigger completed callbacks when all properties finish animating - local callbackList = completedCallbacks[instance] - if callbackList then - -- flush callback list before we run any callbacks in case - -- one of the callbacks recursively adds another callback - completedCallbacks[instance] = nil - - for _, callback in callbackList do - task.spawn(callback) - end - end - end - end -end - -RunService.PreSimulation:Connect(function(dt) - processSprings(springStates_other, dt) -end) - -RunService.PostSimulation:Connect(function(dt) - processSprings(springStates_render, dt) -end) - -local function assertType(argNum: number, fnName: string, expectedType: string, value: unknown) - if not expectedType:find(typeof(value)) then - error(`bad argument #{argNum} to {fnName} ({expectedType} expected, got {typeof(value)})`, 3) - end -end - --- API -local spr = {} - -function spr.target(instance: Instance, dampingRatio: number, frequency: number, properties: {[string]: any}) - if STRICT_RUNTIME_TYPES then - assertType(1, "spr.target", "Instance", instance) - assertType(2, "spr.target", "number", dampingRatio) - assertType(3, "spr.target", "number", frequency) - assertType(4, "spr.target", "table", properties) - end - - if dampingRatio ~= dampingRatio or dampingRatio < 0 then - error(("expected damping ratio >= 0; got %.2f"):format(dampingRatio), 2) - end - - if frequency ~= frequency or frequency < 0 then - error(("expected undamped frequency >= 0; got %.2f"):format(frequency), 2) - end - - local targetRecord = (if instance:IsA("Camera") then springStates_render else springStates_other) :: {[Instance]: {[string]: any}} - - local state = targetRecord[instance] - if not state then - state = {} - targetRecord[instance] = state - end - - for propName, propTarget in properties do - local propValue = getProperty(instance, propName) - - if STRICT_RUNTIME_TYPES and typeof(propTarget) ~= typeof(propValue) then - error(`bad property {propName} to spr.target ({typeof(propValue)} expected, got {typeof(propTarget)})`, 2) - end - - -- Special case infinite frequency for an instantaneous change - if frequency == math.huge then - setProperty(instance, propName, propTarget) - state[propName] = nil - continue - end - - local spring = state[propName] - if not spring then - local md = typeMetadata[typeof(propTarget)] - if not md then - error("unsupported type: " .. typeof(propTarget), 2) - end - - spring = md.springType(dampingRatio, frequency, propValue, propTarget, md) - state[propName] = spring - end - - spring:setGoal(propTarget) - spring:setDampingRatio(dampingRatio) - spring:setFrequency(frequency) - end - - if not next(state) then - targetRecord[instance] = nil - end -end - -function spr.stop(instance: Instance, property: string?) - if STRICT_RUNTIME_TYPES then - assertType(1, "spr.stop", "Instance", instance) - assertType(2, "spr.stop", "string|nil", property) - end - - if property then - local state = springStates_other[instance] or springStates_render[instance] - if state then - state[property] = nil - end - else - springStates_other[instance] = nil - springStates_render[instance] = nil - end -end - -function spr.completed(instance: Instance, callback: ()->()) - if STRICT_RUNTIME_TYPES then - assertType(1, "spr.completed", "Instance", instance) - assertType(2, "spr.completed", "function", callback) - end - - local callbackList = completedCallbacks[instance] - if callbackList then - table.insert(callbackList, callback) - else - completedCallbacks[instance] = {callback} - end -end - -return table.freeze(spr) diff --git a/src/Spring.luau b/src/Spring.luau index d1efb91..c2c79bd 100644 --- a/src/Spring.luau +++ b/src/Spring.luau @@ -1,10 +1,9 @@ -- Variables local Root = script.Parent local Debugger = require(Root.Parent.roblox_packages.debugger) +local Spr = require(Root.Parent.roblox_packages.spr) local Types = require(Root.Types) -local Spr = require(script.Parent.Spr) - local Class = {} local Animatable = { "boolean", @@ -40,7 +39,7 @@ end function Class.Stop(self: Types.Spring) if self._Instances then -- Iterate through all instances to make sure if the spring is used on - -- multiple objects, they are all stopped + -- multiple objects, they are all stopped for _, instance in self._Instances do Spr.stop(instance) end diff --git a/src/State.luau b/src/State.luau index 3cfa904..a571b78 100644 --- a/src/State.luau +++ b/src/State.luau @@ -22,21 +22,21 @@ end [Open Documentation](https://lumin-org.github.io/ui/api/state/#set) ]=] function Class.Set(self: Types.State, newValue: T): T - Debugger.Assert(type(newValue) ~= "table", "InvalidType", "any", "table") - + Debugger.Assert(type(newValue) ~= "table", "InvalidType", "any", "table") + local OldValue = self._Value - -- Don't waste resources is new value is the same as old + -- Don't waste resources is new value is the same as old if self._Value == newValue then return newValue end - + self._Value = newValue - -- Run all listeners when the value is changed + -- Run all listeners when the value is changed for _, fn in self._Listeners do task.spawn(fn, newValue, OldValue) - end + end return newValue end @@ -57,8 +57,8 @@ function Class.Listen(self: Types.State, listener: (new: any, old: any) -> ()): end function Class._Bind(self: Types.State, prop: string, instance: Instance) - (instance :: any)[prop] = self._Value -- Set the prop initially to the current value - ;(self :: any):Listen(function(newValue) + (instance :: any)[prop] = self._Value; -- Set the prop initially to the current value + (self :: any):Listen(function(newValue) (instance :: any)[prop] = newValue -- Change the prop to the new value when the state is changed end) end @@ -81,11 +81,11 @@ end [Open Documentation](https://lumin-org.github.io/ui/api/#state) ]=] return function(initial: any): Types.StateExport - Debugger.Assert(type(initial) ~= "table", "InvalidType", "any", "table") + Debugger.Assert(type(initial) ~= "table", "InvalidType", "any", "table") local self = setmetatable({}, { __index = Class }) - self._Type = "State" + self._Type = "State" self._Value = initial self._Listeners = {} diff --git a/src/Tags.luau b/src/Tags.luau index 22e580c..683241c 100644 --- a/src/Tags.luau +++ b/src/Tags.luau @@ -11,13 +11,13 @@ local Action = require(Root.Action) [Open Documentation](https://lumin-org.github.io/ui/api/keys/#tag) ]=] return function(names: string): Types.Action - if not Action.List["Tags"] then - Action.New("Tags", function(instance: Instance) - local TagsList = names:split(" ") -- Split tag list for multiple tags - for _, tag in TagsList do - instance:AddTag(tag) - end - end) - end + if not Action.List["Tags"] then + Action.New("Tags", function(instance: Instance) + local TagsList = names:split(" ") -- Split tag list for multiple tags + for _, tag in TagsList do + instance:AddTag(tag) + end + end) + end return Action.List["Tags"] end diff --git a/src/Types.luau b/src/Types.luau index 015728a..1a67376 100644 --- a/src/Types.luau +++ b/src/Types.luau @@ -14,7 +14,7 @@ export type Animatable = export type Action = (instance: Instance, ...any) -> () export type Constructor = { - _Type: string, + _Type: string, _Bind: (self: T, prop: string, instance: Instance) -> (), Get: (self: T) -> U, Destroy: (self: T) -> (), @@ -27,7 +27,7 @@ export type StateExport = { export type State = typeof(setmetatable( {} :: { - _Type: "State", + _Type: "State", _Value: any, _Listeners: { (newValue: any, oldValue: any) -> () }, }, @@ -40,11 +40,11 @@ export type SpringExport = { export type Spring = typeof(setmetatable( {} :: { - _Type: "Spring", + _Type: "Spring", _Goal: StateExport, _Damping: number, _Frequency: number, - _Instances: {Instance}, + _Instances: { Instance }, }, {} :: { __index: SpringExport } )) @@ -53,10 +53,10 @@ export type ComputedExport = Constructor export type Computed = typeof(setmetatable( {} :: { - _Type: "Computed", + _Type: "Computed", _Processor: (use: (value: State | any) -> ()) -> (), _Value: any, - _Dependencies: { State | Spring }, + _Dependencies: { State | Spring }, _Instances: { [string]: Instance }?, }, {} :: { __index: ComputedExport } diff --git a/src/init.luau b/src/init.luau index 6a7c9db..729de61 100644 --- a/src/init.luau +++ b/src/init.luau @@ -12,19 +12,19 @@ export type Animatable = Types.Animatable -- Module Debugger.SetMetadata({ - PackageURL = "https://github.com/lumin-org/ui", - PackageName = "UI", + PackageURL = "https://github.com/lumin-org/ui", + PackageName = "UI", }) return table.freeze({ New = require(script.New), - Update = require(script.Update), + Update = require(script.Update), State = require(script.State), - Computed = require(script.Computed), + Computed = require(script.Computed), Spring = require(script.Spring), - Action = require(script.Action).New, - Event = require(script.Event), - Changed = require(script.Change), - Tags = require(script.Tags), - Clean = require(script.Clean), + Action = require(script.Action).New, + Event = require(script.Event), + Changed = require(script.Change), + Tags = require(script.Tags), + Clean = require(script.Clean), }) diff --git a/standalone.project.json b/standalone.project.json index 957486a..bde054b 100644 --- a/standalone.project.json +++ b/standalone.project.json @@ -1,9 +1,9 @@ { "name": "ui", "tree": { - "standalone": { + "src": { "$className": "Folder", - "src": { + "init": { "$path": "src" }, "roblox_packages": {