@adiholick/nvstudio-mcp 2.0.0 → 2.0.2
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 +43 -15
- package/dist/build-plugin.js +15 -9
- package/dist/index.js +1 -1
- package/package.json +4 -2
- package/public/index.html +360 -309
- package/studio-plugin/Main.server.lua +9 -1
- package/studio-plugin/nvstudio_mcp.rbxmx +14 -9
- package/studio-plugin/ui_manager.lua +2 -2
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
|
|
11
|
+
Langkah instalasi dirancang **sangat mudah dan otomatis!**
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
---
|
|
44
60
|
|
|
45
|
-
|
|
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
|
-
*(
|
|
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
|
|
package/dist/build-plugin.js
CHANGED
|
@@ -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"
|
|
42
|
+
xml += `\t\t\t<SharedString name="Tags"></SharedString>\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
|
-
|
|
53
|
-
|
|
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,7 +59,7 @@ 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">${
|
|
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
64
|
xml += `\t\t\t\t<SharedString name="Tags"></SharedString>\n`;
|
|
64
65
|
if (isMain) {
|
|
@@ -73,11 +74,7 @@ function buildRbxmx() {
|
|
|
73
74
|
}
|
|
74
75
|
// Close Folder
|
|
75
76
|
xml += `\t</Item>\n`;
|
|
76
|
-
//
|
|
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/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adiholick/nvstudio-mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Local Bridge Server MCP for Roblox Studio",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"bin":
|
|
6
|
+
"bin": {
|
|
7
|
+
"nvstudio-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
7
9
|
"files": [
|
|
8
10
|
"dist",
|
|
9
11
|
"public",
|
package/public/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>nvstudio-mcp | Dashboard</title>
|
|
6
|
+
<title>nvstudio-mcp | Bridge Dashboard</title>
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
@@ -11,23 +11,29 @@
|
|
|
11
11
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
12
12
|
|
|
13
13
|
:root {
|
|
14
|
-
--bg: #
|
|
15
|
-
--surface: #
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
|
|
22
|
-
--text
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--success:
|
|
30
|
-
--
|
|
14
|
+
--bg: #000000;
|
|
15
|
+
--surface: #0a0a0a;
|
|
16
|
+
--surface-glass: rgba(10, 10, 10, 0.7);
|
|
17
|
+
--surface-card: #121212;
|
|
18
|
+
--surface-hover: #1c1c1c;
|
|
19
|
+
--border: rgba(255, 255, 255, 0.08);
|
|
20
|
+
--border-highlight: rgba(255, 255, 255, 0.15);
|
|
21
|
+
|
|
22
|
+
--text: #f4f4f5;
|
|
23
|
+
--text-muted: #a1a1aa;
|
|
24
|
+
--text-dim: #71717a;
|
|
25
|
+
|
|
26
|
+
--accent: #3b82f6;
|
|
27
|
+
--accent-glow: rgba(59, 130, 246, 0.2);
|
|
28
|
+
--success: #10b981;
|
|
29
|
+
--success-glow: rgba(16, 185, 129, 0.25);
|
|
30
|
+
--error: #ef4444;
|
|
31
|
+
--error-glow: rgba(239, 68, 68, 0.2);
|
|
32
|
+
--task: #8b5cf6;
|
|
33
|
+
--response: #14b8a6;
|
|
34
|
+
--system: #a1a1aa;
|
|
35
|
+
|
|
36
|
+
--sidebar-w: 260px;
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
html, body {
|
|
@@ -37,421 +43,437 @@
|
|
|
37
43
|
font-family: 'Inter', sans-serif;
|
|
38
44
|
font-size: 14px;
|
|
39
45
|
overflow: hidden;
|
|
46
|
+
-webkit-font-smoothing: antialiased;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/* ── LAYOUT ── */
|
|
43
50
|
.layout {
|
|
44
51
|
display: flex;
|
|
45
52
|
height: 100vh;
|
|
53
|
+
background: radial-gradient(circle at 50% 0%, rgba(20, 20, 25, 1) 0%, rgba(0, 0, 0, 1) 100%);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
/* ── SIDEBAR ── */
|
|
49
57
|
.sidebar {
|
|
50
58
|
width: var(--sidebar-w);
|
|
51
59
|
min-width: var(--sidebar-w);
|
|
52
|
-
background: var(--surface);
|
|
60
|
+
background: var(--surface-glass);
|
|
61
|
+
backdrop-filter: blur(12px);
|
|
53
62
|
border-right: 1px solid var(--border);
|
|
54
63
|
display: flex;
|
|
55
64
|
flex-direction: column;
|
|
56
65
|
overflow: hidden;
|
|
66
|
+
z-index: 10;
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
.sidebar-logo {
|
|
60
|
-
padding: 24px
|
|
70
|
+
padding: 24px;
|
|
61
71
|
border-bottom: 1px solid var(--border);
|
|
62
72
|
}
|
|
63
73
|
.sidebar-logo .logo-mark {
|
|
64
74
|
display: flex;
|
|
65
75
|
align-items: center;
|
|
66
|
-
gap:
|
|
76
|
+
gap: 14px;
|
|
67
77
|
}
|
|
68
78
|
.sidebar-logo .logo-icon {
|
|
69
|
-
width:
|
|
70
|
-
background:
|
|
71
|
-
border-radius:
|
|
79
|
+
width: 38px; height: 38px;
|
|
80
|
+
background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%);
|
|
81
|
+
border-radius: 10px;
|
|
72
82
|
display: flex; align-items: center; justify-content: center;
|
|
73
83
|
flex-shrink: 0;
|
|
84
|
+
box-shadow: 0 4px 12px var(--accent-glow);
|
|
74
85
|
}
|
|
75
|
-
.sidebar-logo .logo-icon svg { color: #
|
|
86
|
+
.sidebar-logo .logo-icon svg { color: #fff; width: 22px; height: 22px; }
|
|
87
|
+
|
|
88
|
+
.logo-text-wrapper { display: flex; flex-direction: column; }
|
|
76
89
|
.sidebar-logo .logo-name {
|
|
77
|
-
font-size:
|
|
90
|
+
font-size: 14px;
|
|
78
91
|
font-weight: 700;
|
|
79
|
-
color: var(--
|
|
92
|
+
color: var(--text);
|
|
80
93
|
letter-spacing: 0.02em;
|
|
81
94
|
}
|
|
82
95
|
.sidebar-logo .logo-sub {
|
|
83
|
-
font-size:
|
|
84
|
-
color: var(--
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
font-size: 11px;
|
|
97
|
+
color: var(--accent);
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
margin-top: 3px;
|
|
100
|
+
letter-spacing: 0.05em;
|
|
87
101
|
text-transform: uppercase;
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
/* Status Indicator */
|
|
91
105
|
.sidebar-status {
|
|
92
|
-
padding:
|
|
106
|
+
padding: 18px 24px;
|
|
93
107
|
border-bottom: 1px solid var(--border);
|
|
94
108
|
display: flex;
|
|
95
109
|
align-items: center;
|
|
96
|
-
gap:
|
|
110
|
+
gap: 12px;
|
|
111
|
+
background: rgba(0, 0, 0, 0.2);
|
|
112
|
+
}
|
|
113
|
+
.status-indicator-ring {
|
|
114
|
+
position: relative;
|
|
115
|
+
width: 10px; height: 10px;
|
|
116
|
+
display: flex; align-items: center; justify-content: center;
|
|
97
117
|
}
|
|
98
118
|
.status-dot {
|
|
99
|
-
width:
|
|
119
|
+
width: 10px; height: 10px;
|
|
100
120
|
border-radius: 50%;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
z-index: 2;
|
|
122
|
+
transition: all 0.3s ease;
|
|
123
|
+
}
|
|
124
|
+
.status-dot.connected { background: var(--success); box-shadow: 0 0 10px var(--success); }
|
|
125
|
+
.status-dot.waiting { background: var(--error); box-shadow: 0 0 10px var(--error); }
|
|
126
|
+
|
|
127
|
+
.status-ping {
|
|
128
|
+
position: absolute;
|
|
129
|
+
width: 100%; height: 100%;
|
|
130
|
+
border-radius: 50%;
|
|
131
|
+
background: inherit;
|
|
132
|
+
animation: ping 2s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
133
|
+
opacity: 0;
|
|
134
|
+
z-index: 1;
|
|
104
135
|
}
|
|
105
|
-
.status-dot.connected { background: var(--
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
color: var(--text-muted);
|
|
110
|
-
font-weight: 500;
|
|
111
|
-
letter-spacing: 0.06em;
|
|
112
|
-
text-transform: uppercase;
|
|
136
|
+
.status-dot.connected + .status-ping { background: var(--success); opacity: 0.5; }
|
|
137
|
+
|
|
138
|
+
@keyframes ping {
|
|
139
|
+
75%, 100% { transform: scale(2.5); opacity: 0; }
|
|
113
140
|
}
|
|
114
141
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
142
|
+
.status-label {
|
|
143
|
+
font-size: 13px;
|
|
144
|
+
font-weight: 500;
|
|
145
|
+
color: var(--text);
|
|
118
146
|
}
|
|
119
147
|
|
|
120
|
-
/*
|
|
148
|
+
/* Navigation */
|
|
121
149
|
.sidebar-nav {
|
|
122
150
|
flex: 1;
|
|
123
|
-
padding: 12px
|
|
151
|
+
padding: 20px 12px;
|
|
124
152
|
overflow-y: auto;
|
|
125
153
|
}
|
|
126
154
|
.nav-group-label {
|
|
127
|
-
font-size:
|
|
155
|
+
font-size: 11px;
|
|
128
156
|
font-weight: 600;
|
|
129
157
|
color: var(--text-dim);
|
|
130
|
-
letter-spacing: 0.1em;
|
|
131
158
|
text-transform: uppercase;
|
|
132
|
-
|
|
159
|
+
letter-spacing: 0.1em;
|
|
160
|
+
margin: 0 12px 8px;
|
|
133
161
|
}
|
|
134
162
|
.nav-item {
|
|
135
163
|
display: flex;
|
|
136
164
|
align-items: center;
|
|
137
165
|
gap: 10px;
|
|
138
|
-
padding:
|
|
139
|
-
|
|
166
|
+
padding: 10px 12px;
|
|
167
|
+
border-radius: 8px;
|
|
140
168
|
color: var(--text-muted);
|
|
141
|
-
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
transition: all 0.2s;
|
|
171
|
+
margin-bottom: 4px;
|
|
142
172
|
font-weight: 500;
|
|
143
|
-
transition: color 0.15s, background 0.15s;
|
|
144
|
-
border-left: 2px solid transparent;
|
|
145
|
-
user-select: none;
|
|
146
173
|
}
|
|
147
|
-
.nav-item:hover {
|
|
148
|
-
|
|
149
|
-
|
|
174
|
+
.nav-item:hover {
|
|
175
|
+
background: var(--surface-hover);
|
|
176
|
+
color: var(--text);
|
|
177
|
+
}
|
|
178
|
+
.nav-item.active {
|
|
179
|
+
background: rgba(59, 130, 246, 0.1);
|
|
180
|
+
color: var(--accent);
|
|
181
|
+
border-left: 2px solid var(--accent);
|
|
182
|
+
}
|
|
183
|
+
.nav-item svg { width: 16px; height: 16px; stroke: currentColor; stroke-width: 2; fill: none; }
|
|
150
184
|
|
|
151
185
|
.sidebar-footer {
|
|
152
|
-
padding:
|
|
186
|
+
padding: 20px 24px;
|
|
153
187
|
border-top: 1px solid var(--border);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
188
|
+
display: flex;
|
|
189
|
+
justify-content: space-between;
|
|
190
|
+
align-items: center;
|
|
191
|
+
font-size: 12px;
|
|
157
192
|
color: var(--text-dim);
|
|
158
|
-
|
|
193
|
+
}
|
|
194
|
+
.badge-v2 {
|
|
195
|
+
background: rgba(59, 130, 246, 0.15);
|
|
196
|
+
color: var(--accent);
|
|
197
|
+
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
198
|
+
padding: 3px 8px;
|
|
199
|
+
border-radius: 4px;
|
|
200
|
+
font-weight: 600;
|
|
201
|
+
font-size: 10px;
|
|
202
|
+
letter-spacing: 0.05em;
|
|
159
203
|
}
|
|
160
204
|
|
|
161
|
-
/* ── MAIN ── */
|
|
205
|
+
/* ── MAIN CONTENT ── */
|
|
162
206
|
.main {
|
|
163
207
|
flex: 1;
|
|
164
208
|
display: flex;
|
|
165
209
|
flex-direction: column;
|
|
166
210
|
overflow: hidden;
|
|
167
|
-
|
|
211
|
+
position: relative;
|
|
168
212
|
}
|
|
169
213
|
|
|
170
|
-
/* Topbar */
|
|
171
214
|
.topbar {
|
|
172
|
-
height:
|
|
215
|
+
height: 70px;
|
|
216
|
+
padding: 0 32px;
|
|
217
|
+
border-bottom: 1px solid var(--border);
|
|
173
218
|
display: flex;
|
|
174
219
|
align-items: center;
|
|
175
220
|
justify-content: space-between;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
221
|
+
background: rgba(0, 0, 0, 0.4);
|
|
222
|
+
backdrop-filter: blur(10px);
|
|
223
|
+
z-index: 5;
|
|
179
224
|
}
|
|
180
225
|
.page-title {
|
|
181
|
-
font-size:
|
|
226
|
+
font-size: 18px;
|
|
182
227
|
font-weight: 600;
|
|
183
|
-
color: var(--
|
|
184
|
-
}
|
|
185
|
-
.topbar-right {
|
|
228
|
+
color: var(--text);
|
|
186
229
|
display: flex;
|
|
187
230
|
align-items: center;
|
|
188
231
|
gap: 12px;
|
|
189
232
|
}
|
|
233
|
+
|
|
234
|
+
.topbar-right { display: flex; align-items: center; gap: 16px; }
|
|
190
235
|
.topbar-badge {
|
|
191
|
-
font-size:
|
|
236
|
+
font-size: 12px;
|
|
192
237
|
font-weight: 500;
|
|
193
|
-
font-family: 'JetBrains Mono', monospace;
|
|
194
238
|
color: var(--text-muted);
|
|
195
|
-
background: var(--
|
|
239
|
+
background: var(--surface-card);
|
|
196
240
|
border: 1px solid var(--border);
|
|
197
241
|
padding: 4px 10px;
|
|
198
242
|
border-radius: 20px;
|
|
199
243
|
}
|
|
200
244
|
.clear-btn {
|
|
201
|
-
|
|
202
|
-
font-weight: 500;
|
|
203
|
-
color: var(--text-dim);
|
|
204
|
-
background: none;
|
|
245
|
+
background: transparent;
|
|
205
246
|
border: 1px solid var(--border);
|
|
247
|
+
color: var(--text-muted);
|
|
248
|
+
font-family: inherit;
|
|
249
|
+
font-size: 12px;
|
|
250
|
+
font-weight: 500;
|
|
206
251
|
padding: 5px 12px;
|
|
207
252
|
border-radius: 6px;
|
|
208
253
|
cursor: pointer;
|
|
209
|
-
transition:
|
|
254
|
+
transition: all 0.2s;
|
|
255
|
+
}
|
|
256
|
+
.clear-btn:hover {
|
|
257
|
+
background: var(--surface-hover);
|
|
258
|
+
color: var(--text);
|
|
259
|
+
border-color: var(--text-dim);
|
|
210
260
|
}
|
|
211
|
-
.clear-btn:hover { color: var(--white); border-color: var(--border2); }
|
|
212
261
|
|
|
213
|
-
/* Content */
|
|
214
262
|
.content {
|
|
215
263
|
flex: 1;
|
|
216
|
-
overflow: hidden;
|
|
217
264
|
position: relative;
|
|
265
|
+
overflow: hidden;
|
|
218
266
|
}
|
|
219
|
-
|
|
220
|
-
/* ── PAGES ── */
|
|
221
267
|
.page {
|
|
222
|
-
|
|
223
|
-
|
|
268
|
+
position: absolute;
|
|
269
|
+
inset: 0;
|
|
270
|
+
padding: 32px;
|
|
224
271
|
overflow-y: auto;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
272
|
+
opacity: 0;
|
|
273
|
+
pointer-events: none;
|
|
274
|
+
transition: opacity 0.3s ease;
|
|
275
|
+
}
|
|
276
|
+
.page.active {
|
|
277
|
+
opacity: 1;
|
|
278
|
+
pointer-events: auto;
|
|
228
279
|
}
|
|
229
|
-
.page.active { display: flex; }
|
|
230
280
|
|
|
231
281
|
/* ── ACTIVITY LOG PAGE ── */
|
|
232
|
-
|
|
233
|
-
padding: 0;
|
|
234
|
-
}
|
|
235
|
-
#page-activity .log-toolbar {
|
|
282
|
+
.log-toolbar {
|
|
236
283
|
display: flex;
|
|
237
|
-
align-items: center;
|
|
238
284
|
justify-content: space-between;
|
|
239
|
-
|
|
285
|
+
align-items: center;
|
|
286
|
+
padding: 16px 32px;
|
|
287
|
+
background: var(--surface-glass);
|
|
240
288
|
border-bottom: 1px solid var(--border);
|
|
241
|
-
flex-shrink: 0;
|
|
242
|
-
gap: 12px;
|
|
243
|
-
}
|
|
244
|
-
.filter-btns {
|
|
245
|
-
display: flex;
|
|
246
|
-
gap: 4px;
|
|
247
289
|
}
|
|
290
|
+
.filter-btns { display: flex; gap: 8px; }
|
|
248
291
|
.filter-btn {
|
|
249
|
-
|
|
250
|
-
font-weight: 500;
|
|
251
|
-
padding: 5px 12px;
|
|
252
|
-
border-radius: 5px;
|
|
292
|
+
background: var(--surface-card);
|
|
253
293
|
border: 1px solid var(--border);
|
|
254
|
-
background: none;
|
|
255
294
|
color: var(--text-muted);
|
|
295
|
+
font-family: inherit;
|
|
296
|
+
font-size: 12px;
|
|
297
|
+
padding: 6px 14px;
|
|
298
|
+
border-radius: 6px;
|
|
256
299
|
cursor: pointer;
|
|
257
|
-
transition: all 0.
|
|
300
|
+
transition: all 0.2s;
|
|
301
|
+
font-weight: 500;
|
|
302
|
+
}
|
|
303
|
+
.filter-btn:hover { background: var(--surface-hover); color: var(--text); }
|
|
304
|
+
.filter-btn.active {
|
|
305
|
+
background: var(--text);
|
|
306
|
+
color: var(--bg);
|
|
307
|
+
border-color: var(--text);
|
|
258
308
|
}
|
|
259
|
-
.filter-btn:hover { color: var(--white); border-color: var(--border2); }
|
|
260
|
-
.filter-btn.active { background: var(--white); color: #000; border-color: var(--white); }
|
|
261
309
|
|
|
262
310
|
.scroll-lock-btn {
|
|
263
|
-
display: flex;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
font-size: 11px;
|
|
267
|
-
color: var(--text-dim);
|
|
268
|
-
background: none;
|
|
269
|
-
border: 1px solid var(--border);
|
|
270
|
-
padding: 5px 10px;
|
|
271
|
-
border-radius: 5px;
|
|
272
|
-
cursor: pointer;
|
|
273
|
-
transition: all 0.15s;
|
|
311
|
+
display: flex; align-items: center; gap: 6px;
|
|
312
|
+
background: transparent; border: none;
|
|
313
|
+
color: var(--text-muted); font-family: inherit; font-size: 12px; cursor: pointer; transition: 0.2s;
|
|
274
314
|
}
|
|
275
|
-
.scroll-lock-btn:hover { color: var(--text
|
|
276
|
-
.scroll-lock-btn
|
|
277
|
-
.scroll-lock-btn svg {
|
|
315
|
+
.scroll-lock-btn:hover { color: var(--text); }
|
|
316
|
+
.scroll-lock-btn svg { width: 14px; height: 14px; opacity: 0.5; }
|
|
317
|
+
.scroll-lock-btn.locked svg { opacity: 1; stroke: var(--accent); }
|
|
318
|
+
.scroll-lock-btn.locked { color: var(--accent); }
|
|
278
319
|
|
|
279
320
|
.log-list {
|
|
280
321
|
flex: 1;
|
|
281
322
|
overflow-y: auto;
|
|
282
|
-
padding:
|
|
283
|
-
}
|
|
284
|
-
.log-entry {
|
|
323
|
+
padding: 16px 32px;
|
|
285
324
|
display: flex;
|
|
286
|
-
|
|
287
|
-
gap:
|
|
288
|
-
|
|
289
|
-
border-bottom: 1px solid var(--border);
|
|
290
|
-
font-family: 'JetBrains Mono', monospace;
|
|
291
|
-
font-size: 12px;
|
|
292
|
-
line-height: 1.6;
|
|
293
|
-
transition: background 0.1s;
|
|
294
|
-
}
|
|
295
|
-
.log-entry:hover { background: var(--surface2); }
|
|
296
|
-
.log-entry-time {
|
|
297
|
-
color: var(--text-dim);
|
|
298
|
-
min-width: 80px;
|
|
299
|
-
flex-shrink: 0;
|
|
300
|
-
margin-right: 16px;
|
|
301
|
-
}
|
|
302
|
-
.log-entry-badge {
|
|
303
|
-
display: inline-flex;
|
|
304
|
-
align-items: center;
|
|
305
|
-
font-size: 9px;
|
|
306
|
-
font-weight: 700;
|
|
307
|
-
letter-spacing: 0.08em;
|
|
308
|
-
text-transform: uppercase;
|
|
309
|
-
padding: 2px 6px;
|
|
310
|
-
border-radius: 3px;
|
|
311
|
-
margin-right: 12px;
|
|
312
|
-
flex-shrink: 0;
|
|
313
|
-
min-width: 60px;
|
|
314
|
-
justify-content: center;
|
|
315
|
-
font-family: 'Inter', sans-serif;
|
|
325
|
+
flex-direction: column;
|
|
326
|
+
gap: 8px;
|
|
327
|
+
height: calc(100vh - 140px); /* 70px topbar + ~70px toolbar */
|
|
316
328
|
}
|
|
317
|
-
|
|
318
|
-
.badge-response { background: rgba(255,255,255,0.06); color: var(--response); }
|
|
319
|
-
.badge-error { background: rgba(255,85,85,0.12); color: var(--error); }
|
|
320
|
-
.badge-system { background: rgba(255,255,255,0.04); color: var(--system); }
|
|
321
|
-
.log-entry-msg { color: var(--text); word-break: break-word; }
|
|
322
|
-
|
|
329
|
+
|
|
323
330
|
.log-empty {
|
|
324
331
|
display: flex;
|
|
325
332
|
flex-direction: column;
|
|
326
333
|
align-items: center;
|
|
327
334
|
justify-content: center;
|
|
328
335
|
height: 100%;
|
|
336
|
+
color: var(--text-dim);
|
|
337
|
+
gap: 16px;
|
|
338
|
+
}
|
|
339
|
+
.log-empty svg { width: 48px; height: 48px; opacity: 0.2; stroke: currentColor; stroke-width: 1.5; fill: none; }
|
|
340
|
+
.log-empty p { font-size: 14px; font-weight: 500; }
|
|
341
|
+
|
|
342
|
+
.log-entry {
|
|
343
|
+
display: flex;
|
|
329
344
|
gap: 12px;
|
|
345
|
+
padding: 12px 16px;
|
|
346
|
+
background: rgba(255, 255, 255, 0.02);
|
|
347
|
+
border: 1px solid var(--border);
|
|
348
|
+
border-radius: 8px;
|
|
349
|
+
font-family: 'JetBrains Mono', monospace;
|
|
350
|
+
font-size: 13px;
|
|
351
|
+
line-height: 1.5;
|
|
352
|
+
transition: transform 0.2s, background 0.2s;
|
|
353
|
+
}
|
|
354
|
+
.log-entry:hover {
|
|
355
|
+
background: rgba(255, 255, 255, 0.04);
|
|
356
|
+
border-color: var(--border-highlight);
|
|
357
|
+
}
|
|
358
|
+
.log-entry-time {
|
|
330
359
|
color: var(--text-dim);
|
|
360
|
+
font-size: 12px;
|
|
361
|
+
flex-shrink: 0;
|
|
362
|
+
white-space: nowrap;
|
|
331
363
|
}
|
|
332
|
-
.log-
|
|
333
|
-
|
|
364
|
+
.log-entry-badge {
|
|
365
|
+
font-size: 10px; font-weight: 700;
|
|
366
|
+
padding: 2px 8px; border-radius: 4px;
|
|
367
|
+
height: fit-content; flex-shrink: 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.badge-task { color: var(--task); background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(139, 92, 246, 0.3); }
|
|
371
|
+
.badge-response { color: var(--response); background: rgba(20, 184, 166, 0.15); border: 1px solid rgba(20, 184, 166, 0.3); }
|
|
372
|
+
.badge-error { color: var(--error); background: rgba(239, 68, 68, 0.15); border: 1px solid rgba(239, 68, 68, 0.3); }
|
|
373
|
+
.badge-system { color: var(--system); background: rgba(161, 161, 170, 0.15); border: 1px solid rgba(161, 161, 170, 0.3); }
|
|
374
|
+
|
|
375
|
+
.log-entry-msg { color: var(--text-primary); word-break: break-word; }
|
|
334
376
|
|
|
335
377
|
/* ── STATS PAGE ── */
|
|
336
378
|
.stats-grid {
|
|
337
379
|
display: grid;
|
|
338
|
-
grid-template-columns: repeat(
|
|
339
|
-
gap:
|
|
380
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
381
|
+
gap: 20px;
|
|
382
|
+
max-width: 1000px;
|
|
340
383
|
}
|
|
341
384
|
.stat-card {
|
|
342
|
-
background: var(--surface);
|
|
385
|
+
background: var(--surface-card);
|
|
343
386
|
border: 1px solid var(--border);
|
|
344
|
-
border-radius:
|
|
345
|
-
padding:
|
|
346
|
-
|
|
347
|
-
flex-direction: column;
|
|
348
|
-
gap: 8px;
|
|
349
|
-
}
|
|
350
|
-
.stat-card.full { grid-column: 1 / -1; }
|
|
351
|
-
.stat-card-label {
|
|
352
|
-
font-size: 11px;
|
|
353
|
-
font-weight: 600;
|
|
354
|
-
color: var(--text-dim);
|
|
355
|
-
letter-spacing: 0.08em;
|
|
356
|
-
text-transform: uppercase;
|
|
357
|
-
}
|
|
358
|
-
.stat-card-value {
|
|
359
|
-
font-size: 36px;
|
|
360
|
-
font-weight: 700;
|
|
361
|
-
color: var(--white);
|
|
362
|
-
font-family: 'JetBrains Mono', monospace;
|
|
363
|
-
line-height: 1;
|
|
364
|
-
}
|
|
365
|
-
.stat-card-sub {
|
|
366
|
-
font-size: 12px;
|
|
367
|
-
color: var(--text-muted);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
.progress-bar-wrap {
|
|
371
|
-
height: 4px;
|
|
372
|
-
background: var(--surface3);
|
|
373
|
-
border-radius: 2px;
|
|
387
|
+
border-radius: 12px;
|
|
388
|
+
padding: 24px;
|
|
389
|
+
position: relative;
|
|
374
390
|
overflow: hidden;
|
|
375
|
-
margin-top: 4px;
|
|
376
391
|
}
|
|
377
|
-
.
|
|
378
|
-
|
|
379
|
-
background: var(--
|
|
380
|
-
|
|
381
|
-
transition: width 0.6s ease;
|
|
382
|
-
min-width: 2px;
|
|
392
|
+
.stat-card::before {
|
|
393
|
+
content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 2px;
|
|
394
|
+
background: linear-gradient(90deg, transparent, var(--border-highlight), transparent);
|
|
395
|
+
opacity: 0; transition: opacity 0.3s;
|
|
383
396
|
}
|
|
397
|
+
.stat-card:hover::before { opacity: 1; }
|
|
398
|
+
.stat-card.full { grid-column: 1 / -1; }
|
|
399
|
+
|
|
400
|
+
.stat-card-label { font-size: 13px; font-weight: 500; color: var(--text-muted); margin-bottom: 8px; }
|
|
401
|
+
.stat-card-value { font-size: 32px; font-weight: 700; color: var(--text); letter-spacing: -0.02em; margin-bottom: 4px; }
|
|
402
|
+
.stat-card-sub { font-size: 12px; color: var(--text-dim); }
|
|
403
|
+
|
|
404
|
+
.progress-bar-wrap { height: 4px; background: rgba(255,255,255,0.05); border-radius: 2px; margin-top: 16px; overflow: hidden; }
|
|
405
|
+
.progress-bar { height: 100%; background: var(--success); border-radius: 2px; transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1); }
|
|
384
406
|
.progress-bar.error { background: var(--error); }
|
|
385
407
|
|
|
386
|
-
|
|
387
|
-
.info-
|
|
388
|
-
.info-row {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
font-size: 13px;
|
|
393
|
-
}
|
|
394
|
-
.info-row-label { color: var(--text-muted); }
|
|
395
|
-
.info-row-value {
|
|
396
|
-
font-family: 'JetBrains Mono', monospace;
|
|
397
|
-
font-size: 12px;
|
|
398
|
-
color: var(--white);
|
|
399
|
-
background: var(--surface2);
|
|
400
|
-
padding: 3px 8px;
|
|
401
|
-
border-radius: 4px;
|
|
402
|
-
}
|
|
403
|
-
|
|
408
|
+
.info-list { display: flex; flex-direction: column; gap: 16px; margin-top: 20px; }
|
|
409
|
+
.info-row { display: flex; justify-content: space-between; align-items: center; padding-bottom: 16px; border-bottom: 1px dashed var(--border); }
|
|
410
|
+
.info-row:last-child { border-bottom: none; padding-bottom: 0; }
|
|
411
|
+
.info-row-label { font-size: 13px; color: var(--text-muted); }
|
|
412
|
+
.info-row-value { font-size: 14px; font-weight: 600; color: var(--text); font-family: 'JetBrains Mono', monospace; }
|
|
413
|
+
|
|
404
414
|
/* ── ABOUT PAGE ── */
|
|
405
|
-
.about-section { max-width:
|
|
415
|
+
.about-section { max-width: 680px; }
|
|
406
416
|
.about-hero {
|
|
407
|
-
background: var(--surface);
|
|
417
|
+
background: linear-gradient(145deg, var(--surface-card) 0%, rgba(10,10,10,1) 100%);
|
|
408
418
|
border: 1px solid var(--border);
|
|
409
|
-
border-radius:
|
|
410
|
-
padding:
|
|
419
|
+
border-radius: 16px;
|
|
420
|
+
padding: 40px;
|
|
411
421
|
display: flex;
|
|
412
422
|
flex-direction: column;
|
|
413
|
-
gap:
|
|
414
|
-
margin-bottom:
|
|
423
|
+
gap: 16px;
|
|
424
|
+
margin-bottom: 24px;
|
|
425
|
+
position: relative;
|
|
426
|
+
overflow: hidden;
|
|
427
|
+
}
|
|
428
|
+
.about-hero::after {
|
|
429
|
+
content: ''; position: absolute; top: -50%; right: -20%; width: 400px; height: 400px;
|
|
430
|
+
background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%);
|
|
431
|
+
pointer-events: none;
|
|
415
432
|
}
|
|
416
|
-
.about-hero h2 { font-size:
|
|
417
|
-
.
|
|
418
|
-
.about-
|
|
433
|
+
.about-hero h2 { font-size: 28px; font-weight: 700; color: var(--text); letter-spacing: -0.02em; display: flex; align-items: center; gap: 12px;}
|
|
434
|
+
.v2-chip { font-size: 12px; background: var(--accent); color: white; padding: 2px 8px; border-radius: 12px; vertical-align: middle;}
|
|
435
|
+
.about-hero p { font-size: 15px; color: var(--text-muted); line-height: 1.6; }
|
|
436
|
+
|
|
437
|
+
.about-links { display: flex; gap: 12px; margin-top: 8px; }
|
|
419
438
|
.about-link {
|
|
420
|
-
font-size:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
padding: 7px 14px;
|
|
426
|
-
border-radius: 6px;
|
|
427
|
-
text-decoration: none;
|
|
428
|
-
transition: all 0.15s;
|
|
429
|
-
display: inline-flex;
|
|
430
|
-
align-items: center;
|
|
431
|
-
gap: 6px;
|
|
439
|
+
font-size: 13px; font-weight: 600; color: var(--text);
|
|
440
|
+
background: rgba(255,255,255,0.05); border: 1px solid var(--border);
|
|
441
|
+
padding: 10px 18px; border-radius: 8px;
|
|
442
|
+
text-decoration: none; transition: all 0.2s;
|
|
443
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
432
444
|
}
|
|
433
|
-
.about-link:hover {
|
|
445
|
+
.about-link:hover { background: rgba(255,255,255,0.1); border-color: var(--border-highlight); transform: translateY(-1px); }
|
|
434
446
|
|
|
435
447
|
.about-card {
|
|
436
|
-
background: var(--surface);
|
|
448
|
+
background: var(--surface-card);
|
|
437
449
|
border: 1px solid var(--border);
|
|
438
|
-
border-radius:
|
|
439
|
-
padding:
|
|
440
|
-
margin-bottom:
|
|
450
|
+
border-radius: 12px;
|
|
451
|
+
padding: 24px 32px;
|
|
452
|
+
margin-bottom: 20px;
|
|
453
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
|
454
|
+
}
|
|
455
|
+
.about-card h3 { font-size: 16px; font-weight: 600; color: var(--text); margin-bottom: 16px; border-bottom: 1px solid var(--border); padding-bottom: 12px;}
|
|
456
|
+
.about-card ul { display: flex; flex-direction: column; gap: 12px; padding-left: 20px; }
|
|
457
|
+
.about-card li { font-size: 14px; color: var(--text-muted); line-height: 1.6; }
|
|
458
|
+
.about-card li strong { color: var(--text); }
|
|
459
|
+
.about-card code {
|
|
460
|
+
background: rgba(255,255,255,0.1);
|
|
461
|
+
color: var(--accent);
|
|
462
|
+
padding: 2px 6px;
|
|
463
|
+
border-radius: 4px;
|
|
464
|
+
font-family: 'JetBrains Mono', monospace;
|
|
465
|
+
font-size: 12px;
|
|
441
466
|
}
|
|
442
|
-
.about-card h3 { font-size: 13px; font-weight: 600; color: var(--white); margin-bottom: 10px; }
|
|
443
|
-
.about-card ul { display: flex; flex-direction: column; gap: 7px; padding-left: 16px; }
|
|
444
|
-
.about-card li { font-size: 13px; color: var(--text-muted); line-height: 1.5; }
|
|
445
467
|
|
|
446
468
|
/* Scrollbar */
|
|
447
|
-
::-webkit-scrollbar { width:
|
|
469
|
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
448
470
|
::-webkit-scrollbar-track { background: transparent; }
|
|
449
|
-
::-webkit-scrollbar-thumb { background:
|
|
450
|
-
::-webkit-scrollbar-thumb:hover { background:
|
|
471
|
+
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 3px; }
|
|
472
|
+
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
|
|
451
473
|
|
|
452
474
|
/* Transitions */
|
|
453
|
-
.log-entry { animation:
|
|
454
|
-
@keyframes
|
|
475
|
+
.log-entry { animation: slideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1); }
|
|
476
|
+
@keyframes slideIn { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
|
|
455
477
|
</style>
|
|
456
478
|
</head>
|
|
457
479
|
<body>
|
|
@@ -462,20 +484,23 @@
|
|
|
462
484
|
<div class="sidebar-logo">
|
|
463
485
|
<div class="logo-mark">
|
|
464
486
|
<div class="logo-icon">
|
|
465
|
-
<svg
|
|
487
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
466
488
|
<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
|
|
467
489
|
</svg>
|
|
468
490
|
</div>
|
|
469
|
-
<div>
|
|
491
|
+
<div class="logo-text-wrapper">
|
|
470
492
|
<div class="logo-name">nvstudio-mcp</div>
|
|
471
|
-
<div class="logo-sub">
|
|
493
|
+
<div class="logo-sub">Zero-Delay Engine</div>
|
|
472
494
|
</div>
|
|
473
495
|
</div>
|
|
474
496
|
</div>
|
|
475
497
|
|
|
476
498
|
<div class="sidebar-status">
|
|
477
|
-
<div class="status-
|
|
478
|
-
|
|
499
|
+
<div class="status-indicator-ring">
|
|
500
|
+
<div class="status-dot waiting" id="status-dot"></div>
|
|
501
|
+
<div class="status-ping"></div>
|
|
502
|
+
</div>
|
|
503
|
+
<span class="status-label" id="status-label">Menunggu Studio...</span>
|
|
479
504
|
</div>
|
|
480
505
|
|
|
481
506
|
<nav class="sidebar-nav">
|
|
@@ -489,15 +514,16 @@
|
|
|
489
514
|
Statistics
|
|
490
515
|
</div>
|
|
491
516
|
|
|
492
|
-
<div class="nav-group-label" style="margin-top:
|
|
517
|
+
<div class="nav-group-label" style="margin-top:20px;">Info</div>
|
|
493
518
|
<div class="nav-item" data-page="about">
|
|
494
519
|
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
|
495
|
-
About
|
|
520
|
+
About v2.0
|
|
496
521
|
</div>
|
|
497
522
|
</nav>
|
|
498
523
|
|
|
499
524
|
<div class="sidebar-footer">
|
|
500
|
-
<
|
|
525
|
+
<span class="badge-v2" id="pkg-version">v2.0.1</span>
|
|
526
|
+
<span style="font-size:10px; opacity:0.5;">Model Context Protocol</span>
|
|
501
527
|
</div>
|
|
502
528
|
</aside>
|
|
503
529
|
|
|
@@ -517,10 +543,10 @@
|
|
|
517
543
|
<div class="page active" id="page-activity" style="padding:0; gap:0;">
|
|
518
544
|
<div class="log-toolbar">
|
|
519
545
|
<div class="filter-btns">
|
|
520
|
-
<button class="filter-btn active" data-filter="all">All</button>
|
|
521
|
-
<button class="filter-btn" data-filter="task">Task</button>
|
|
546
|
+
<button class="filter-btn active" data-filter="all">All Logs</button>
|
|
547
|
+
<button class="filter-btn" data-filter="task">Task / Patch</button>
|
|
522
548
|
<button class="filter-btn" data-filter="response">Response</button>
|
|
523
|
-
<button class="filter-btn" data-filter="error">Error</button>
|
|
549
|
+
<button class="filter-btn" data-filter="error">Error / Validate</button>
|
|
524
550
|
<button class="filter-btn" data-filter="system">System</button>
|
|
525
551
|
</div>
|
|
526
552
|
<button class="scroll-lock-btn locked" id="scroll-lock-btn">
|
|
@@ -556,33 +582,37 @@
|
|
|
556
582
|
<div class="stat-card">
|
|
557
583
|
<div class="stat-card-label">Succeeded</div>
|
|
558
584
|
<div class="stat-card-value" id="stat-success">0</div>
|
|
559
|
-
<div class="stat-card-sub">Perintah berhasil</div>
|
|
585
|
+
<div class="stat-card-sub">Perintah berhasil dieksekusi</div>
|
|
560
586
|
</div>
|
|
561
587
|
<div class="stat-card">
|
|
562
588
|
<div class="stat-card-label">Failed</div>
|
|
563
589
|
<div class="stat-card-value" id="stat-error">0</div>
|
|
564
|
-
<div class="stat-card-sub">Perintah gagal</div>
|
|
590
|
+
<div class="stat-card-sub">Perintah gagal atau error sintaks</div>
|
|
565
591
|
<div class="progress-bar-wrap">
|
|
566
592
|
<div class="progress-bar error" id="bar-error" style="width:0%"></div>
|
|
567
593
|
</div>
|
|
568
594
|
</div>
|
|
569
595
|
<div class="stat-card full">
|
|
570
|
-
<div class="stat-card-label">Session
|
|
596
|
+
<div class="stat-card-label">Session Diagnostics</div>
|
|
571
597
|
<div class="info-list">
|
|
572
598
|
<div class="info-row">
|
|
573
|
-
<span class="info-row-label">
|
|
574
|
-
<span class="info-row-value"
|
|
599
|
+
<span class="info-row-label">Mode Polling</span>
|
|
600
|
+
<span class="info-row-value" style="color:var(--accent);">Zero-Delay Long-Polling (<50ms)</span>
|
|
601
|
+
</div>
|
|
602
|
+
<div class="info-row">
|
|
603
|
+
<span class="info-row-label">Luau Pre-Validation</span>
|
|
604
|
+
<span class="info-row-value" style="color:var(--success);">Active ✓</span>
|
|
575
605
|
</div>
|
|
576
606
|
<div class="info-row">
|
|
577
|
-
<span class="info-row-label">Port
|
|
607
|
+
<span class="info-row-label">Port Server</span>
|
|
578
608
|
<span class="info-row-value">3055</span>
|
|
579
609
|
</div>
|
|
580
610
|
<div class="info-row">
|
|
581
|
-
<span class="info-row-label">Studio
|
|
611
|
+
<span class="info-row-label">Studio Connection</span>
|
|
582
612
|
<span class="info-row-value" id="info-studio">Disconnected</span>
|
|
583
613
|
</div>
|
|
584
614
|
<div class="info-row">
|
|
585
|
-
<span class="info-row-label">Uptime</span>
|
|
615
|
+
<span class="info-row-label">Session Uptime</span>
|
|
586
616
|
<span class="info-row-value" id="info-uptime">—</span>
|
|
587
617
|
</div>
|
|
588
618
|
</div>
|
|
@@ -594,38 +624,37 @@
|
|
|
594
624
|
<div class="page" id="page-about">
|
|
595
625
|
<div class="about-section">
|
|
596
626
|
<div class="about-hero">
|
|
597
|
-
<h2>nvstudio-mcp</h2>
|
|
598
|
-
<p>Jembatan pintar berbasis <strong>Model Context Protocol (MCP)</strong> yang menghubungkan AI Agent (Antigravity IDE, Cursor, Claude) secara langsung ke dalam <strong>Roblox Studio</strong
|
|
627
|
+
<h2>nvstudio-mcp <span class="v2-chip">v2.0.0</span></h2>
|
|
628
|
+
<p>Jembatan pintar berbasis <strong>Model Context Protocol (MCP)</strong> yang menghubungkan AI Agent (Antigravity IDE, Cursor, Claude) secara langsung ke dalam <strong>Roblox Studio</strong>.</p>
|
|
599
629
|
<div class="about-links">
|
|
600
630
|
<a class="about-link" href="https://github.com/Adiholick/nvstudio-mcp" target="_blank">
|
|
601
|
-
<svg width="
|
|
631
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
|
|
602
632
|
GitHub
|
|
603
633
|
</a>
|
|
604
634
|
<a class="about-link" href="https://www.npmjs.com/package/@adiholick/nvstudio-mcp" target="_blank">
|
|
605
|
-
<svg width="
|
|
635
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0V0zm7.315 5.876H2.17v12.248H12v-9.6h-4.685V5.876zm9.515 0h-5.145v12.248h5.145V5.876zm-7.315 4.8H9.87v4.8H9.515v-4.8z"/></svg>
|
|
606
636
|
NPM Package
|
|
607
637
|
</a>
|
|
608
638
|
</div>
|
|
609
639
|
</div>
|
|
610
640
|
|
|
611
641
|
<div class="about-card">
|
|
612
|
-
<h3
|
|
642
|
+
<h3>⚡ Arsitektur v2.0 Baru</h3>
|
|
613
643
|
<ul>
|
|
614
|
-
<li>Plugin
|
|
615
|
-
<li>
|
|
616
|
-
<li>
|
|
617
|
-
<li>Dashboard ini menampilkan semua aktivitas secara real-time via WebSocket.</li>
|
|
644
|
+
<li><strong>Zero-Delay Long-Polling:</strong> Plugin Studio kini menunggu task secara realtime hingga 20 detik tanpa putus. Memangkas latensi drastis menjadi < 50ms (dibandingkan polling 1-detik di v1).</li>
|
|
645
|
+
<li><strong>Luau Pre-Commit Validator:</strong> Mencegah engine Roblox crash dengan memvalidasi sintaks Luau <i>sebelum</i> kode dikirim ke Studio.</li>
|
|
646
|
+
<li><strong>Partial Script Patching:</strong> AI kini menggunakan metode <code>patch_script_source</code> (pengeditan parsial per baris) yang jauh lebih cepat dan menghemat ribuan token.</li>
|
|
618
647
|
</ul>
|
|
619
648
|
</div>
|
|
620
649
|
|
|
621
650
|
<div class="about-card">
|
|
622
|
-
<h3
|
|
651
|
+
<h3>🛠️ Commands MCP Unggulan</h3>
|
|
623
652
|
<ul>
|
|
624
|
-
<li><code>
|
|
625
|
-
<li><code>
|
|
626
|
-
<li><code>
|
|
627
|
-
<li><code>
|
|
628
|
-
<li><code>
|
|
653
|
+
<li><code>patch_script_source</code> — Mengedit bagian spesifik dari script (super cepat).</li>
|
|
654
|
+
<li><code>execute_luau</code> — Menjalankan script Luau secara realtime di Studio.</li>
|
|
655
|
+
<li><code>insert_asset</code> — Memasukkan model 3D/asset dari library Roblox.</li>
|
|
656
|
+
<li><code>generate_terrain</code> — Men-generate atau memanipulasi terrain voxel.</li>
|
|
657
|
+
<li><code>get_children</code> / <code>get_script_source</code> — Inspeksi hirarki dan source code.</li>
|
|
629
658
|
</ul>
|
|
630
659
|
</div>
|
|
631
660
|
</div>
|
|
@@ -654,7 +683,7 @@
|
|
|
654
683
|
const pages = document.querySelectorAll('.page');
|
|
655
684
|
const topbarTitle = document.getElementById('topbar-title');
|
|
656
685
|
const clearBtn = document.getElementById('clear-btn');
|
|
657
|
-
const pageTitles = { activity: 'Activity Log', stats: '
|
|
686
|
+
const pageTitles = { activity: 'Activity Log', stats: 'Session Diagnostics', about: 'About v2.0' };
|
|
658
687
|
|
|
659
688
|
navItems.forEach(item => {
|
|
660
689
|
item.addEventListener('click', () => {
|
|
@@ -701,13 +730,11 @@
|
|
|
701
730
|
|
|
702
731
|
if (filtered.length === 0) {
|
|
703
732
|
logEmpty.style.display = 'flex';
|
|
704
|
-
// Remove all entries but keep empty
|
|
705
733
|
[...logList.children].forEach(c => { if (c !== logEmpty) c.remove(); });
|
|
706
734
|
return;
|
|
707
735
|
}
|
|
708
736
|
logEmpty.style.display = 'none';
|
|
709
737
|
|
|
710
|
-
// Full re-render
|
|
711
738
|
const frag = document.createDocumentFragment();
|
|
712
739
|
filtered.forEach(log => {
|
|
713
740
|
const el = createLogEl(log);
|
|
@@ -718,17 +745,25 @@
|
|
|
718
745
|
logEmpty.style.display = 'none';
|
|
719
746
|
logList.appendChild(frag);
|
|
720
747
|
|
|
721
|
-
if (autoScroll) logList.scrollTop = 0;
|
|
748
|
+
if (autoScroll) logList.scrollTop = 0;
|
|
722
749
|
}
|
|
723
750
|
|
|
724
751
|
function createLogEl(log) {
|
|
725
752
|
const el = document.createElement('div');
|
|
726
753
|
el.className = 'log-entry';
|
|
727
754
|
el.dataset.type = log.type;
|
|
728
|
-
|
|
755
|
+
|
|
756
|
+
let displayType = log.type.toUpperCase();
|
|
757
|
+
// Custom badges for v2 features based on message content
|
|
758
|
+
if (log.type === 'task' && log.message.includes('patch_script_source')) {
|
|
759
|
+
displayType = 'PATCH';
|
|
760
|
+
} else if (log.type === 'error' && log.message.includes('Syntax Error')) {
|
|
761
|
+
displayType = 'SYNTAX';
|
|
762
|
+
}
|
|
763
|
+
|
|
729
764
|
el.innerHTML = `
|
|
730
765
|
<span class="log-entry-time">${log.time}</span>
|
|
731
|
-
<span class="log-entry-badge badge-${log.type}">${
|
|
766
|
+
<span class="log-entry-badge badge-${log.type}">${displayType}</span>
|
|
732
767
|
<span class="log-entry-msg">${escapeHtml(log.message)}</span>
|
|
733
768
|
`;
|
|
734
769
|
return el;
|
|
@@ -740,7 +775,7 @@
|
|
|
740
775
|
|
|
741
776
|
function prependLog(log) {
|
|
742
777
|
allLogs.unshift(log);
|
|
743
|
-
if (allLogs.length >
|
|
778
|
+
if (allLogs.length > 1000) allLogs.pop(); // Keep max 1000 logs in memory
|
|
744
779
|
logCount.textContent = `${allLogs.length} event${allLogs.length !== 1 ? 's' : ''}`;
|
|
745
780
|
|
|
746
781
|
if (activeFilter !== 'all' && log.type !== activeFilter) return;
|
|
@@ -763,14 +798,22 @@
|
|
|
763
798
|
const rate = s.totalTasks > 0 ? Math.round((s.successCount / s.totalTasks) * 100) : null;
|
|
764
799
|
document.getElementById('stat-rate').textContent = rate !== null ? rate + '%' : '—';
|
|
765
800
|
document.getElementById('bar-success').style.width = (rate || 0) + '%';
|
|
801
|
+
|
|
766
802
|
const errRate = s.totalTasks > 0 ? Math.round((s.errorCount / s.totalTasks) * 100) : 0;
|
|
767
803
|
document.getElementById('bar-error').style.width = errRate + '%';
|
|
768
804
|
|
|
769
805
|
if (s.sessionStart) {
|
|
770
806
|
sessionStartTime = new Date(s.sessionStart);
|
|
771
|
-
document.getElementById('info-session').textContent = sessionStartTime.toLocaleString('id-ID');
|
|
772
807
|
}
|
|
773
|
-
|
|
808
|
+
|
|
809
|
+
const statusEl = document.getElementById('info-studio');
|
|
810
|
+
if (s.studioConnected) {
|
|
811
|
+
statusEl.textContent = 'Connected ✓';
|
|
812
|
+
statusEl.style.color = 'var(--success)';
|
|
813
|
+
} else {
|
|
814
|
+
statusEl.textContent = 'Disconnected';
|
|
815
|
+
statusEl.style.color = 'var(--text-muted)';
|
|
816
|
+
}
|
|
774
817
|
}
|
|
775
818
|
|
|
776
819
|
/* ── Uptime ── */
|
|
@@ -786,8 +829,16 @@
|
|
|
786
829
|
/* ── Studio Status ── */
|
|
787
830
|
function setStudioStatus(connected) {
|
|
788
831
|
statusDot.className = 'status-dot ' + (connected ? 'connected' : 'waiting');
|
|
789
|
-
statusLabel.textContent = connected ? 'Studio Connected' : '
|
|
790
|
-
|
|
832
|
+
statusLabel.textContent = connected ? 'Studio Connected' : 'Menunggu Studio...';
|
|
833
|
+
|
|
834
|
+
const statusEl = document.getElementById('info-studio');
|
|
835
|
+
if (connected) {
|
|
836
|
+
statusEl.textContent = 'Connected ✓';
|
|
837
|
+
statusEl.style.color = 'var(--success)';
|
|
838
|
+
} else {
|
|
839
|
+
statusEl.textContent = 'Disconnected';
|
|
840
|
+
statusEl.style.color = 'var(--text-muted)';
|
|
841
|
+
}
|
|
791
842
|
}
|
|
792
843
|
|
|
793
844
|
/* ── Socket Events ── */
|
|
@@ -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"
|
|
11
|
+
<SharedString name="Tags"></SharedString>
|
|
12
12
|
</Properties>
|
|
13
13
|
<Item class="ModuleScript" referent="RBX_create_instance">
|
|
14
14
|
<Properties>
|
|
@@ -367,14 +367,14 @@ end
|
|
|
367
367
|
]]></ProtectedString>
|
|
368
368
|
</Properties>
|
|
369
369
|
</Item>
|
|
370
|
-
<Item class="Script" referent="
|
|
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
|
|
377
|
+
<string name="Name">Main</string>
|
|
378
378
|
<int64 name="SourceAssetId">-1</int64>
|
|
379
379
|
<SharedString name="Tags"></SharedString>
|
|
380
380
|
<token name="RunContext">0</token>
|
|
@@ -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()
|
|
@@ -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
|
-
|
|
949
|
-
false, --
|
|
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
|
|
@@ -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
|
-
|
|
9
|
-
false, --
|
|
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
|