-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpackage.json
More file actions
755 lines (755 loc) · 41.8 KB
/
Copy pathpackage.json
File metadata and controls
755 lines (755 loc) · 41.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
{
"name": "luix-roblox",
"displayName": "Luix Roblox",
"description": "All-in-one Roblox UI authoring helper for React-Luau, Fusion, Vide, and Roact — prop completions, hover docs, inlay hints, color preview, deprecation diagnostics, and more.",
"version": "1.5.0",
"publisher": "ericplane",
"icon": "assets/icon.png",
"author": {
"name": "ericplane",
"url": "https://github.com/ericplane"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/ericplane/Luix.git"
},
"bugs": {
"url": "https://github.com/ericplane/Luix/issues"
},
"homepage": "https://github.com/ericplane/Luix#readme",
"engines": {
"vscode": "^1.85.0"
},
"categories": [
"Programming Languages",
"Snippets",
"Linters"
],
"keywords": [
"roblox",
"luau",
"lua",
"react",
"react-luau",
"roact",
"fusion",
"vide",
"ui",
"intellisense",
"completion",
"wally",
"rojo",
"gradient",
"colorsequence",
"numbersequence",
"tween",
"tweeninfo",
"sprite",
"atlas",
"opencloud"
],
"galleryBanner": {
"color": "#15151a",
"theme": "dark"
},
"activationEvents": [
"onLanguage:lua",
"onLanguage:luau"
],
"main": "./out/extension.js",
"contributes": {
"languages": [
{
"id": "lua",
"aliases": [
"Lua"
],
"extensions": [
".lua"
]
},
{
"id": "luau",
"aliases": [
"Luau"
],
"extensions": [
".luau"
]
}
],
"viewsContainers": {
"activitybar": [
{
"id": "luix",
"title": "Luix",
"icon": "assets/sidebar-icon.svg"
}
]
},
"views": {
"luix": [
{
"id": "luix.workspace",
"name": "Workspace"
},
{
"id": "luix.components",
"name": "Components"
}
]
},
"viewsWelcome": [
{
"view": "luix.components",
"contents": "No UI components found in this workspace yet.\n\nLuix lists every function that returns an `e(...)`, `New \"X\" { ... }`, or `create \"X\" { ... }` element, or that carries a `---@extends ClassName` annotation.\n\n[Create a new component](command:luix.newComponentHere)\n\n[Open the Luix documentation](https://github.com/ericplane/luix)\n\nIf you do have components but they're not appearing, they might be excluded by `luix.exclude` — check your [settings](command:workbench.action.openSettings?%22luix%22).",
"when": "workspaceFolderCount > 0"
},
{
"view": "luix.components",
"contents": "Open a workspace folder to see components.\n\n[Open Folder](command:vscode.openFolder)",
"when": "workspaceFolderCount == 0"
}
],
"commands": [
{
"command": "luix.pickActiveFramework",
"title": "Luix: Set active framework…",
"category": "Luix"
},
{
"command": "luix.wally.regenerateTypes",
"title": "Luix: Regenerate Wally types",
"category": "Luix"
},
{
"command": "luix.wally.install",
"title": "Luix: Run wally install",
"category": "Luix"
},
{
"command": "luix.rojo.generateSourcemap",
"title": "Luix: Generate Rojo sourcemap",
"category": "Luix"
},
{
"command": "luix.newComponent.react",
"title": "Luix: New React component file",
"category": "Luix"
},
{
"command": "luix.newComponent.roact",
"title": "Luix: New Roact component file",
"category": "Luix"
},
{
"command": "luix.newComponent.fusion",
"title": "Luix: New Fusion component file",
"category": "Luix"
},
{
"command": "luix.newComponent.vide",
"title": "Luix: New Vide component file",
"category": "Luix"
},
{
"command": "luix.newComponentHere",
"title": "Luix: New component here…",
"category": "Luix"
},
{
"command": "luix.refreshComponents",
"title": "Luix: Refresh component browser",
"category": "Luix",
"icon": "$(refresh)"
},
{
"command": "luix.refreshWorkspace",
"title": "Luix: Refresh workspace view",
"category": "Luix",
"icon": "$(refresh)"
},
{
"command": "luix.componentsView.toggleMode",
"title": "Luix: Toggle components view (tree / flat)",
"category": "Luix",
"icon": "$(list-tree)"
},
{
"command": "luix.extractToComponent",
"title": "Luix: Extract to component…",
"category": "Luix"
},
{
"command": "luix.imageGutter.purgeCache",
"title": "Luix: Purge image preview cache",
"category": "Luix",
"icon": "$(trash)"
},
{
"command": "luix.imageGutter.openCacheFolder",
"title": "Luix: Open image preview cache folder",
"category": "Luix",
"icon": "$(folder-opened)"
},
{
"command": "luix.palette.addEntry",
"title": "Luix: Save Color3 to palette",
"category": "Luix"
},
{
"command": "luix.openGradientEditor",
"title": "Luix: Open gradient editor",
"category": "Luix",
"icon": "$(symbol-color)"
},
{
"command": "luix.openRectEditor",
"title": "Luix: Open sprite rect editor",
"category": "Luix",
"icon": "$(symbol-array)"
}
],
"menus": {
"view/title": [
{
"command": "luix.componentsView.toggleMode",
"when": "view == luix.components",
"group": "navigation@1"
},
{
"command": "luix.refreshComponents",
"when": "view == luix.components",
"group": "navigation@2"
},
{
"command": "luix.refreshWorkspace",
"when": "view == luix.workspace",
"group": "navigation"
}
],
"explorer/context": [
{
"command": "luix.newComponentHere",
"when": "explorerResourceIsFolder",
"group": "navigation@5"
}
],
"editor/context": [
{
"command": "luix.extractToComponent",
"when": "editorLangId == lua || editorLangId == luau",
"group": "1_modification@99"
}
]
},
"configuration": {
"title": "Luix",
"properties": {
"luix.frameworks": {
"type": "array",
"items": {
"type": "string",
"enum": [
"react",
"roact",
"fusion",
"vide"
]
},
"default": [
"react",
"roact",
"fusion",
"vide"
],
"markdownDescription": "Which Roblox UI frameworks Luix recognizes. Disable the ones you don't use to keep completions tight. Each framework has its own call syntax — Luix picks up `e(...)` / `Roact.createElement(...)` (parens form) and `New \"X\" {...}` / `create \"X\" {...}` (curried form) accordingly.\n\nNot to be confused with `#luix.activeFramework#`: that's the *single* framework Luix targets for snippets / scaffold suggestions per file (auto-detected by default); this is the *set* of frameworks the parser is willing to recognise at all."
},
"luix.activeFramework": {
"type": "string",
"enum": [
"auto",
"react",
"roact",
"fusion",
"vide"
],
"enumDescriptions": [
"Detect per file from imports / factory calls. Falls back to a workspace-wide best guess when a file has no signals yet.",
"Force React-Luau (`e(...)`) snippets and completions everywhere.",
"Force Roact (`Roact.createElement(...)`) snippets and completions everywhere.",
"Force Fusion (`New \"...\" { ... }`) snippets and completions everywhere.",
"Force Vide (`create \"...\" { ... }` / `Vide.create(...)`) snippets and completions everywhere."
],
"default": "auto",
"markdownDescription": "Which framework's snippets and completions Luix surfaces. Default `auto` picks per file in this priority order:\n\n1. This setting, if not `auto`.\n2. The file's own `require(...)` imports of `react` / `roact` / `fusion` / `vide`.\n3. The file's first factory call (`e(...)`, `New \"...\"`, `create \"...\"`, …).\n4. A workspace-wide best guess inferred from sampled indexed files.\n5. None — Luix's UI suggestions stay quiet.\n\nThe Luix status-bar item shows the current pick and clicking it flips this setting (workspace-scope when a workspace is open, global otherwise). Distinct from `#luix.frameworks#`, which controls which frameworks Luix's *parser* recognises in the first place."
},
"luix.react.aliases": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Override the React-Luau factory aliases (`e`, `createElement`, `React.createElement`). Leave empty to use the defaults."
},
"luix.roact.aliases": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Override the Roact factory aliases (`Roact.createElement`). Leave empty to use the default."
},
"luix.fusion.aliases": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Override the Fusion factory aliases (`New`, `Fusion.New`). Leave empty to use the defaults."
},
"luix.vide.aliases": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Override the Vide factory aliases (`create`, `vide.create`). Leave empty to use the defaults."
},
"luix.vide.directInstanceCalls": {
"type": "boolean",
"default": true,
"markdownDescription": "When **Vide** is in `#luix.frameworks#`, recognize Vide's bare-call shape for built-in Roblox UI classes — `Frame({ Size = … })`, `TextButton({ Activated = … })`, `ScrollingFrame { … }`, etc. — and surface the matching class's prop + event completions inside the table.\n\nThe class allowlist is the same set Luix already has prop data for (Frame, ScrollingFrame, TextLabel, TextButton, ImageLabel, ImageButton, ScreenGui, BillboardGui, every `UI*` constraint / layout / decorator …), minus the abstract bases (`GuiObject`, `GuiButton`, `Instance`). Non-UI Roblox class names (`Camera`, `Sound`, `Tween`, `Workspace`, …) are deliberately *not* in the set — Luix doesn't have prop data for them and they're common local-variable names.\n\nWorkspace components always shadow built-in class names: if you've defined your own `local function Frame(props)` somewhere, *that* component's declared props win over Roblox's `Frame`.\n\nDisable if you have local variables named after UI classes that you call with a table for unrelated reasons."
},
"luix.props": {
"type": "object",
"default": {},
"additionalProperties": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "object",
"properties": {
"extends": {
"type": "string",
"description": "Name of the class to inherit props from (e.g. \"Frame\")."
},
"props": {
"type": "array",
"items": {
"type": "string"
},
"description": "Additional props specific to this component."
}
},
"additionalProperties": false
}
]
},
"markdownDescription": "Per-class prop list. Each entry overrides the built-in defaults for that class — presence of a key is authoritative, so an explicit empty array (`[]`) disables completions for that class. Custom component identifiers (e.g. `MyButton`) can be added here too. Two shapes:\n\n```jsonc\n{\n // Array form: just the prop list.\n \"TextLabel\": [\"Text\", \"TextColor3\"],\n \"MyButton\": [\"label\", \"onClick\", \"disabled\"],\n // Object form: extend a class and add extras.\n \"GamepassCard\": { \"extends\": \"Frame\", \"props\": [\"gamepassId\"] }\n}\n```\n\nMost custom components don't need this — the extension can usually infer props from in-file annotations, typed signatures, or the component's root `createElement` call. Use config when the component lives in a file the extension can't reach or when you want central overrides."
},
"luix.createElementAliases": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Legacy global alias override. Prefer the per-framework `luix.<framework>.aliases` settings — they let you keep the call-shape distinction (parens vs curried). This list, if non-empty, is treated as parens-form and added to whatever frameworks are enabled."
},
"luix.snippetMode": {
"type": "string",
"enum": [
"value",
"value-with-comma",
"name-only"
],
"enumDescriptions": [
"Insert `Name = ` and place the cursor after the equals sign.",
"Insert `Name = ,` with the first tab stop at the value and the final tab stop after the comma (recommended for Lua table syntax).",
"Insert just the property name."
],
"default": "value-with-comma",
"markdownDescription": "Controls what gets inserted when you accept a completion."
},
"luix.typeAwareValues": {
"type": "boolean",
"default": true,
"markdownDescription": "When the prop's type is known (e.g. `BackgroundColor3` is `Color3`), insert a value template alongside the prop name. For example, `BackgroundColor3` becomes `BackgroundColor3 = Color3.fromRGB(255, 255, 255)` with tab stops at each color channel. Disable for the older minimal-snippet behavior."
},
"luix.documentSymbols.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), Luix provides Document Symbols for the React tree — visible in the Outline panel, the Breadcrumbs bar, and `Cmd+Shift+O` (Go to Symbol in File). Disable if you prefer your editor's outline to show only top-level Lua structure."
},
"luix.colorPreview.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), `Color3.fromRGB(...)` and `Color3.new(...)` get a gutter swatch and a color picker. Disable if another extension (such as a Roblox API extension) already provides this and you're seeing duplicate pickers."
},
"luix.palette": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "string"
},
"markdownDescription": "Project palette: named colors that appear as completions when you type `Color3.`. Each value should be the full Roblox expression to insert. Example:\n\n```jsonc\n{\n \"luix.palette\": {\n \"primary\": \"Color3.fromRGB(124, 92, 255)\",\n \"background\": \"Color3.fromRGB(21, 21, 26)\",\n \"surface\": \"Color3.fromRGB(28, 30, 38)\",\n \"text\": \"Color3.fromRGB(255, 255, 255)\"\n }\n}\n```\n\nTyping `BackgroundColor3 = Color3.` then `palette.primary` inserts the full `Color3.fromRGB(124, 92, 255)` expression."
},
"luix.exclude": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Extra directory names to skip when indexing components. Luix already excludes `Packages`, `DevPackages`, `ServerPackages`, `_Index`, `node_modules`, `out`, and `dist` by default — list anything else here. Match is by directory **name**, anywhere in the file path (so `\"Tests\"` skips every `Tests/` folder in the workspace)."
},
"luix.componentsRoot": {
"type": "string",
"default": "",
"markdownDescription": "Optional. Workspace-relative path to the folder that holds your UI components (e.g. `src/Client/UI/Components`). When set, the Components view's tree mode is rooted there and only shows components under that folder. Flat mode still shows everything. Leave empty to root the tree at each workspace folder."
},
"luix.inlayHints.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), every multi-line element call gets a label at its closing `)` (parens form) or `}` (curried form) showing the element's class. VS Code's master toggle is `editor.inlayHints.enabled`; this lets you disable just Luix's hints."
},
"luix.inlayHints.scope": {
"type": "string",
"enum": [
"all",
"ancestors"
],
"enumDescriptions": [
"Show hints for every multi-line element call in the file.",
"Only show hints for calls that contain the cursor (the innermost call and each of its ancestors). Keeps the rest of the file uncluttered. (Default.)"
],
"default": "ancestors",
"markdownDescription": "How many inlay hints to show at once."
},
"luix.inlayHints.position": {
"type": "string",
"enum": [
"before-comma",
"after-comma"
],
"enumDescriptions": [
"Render the label between the closing `)` / `}` and the trailing `,`.",
"Render the label after the trailing `,`, so the comma stays glued to the closing punctuation. (Default.)"
],
"default": "after-comma",
"markdownDescription": "Where to render the inlay-hint label relative to the trailing comma."
},
"luix.warnReservedPropNames": {
"type": "boolean",
"default": false,
"markdownDescription": "Warn when a custom component declares a `---@prop` whose name shadows a Roblox property of its declared (or detected) base class. Off by default to avoid surprising users with new warnings."
},
"luix.deprecationDiagnostics": {
"type": "boolean",
"default": true,
"markdownDescription": "Show diagnostics for deprecated patterns: `Font = Enum.Font.X` (use `FontFace`), `TextColor = ...` (typo of `TextColor3`). Quick-fixes available via the lightbulb."
},
"luix.autoImport.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "Opt-in. When enabled, `e(SomeComponent, …)` for a component the workspace knows about but the current file doesn't `require` produces an Information diagnostic with a quick-fix that inserts the require line."
},
"luix.autoImport.style": {
"type": "string",
"enum": [
"relative",
"alias"
],
"enumDescriptions": [
"Compute a `script.Parent…X` chain from the current file's directory to the component's file.",
"Use one of the configured aliases below. Falls back to `relative` if no alias matches."
],
"default": "relative",
"markdownDescription": "How to build the `require(...)` path for auto-imports."
},
"luix.autoImport.aliases": {
"type": "array",
"default": [],
"items": {
"type": "object",
"properties": {
"filesystemPath": {
"type": "string",
"description": "Workspace-relative path prefix on disk, e.g. `src/Client/UI/Components`."
},
"robloxPath": {
"type": "string",
"description": "Roblox-style prefix to substitute. Safe targets: `ReplicatedStorage.*`, `ServerStorage.*`, `ServerScriptService.*`, or relative `script.Parent.*`. AVOID `game.StarterPlayer.StarterPlayerScripts.*` / `StarterCharacterScripts.*` / `StarterGui.*` — those paths exist at edit time but not at runtime (Roblox copies StarterPlayer*Scripts into `Players.LocalPlayer.PlayerScripts` / the character; StarterGui into `PlayerGui`). Use the runtime path (e.g. `Players.LocalPlayer.PlayerScripts.UI`) or stick with the default `relative` style, which sidesteps the issue entirely."
}
},
"required": [
"filesystemPath",
"robloxPath"
]
},
"markdownDescription": "For `alias` style: map workspace-relative filesystem prefixes to Roblox-style require prefixes. The first matching alias wins.\n\n**Runtime-copy caveat.** Roblox copies `StarterPlayer.StarterPlayerScripts` / `StarterCharacterScripts` / `StarterGui` into `Players.LocalPlayer.PlayerScripts` / the character / `PlayerGui` when the player joins. An alias whose `robloxPath` includes `StarterPlayer.*Scripts` or `StarterGui` will work in Studio Edit but fail in actual gameplay. For UI code that lives in `StarterPlayerScripts`, either:\n\n- leave aliases empty and let `relative` mode emit `script.Parent.…` chains (relative offsets are preserved across the copy), OR\n- target the runtime path: `Players.LocalPlayer.PlayerScripts.UI`.\n\nServices that don't get copied (`ReplicatedStorage`, `ServerStorage`, `ServerScriptService`) are safe absolute targets."
},
"luix.richText.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), typing `<` inside a string literal surfaces Roblox RichText tags (`<b>`, `<i>`, `<font …>`, `<stroke …>`, `<mark …>`, `<br/>`, …) with snippets that include the matching close tag. Inside an open `<font …>`, `<stroke …>`, or `<mark …>`, attribute completion (`color`, `size`, `face`, `family`, `weight`, `transparency`, `thickness`, `joins`) fires so multiple attributes can be combined. Also auto-inserts `</tag>` when you finish typing an opening tag like `<font size=\"18\">`. Inner attribute quotes adapt to the outer Lua string's quote so they don't need backslash escaping. `color=\"...\"` values in `<font>`, `<stroke>`, and `<mark>` get VS Code's inline color picker, and a `Text = \"…<font…>…\"` value with no `RichText = true` in the same props table gets a warning. Disable if you don't use RichText or want a quieter completion list inside strings."
},
"luix.color3.defaultFormat": {
"type": "string",
"enum": [
"fromRGB",
"fromHex",
"new",
"fromHSV"
],
"enumDescriptions": [
"`Color3.fromRGB(255, 255, 255)` — integer 0-255 channels.",
"`Color3.fromHex(\"#FFFFFF\")` — hex string.",
"`Color3.new(1, 1, 1)` — float 0-1 channels.",
"`Color3.fromHSV(0, 0, 1)` — hue/saturation/value, all 0-1."
],
"default": "fromRGB",
"markdownDescription": "Constructor used when Luix inserts a `Color3` value template for a prop completion (`BackgroundColor3`, `TextColor3`, `BorderColor3`, …). Doesn't affect the color picker's round-trip — the picker always preserves the form your existing value already uses."
},
"luix.richText.defaultColorFormat": {
"type": "string",
"enum": [
"hex",
"rgb"
],
"enumDescriptions": [
"Default placeholders are written as `#FFFFFF`.",
"Default placeholders are written as `rgb(255, 255, 255)`."
],
"default": "hex",
"markdownDescription": "Format used for the placeholder color values in RichText snippets — `<font color=\"…\">`, `<stroke color=\"…\">`, `<mark color=\"…\">`. Doesn't affect the color picker's round-trip — the picker always preserves whichever form your existing value already uses."
},
"luix.richText.colorPicker": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), `color=\"…\"` values inside `<font>`, `<stroke>`, and `<mark>` RichText tags get VS Code's inline color swatch and picker. Independent from `#luix.colorPreview.enabled#` (which controls the `Color3.fromRGB(…)` / `Color3.new(…)` picker) so you can disable the Color3 picker — e.g. to avoid clashing with another Roblox-API extension — while keeping the RichText one."
},
"luix.robloxGlyphs.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), Roblox's private-use-area glyphs — Robux (`U+E002`), Premium (`U+E001`), Verified (`U+E000`), Roblox Plus (`U+E003`) — get an inlay-hint label so you can tell what each `[]`-box represents in your strings, plus a hover with the codepoint and Luau escape. Type `:robux:`, `:premium:`, `:verified:`, or `:roblox-plus:` inside a string to insert the literal glyph."
},
"luix.robloxGlyphs.custom": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "string"
},
"markdownDescription": "Your own `:slug:` shortcuts for characters your keyboard can't reach. Keys are slugs typed between colons inside a string, values are the literal text inserted on accept. Slugs that collide with the built-in glyphs (`robux`, `premium`, `verified`, `roblox-plus`) are ignored.\n\nExample:\n\n```jsonc\n{\n \"luix.robloxGlyphs.custom\": {\n \"gbp\": \"£\",\n \"euro\": \"€\",\n \"yen\": \"¥\",\n \"shrug\": \"¯\\\\_(ツ)_/¯\"\n }\n}\n```\n\nThen typing `:gbp:` inside a string and accepting inserts `£`."
},
"luix.componentReferencesLens.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), each component definition gets a `N references` CodeLens above it. Click to peek every workspace call site of `e(MyButton, …)` style invocations. Disable if CodeLens noise bothers you."
},
"luix.frameStatsLens.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, every element call gets a `▸ Frame — N descendants, D layers deep` CodeLens. Off by default because it adds a lot of visual noise; useful when hunting for layout bloat."
},
"luix.frameStatsLens.minDescendants": {
"type": "number",
"default": 5,
"markdownDescription": "Only show the frame-stats CodeLens for elements with at least this many descendants. Default `5` keeps trivial elements quiet."
},
"luix.classNameCompletion.triggerOnOpenParen": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, typing `e(` opens the class picker without needing to also type the opening quote. Accepting inserts `\"ClassName\", { … })`. Off by default because the trigger char `(` is broad — every Lua function call would briefly invoke the provider (which suppresses itself for non-factory calls, but the extra invocations have a small cost)."
},
"luix.imageGutter.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, shows a thumbnail of each `rbxassetid://NNNN` image in the gutter next to its line. Thumbnails are downloaded once per asset and cached to disk (see `#luix.imageGutter.cacheLocation#` for where) — reopening a file is instant. **Off by default** because it persists files to disk; the in-hover preview works either way. The Luix sidebar surfaces a one-click *Enable image gutter previews* entry while this is off."
},
"luix.imageGutter.cacheLocation": {
"type": "string",
"enum": [
"global",
"workspace"
],
"enumDescriptions": [
"VS Code's per-extension global storage (shared across every workspace).",
"`.luix/assetThumbs/` inside the current workspace. A `.luix/.gitignore` is auto-written so the cache doesn't leak into commits."
],
"default": "global",
"markdownDescription": "Where Luix stores downloaded Roblox asset thumbnails for the inline gutter preview.\n\n- **`global`** (default) — shared across every workspace, lives under VS Code's extension storage.\n- **`workspace`** — keeps thumbnails per-project under `.luix/assetThumbs/`. A `.luix/.gitignore` is auto-written.\n\nFlip via Settings or `#luix.imageGutter.cacheLocation#`. The purge button in the Luix sidebar wipes both locations, so switching modes never strands stale files."
},
"luix.contrastWarnings.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, walks the element tree and flags any `TextColor3` whose WCAG-AA contrast ratio against the nearest ancestor's `BackgroundColor3` is below 4.5:1 (the spec's threshold for normal text). Both colors must be literal `Color3.fromRGB/new/fromHex/fromHSV` expressions — reactive values (Fusion `Value`, Vide source, `Computed`) are skipped. Off by default because the heuristic is strict and the warnings can pile up on existing codebases."
},
"luix.unusedProps.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), flags props declared in a component's parameter type (`props: { Foo: …, Bar: … }`) or its `@luix-props` annotation that are never read in the body. Skipped automatically when the body forwards `props` wholesale (e.g. `e(Base, props)`, `for k, v in props do`, computed-key indexing) since usage can't be determined statically. Shown as a `Hint` with the unused-declaration grey-out treatment — never as a warning or error."
},
"luix.gradient.codeLensEnabled": {
"type": "boolean",
"default": true,
"markdownDescription": "Shows the gradient-editor CodeLenses:\n\n- **Edit UIGradient** — above every `e(\"UIGradient\", { … })` element call. Opens the combined editor (colour ramp + transparency curve + rotation + preview square that multiplies by the parent's `BackgroundColor3`).\n- **Edit gradient** — above any standalone `ColorSequence.new(...)` literal that isn't inside a UIGradient. Opens the focused colour-band editor.\n- **Edit sequence** — above any standalone `NumberSequence.new(...)` literal that isn't inside a UIGradient. Opens the focused value-curve editor.\n\nLiterals nested inside a `UIGradient` props block are deliberately suppressed in favour of the single combined **Edit UIGradient** lens. Only literals whose arguments are themselves literals get a lens — expressions involving variables (`Color3.fromRGB(props.red, …)`) stay inert."
},
"luix.gradient.previewOnHover": {
"type": "boolean",
"default": true,
"markdownDescription": "Renders an inline SVG preview in the hover tooltip:\n\n- **`ColorSequence.new(...)`** → a gradient strip sampled from the literal's stops.\n- **`NumberSequence.new(...)`** → a value-curve graph of the literal's keypoints.\n\nBoth are generated from the literal values you've typed — no network requests, no caching."
},
"luix.hoverPreviews.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "Show inline SVG previews on hover for: `TweenInfo.new(...)` (easing curve), `e(\"UIPadding\", …)` (box with labelled sides), `e(\"UICorner\", …)` (rounded corner sample), and `e(\"UIStroke\", …)` (stroke thickness sample). All previews are rendered from the literal values you've typed — no network requests."
},
"luix.sortProps.onSave": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, every props table in the document is automatically sorted into the configured category order on save. **Off by default** so saving a teammate's file doesn't reshape their layout. Tables containing `--` comments are skipped to avoid losing the comments. Use the **Sort props by category** code action (lightbulb on any element call) to sort manually without enabling this. Sort order is controlled by `#luix.sortProps.categoryOrder#`."
},
"luix.sortProps.categoryOrder": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"Identification",
"Layout",
"Style",
"Visibility",
"Image",
"Text",
"Behavior",
"Events",
"Refs",
"Children",
"Other"
],
"markdownDescription": "Order in which prop categories are arranged when sorting. Each category contains the props you'd expect:\n\n- **Identification** — Name, ClassName, Key\n- **Layout** — AnchorPoint, Position, Size, AutomaticSize, Rotation, LayoutOrder, ZIndex, SizeConstraint\n- **Style** — BackgroundColor3, BackgroundTransparency, Border*\n- **Visibility** — Visible, ClipsDescendants\n- **Image** — Image, ImageColor3, ImageRectOffset/Size, ScaleType, …\n- **Text** — Text, TextColor3, FontFace, TextSize, RichText, …\n- **Behavior** — Active, Selectable, AutoButtonColor, …\n- **Events** — anything matching `[…Event…]`, `[…Change…]`, or known event names (`Activated`, `MouseEnter`, …)\n- **Refs** — `ref`, `key`\n- **Children** — `[Children]`\n- **Other** — anything else (kept in original order)\n\nReorder the entries to change where each category lands in the sorted output. Unknown category names are ignored."
},
"luix.rectEditor.codeLensEnabled": {
"type": "boolean",
"default": true,
"markdownDescription": "Shows an **Edit sprite rect** CodeLens above every `ImageLabel` / `ImageButton` whose `Image` prop is a literal `rbxassetid://…` string. Clicking it opens a side-panel editor: the asset's thumbnail is fetched at the largest available size (768×768) and a draggable rectangle lets you pick `ImageRectOffset` and `ImageRectSize` visually. Resetting to the whole image strips both props since `ImageRectSize = Vector2.new(0, 0)` is Roblox's default."
},
"luix.openCloud.apiKey": {
"type": "string",
"default": "",
"markdownDescription": "Optional Roblox **Open Cloud API key**. When set, the rect editor auto-detects the *native* pixel dimensions of `rbxassetid://…` Image assets by hitting `apis.roblox.com/asset-delivery-api/v1/assetId/{id}`, downloading the asset, and reading the PNG/JPEG header — so coordinates you pick on the thumbnail are correct against the source asset's real pixel space.\n\nCreate a key at [create.roblox.com/dashboard/credentials](https://create.roblox.com/dashboard/credentials) — under **Access Permissions** pick the **legacy-asset** API system and grant the **`legacy-asset:manage`** operation. Results are cached per asset ID in your global state so the API is only called once per asset, ever. Without a key, the editor falls back to thumbnail dimensions and you can type `Source W` / `Source H` manually."
},
"luix.indexPersistence.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), the workspace component index is persisted to disk between sessions so unchanged files don't need to be re-parsed on cold start. Has no user-visible behavioral difference — only a startup-time saving on large workspaces. Disable if you'd rather Luix never write to its global storage."
},
"luix.workspaceValidation.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, the Luix sidebar shows a `Project diagnostics — N warnings · M errors across X files` summary aggregated from VS Code's diagnostics collection. Counts only files VS Code has produced diagnostics for (typically: opened files + files surfaced by other extensions / language servers). Off by default."
},
"luix.useRobloxApiDump": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, fetches the community-maintained Roblox API dump and merges any *additional* properties it reports for classes Luix already knows about into the in-memory prop map. Cached on disk for 24 hours; the dump is additive only (built-in props always win on conflicts) so a stale or partial fetch never breaks existing completions. Off by default because the hand-curated built-in data is reliable and works offline."
},
"luix.spacing": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "string"
},
"markdownDescription": "Named spacing tokens that surface as completions when you type `UDim.`. Each value should be the full Roblox expression to insert.\n\n```jsonc\n{\n \"luix.spacing\": {\n \"xs\": \"UDim.new(0, 4)\",\n \"sm\": \"UDim.new(0, 8)\",\n \"md\": \"UDim.new(0, 16)\",\n \"lg\": \"UDim.new(0, 24)\"\n }\n}\n```\n\nMirror of `luix.palette` for `Color3`."
},
"luix.fonts": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "string"
},
"markdownDescription": "Named font tokens that surface as completions when you type `Font.`. Each value should be the full Roblox expression to insert.\n\n```jsonc\n{\n \"luix.fonts\": {\n \"display\": \"Font.fromName(\\\"Gotham\\\", Enum.FontWeight.Bold)\",\n \"body\": \"Font.fromName(\\\"SourceSansPro\\\", Enum.FontWeight.Regular)\"\n }\n}\n```"
},
"luix.font.defaultFamily": {
"type": "string",
"default": "Montserrat",
"markdownDescription": "Family name used as the placeholder when Luix inserts a `Font.fromName(\"…\", Enum.FontWeight.Regular)` snippet (e.g. after accepting `FontFace` + `fromName` from the suggest dropdown). Defaults to `Montserrat` — the canonical name Roblox now aliases the legacy `Gotham*` enum values to.\n\nSet this to your project's house font so accepted snippets land with the right family selected. Built-in fonts: `Montserrat`, `BuilderSans`, `Gotham`, `Roboto`, `SourceSansPro`, etc. — or any family you've added to `#luix.customFonts#`."
},
"luix.customFonts": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "array",
"items": {
"type": "string",
"enum": [
"Thin",
"ExtraLight",
"Light",
"Regular",
"Medium",
"SemiBold",
"Bold",
"ExtraBold",
"Heavy"
]
}
},
"markdownDescription": "Custom Roblox font families and their supported weights, surfaced in the same `Font.fromName(\"…\")` and `Enum.FontWeight.` completions as the built-in catalog. Useful when you've uploaded your own font assets and registered them as a family. Keys are the family name string you pass to `Font.fromName`; values are the list of weights the family ships. Invalid weight names (anything not in the `FontWeight` enum) are silently filtered.\n\n```jsonc\n{\n \"luix.customFonts\": {\n \"MyBrandSans\": [\"Light\", \"Regular\", \"Medium\", \"Bold\"],\n \"MyBrandSerif\": [\"Regular\", \"Bold\"]\n }\n}\n```\n\nCustom entries surface above built-ins in the dropdown and are tagged *Custom font* in the suggest details. If a custom family shares a name with a built-in, the custom weight list wins."
},
"luix.propValidation.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "When enabled (default), surfaces four prop-table diagnostics:\n\n- **Unknown property** on a known Roblox class (`ScrollingDirection` on a `Frame`), with a *Did you mean?* quick-fix.\n- **Duplicate key** in the same props table (`Size = …, Size = …`).\n- **Wrong enum type** — e.g. `BorderMode = Enum.Font.X` when `BorderMode` expects `Enum.BorderMode`.\n- **Overridden by component** — passing a prop to a custom component (`e(MyCard, { Position = ... })`) when that component hard-codes the same prop in its root element, so the call-site value would be silently ignored. Information-level since the heuristic is purely textual.\n\nDisable to silence all four."
}
}
},
"configurationDefaults": {
"[lua]": {
"editor.quickSuggestions": {
"other": "on",
"comments": "off",
"strings": "on"
}
},
"[luau]": {
"editor.quickSuggestions": {
"other": "on",
"comments": "off",
"strings": "on"
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src",
"test": "vscode-test",
"package": "vsce package",
"build-icon": "node scripts/build-icon.mjs"
},
"devDependencies": {
"@types/vscode": "^1.85.0",
"@types/mocha": "^10.0.10",
"@types/node": "^22.19.19",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
"eslint": "^10.4.1",
"sharp": "^0.34.5",
"typescript": "^5.9.3",
"typescript-eslint": "^8.60.1"
},
"overrides": {
"serialize-javascript": "^7.0.5",
"diff": "^9.0.0"
}
}