@adiholick/nvstudio-mcp 2.0.1 → 2.0.3

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/README.md CHANGED
@@ -8,9 +8,14 @@ Sistem ini didesain menggunakan **Hybrid Architecture** yang memisahkan logika E
8
8
 
9
9
  ## 📥 Instalasi
10
10
 
11
- Langkah instalasinya **sangat gampang!**
11
+ Langkah instalasi dirancang **sangat mudah dan otomatis!**
12
12
 
13
- ### 1. Jalankan Installer (Otomatis)
13
+ > 💡 **Apakah perlu install plugin Roblox secara manual?**
14
+ > **TIDAK PERLU.** Jika Anda menjalankan skrip installer di bawah (Langkah 1), installer akan **secara otomatis menyalin plugin (`nvstudio_mcp.rbxmx`)** langsung ke folder plugin Roblox Studio di sistem operasi Anda.
15
+
16
+ ---
17
+
18
+ ### Langkah 1: Jalankan Installer (Otomatis & Direkomendasikan)
14
19
 
15
20
  Pilih perintah sesuai terminal yang Anda gunakan:
16
21
 
@@ -24,25 +29,40 @@ irm https://raw.githubusercontent.com/Adiholick/nvstudio-mcp/main/install.ps1 |
24
29
  curl -fsSL https://raw.githubusercontent.com/Adiholick/nvstudio-mcp/main/install.sh | bash
25
30
  ```
26
31
 
32
+ **Apa saja yang dilakukan installer ini secara otomatis?**
33
+ 1. 📦 Mengunduh source code, memasang dependensi Node.js, dan mengkompilasi TypeScript (*Bridge Server*).
34
+ 2. 🧩 **Memasang Plugin Roblox Studio (`nvstudio_mcp.rbxmx`) otomatis** ke folder sistem Roblox Studio Anda (`%LOCALAPPDATA%\Roblox\Plugins` di Windows atau `~/Documents/ROBLOX/Plugins` di macOS).
35
+ 3. 🤖 Mengintegrasikan *AI Agent Skills* anti-halusinasi langsung ke **Antigravity IDE** (`~/.gemini/config/plugins/nvstudio-mcp`).
36
+ 4. ⚙️ Melakukan auto-konfigurasi jika mendeteksi editor **Cursor** (`.cursor/mcp.json`).
27
37
 
28
- **Installer ini akan secara otomatis melakukan hal berikut:**
29
- - Memasang dependensi Node.js dan melakukan kompilasi TypeScript (*Bridge Server*).
30
- - Menyalin file plugin `nvstudio_mcp.rbxmx` ke dalam sistem Plugin Roblox Studio di OS Anda (Windows/macOS/WSL) secara otomatis.
31
- - Mengintegrasikan *AI Agent Skills* langsung ke **Antigravity IDE** (`~/.gemini/config/plugins/nvstudio-mcp`).
32
- - Melakukan auto-konfigurasi JSON jika Anda menggunakan **Cursor Editor**.
38
+ ---
33
39
 
34
- ### 2. Mengaktifkan Izin di Roblox Studio
35
- 1. Buka proyek game Anda di Roblox Studio.
36
- 2. Di pojok kiri atas, buka **Home** -> **Game Settings** -> **Security**.
37
- 3. Centang (aktifkan) opsi **`Allow HTTP Requests`** agar plugin dapat berkomunikasi dengan AI.
40
+ ### Langkah 2: Mengaktifkan Izin di Roblox Studio (Wajib)
41
+
42
+ Karena pembatasan keamanan bawaan Roblox Studio, akses HTTP harus diizinkan sekali per place/game:
43
+ 1. Buka game/place Anda di **Roblox Studio**.
44
+ 2. Di toolbar atas, buka tab **Home** ➔ **Game Settings** ➔ pilih menu **Security**.
45
+ 3. Centang (aktifkan) **`Allow HTTP Requests`**, lalu klik **Save**.
46
+ 4. **Auto-Detect Aktif!** Plugin Roblox Studio kini memiliki heartbeat pintar yang akan otomatis terhubung (*Auto-Connect*) segera setelah MCP server aktif — Anda tidak perlu lagi repot menekan tombol connect secara manual.
38
47
 
39
48
  ---
40
49
 
41
- ## 🔌 Cara Menggunakan (Untuk AI Lain)
50
+ ### 📂 Alternatif: Instalasi Plugin Secara Manual (Opsional)
51
+
52
+ Jika Anda **tidak** menggunakan skrip installer otomatis di atas (misalnya hanya menambahkan server via `npx` di klien AI), pasang file plugin Roblox Studio secara manual:
53
+ 1. Ambil file `nvstudio_mcp.rbxmx` dari folder [studio-plugin/nvstudio_mcp.rbxmx](file:///studio-plugin/nvstudio_mcp.rbxmx).
54
+ 2. Pindahkan/salin file tersebut ke direktori plugin Roblox di komputer Anda:
55
+ - **Windows:** `%LOCALAPPDATA%\Roblox\Plugins` (atau `C:\Users\<Username>\AppData\Local\Roblox\Plugins`)
56
+ - **macOS:** `~/Documents/ROBLOX/Plugins`
57
+ 3. Restart atau buka kembali Roblox Studio. Plugin akan muncul di tab **Plugins**.
42
58
 
43
- Jika Anda menggunakan AI Client lain (misalnya: **Claude Desktop** atau klien yang tidak dikonfigurasi otomatis), Anda dapat menambahkan `nvstudio-mcp` secara manual ke konfigurasi `mcp.json` Anda.
59
+ ---
44
60
 
45
- Salin konfigurasi ini:
61
+ ## 🔌 Cara Menggunakan (Konfigurasi MCP Klien AI)
62
+
63
+ Jika Anda menggunakan AI Client lain seperti **Claude Desktop**, **Windsurf**, atau ingin menambahkan konfigurasi secara manual:
64
+
65
+ Tambahkan konfigurasi berikut ke file `mcp.json` Anda:
46
66
  ```json
47
67
  {
48
68
  "mcpServers": {
@@ -56,7 +76,15 @@ Salin konfigurasi ini:
56
76
  }
57
77
  }
58
78
  ```
59
- *(Dengan ini, AI Anda akan selalu mengunduh dan menjalankan versi terbaru dari server Bridge langsung dari NPM, tanpa perlu path folder lokal!)*
79
+ *(AI Anda akan selalu menjalankan versi terbaru dari server Bridge langsung dari NPM!)*
80
+
81
+ ---
82
+
83
+ ## 🖥️ Dashboard Visual & Fitur Pintar v2.0
84
+
85
+ - **Visual Dashboard Real-time:** Buka [http://localhost:3055](http://localhost:3055) di browser Anda untuk melihat monitor antrean perintah, log aktivitas AI, status koneksi Studio, dan statistik eksekusi secara visual.
86
+ - **Auto-Detect & Auto-Connect:** Plugin Roblox Studio otomatis menyambung ke server saat aktif dan kembali menunggu (*Waiting*) saat server mati tanpa interupsi manual.
87
+ - **Anti-Zombie Port Handling:** Tidak perlu khawatir error `EADDRINUSE`. Jika port `3055` tertahan oleh proses zombie akibat restart editor/IDE, server akan otomatis membersihkan port tersebut secara aman.
60
88
 
61
89
  ---
62
90
 
@@ -39,7 +39,7 @@ function buildRbxmx() {
39
39
  xml += `\t\t\t<bool name="DefinesCapabilities">false</bool>\n`;
40
40
  xml += `\t\t\t<string name="Name">nvstudio_mcp</string>\n`;
41
41
  xml += `\t\t\t<int64 name="SourceAssetId">-1</int64>\n`;
42
- xml += `\t\t\t<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>\n`;
42
+ xml += `\t\t\t<BinaryString name="Tags"></BinaryString>\n`;
43
43
  xml += `\t\t</Properties>\n`;
44
44
  // Process files
45
45
  for (const file of files) {
@@ -49,8 +49,9 @@ function buildRbxmx() {
49
49
  // Main.server.lua is a Script, others are ModuleScripts
50
50
  const isMain = name === 'Main.server';
51
51
  const className = isMain ? 'Script' : 'ModuleScript';
52
- // Use a simple hash-like referent
53
- const referent = `RBX_${name.replace(/[^a-zA-Z0-9]/g, '_')}`;
52
+ const scriptName = isMain ? 'Main' : name;
53
+ // Use a clean referent
54
+ const referent = `RBX_${scriptName.replace(/[^a-zA-Z0-9]/g, '_')}`;
54
55
  xml += `\t\t<Item class="${className}" referent="${referent}">\n`;
55
56
  xml += `\t\t\t<Properties>\n`;
56
57
  xml += `\t\t\t\t<BinaryString name="AttributesSerialize"></BinaryString>\n`;
@@ -58,9 +59,9 @@ function buildRbxmx() {
58
59
  xml += `\t\t\t\t<bool name="DefinesCapabilities">false</bool>\n`;
59
60
  xml += `\t\t\t\t<bool name="Disabled">false</bool>\n`;
60
61
  xml += `\t\t\t\t<Content name="LinkedSource"><null></null></Content>\n`;
61
- xml += `\t\t\t\t<string name="Name">${name}</string>\n`;
62
+ xml += `\t\t\t\t<string name="Name">${scriptName}</string>\n`;
62
63
  xml += `\t\t\t\t<int64 name="SourceAssetId">-1</int64>\n`;
63
- xml += `\t\t\t\t<SharedString name="Tags"></SharedString>\n`;
64
+ xml += `\t\t\t\t<BinaryString name="Tags"></BinaryString>\n`;
64
65
  if (isMain) {
65
66
  xml += `\t\t\t\t<token name="RunContext">0</token>\n`;
66
67
  }
@@ -73,11 +74,7 @@ function buildRbxmx() {
73
74
  }
74
75
  // Close Folder
75
76
  xml += `\t</Item>\n`;
76
- // Shared Strings definition (required by Roblox XML format if Tags is used)
77
- xml += `\t<SharedStrings>\n`;
78
- xml += `\t\t<SharedString md5="yuZpQdnvvUBOTYh1jqZ2cA=="></SharedString>\n`;
79
- xml += `\t</SharedStrings>\n`;
80
- // Close Roblox
77
+ // Close Roblox root (No SharedStrings block needed when all Tags are empty)
81
78
  xml += `</roblox>\n`;
82
79
  fs_1.default.writeFileSync(OUTPUT_FILE, xml, 'utf-8');
83
80
  console.log(`[Build] Berhasil membuat ${OUTPUT_FILE}`);
@@ -111,5 +108,14 @@ function copyToRobloxPlugins() {
111
108
  else {
112
109
  console.log(`[Build] Platform ${process.platform} tidak didukung untuk auto-copy plugin.`);
113
110
  }
111
+ // Also update ~/.nvstudio-mcp/studio-plugin/nvstudio_mcp.rbxmx if present
112
+ const homeClonePlugin = path_1.default.join(os_1.default.homedir(), '.nvstudio-mcp', 'studio-plugin', 'nvstudio_mcp.rbxmx');
113
+ if (fs_1.default.existsSync(path_1.default.dirname(homeClonePlugin))) {
114
+ try {
115
+ fs_1.default.copyFileSync(OUTPUT_FILE, homeClonePlugin);
116
+ console.log(`[Build] ✅ Berhasil memperbarui ${homeClonePlugin}`);
117
+ }
118
+ catch { }
119
+ }
114
120
  }
115
121
  buildRbxmx();
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@adiholick/nvstudio-mcp",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Local Bridge Server MCP for Roblox Studio",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
- "nvstudio-mcp": "./dist/index.js"
7
+ "nvstudio-mcp": "dist/index.js"
8
8
  },
9
9
  "files": [
10
10
  "dist",
@@ -13,7 +13,7 @@ local ui = nil
13
13
 
14
14
  -- Membangun Tombol Toolbar (Toggle)
15
15
  local toolbar = plugin:CreateToolbar("NVStudio MCP")
16
- local toggleBtn = toolbar:CreateButton("Dashboard", "Buka Dashboard NVStudio MCP", "rbxassetid://82696893597722")
16
+ local toggleBtn = toolbar:CreateButton("Dashboard", "Buka Dashboard NVStudio MCP", "rbxassetid://82696893597722", "NVStudio")
17
17
 
18
18
  -- Global State & Context AI
19
19
  local ctx = {
@@ -41,6 +41,14 @@ if uiModule then
41
41
  ui:setAutoConnectState(autoConnect)
42
42
  ui:setStatus("disconnected")
43
43
 
44
+ -- Sync state tombol toolbar dengan status widget
45
+ if ui.widget then
46
+ toggleBtn:SetActive(ui.widget.Enabled)
47
+ ui.widget:GetPropertyChangedSignal("Enabled"):Connect(function()
48
+ toggleBtn:SetActive(ui.widget.Enabled)
49
+ end)
50
+ end
51
+
44
52
  -- Event Toggle GUI Panel
45
53
  toggleBtn.Click:Connect(function()
46
54
  ui:toggleVisibility()
@@ -8,7 +8,7 @@
8
8
  <bool name="DefinesCapabilities">false</bool>
9
9
  <string name="Name">nvstudio_mcp</string>
10
10
  <int64 name="SourceAssetId">-1</int64>
11
- <SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
11
+ <BinaryString name="Tags"></BinaryString>
12
12
  </Properties>
13
13
  <Item class="ModuleScript" referent="RBX_create_instance">
14
14
  <Properties>
@@ -19,7 +19,7 @@
19
19
  <Content name="LinkedSource"><null></null></Content>
20
20
  <string name="Name">create_instance</string>
21
21
  <int64 name="SourceAssetId">-1</int64>
22
- <SharedString name="Tags"></SharedString>
22
+ <BinaryString name="Tags"></BinaryString>
23
23
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
24
24
  local success, config = pcall(function() return ctx.HttpService:JSONDecode(data) end)
25
25
  if not success or not config.ClassName then
@@ -56,7 +56,7 @@ end
56
56
  <Content name="LinkedSource"><null></null></Content>
57
57
  <string name="Name">delete_instance</string>
58
58
  <int64 name="SourceAssetId">-1</int64>
59
- <SharedString name="Tags"></SharedString>
59
+ <BinaryString name="Tags"></BinaryString>
60
60
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
61
61
  -- Guardrail: Cegah penghapusan Service utama
62
62
  local protectedServices = {
@@ -100,7 +100,7 @@ end
100
100
  <Content name="LinkedSource"><null></null></Content>
101
101
  <string name="Name">execute_luau</string>
102
102
  <int64 name="SourceAssetId">-1</int64>
103
- <SharedString name="Tags"></SharedString>
103
+ <BinaryString name="Tags"></BinaryString>
104
104
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
105
105
  local code = data
106
106
  if not code or code == "" then
@@ -184,7 +184,7 @@ end
184
184
  <Content name="LinkedSource"><null></null></Content>
185
185
  <string name="Name">generate_terrain</string>
186
186
  <int64 name="SourceAssetId">-1</int64>
187
- <SharedString name="Tags"></SharedString>
187
+ <BinaryString name="Tags"></BinaryString>
188
188
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
189
189
  local success, config = pcall(function() return ctx.HttpService:JSONDecode(data) end)
190
190
  if not success or not config.Size or not config.Position or not config.Material then
@@ -220,7 +220,7 @@ end
220
220
  <Content name="LinkedSource"><null></null></Content>
221
221
  <string name="Name">get_children</string>
222
222
  <int64 name="SourceAssetId">-1</int64>
223
- <SharedString name="Tags"></SharedString>
223
+ <BinaryString name="Tags"></BinaryString>
224
224
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
225
225
  local children = {}
226
226
  for _, child in ipairs(targetInstance:GetChildren()) do
@@ -240,7 +240,7 @@ end
240
240
  <Content name="LinkedSource"><null></null></Content>
241
241
  <string name="Name">get_logs</string>
242
242
  <int64 name="SourceAssetId">-1</int64>
243
- <SharedString name="Tags"></SharedString>
243
+ <BinaryString name="Tags"></BinaryString>
244
244
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
245
245
  if #ctx.recentLogs == 0 then
246
246
  return { status = "success", result = {"Console kosong. Tidak ada error atau log terbaru."} }
@@ -260,7 +260,7 @@ end
260
260
  <Content name="LinkedSource"><null></null></Content>
261
261
  <string name="Name">get_properties</string>
262
262
  <int64 name="SourceAssetId">-1</int64>
263
- <SharedString name="Tags"></SharedString>
263
+ <BinaryString name="Tags"></BinaryString>
264
264
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
265
265
  local CollectionService = game:GetService("CollectionService")
266
266
 
@@ -325,7 +325,7 @@ end
325
325
  <Content name="LinkedSource"><null></null></Content>
326
326
  <string name="Name">get_script_source</string>
327
327
  <int64 name="SourceAssetId">-1</int64>
328
- <SharedString name="Tags"></SharedString>
328
+ <BinaryString name="Tags"></BinaryString>
329
329
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
330
330
  if targetInstance:IsA("LuaSourceContainer") then
331
331
  return { status = "success", result = targetInstance.Source }
@@ -345,7 +345,7 @@ end
345
345
  <Content name="LinkedSource"><null></null></Content>
346
346
  <string name="Name">insert_asset</string>
347
347
  <int64 name="SourceAssetId">-1</int64>
348
- <SharedString name="Tags"></SharedString>
348
+ <BinaryString name="Tags"></BinaryString>
349
349
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
350
350
  local InsertService = game:GetService("InsertService")
351
351
  local assetId = tonumber(data)
@@ -367,16 +367,16 @@ end
367
367
  ]]></ProtectedString>
368
368
  </Properties>
369
369
  </Item>
370
- <Item class="Script" referent="RBX_Main_server">
370
+ <Item class="Script" referent="RBX_Main">
371
371
  <Properties>
372
372
  <BinaryString name="AttributesSerialize"></BinaryString>
373
373
  <SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
374
374
  <bool name="DefinesCapabilities">false</bool>
375
375
  <bool name="Disabled">false</bool>
376
376
  <Content name="LinkedSource"><null></null></Content>
377
- <string name="Name">Main.server</string>
377
+ <string name="Name">Main</string>
378
378
  <int64 name="SourceAssetId">-1</int64>
379
- <SharedString name="Tags"></SharedString>
379
+ <BinaryString name="Tags"></BinaryString>
380
380
  <token name="RunContext">0</token>
381
381
  <ProtectedString name="Source"><![CDATA[local HttpService = game:GetService("HttpService")
382
382
  local LogService = game:GetService("LogService")
@@ -393,7 +393,7 @@ local ui = nil
393
393
 
394
394
  -- Membangun Tombol Toolbar (Toggle)
395
395
  local toolbar = plugin:CreateToolbar("NVStudio MCP")
396
- local toggleBtn = toolbar:CreateButton("Dashboard", "Buka Dashboard NVStudio MCP", "rbxassetid://82696893597722")
396
+ local toggleBtn = toolbar:CreateButton("Dashboard", "Buka Dashboard NVStudio MCP", "rbxassetid://82696893597722", "NVStudio")
397
397
 
398
398
  -- Global State & Context AI
399
399
  local ctx = {
@@ -421,6 +421,14 @@ if uiModule then
421
421
  ui:setAutoConnectState(autoConnect)
422
422
  ui:setStatus("disconnected")
423
423
 
424
+ -- Sync state tombol toolbar dengan status widget
425
+ if ui.widget then
426
+ toggleBtn:SetActive(ui.widget.Enabled)
427
+ ui.widget:GetPropertyChangedSignal("Enabled"):Connect(function()
428
+ toggleBtn:SetActive(ui.widget.Enabled)
429
+ end)
430
+ end
431
+
424
432
  -- Event Toggle GUI Panel
425
433
  toggleBtn.Click:Connect(function()
426
434
  ui:toggleVisibility()
@@ -692,7 +700,7 @@ end)
692
700
  <Content name="LinkedSource"><null></null></Content>
693
701
  <string name="Name">patch_script_source</string>
694
702
  <int64 name="SourceAssetId">-1</int64>
695
- <SharedString name="Tags"></SharedString>
703
+ <BinaryString name="Tags"></BinaryString>
696
704
  <ProtectedString name="Source"><![CDATA[local HttpService = game:GetService("HttpService")
697
705
 
698
706
  return function(targetInstance, data, ctx, targetPath)
@@ -802,7 +810,7 @@ end
802
810
  <Content name="LinkedSource"><null></null></Content>
803
811
  <string name="Name">rollback_script</string>
804
812
  <int64 name="SourceAssetId">-1</int64>
805
- <SharedString name="Tags"></SharedString>
813
+ <BinaryString name="Tags"></BinaryString>
806
814
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
807
815
  if targetInstance:IsA("LuaSourceContainer") then
808
816
  local prevSource = ctx.scriptHistory[targetPath]
@@ -828,7 +836,7 @@ end
828
836
  <Content name="LinkedSource"><null></null></Content>
829
837
  <string name="Name">script_grep</string>
830
838
  <int64 name="SourceAssetId">-1</int64>
831
- <SharedString name="Tags"></SharedString>
839
+ <BinaryString name="Tags"></BinaryString>
832
840
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
833
841
  local query = tostring(data)
834
842
  if not query or query == "" then
@@ -904,7 +912,7 @@ end
904
912
  <Content name="LinkedSource"><null></null></Content>
905
913
  <string name="Name">search_instance</string>
906
914
  <int64 name="SourceAssetId">-1</int64>
907
- <SharedString name="Tags"></SharedString>
915
+ <BinaryString name="Tags"></BinaryString>
908
916
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
909
917
  local searchName = tostring(data)
910
918
  local results = {}
@@ -937,7 +945,7 @@ end
937
945
  <Content name="LinkedSource"><null></null></Content>
938
946
  <string name="Name">ui_manager</string>
939
947
  <int64 name="SourceAssetId">-1</int64>
940
- <SharedString name="Tags"></SharedString>
948
+ <BinaryString name="Tags"></BinaryString>
941
949
  <ProtectedString name="Source"><![CDATA[local UIManager = {}
942
950
  local widget = nil
943
951
 
@@ -945,8 +953,8 @@ function UIManager.init(pluginInstance, studioId)
945
953
  -- Membuat Widget Panel yang bisa di-dock (menempel)
946
954
  local widgetInfo = DockWidgetPluginGuiInfo.new(
947
955
  Enum.InitialDockState.Right,
948
- false, -- Initially hidden until toolbar button is clicked
949
- false, -- Override enabled
956
+ true, -- Initially visible on first install so user immediately sees the dashboard
957
+ false, -- Don't override user's saved state on subsequent launches
950
958
  380, -- Default width
951
959
  520, -- Default height
952
960
  280, -- Min width
@@ -1393,7 +1401,7 @@ return UIManager
1393
1401
  <Content name="LinkedSource"><null></null></Content>
1394
1402
  <string name="Name">update_script_source</string>
1395
1403
  <int64 name="SourceAssetId">-1</int64>
1396
- <SharedString name="Tags"></SharedString>
1404
+ <BinaryString name="Tags"></BinaryString>
1397
1405
  <ProtectedString name="Source"><![CDATA[return function(targetInstance, data, ctx, targetPath)
1398
1406
  if targetInstance:IsA("LuaSourceContainer") then
1399
1407
  local newCode = data or ""
@@ -1414,7 +1422,4 @@ end
1414
1422
  </Properties>
1415
1423
  </Item>
1416
1424
  </Item>
1417
- <SharedStrings>
1418
- <SharedString md5="yuZpQdnvvUBOTYh1jqZ2cA=="></SharedString>
1419
- </SharedStrings>
1420
1425
  </roblox>
@@ -5,8 +5,8 @@ function UIManager.init(pluginInstance, studioId)
5
5
  -- Membuat Widget Panel yang bisa di-dock (menempel)
6
6
  local widgetInfo = DockWidgetPluginGuiInfo.new(
7
7
  Enum.InitialDockState.Right,
8
- false, -- Initially hidden until toolbar button is clicked
9
- false, -- Override enabled
8
+ true, -- Initially visible on first install so user immediately sees the dashboard
9
+ false, -- Don't override user's saved state on subsequent launches
10
10
  380, -- Default width
11
11
  520, -- Default height
12
12
  280, -- Min width