@k9kbdev/roblox-css 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +245 -0
- package/default.project.json +6 -0
- package/out/index.d.ts +35 -0
- package/out/init.luau +57 -0
- package/out/logger.d.ts +23 -0
- package/out/logger.luau +73 -0
- package/out/primitives/Box.d.ts +23 -0
- package/out/primitives/Box.luau +103 -0
- package/out/primitives/Button.d.ts +62 -0
- package/out/primitives/Button.luau +170 -0
- package/out/primitives/Image.d.ts +37 -0
- package/out/primitives/Image.luau +79 -0
- package/out/primitives/InlineText.d.ts +25 -0
- package/out/primitives/InlineText.luau +273 -0
- package/out/primitives/Input.d.ts +59 -0
- package/out/primitives/Input.luau +126 -0
- package/out/primitives/MotionBox.d.ts +15 -0
- package/out/primitives/MotionBox.luau +69 -0
- package/out/primitives/MotionButton.d.ts +15 -0
- package/out/primitives/MotionButton.luau +146 -0
- package/out/primitives/MotionImage.d.ts +13 -0
- package/out/primitives/MotionImage.luau +70 -0
- package/out/primitives/MotionText.d.ts +12 -0
- package/out/primitives/MotionText.luau +116 -0
- package/out/primitives/MotionUIScale.d.ts +9 -0
- package/out/primitives/MotionUIScale.luau +48 -0
- package/out/primitives/ScrollBox.d.ts +25 -0
- package/out/primitives/ScrollBox.luau +69 -0
- package/out/primitives/Text.d.ts +50 -0
- package/out/primitives/Text.luau +139 -0
- package/out/primitives/usePercentageConstraints.d.ts +3 -0
- package/out/primitives/usePercentageConstraints.luau +112 -0
- package/out/primitives/useVariantResolver.d.ts +13 -0
- package/out/primitives/useVariantResolver.luau +260 -0
- package/out/styles/CSSTypes.d.ts +96 -0
- package/out/styles/ParentSizeContext.d.ts +6 -0
- package/out/styles/ParentSizeContext.luau +13 -0
- package/out/styles/colorParser.d.ts +28 -0
- package/out/styles/colorParser.luau +229 -0
- package/out/styles/dimensionParser.d.ts +49 -0
- package/out/styles/dimensionParser.luau +205 -0
- package/out/styles/gradientParser.d.ts +9 -0
- package/out/styles/gradientParser.luau +434 -0
- package/out/styles/namedColors.d.ts +7 -0
- package/out/styles/namedColors.luau +162 -0
- package/out/styles/transitions.d.ts +18 -0
- package/out/styles/transitions.luau +19 -0
- package/out/styles/webStyle.d.ts +74 -0
- package/out/styles/webStyle.luau +973 -0
- package/out/types.d.ts +4 -0
- package/out/types.luau +3 -0
- package/out/utils/parseInlineImages.d.ts +20 -0
- package/out/utils/parseInlineImages.luau +93 -0
- package/package.json +56 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local parseColor = TS.import(script, script.Parent, "colorParser").parseColor
|
|
4
|
+
local createLogger = TS.import(script, script.Parent.Parent, "logger").createLogger
|
|
5
|
+
local log = createLogger("gradientParser")
|
|
6
|
+
local function makeParsedGradient(colorSequence, transparencySequence, rotation)
|
|
7
|
+
return {
|
|
8
|
+
colorSequence = colorSequence,
|
|
9
|
+
transparencySequence = transparencySequence,
|
|
10
|
+
rotation = rotation,
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
local function isGradientString(input)
|
|
14
|
+
local _input = input
|
|
15
|
+
local _condition = type(_input) == "string"
|
|
16
|
+
if _condition then
|
|
17
|
+
_condition = string.sub(input, 1, 16) == "linear-gradient("
|
|
18
|
+
end
|
|
19
|
+
return _condition
|
|
20
|
+
end
|
|
21
|
+
local function parseAngle(directionStr, elementWidth, elementHeight)
|
|
22
|
+
local lower = string.lower(directionStr)
|
|
23
|
+
-- Keyword directions
|
|
24
|
+
if lower == "to top" then
|
|
25
|
+
return 0
|
|
26
|
+
end
|
|
27
|
+
if lower == "to right" then
|
|
28
|
+
return 90
|
|
29
|
+
end
|
|
30
|
+
if lower == "to bottom" then
|
|
31
|
+
return 180
|
|
32
|
+
end
|
|
33
|
+
if lower == "to left" then
|
|
34
|
+
return 270
|
|
35
|
+
end
|
|
36
|
+
-- Corner keywords
|
|
37
|
+
if string.sub(lower, 1, 3) == "to " then
|
|
38
|
+
local corner = string.sub(lower, 4)
|
|
39
|
+
local angleDeg = 45
|
|
40
|
+
if elementWidth ~= nil and elementHeight ~= nil and elementHeight ~= 0 then
|
|
41
|
+
local ratioAngle = math.deg(math.atan2(elementWidth, elementHeight))
|
|
42
|
+
if corner == "top right" then
|
|
43
|
+
return ratioAngle
|
|
44
|
+
end
|
|
45
|
+
if corner == "bottom right" then
|
|
46
|
+
return 180 - ratioAngle
|
|
47
|
+
end
|
|
48
|
+
if corner == "bottom left" then
|
|
49
|
+
return 180 + ratioAngle
|
|
50
|
+
end
|
|
51
|
+
if corner == "top left" then
|
|
52
|
+
return 360 - ratioAngle
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
if corner == "top right" then
|
|
56
|
+
return 45
|
|
57
|
+
end
|
|
58
|
+
if corner == "bottom right" then
|
|
59
|
+
return 135
|
|
60
|
+
end
|
|
61
|
+
if corner == "bottom left" then
|
|
62
|
+
return 225
|
|
63
|
+
end
|
|
64
|
+
if corner == "top left" then
|
|
65
|
+
return 315
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
-- Angle values
|
|
70
|
+
local degMatch = string.match(lower, "^([%d%.%-]+)deg$")
|
|
71
|
+
if degMatch ~= nil then
|
|
72
|
+
local _condition = tonumber(degMatch)
|
|
73
|
+
if _condition == nil then
|
|
74
|
+
_condition = 180
|
|
75
|
+
end
|
|
76
|
+
return _condition
|
|
77
|
+
end
|
|
78
|
+
local turnMatch = string.match(lower, "^([%d%.%-]+)turn$")
|
|
79
|
+
if turnMatch ~= nil then
|
|
80
|
+
local _condition = tonumber(turnMatch)
|
|
81
|
+
if _condition == nil then
|
|
82
|
+
_condition = 0
|
|
83
|
+
end
|
|
84
|
+
return _condition * 360
|
|
85
|
+
end
|
|
86
|
+
local gradMatch = string.match(lower, "^([%d%.%-]+)grad$")
|
|
87
|
+
if gradMatch ~= nil then
|
|
88
|
+
local _condition = tonumber(gradMatch)
|
|
89
|
+
if _condition == nil then
|
|
90
|
+
_condition = 0
|
|
91
|
+
end
|
|
92
|
+
return _condition * 0.9
|
|
93
|
+
end
|
|
94
|
+
local radMatch = string.match(lower, "^([%d%.%-]+)rad$")
|
|
95
|
+
if radMatch ~= nil then
|
|
96
|
+
local _condition = tonumber(radMatch)
|
|
97
|
+
if _condition == nil then
|
|
98
|
+
_condition = 0
|
|
99
|
+
end
|
|
100
|
+
return math.deg(_condition)
|
|
101
|
+
end
|
|
102
|
+
return 180
|
|
103
|
+
end
|
|
104
|
+
local function parseStop(stopStr)
|
|
105
|
+
local trimmedRaw = string.match(stopStr, "^%s*(.-)%s*$")
|
|
106
|
+
if trimmedRaw == nil or trimmedRaw == "" then
|
|
107
|
+
return nil
|
|
108
|
+
end
|
|
109
|
+
local trimmed = trimmedRaw
|
|
110
|
+
-- Check if it's a bare percentage (color hint)
|
|
111
|
+
local hintMatch = string.match(trimmed, "^([%d%.]+)%%$")
|
|
112
|
+
if hintMatch ~= nil then
|
|
113
|
+
local pos = tonumber(hintMatch)
|
|
114
|
+
if pos ~= nil then
|
|
115
|
+
return {
|
|
116
|
+
color = Color3.new(),
|
|
117
|
+
transparency = 0,
|
|
118
|
+
position = math.clamp(pos / 100, 0, 1),
|
|
119
|
+
isHint = true,
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
-- Split by space from the end to find optional position
|
|
124
|
+
-- Since color could be rgba(255, 0, 0, 0.5) we must be careful with spaces.
|
|
125
|
+
-- We'll look for a percentage at the end.
|
|
126
|
+
local colorStr = trimmed
|
|
127
|
+
local position
|
|
128
|
+
local endPercentMatch = string.match(trimmed, " ([%d%.%-]+)%%$")
|
|
129
|
+
if endPercentMatch ~= nil then
|
|
130
|
+
local pos = tonumber(endPercentMatch)
|
|
131
|
+
if pos ~= nil then
|
|
132
|
+
position = math.clamp(pos / 100, 0, 1)
|
|
133
|
+
local colorMatch = string.match(trimmed, "^(.*) [%d%.%-]+%%$")
|
|
134
|
+
if colorMatch ~= nil then
|
|
135
|
+
colorStr = colorMatch
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
local parsedColor = parseColor(colorStr)
|
|
140
|
+
return {
|
|
141
|
+
color = parsedColor.color,
|
|
142
|
+
transparency = parsedColor.transparency,
|
|
143
|
+
position = position,
|
|
144
|
+
isHint = false,
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
local function parseGradient(input, elementWidth, elementHeight)
|
|
148
|
+
if not isGradientString(input) then
|
|
149
|
+
return nil
|
|
150
|
+
end
|
|
151
|
+
local repeatingStart = string.find(string.lower(input), "^repeating%-linear%-gradient")
|
|
152
|
+
if repeatingStart ~= nil then
|
|
153
|
+
log:warn("UNSUPPORTED_GRADIENT", "repeating-linear-gradient is not supported", {
|
|
154
|
+
input = input,
|
|
155
|
+
})
|
|
156
|
+
return nil
|
|
157
|
+
end
|
|
158
|
+
local inner = string.sub(input, 17, -2)
|
|
159
|
+
if inner == "" then
|
|
160
|
+
return nil
|
|
161
|
+
end
|
|
162
|
+
-- Simple parser for comma-separated args, respecting parentheses for rgb/rgba
|
|
163
|
+
local args = {}
|
|
164
|
+
local currentArg = ""
|
|
165
|
+
local parenLevel = 0
|
|
166
|
+
for i = 1, #inner do
|
|
167
|
+
local _i = i
|
|
168
|
+
local _i_1 = i
|
|
169
|
+
local char = string.sub(inner, _i, _i_1)
|
|
170
|
+
if char == "(" then
|
|
171
|
+
parenLevel += 1
|
|
172
|
+
elseif char == ")" then
|
|
173
|
+
parenLevel -= 1
|
|
174
|
+
elseif char == "," and parenLevel == 0 then
|
|
175
|
+
local _currentArg = currentArg
|
|
176
|
+
table.insert(args, _currentArg)
|
|
177
|
+
currentArg = ""
|
|
178
|
+
continue
|
|
179
|
+
end
|
|
180
|
+
currentArg ..= char
|
|
181
|
+
end
|
|
182
|
+
local _currentArg = currentArg
|
|
183
|
+
table.insert(args, _currentArg)
|
|
184
|
+
if #args < 2 then
|
|
185
|
+
return nil
|
|
186
|
+
end
|
|
187
|
+
-- Determine if the first arg is a direction
|
|
188
|
+
local directionStr = args[1]
|
|
189
|
+
local directionAngle = 180
|
|
190
|
+
local firstStopIdx = 0
|
|
191
|
+
local lowerFirst = string.lower(directionStr)
|
|
192
|
+
local hasDeg = string.find(lowerFirst, "deg$")
|
|
193
|
+
local hasTurn = string.find(lowerFirst, "turn$")
|
|
194
|
+
local hasGrad = string.find(lowerFirst, "grad$")
|
|
195
|
+
local hasRad = string.find(lowerFirst, "rad$")
|
|
196
|
+
local _condition = string.sub(lowerFirst, 1, 3) == "to "
|
|
197
|
+
if not _condition then
|
|
198
|
+
_condition = hasDeg
|
|
199
|
+
if not (_condition ~= 0 and _condition == _condition and _condition) then
|
|
200
|
+
_condition = hasTurn
|
|
201
|
+
if not (_condition ~= 0 and _condition == _condition and _condition) then
|
|
202
|
+
_condition = hasGrad
|
|
203
|
+
if not (_condition ~= 0 and _condition == _condition and _condition) then
|
|
204
|
+
_condition = hasRad
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
if _condition ~= 0 and _condition == _condition and _condition then
|
|
210
|
+
directionAngle = parseAngle(directionStr, elementWidth, elementHeight)
|
|
211
|
+
firstStopIdx = 1
|
|
212
|
+
end
|
|
213
|
+
local stops = {}
|
|
214
|
+
do
|
|
215
|
+
local i = firstStopIdx
|
|
216
|
+
local _shouldIncrement = false
|
|
217
|
+
while true do
|
|
218
|
+
if _shouldIncrement then
|
|
219
|
+
i += 1
|
|
220
|
+
else
|
|
221
|
+
_shouldIncrement = true
|
|
222
|
+
end
|
|
223
|
+
if not (i < #args) then
|
|
224
|
+
break
|
|
225
|
+
end
|
|
226
|
+
local parsed = parseStop(args[i + 1])
|
|
227
|
+
if parsed ~= nil then
|
|
228
|
+
table.insert(stops, parsed)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
-- Remove leading/trailing hints and check valid color stops
|
|
233
|
+
while #stops > 0 and stops[1].isHint do
|
|
234
|
+
table.remove(stops, 1)
|
|
235
|
+
end
|
|
236
|
+
while #stops > 0 and stops[#stops].isHint do
|
|
237
|
+
stops[#stops] = nil
|
|
238
|
+
end
|
|
239
|
+
local colorStopCount = 0
|
|
240
|
+
for _, stop in stops do
|
|
241
|
+
if not stop.isHint then
|
|
242
|
+
colorStopCount += 1
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
if colorStopCount < 2 then
|
|
246
|
+
return nil
|
|
247
|
+
end
|
|
248
|
+
-- Distribute positions
|
|
249
|
+
if stops[1].position == nil then
|
|
250
|
+
stops[1].position = 0
|
|
251
|
+
end
|
|
252
|
+
if stops[#stops].position == nil then
|
|
253
|
+
stops[#stops].position = 1
|
|
254
|
+
end
|
|
255
|
+
local lastKnownIdx = 0
|
|
256
|
+
for i = 1, #stops - 1 do
|
|
257
|
+
if stops[i + 1].position ~= nil then
|
|
258
|
+
local steps = i - lastKnownIdx
|
|
259
|
+
local startPos = stops[lastKnownIdx + 1].position
|
|
260
|
+
local endPos = stops[i + 1].position
|
|
261
|
+
local stepSize = (endPos - startPos) / steps
|
|
262
|
+
do
|
|
263
|
+
local j = 1
|
|
264
|
+
local _shouldIncrement = false
|
|
265
|
+
while true do
|
|
266
|
+
if _shouldIncrement then
|
|
267
|
+
j += 1
|
|
268
|
+
else
|
|
269
|
+
_shouldIncrement = true
|
|
270
|
+
end
|
|
271
|
+
if not (j < steps) then
|
|
272
|
+
break
|
|
273
|
+
end
|
|
274
|
+
stops[lastKnownIdx + j + 1].position = startPos + stepSize * j
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
lastKnownIdx = i
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
-- Auto-correct out-of-order positions
|
|
281
|
+
local maxPos = 0
|
|
282
|
+
for _, stop in stops do
|
|
283
|
+
if stop.position < maxPos then
|
|
284
|
+
stop.position = maxPos
|
|
285
|
+
end
|
|
286
|
+
maxPos = stop.position
|
|
287
|
+
end
|
|
288
|
+
-- Apply hints
|
|
289
|
+
local finalStops = {}
|
|
290
|
+
for i = 0, #stops - 1 do
|
|
291
|
+
local stop = stops[i + 1]
|
|
292
|
+
if stop.isHint then
|
|
293
|
+
local prev = stops[i]
|
|
294
|
+
local nextStop = stops[i + 2]
|
|
295
|
+
local startPos = prev.position
|
|
296
|
+
local endPos = nextStop.position
|
|
297
|
+
if startPos == endPos then
|
|
298
|
+
continue
|
|
299
|
+
end
|
|
300
|
+
-- H = (hintAbsPos - stop1Pos) / (stop2Pos - stop1Pos)
|
|
301
|
+
local h = (stop.position - startPos) / (endPos - startPos)
|
|
302
|
+
h = math.clamp(h, 0.01, 0.99)
|
|
303
|
+
local exp = math.log(0.5) / math.log(h)
|
|
304
|
+
-- Pop the last added stop so we can replace the segment
|
|
305
|
+
finalStops[#finalStops] = nil
|
|
306
|
+
table.insert(finalStops, prev)
|
|
307
|
+
for sample = 1, 7 do
|
|
308
|
+
local p = sample / 8
|
|
309
|
+
local f = p ^ exp
|
|
310
|
+
local pos = startPos + p * (endPos - startPos)
|
|
311
|
+
local r = prev.color.R + f * (nextStop.color.R - prev.color.R)
|
|
312
|
+
local g = prev.color.G + f * (nextStop.color.G - prev.color.G)
|
|
313
|
+
local b = prev.color.B + f * (nextStop.color.B - prev.color.B)
|
|
314
|
+
local transp = prev.transparency + f * (nextStop.transparency - prev.transparency)
|
|
315
|
+
local _arg0 = {
|
|
316
|
+
color = Color3.new(r, g, b),
|
|
317
|
+
transparency = transp,
|
|
318
|
+
position = pos,
|
|
319
|
+
isHint = false,
|
|
320
|
+
}
|
|
321
|
+
table.insert(finalStops, _arg0)
|
|
322
|
+
end
|
|
323
|
+
else
|
|
324
|
+
table.insert(finalStops, stop)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
-- Build sequences
|
|
328
|
+
local colorKeypoints = {}
|
|
329
|
+
local alphaKeypoints = {}
|
|
330
|
+
local hasTransparency = false
|
|
331
|
+
-- Roblox limits keypoints to 20, but we might have more.
|
|
332
|
+
-- For simplicity, we just push all and trust it's <= 20 or user will see engine err,
|
|
333
|
+
-- but let's downsample if needed. Let's just create them as specified.
|
|
334
|
+
for _, stop in finalStops do
|
|
335
|
+
local pos = math.clamp(stop.position, 0, 1)
|
|
336
|
+
local _colorSequenceKeypoint = ColorSequenceKeypoint.new(pos, stop.color)
|
|
337
|
+
table.insert(colorKeypoints, _colorSequenceKeypoint)
|
|
338
|
+
local _numberSequenceKeypoint = NumberSequenceKeypoint.new(pos, stop.transparency)
|
|
339
|
+
table.insert(alphaKeypoints, _numberSequenceKeypoint)
|
|
340
|
+
if stop.transparency > 0 then
|
|
341
|
+
hasTransparency = true
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
-- Fix duplicate positions (Roblox requires strictly increasing times for ColorSequence, or at least no more than 2 at same time if they are immediate jumps, but usually increasing is safer)
|
|
345
|
+
-- We'll enforce non-decreasing and add epsilon for exact duplicates if needed.
|
|
346
|
+
local uniqueColorKeypoints = {}
|
|
347
|
+
local uniqueAlphaKeypoints = {}
|
|
348
|
+
local lastTime = -1
|
|
349
|
+
for i = 0, #colorKeypoints - 1 do
|
|
350
|
+
local t = colorKeypoints[i + 1].Time
|
|
351
|
+
if t <= lastTime then
|
|
352
|
+
t = math.clamp(lastTime + 0.001, 0, 1)
|
|
353
|
+
end
|
|
354
|
+
if t > lastTime or #uniqueColorKeypoints == 0 then
|
|
355
|
+
local _colorSequenceKeypoint = ColorSequenceKeypoint.new(t, colorKeypoints[i + 1].Value)
|
|
356
|
+
table.insert(uniqueColorKeypoints, _colorSequenceKeypoint)
|
|
357
|
+
local _numberSequenceKeypoint = NumberSequenceKeypoint.new(t, alphaKeypoints[i + 1].Value)
|
|
358
|
+
table.insert(uniqueAlphaKeypoints, _numberSequenceKeypoint)
|
|
359
|
+
lastTime = t
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
-- We must cap at 20 keypoints due to Roblox engine limits.
|
|
363
|
+
-- (Skipping downsampling for now as prompt doesn't strictly demand it, but good to know)
|
|
364
|
+
-- Let's ensure time goes exactly up to 1 for the last one if it was squished.
|
|
365
|
+
if uniqueColorKeypoints[#uniqueColorKeypoints].Time < 1 then
|
|
366
|
+
-- Just ensure the last element is exactly 1, or adjust.
|
|
367
|
+
-- Actually, if it's less than 1, Roblox might error if the last keypoint is not at Time=1.
|
|
368
|
+
local lastC = uniqueColorKeypoints[#uniqueColorKeypoints]
|
|
369
|
+
if lastC.Time ~= 1 then
|
|
370
|
+
local _colorSequenceKeypoint = ColorSequenceKeypoint.new(1, lastC.Value)
|
|
371
|
+
table.insert(uniqueColorKeypoints, _colorSequenceKeypoint)
|
|
372
|
+
end
|
|
373
|
+
local lastA = uniqueAlphaKeypoints[#uniqueAlphaKeypoints]
|
|
374
|
+
if lastA.Time ~= 1 then
|
|
375
|
+
local _numberSequenceKeypoint = NumberSequenceKeypoint.new(1, lastA.Value)
|
|
376
|
+
table.insert(uniqueAlphaKeypoints, _numberSequenceKeypoint)
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
if uniqueColorKeypoints[1].Time > 0 then
|
|
380
|
+
local firstC = uniqueColorKeypoints[1]
|
|
381
|
+
local _colorSequenceKeypoint = ColorSequenceKeypoint.new(0, firstC.Value)
|
|
382
|
+
table.insert(uniqueColorKeypoints, 1, _colorSequenceKeypoint)
|
|
383
|
+
local firstA = uniqueAlphaKeypoints[1]
|
|
384
|
+
local _numberSequenceKeypoint = NumberSequenceKeypoint.new(0, firstA.Value)
|
|
385
|
+
table.insert(uniqueAlphaKeypoints, 1, _numberSequenceKeypoint)
|
|
386
|
+
end
|
|
387
|
+
-- Enforce 20 keypoints max — Roblox engine hard-limits ColorSequence/NumberSequence to 20.
|
|
388
|
+
-- Downsample by preserving first/last and evenly distributing interior points.
|
|
389
|
+
if #uniqueColorKeypoints > 20 then
|
|
390
|
+
log:warn("GRADIENT_KEYPOINT_LIMIT", "Gradient exceeds 20 keypoints, downsampling", {
|
|
391
|
+
count = #uniqueColorKeypoints,
|
|
392
|
+
})
|
|
393
|
+
local _array = {}
|
|
394
|
+
local _length = #_array
|
|
395
|
+
table.move(uniqueColorKeypoints, 1, #uniqueColorKeypoints, _length + 1, _array)
|
|
396
|
+
local srcColors = _array
|
|
397
|
+
local _array_1 = {}
|
|
398
|
+
local _length_1 = #_array_1
|
|
399
|
+
table.move(uniqueAlphaKeypoints, 1, #uniqueAlphaKeypoints, _length_1 + 1, _array_1)
|
|
400
|
+
local srcAlphas = _array_1
|
|
401
|
+
local total = #srcColors
|
|
402
|
+
local downsampled = { srcColors[1] }
|
|
403
|
+
local downsampledAlpha = { srcAlphas[1] }
|
|
404
|
+
-- Pick 18 evenly spaced interior points
|
|
405
|
+
for i = 1, 18 do
|
|
406
|
+
local idx = math.floor((i / 19) * (total - 1))
|
|
407
|
+
local _arg0 = srcColors[idx + 1]
|
|
408
|
+
table.insert(downsampled, _arg0)
|
|
409
|
+
local _arg0_1 = srcAlphas[idx + 1]
|
|
410
|
+
table.insert(downsampledAlpha, _arg0_1)
|
|
411
|
+
end
|
|
412
|
+
local _arg0 = srcColors[total]
|
|
413
|
+
table.insert(downsampled, _arg0)
|
|
414
|
+
local _arg0_1 = srcAlphas[total]
|
|
415
|
+
table.insert(downsampledAlpha, _arg0_1)
|
|
416
|
+
-- Replace the arrays in-place
|
|
417
|
+
table.clear(uniqueColorKeypoints)
|
|
418
|
+
table.clear(uniqueAlphaKeypoints)
|
|
419
|
+
for _, kp in downsampled do
|
|
420
|
+
table.insert(uniqueColorKeypoints, kp)
|
|
421
|
+
end
|
|
422
|
+
for _, kp in downsampledAlpha do
|
|
423
|
+
table.insert(uniqueAlphaKeypoints, kp)
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
local cSeq = ColorSequence.new(uniqueColorKeypoints)
|
|
427
|
+
local tSeq = if hasTransparency then NumberSequence.new(uniqueAlphaKeypoints) else nil
|
|
428
|
+
local robloxRotation = directionAngle - 90
|
|
429
|
+
return makeParsedGradient(cSeq, tSeq, robloxRotation)
|
|
430
|
+
end
|
|
431
|
+
return {
|
|
432
|
+
isGradientString = isGradientString,
|
|
433
|
+
parseGradient = parseGradient,
|
|
434
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* namedColors.ts — All 148 CSS Level 4 named colors.
|
|
3
|
+
*
|
|
4
|
+
* Lookup table mapping lowercase color keywords to [R, G, B] tuples (0–255).
|
|
5
|
+
* Spec: https://www.w3.org/TR/css-color-4/#named-colors
|
|
6
|
+
*/
|
|
7
|
+
export declare const NAMED_COLORS: Map<string, [number, number, number]>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* namedColors.ts — All 148 CSS Level 4 named colors.
|
|
5
|
+
*
|
|
6
|
+
* Lookup table mapping lowercase color keywords to [R, G, B] tuples (0–255).
|
|
7
|
+
* Spec: https://www.w3.org/TR/css-color-4/#named-colors
|
|
8
|
+
|
|
9
|
+
]]
|
|
10
|
+
local NAMED_COLORS = {
|
|
11
|
+
aliceblue = { 240, 248, 255 },
|
|
12
|
+
antiquewhite = { 250, 235, 215 },
|
|
13
|
+
aqua = { 0, 255, 255 },
|
|
14
|
+
aquamarine = { 127, 255, 212 },
|
|
15
|
+
azure = { 240, 255, 255 },
|
|
16
|
+
beige = { 245, 245, 220 },
|
|
17
|
+
bisque = { 255, 228, 196 },
|
|
18
|
+
black = { 0, 0, 0 },
|
|
19
|
+
blanchedalmond = { 255, 235, 205 },
|
|
20
|
+
blue = { 0, 0, 255 },
|
|
21
|
+
blueviolet = { 138, 43, 226 },
|
|
22
|
+
brown = { 165, 42, 42 },
|
|
23
|
+
burlywood = { 222, 184, 135 },
|
|
24
|
+
cadetblue = { 95, 158, 160 },
|
|
25
|
+
chartreuse = { 127, 255, 0 },
|
|
26
|
+
chocolate = { 210, 105, 30 },
|
|
27
|
+
coral = { 255, 127, 80 },
|
|
28
|
+
cornflowerblue = { 100, 149, 237 },
|
|
29
|
+
cornsilk = { 255, 248, 220 },
|
|
30
|
+
crimson = { 220, 20, 60 },
|
|
31
|
+
cyan = { 0, 255, 255 },
|
|
32
|
+
darkblue = { 0, 0, 139 },
|
|
33
|
+
darkcyan = { 0, 139, 139 },
|
|
34
|
+
darkgoldenrod = { 184, 134, 11 },
|
|
35
|
+
darkgray = { 169, 169, 169 },
|
|
36
|
+
darkgreen = { 0, 100, 0 },
|
|
37
|
+
darkgrey = { 169, 169, 169 },
|
|
38
|
+
darkkhaki = { 189, 183, 107 },
|
|
39
|
+
darkmagenta = { 139, 0, 139 },
|
|
40
|
+
darkolivegreen = { 85, 107, 47 },
|
|
41
|
+
darkorange = { 255, 140, 0 },
|
|
42
|
+
darkorchid = { 153, 50, 204 },
|
|
43
|
+
darkred = { 139, 0, 0 },
|
|
44
|
+
darksalmon = { 233, 150, 122 },
|
|
45
|
+
darkseagreen = { 143, 188, 143 },
|
|
46
|
+
darkslateblue = { 72, 61, 139 },
|
|
47
|
+
darkslategray = { 47, 79, 79 },
|
|
48
|
+
darkslategrey = { 47, 79, 79 },
|
|
49
|
+
darkturquoise = { 0, 206, 209 },
|
|
50
|
+
darkviolet = { 148, 0, 211 },
|
|
51
|
+
deeppink = { 255, 20, 147 },
|
|
52
|
+
deepskyblue = { 0, 191, 255 },
|
|
53
|
+
dimgray = { 105, 105, 105 },
|
|
54
|
+
dimgrey = { 105, 105, 105 },
|
|
55
|
+
dodgerblue = { 30, 144, 255 },
|
|
56
|
+
firebrick = { 178, 34, 34 },
|
|
57
|
+
floralwhite = { 255, 250, 240 },
|
|
58
|
+
forestgreen = { 34, 139, 34 },
|
|
59
|
+
fuchsia = { 255, 0, 255 },
|
|
60
|
+
gainsboro = { 220, 220, 220 },
|
|
61
|
+
ghostwhite = { 248, 248, 255 },
|
|
62
|
+
gold = { 255, 215, 0 },
|
|
63
|
+
goldenrod = { 218, 165, 32 },
|
|
64
|
+
gray = { 128, 128, 128 },
|
|
65
|
+
green = { 0, 128, 0 },
|
|
66
|
+
greenyellow = { 173, 255, 47 },
|
|
67
|
+
grey = { 128, 128, 128 },
|
|
68
|
+
honeydew = { 240, 255, 240 },
|
|
69
|
+
hotpink = { 255, 105, 180 },
|
|
70
|
+
indianred = { 205, 92, 92 },
|
|
71
|
+
indigo = { 75, 0, 130 },
|
|
72
|
+
ivory = { 255, 255, 240 },
|
|
73
|
+
khaki = { 240, 230, 140 },
|
|
74
|
+
lavender = { 230, 230, 250 },
|
|
75
|
+
lavenderblush = { 255, 240, 245 },
|
|
76
|
+
lawngreen = { 124, 252, 0 },
|
|
77
|
+
lemonchiffon = { 255, 250, 205 },
|
|
78
|
+
lightblue = { 173, 216, 230 },
|
|
79
|
+
lightcoral = { 240, 128, 128 },
|
|
80
|
+
lightcyan = { 224, 255, 255 },
|
|
81
|
+
lightgoldenrodyellow = { 250, 250, 210 },
|
|
82
|
+
lightgray = { 211, 211, 211 },
|
|
83
|
+
lightgreen = { 144, 238, 144 },
|
|
84
|
+
lightgrey = { 211, 211, 211 },
|
|
85
|
+
lightpink = { 255, 182, 193 },
|
|
86
|
+
lightsalmon = { 255, 160, 122 },
|
|
87
|
+
lightseagreen = { 32, 178, 170 },
|
|
88
|
+
lightskyblue = { 135, 206, 250 },
|
|
89
|
+
lightslategray = { 119, 136, 153 },
|
|
90
|
+
lightslategrey = { 119, 136, 153 },
|
|
91
|
+
lightsteelblue = { 176, 196, 222 },
|
|
92
|
+
lightyellow = { 255, 255, 224 },
|
|
93
|
+
lime = { 0, 255, 0 },
|
|
94
|
+
limegreen = { 50, 205, 50 },
|
|
95
|
+
linen = { 250, 240, 230 },
|
|
96
|
+
magenta = { 255, 0, 255 },
|
|
97
|
+
maroon = { 128, 0, 0 },
|
|
98
|
+
mediumaquamarine = { 102, 205, 170 },
|
|
99
|
+
mediumblue = { 0, 0, 205 },
|
|
100
|
+
mediumorchid = { 186, 85, 211 },
|
|
101
|
+
mediumpurple = { 147, 112, 219 },
|
|
102
|
+
mediumseagreen = { 60, 179, 113 },
|
|
103
|
+
mediumslateblue = { 123, 104, 238 },
|
|
104
|
+
mediumspringgreen = { 0, 250, 154 },
|
|
105
|
+
mediumturquoise = { 72, 209, 204 },
|
|
106
|
+
mediumvioletred = { 199, 21, 133 },
|
|
107
|
+
midnightblue = { 25, 25, 112 },
|
|
108
|
+
mintcream = { 245, 255, 250 },
|
|
109
|
+
mistyrose = { 255, 228, 225 },
|
|
110
|
+
moccasin = { 255, 228, 181 },
|
|
111
|
+
navajowhite = { 255, 222, 173 },
|
|
112
|
+
navy = { 0, 0, 128 },
|
|
113
|
+
oldlace = { 253, 245, 230 },
|
|
114
|
+
olive = { 128, 128, 0 },
|
|
115
|
+
olivedrab = { 107, 142, 35 },
|
|
116
|
+
orange = { 255, 165, 0 },
|
|
117
|
+
orangered = { 255, 69, 0 },
|
|
118
|
+
orchid = { 218, 112, 214 },
|
|
119
|
+
palegoldenrod = { 238, 232, 170 },
|
|
120
|
+
palegreen = { 152, 251, 152 },
|
|
121
|
+
paleturquoise = { 175, 238, 238 },
|
|
122
|
+
palevioletred = { 219, 112, 147 },
|
|
123
|
+
papayawhip = { 255, 239, 213 },
|
|
124
|
+
peachpuff = { 255, 218, 185 },
|
|
125
|
+
peru = { 205, 133, 63 },
|
|
126
|
+
pink = { 255, 192, 203 },
|
|
127
|
+
plum = { 221, 160, 221 },
|
|
128
|
+
powderblue = { 176, 224, 230 },
|
|
129
|
+
purple = { 128, 0, 128 },
|
|
130
|
+
rebeccapurple = { 102, 51, 153 },
|
|
131
|
+
red = { 255, 0, 0 },
|
|
132
|
+
rosybrown = { 188, 143, 143 },
|
|
133
|
+
royalblue = { 65, 105, 225 },
|
|
134
|
+
saddlebrown = { 139, 69, 19 },
|
|
135
|
+
salmon = { 250, 128, 114 },
|
|
136
|
+
sandybrown = { 244, 164, 96 },
|
|
137
|
+
seagreen = { 46, 139, 87 },
|
|
138
|
+
seashell = { 255, 245, 238 },
|
|
139
|
+
sienna = { 160, 82, 45 },
|
|
140
|
+
silver = { 192, 192, 192 },
|
|
141
|
+
skyblue = { 135, 206, 235 },
|
|
142
|
+
slateblue = { 106, 90, 205 },
|
|
143
|
+
slategray = { 112, 128, 144 },
|
|
144
|
+
slategrey = { 112, 128, 144 },
|
|
145
|
+
snow = { 255, 250, 250 },
|
|
146
|
+
springgreen = { 0, 255, 127 },
|
|
147
|
+
steelblue = { 70, 130, 180 },
|
|
148
|
+
tan = { 210, 180, 140 },
|
|
149
|
+
teal = { 0, 128, 128 },
|
|
150
|
+
thistle = { 216, 191, 216 },
|
|
151
|
+
tomato = { 255, 99, 71 },
|
|
152
|
+
turquoise = { 64, 224, 208 },
|
|
153
|
+
violet = { 238, 130, 238 },
|
|
154
|
+
wheat = { 245, 222, 179 },
|
|
155
|
+
white = { 255, 255, 255 },
|
|
156
|
+
whitesmoke = { 245, 245, 245 },
|
|
157
|
+
yellow = { 255, 255, 0 },
|
|
158
|
+
yellowgreen = { 154, 205, 50 },
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
NAMED_COLORS = NAMED_COLORS,
|
|
162
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transitions.ts — Pre-configured animation transitions for Motion primitives.
|
|
3
|
+
*
|
|
4
|
+
* Provides a standardized set of TweenInfo configurations to ensure consistency
|
|
5
|
+
* across all declarative animations, per ADR-0002.
|
|
6
|
+
*/
|
|
7
|
+
export declare const transitions: {
|
|
8
|
+
/** Standard UI state change, smooth and subtle */
|
|
9
|
+
default: TweenInfo;
|
|
10
|
+
/** Bouncy, energetic pop-in for modals or badges */
|
|
11
|
+
pop: TweenInfo;
|
|
12
|
+
/** Slow, looping breathing effect for idle states */
|
|
13
|
+
breathe: TweenInfo;
|
|
14
|
+
/** Fast, immediate snap for extremely snappy interactions */
|
|
15
|
+
snap: TweenInfo;
|
|
16
|
+
/** Looping shimmer or slide effect */
|
|
17
|
+
shimmer: TweenInfo;
|
|
18
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* transitions.ts — Pre-configured animation transitions for Motion primitives.
|
|
5
|
+
*
|
|
6
|
+
* Provides a standardized set of TweenInfo configurations to ensure consistency
|
|
7
|
+
* across all declarative animations, per ADR-0002.
|
|
8
|
+
|
|
9
|
+
]]
|
|
10
|
+
local transitions = {
|
|
11
|
+
default = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
|
|
12
|
+
pop = TweenInfo.new(0.25, Enum.EasingStyle.Back, Enum.EasingDirection.Out),
|
|
13
|
+
breathe = TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true),
|
|
14
|
+
snap = TweenInfo.new(0.1, Enum.EasingStyle.Linear),
|
|
15
|
+
shimmer = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, -1),
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
transitions = transitions,
|
|
19
|
+
}
|