@weppy/roblox-mcp 2.0.9 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +39 -0
  3. package/README.md +11 -9
  4. package/docs/assets/screenshots/dashboard/dashboard_playtest.png +0 -0
  5. package/docs/assets/screenshots/plugin/sync/sync-overview.png +0 -0
  6. package/docs/en/dashboard/connection.md +1 -1
  7. package/docs/en/dashboard/overview.md +7 -7
  8. package/docs/en/dashboard/playtest.md +1 -1
  9. package/docs/en/dashboard/settings.md +1 -1
  10. package/docs/en/dashboard/sync.md +1 -1
  11. package/docs/en/dashboard/tools.md +1 -1
  12. package/docs/en/installation/README.md +1 -1
  13. package/docs/en/installation/roblox-explorer.md +2 -2
  14. package/docs/en/installation/roblox-plugin.md +4 -4
  15. package/docs/en/pro-upgrade.md +2 -2
  16. package/docs/en/sync/overview.md +3 -3
  17. package/docs/en/tools/playtest.md +2 -2
  18. package/docs/es/README.md +64 -12
  19. package/docs/es/dashboard/connection.md +1 -1
  20. package/docs/es/dashboard/overview.md +7 -7
  21. package/docs/es/dashboard/playtest.md +1 -1
  22. package/docs/es/dashboard/settings.md +1 -1
  23. package/docs/es/dashboard/sync.md +1 -1
  24. package/docs/es/dashboard/tools.md +1 -1
  25. package/docs/es/installation/README.md +1 -1
  26. package/docs/es/installation/roblox-explorer.md +2 -2
  27. package/docs/es/installation/roblox-plugin.md +4 -4
  28. package/docs/es/pro-upgrade.md +2 -2
  29. package/docs/es/sync/overview.md +3 -3
  30. package/docs/es/tools/playtest.md +2 -2
  31. package/docs/id/README.md +64 -12
  32. package/docs/id/dashboard/connection.md +1 -1
  33. package/docs/id/dashboard/overview.md +7 -7
  34. package/docs/id/dashboard/playtest.md +1 -1
  35. package/docs/id/dashboard/settings.md +1 -1
  36. package/docs/id/dashboard/sync.md +1 -1
  37. package/docs/id/dashboard/tools.md +1 -1
  38. package/docs/id/installation/README.md +1 -1
  39. package/docs/id/installation/roblox-explorer.md +2 -2
  40. package/docs/id/installation/roblox-plugin.md +4 -4
  41. package/docs/id/pro-upgrade.md +2 -2
  42. package/docs/id/sync/overview.md +3 -3
  43. package/docs/id/tools/playtest.md +2 -2
  44. package/docs/ja/README.md +64 -12
  45. package/docs/ja/dashboard/connection.md +1 -1
  46. package/docs/ja/dashboard/overview.md +7 -7
  47. package/docs/ja/dashboard/playtest.md +1 -1
  48. package/docs/ja/dashboard/settings.md +1 -1
  49. package/docs/ja/dashboard/sync.md +1 -1
  50. package/docs/ja/dashboard/tools.md +1 -1
  51. package/docs/ja/installation/README.md +1 -1
  52. package/docs/ja/installation/roblox-explorer.md +2 -2
  53. package/docs/ja/installation/roblox-plugin.md +4 -4
  54. package/docs/ja/pro-upgrade.md +2 -2
  55. package/docs/ja/sync/overview.md +3 -3
  56. package/docs/ja/tools/playtest.md +2 -2
  57. package/docs/ko/README.md +65 -13
  58. package/docs/ko/dashboard/connection.md +1 -1
  59. package/docs/ko/dashboard/overview.md +7 -7
  60. package/docs/ko/dashboard/playtest.md +1 -1
  61. package/docs/ko/dashboard/settings.md +1 -1
  62. package/docs/ko/dashboard/sync.md +1 -1
  63. package/docs/ko/dashboard/tools.md +1 -1
  64. package/docs/ko/installation/README.md +1 -1
  65. package/docs/ko/installation/roblox-explorer.md +2 -2
  66. package/docs/ko/installation/roblox-plugin.md +4 -4
  67. package/docs/ko/pro-upgrade.md +2 -2
  68. package/docs/ko/sync/overview.md +3 -3
  69. package/docs/ko/tools/playtest.md +2 -2
  70. package/docs/pt-br/README.md +64 -12
  71. package/docs/pt-br/dashboard/connection.md +1 -1
  72. package/docs/pt-br/dashboard/overview.md +7 -7
  73. package/docs/pt-br/dashboard/playtest.md +1 -1
  74. package/docs/pt-br/dashboard/settings.md +1 -1
  75. package/docs/pt-br/dashboard/sync.md +1 -1
  76. package/docs/pt-br/dashboard/tools.md +1 -1
  77. package/docs/pt-br/installation/README.md +1 -1
  78. package/docs/pt-br/installation/roblox-explorer.md +2 -2
  79. package/docs/pt-br/installation/roblox-plugin.md +4 -4
  80. package/docs/pt-br/pro-upgrade.md +2 -2
  81. package/docs/pt-br/sync/overview.md +3 -3
  82. package/docs/pt-br/tools/playtest.md +2 -2
  83. package/llms-full.txt +7 -7
  84. package/package.json +1 -1
  85. package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
  86. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogDetailPage-DRPIGDB0.js → ChangelogDetailPage-lzDR6yjY.js} +1 -1
  87. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogPage-DVPYTw2L.js → ChangelogPage-BscI67Aj.js} +1 -1
  88. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConfirmModal-BIJpNntn.js +1 -0
  89. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConnectionPage-CdLqNY4r.js +1 -0
  90. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{InfoLabel-DzXzzs6Q.js → InfoLabel-CBAqTqRy.js} +1 -1
  91. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/OverviewPage-BOm0Ai9y.js +1 -0
  92. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PlaytestPage-BjBAsHLz.js → PlaytestPage-DaDbQ6Qa.js} +1 -1
  93. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PropertyDiff-BZMdMzMr.js → PropertyDiff-D34Apw3G.js} +1 -1
  94. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/SettingsPage-yWzPPZIB.js +1 -0
  95. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{StatusBadge-DtcYlxe-.js → StatusBadge-B-pYMpUG.js} +1 -1
  96. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/SyncPage-E4XZhZn0.js +4 -0
  97. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierPromoProgress-r5fgmYI5.js → TierPromoProgress-CqxKoPOJ.js} +1 -1
  98. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ToolsPage-Cfau3bX3.js → ToolsPage-yaahWgTq.js} +1 -1
  99. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/index-DYnjsp-e.css +1 -0
  100. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/index-E1cuNPsJ.js +129 -0
  101. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{tier-promo-config-CQFDWwo4.js → tier-promo-config-BuGSENUv.js} +1 -1
  102. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{useLiveUptime-5lD1XKnw.js → useLiveUptime-qJDofPOo.js} +1 -1
  103. package/plugins/weppy-roblox-mcp/dashboard/dist/index.html +2 -2
  104. package/plugins/weppy-roblox-mcp/dist/index.js +61 -61
  105. package/plugins/weppy-roblox-mcp/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
  106. package/docs/assets/screenshots/connection_popup.png +0 -0
  107. package/docs/assets/screenshots/sync.png +0 -0
  108. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConfirmModal-Db4rfrTo.js +0 -1
  109. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConfirmModal-tvPLhSO9.css +0 -1
  110. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConnectionPage-Cv2i3LSr.js +0 -1
  111. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/OverviewPage-FoC28UL8.js +0 -1
  112. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/SettingsPage-BKp2VKMW.js +0 -1
  113. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/SyncPage-DcELF5_j.js +0 -4
  114. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/index-BOVxaPTw.css +0 -1
  115. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/index-DWRH2iF4.js +0 -129
  116. /package/docs/assets/screenshots/{connection_guide.png → plugin/connection/connection-guide.png} +0 -0
  117. /package/docs/assets/screenshots/{plugin_main.png → plugin/installation/main-screen.png} +0 -0
  118. /package/docs/assets/screenshots/{plugins_menu.png → plugin/installation/plugins-menu.png} +0 -0
  119. /package/docs/assets/screenshots/{settings.png → plugin/installation/settings-screen.png} +0 -0
  120. /package/docs/assets/screenshots/{weppy_plugin_toolbar.png → plugin/installation/toolbar-button.png} +0 -0
  121. /package/docs/assets/screenshots/{license/license-dashboard.png → plugin/license/dashboard-license-screen.png} +0 -0
  122. /package/docs/assets/screenshots/{license/license-plugin.png → plugin/license/plugin-license-screen.png} +0 -0
  123. /package/docs/assets/screenshots/{sync_conflict.png → plugin/sync/sync-conflict.png} +0 -0
@@ -2,12 +2,20 @@
2
2
 
3
3
  > **WROX** e um servidor MCP que permite agentes de codificacao IA controlarem uma sessao ao vivo do Roblox Studio — crie e edite scripts, instancias, terrain, iluminacao, assets, audio e animacoes com linguagem natural.
4
4
 
5
- **21 ferramentas consolidadas · 140+ acoes · Sync bidirecional · Playtest automatizado · Suporte multi-place**
5
+ **Ferramentas consolidadas baseadas em ações · Sync bidirecional · Playtest automatizado · Suporte multi-place**
6
6
 
7
7
  [English](../../README.md) | [한국어](../ko/README.md) | [日本語](../ja/README.md) | [Español](../es/README.md) | **Português** | [Bahasa Indonesia](../id/README.md)
8
8
 
9
9
  [![Demo — IA criando um jogo Roblox em tempo real](https://img.youtube.com/vi/3jrUpBbZPaw/maxresdefault.jpg)](https://youtu.be/3jrUpBbZPaw)
10
10
 
11
+ ## Por que WROX (Weppy Roblox MCP)?
12
+
13
+ Agentes de codificação IA como Claude, Codex e Gemini são poderosos — mas não conseguem ver nem modificar nada dentro do Roblox Studio. O DataModel, scripts, terrain e iluminação são todos invisíveis para ferramentas externas. Sem uma ponte, a IA só pode gerar trechos de código que você precisa colar manualmente.
14
+
15
+ **WROX** é uma ponte entre agentes de IA e o Roblox Studio. A IA cria e modifica diretamente instâncias, scripts, propriedades, terrain e mais dentro do Studio, e as alterações são refletidas imediatamente no Studio e no dashboard para que você possa **ver exatamente o que mudou**.
16
+
17
+ Sem necessidade de copiar e colar código. A IA trabalha e você confere os resultados.
18
+
11
19
  ## Instalacao rapida
12
20
 
13
21
  **Instalacao em um comando** — Instala o servidor MCP, o plugin do Roblox Studio e registra nos apps de IA em um único passo:
@@ -52,14 +60,15 @@ npx -y @weppy/roblox-mcp
52
60
  | Codex CLI | [Configuracao](installation/ai-apps/codex-cli.md) |
53
61
  | Codex Desktop | [Configuracao](installation/ai-apps/codex-app.md) |
54
62
  | Gemini CLI | [Configuracao](installation/ai-apps/gemini-cli.md) |
63
+ | Antigravity | [Configuracao](installation/ai-apps/antigravity.md) |
55
64
 
56
65
  > Funciona com qualquer cliente MCP compativel. O comando do servidor e `npx -y @weppy/roblox-mcp`.
57
66
 
58
67
  ## Compatibilidade
59
68
 
60
- | Claude Code | Claude Desktop | Cursor | Codex CLI | Gemini CLI |
61
- |:-----------:|:--------------:|:------:|:---------:|:----------:|
62
- | ✅ | ✅ | ✅ | ✅ | ✅ |
69
+ | Claude Code | Claude Desktop | Cursor | Codex CLI | Gemini CLI | Antigravity |
70
+ |:-----------:|:--------------:|:------:|:---------:|:----------:|:-----------:|
71
+ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
63
72
 
64
73
  **Requisitos:** Node.js 18+, Roblox Studio, Windows 10+ ou macOS 12+
65
74
 
@@ -73,17 +82,15 @@ A IA consegue operar diretamente scripts, instancias, propriedades, terreno, ilu
73
82
  - "Cria uma arena de boss no centro do mapa e coloca spawns sem colisao."
74
83
  - "Muda a interface deste modulo e atualiza todos os scripts dependentes."
75
84
 
76
- Nao e apenas geracao de codigo. Sao **acoes executaveis para fluxo real de desenvolvimento**.
77
-
78
85
  ### 2) Sync: mantem contexto completo do projeto para a IA
79
86
 
80
87
  A IA trabalha com um espelho local sincronizado, entao alteracoes em varios arquivos continuam consistentes.
81
88
 
82
- ![Fluxo de Sync — Studio e arquivos locais sincronizados em tempo real](../assets/screenshots/sync.png)
83
-
84
89
  - Basic: sincronizacao unidirecional (Studio -> Local)
85
90
  - Pro: sincronizacao bidirecional + Direction/Apply Mode por tipo + historico + multiplace
86
91
 
92
+ ![Fluxo de Sync — Studio e arquivos locais sincronizados em tempo real](../assets/screenshots/plugin/sync/sync-overview.png)
93
+
87
94
  ### 3) Playtest: a IA executa e valida testes automaticamente
88
95
 
89
96
  A IA pode controlar diretamente o playtest do Studio. Ela pode iniciar e parar Play (F5) ou Run (F8), injetar scripts de teste, coletar logs e gerar relatorios locais automaticamente.
@@ -92,28 +99,30 @@ A IA pode controlar diretamente o playtest do Studio. Ela pode iniciar e parar P
92
99
  - "Escreva e execute um teste para confirmar que o SpawnLocation esta acima do chao."
93
100
  - "Valide com playtest se o script que acabei de alterar roda sem erros."
94
101
 
102
+ ![WROX Playtest Dashboard — historico de testes e relatorio detalhado](../assets/screenshots/dashboard/dashboard_playtest.png)
103
+
95
104
  ### 4) WROX Dashboard: monitore as operacoes da IA em tempo real
96
105
 
97
106
  No dashboard web fornecido pelo servidor MCP, acompanhe em tempo real o status de conexao, historico de execucao de ferramentas, status de sincronizacao e historico de alteracoes do jogo.
98
107
 
99
- ![WROX WROX Dashboard Overview — status do servidor, alteracoes recentes e resumo da sessao](../assets/screenshots/dashboard/dashboard_overview.png)
100
-
101
108
  - Visualize o status de conexao do servidor/plugin/agente de uma so vez
102
109
  - Compare todas as alteracoes feitas pela IA com Before & After no Changelog
103
110
  - Analise o fluxo de trabalho com historico e estatisticas de execucao de ferramentas
104
111
 
112
+ ![WROX WROX Dashboard Overview — status do servidor, alteracoes recentes e resumo da sessao](../assets/screenshots/dashboard/dashboard_overview.png)
113
+
105
114
  ### 5) WROX Roblox Explorer: navegue a hierarquia do Studio no VSCode
106
115
 
107
116
  Visualize a arvore completa de instancias do seu lugar no Roblox Studio diretamente dentro do VSCode. Navegue pelos servicos, abra scripts e arquivos de propriedades sincronizados, e acompanhe o status de sincronizacao — tudo sem trocar para o Studio.
108
117
  WROX Roblox Explorer e uma extensao complementar do VSCode para os dados de sync gerados pelo WROX. A navegacao basica da arvore funciona a partir dos arquivos sincronizados em disco, e os indicadores ao vivo de status sync ou direction ficam mais completos quando o servidor MCP local esta em execucao.
109
118
  Instale pela [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=weppy.weppy-roblox-explorer) ou pela [Open VSX](https://open-vsx.org/extension/weppy/weppy-roblox-explorer).
110
119
 
111
- ![WROX Roblox Explorer — arvore de instancias do Studio exibida na barra lateral do VSCode](../assets/screenshots/roblox-explorer/roblox-explorer-screen.png)
112
-
113
120
  - Icones de classe iguais ao Studio para reconhecimento imediato
114
121
  - Clique para abrir scripts e arquivos de propriedades sincronizados
115
122
  - Suporte multiplace com indicadores de status de sincronizacao
116
123
 
124
+ ![WROX Roblox Explorer — arvore de instancias do Studio exibida na barra lateral do VSCode](../assets/screenshots/roblox-explorer/roblox-explorer-screen.png)
125
+
117
126
  ## Valor imediato para o usuario
118
127
 
119
128
  - Comprimir trabalho repetitivo: transformar muitas edicoes manuais em um pedido
@@ -121,6 +130,14 @@ Instale pela [VS Code Marketplace](https://marketplace.visualstudio.com/items?it
121
130
  - Reduzir risco: decidir com base no estado do sync e no historico
122
131
  - Melhor eficiencia de tokens (Pro): menos round trips com acoes em massa
123
132
 
133
+ ## Casos de uso
134
+
135
+ - **Prototipagem rápida**: Descreva uma mecânica de jogo em linguagem natural e veja a IA construí-la no Studio
136
+ - **Refatoração em massa**: Renomeie uma interface de módulo e atualize todos os scripts dependentes em uma única solicitação
137
+ - **Terrain e ambiente**: Gere terrain procedural, configure iluminação/atmosfera, posicione assets — tudo a partir de um único prompt
138
+ - **Consistência multi-arquivo**: A IA lê o projeto completo via Sync e aplica alterações em scripts relacionados de forma conjunta
139
+ - **Integração de assets**: Pesquise no Creator Store, insira modelos e configure propriedades sem sair do editor
140
+
124
141
  ## Documentacao detalhada
125
142
 
126
143
  - [Guia de instalacao](installation/README.md)
@@ -140,12 +157,47 @@ Instale pela [VS Code Marketplace](https://marketplace.visualstudio.com/items?it
140
157
  - [Playtest e testes automatizados](tools/playtest.md) - controle de playtest e validacao automatica
141
158
  - [Sistema e depuracao](tools/system-and-debugging.md) - conexao, logs e execucao em lote
142
159
 
160
+ ## FAQ
161
+
162
+ ### Como conecto o Claude Code ao Roblox Studio?
163
+ Instale o plugin do Roblox Studio e registre o servidor MCP (`npx -y @weppy/roblox-mcp`) no Claude Code. O Claude poderá ler e escrever scripts diretamente dentro do Studio. Consulte [Configuração do Claude Code](installation/ai-apps/claude-code.md).
164
+
165
+ ### Como uso o Codex CLI com o Roblox Studio?
166
+ Instale o plugin e adicione a configuração do servidor MCP ao Codex CLI. Consulte [Configuração do Codex CLI](installation/ai-apps/codex-cli.md).
167
+
168
+ ### O Roblox MCP funciona com o Cursor?
169
+ Sim. Consulte [Configuração do Cursor](installation/ai-apps/cursor.md). Qualquer cliente de IA compatível com MCP funciona.
170
+
171
+ ### A IA pode criar jogos Roblox com isso?
172
+ Sim. A IA pode criar instâncias, escrever scripts, gerar terrain, configurar iluminação, inserir assets, configurar física e mais — tudo dentro de uma sessão de Studio ao vivo. Vai além da geração de código para ações executáveis.
173
+
174
+ ### Qual é a diferença entre Basic e Pro?
175
+ Basic (gratuito) inclui execução de ferramentas MCP e sincronização unidirecional (Studio -> Local). Pro adiciona sincronização bidirecional, operações em massa, geração de terrain, análise espacial, controle de áudio/animação e suporte multi-place. Consulte o [Guia de upgrade Pro](pro-upgrade.md).
176
+
177
+ ### Como o Weppy se diferencia de outros servidores MCP para Roblox?
178
+ O Weppy usa despacho baseado em ações em vez de ferramentas separadas para cada função. Isso reduz significativamente o consumo de tokens de IA. Também fornece sincronização bidirecional de projeto e suporte multi-place, que a maioria das alternativas não possui.
179
+
180
+ ### É seguro? A IA pode quebrar meu jogo?
181
+ O servidor roda apenas em localhost (127.0.0.1:3002). Caminhos proibidos (CoreGui, CorePackages) são bloqueados. Limitação de taxa (450 req/min) e timeouts de 30 segundos previnem operações descontroladas. Todas as alterações são rastreáveis pelo histórico de sincronização.
182
+
143
183
  ## Upgrade Pro
144
184
 
145
185
  Sync bidirecional, recursos avancados de criacao e eficiencia de tokens de IA — tudo em uma unica atualizacao.
146
186
 
147
187
  [Guia de upgrade Pro](pro-upgrade.md)
148
188
 
189
+ ## Licença
190
+
191
+ Este repositório está licenciado sob `AGPL-3.0`.
192
+
193
+ Licenciamento comercial está disponível separadamente. Consulte [COMMERCIAL-LICENSE.md](../../COMMERCIAL-LICENSE.md).
194
+
195
+ O uso do nome e logotipos Weppy é regido por [TRADEMARKS.md](../../TRADEMARKS.md).
196
+
149
197
  ---
150
198
 
199
+ [![npm version](https://img.shields.io/npm/v/@weppy/roblox-mcp)](https://www.npmjs.com/package/@weppy/roblox-mcp) [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org/) [![Smithery](https://smithery.ai/badge/@hope1026/weppy-roblox-mcp)](https://smithery.ai/server/@hope1026/weppy-roblox-mcp)
200
+
201
+ [![Roblox MCP on Glama](https://glama.ai/mcp/servers/hope1026/roblox-mcp/badges/card.svg)](https://glama.ai/mcp/servers/hope1026/roblox-mcp)
202
+
151
203
  [GitHub Issues](https://github.com/hope1026/weppy-roblox-mcp/issues) · [Discussions](https://github.com/hope1026/weppy-roblox-mcp/discussions) · [npm](https://www.npmjs.com/package/@weppy/roblox-mcp)
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Visao geral
8
8
 
9
- A pagina Connection monitora todos os status de conexao do sistema MCP em um unico local. Esta sempre acessivel quando o servidor esta conectado (L1 ou superior).
9
+ A pagina Connection monitora todos os status de conexao do sistema MCP em um unico local. Esta sempre acessivel quando o dashboard esta em estado **Servidor conectado** ou **Studio conectado**.
10
10
 
11
11
  ## Server Status
12
12
 
@@ -34,17 +34,17 @@ http://localhost:3002
34
34
 
35
35
  > Defina `DASHBOARD_AUTO_OPEN=false` para desativar a abertura automatica (consulte a pagina [Settings](settings.md)).
36
36
 
37
- ## Niveis de conexao
37
+ ## Estados de conexao
38
38
 
39
39
  O Dashboard oferece funcionalidades diferentes de acordo com o status da conexao:
40
40
 
41
- | Nivel | Condicao | Paginas disponiveis |
42
- |:-----:|----------|---------------------|
43
- | **L0** | Servidor desconectado | Exibe apenas a tela de aguardando reconexao |
44
- | **L1** | Servidor conectado, plugin desconectado | Connection, Tools, Settings |
45
- | **L2** | Servidor + plugin ambos conectados | Todas as paginas |
41
+ | Estado | Condicao | Paginas disponiveis |
42
+ |--------|----------|---------------------|
43
+ | **Servidor desconectado** | Servidor desconectado | Exibe apenas a tela de aguardando reconexao |
44
+ | **Servidor conectado** | Servidor conectado, plugin desconectado | Connection, Tools, Settings |
45
+ | **Studio conectado** | Servidor + plugin ambos conectados | Todas as paginas |
46
46
 
47
- Quando o plugin nao esta conectado (L1), as paginas Overview, Changelog, Sync e Playtest nao ficam acessiveis.
47
+ Quando o dashboard esta em estado **Servidor conectado**, as paginas Overview, Changelog, Sync e Playtest nao ficam acessiveis.
48
48
 
49
49
  ## Overview
50
50
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Visao geral
8
8
 
9
- A pagina Playtest exibe o status e os resultados dos playtests executados pela IA. So fica acessivel quando o plugin esta conectado (L2).
9
+ A pagina Playtest exibe o status e os resultados dos playtests executados pela IA. So fica acessivel quando o dashboard esta em estado **Studio conectado**.
10
10
 
11
11
  ## Playtest Status
12
12
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Visao geral
6
6
 
7
- Na pagina Settings, voce pode verificar as informacoes de licenca e as configuracoes do servidor, alem de alterar algumas delas. Esta sempre acessivel quando o servidor esta conectado (L1 ou superior).
7
+ Na pagina Settings, voce pode verificar as informacoes de licenca e as configuracoes do servidor, alem de alterar algumas delas. Esta sempre acessivel quando o dashboard esta em estado **Servidor conectado** ou **Studio conectado**.
8
8
 
9
9
  ## License (somente leitura)
10
10
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## Visao geral
10
10
 
11
- A pagina Sync exibe visualmente o status atual e as configuracoes da sincronizacao Studio <-> arquivos locais. So fica acessivel quando o plugin esta conectado (L2).
11
+ A pagina Sync exibe visualmente o status atual e as configuracoes da sincronizacao Studio <-> arquivos locais. So fica acessivel quando o dashboard esta em estado **Studio conectado**.
12
12
 
13
13
  ## Sync Status
14
14
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Visao geral
8
8
 
9
- A pagina Tools fornece o historico e as estatisticas das ferramentas MCP executadas pela IA. E composta por duas sub-abas: **History** e **Statistics**. Esta sempre acessivel quando o servidor esta conectado (L1 ou superior).
9
+ A pagina Tools fornece o historico e as estatisticas das ferramentas MCP executadas pela IA. E composta por duas sub-abas: **History** e **Statistics**. Esta sempre acessivel quando o dashboard esta em estado **Servidor conectado** ou **Studio conectado**.
10
10
 
11
11
  ## Aba History
12
12
 
@@ -72,7 +72,7 @@ Registre o servidor MCP no seu app de IA. Funciona com qualquer app que suporte
72
72
  ## Opcional: Instalar WROX Roblox Explorer (Extensão VSCode)
73
73
 
74
74
  Navegue pelas árvores de instâncias sincronizadas dentro do VSCode com ícones de classes Roblox.
75
- Esta extensao opcional exige que a instalacao do Roblox MCP acima ja esteja concluida, porque o Explorer le os dados `roblox-project-sync` gerados pelo Sync.
75
+ Esta extensao opcional exige que a instalacao do Roblox MCP acima ja esteja concluida, porque o Explorer le os dados de Sync em `wrox-project-sync` dentro da raiz do projeto.
76
76
 
77
77
  👉 [Guia de instalação do WROX Roblox Explorer](roblox-explorer.md)
78
78
 
@@ -9,7 +9,7 @@ Esta é uma extensao complementar do WROX, e não uma integração Roblox indepe
9
9
 
10
10
  - VSCode 1.85+
11
11
  - [Roblox MCP](../../../README.md) instalado com Sync habilitado (Basic ou Pro)
12
- - `roblox-project-sync/place_*/.sync-meta.json` gerado pelo Sync
12
+ - O Sync ja gerou `wrox-project-sync/place_*/.sync-meta.json` na raiz do projeto
13
13
 
14
14
  ## Instalação
15
15
 
@@ -37,7 +37,7 @@ A navegação basica da arvore funciona a partir dos arquivos sincronizados em d
37
37
 
38
38
  | Configuração | Padrão | Descrição |
39
39
  |--------------|--------|-----------|
40
- | `robloxExplorer.syncRoot` | `""` | Caminho absoluto para a raiz do `roblox-project-sync`. Detectado automaticamente se vazio. |
40
+ | `robloxExplorer.syncRoot` | `""` | Caminho absoluto para a raiz do `wrox-project-sync`. Se estiver vazio, e detectado automaticamente. O WROX usa `{projectRoot}/wrox-project-sync`. |
41
41
  | `robloxExplorer.hidePropsFiles` | `false` | Oculta arquivos de Sync (`.props.json`, `_tree.json`, `.value.json`) no explorador padrão do VSCode. |
42
42
  | `robloxExplorer.autoRefresh` | `true` | Atualiza automaticamente a árvore quando os arquivos Sync mudam. |
43
43
  | `robloxExplorer.showSyncStatus` | `true` | Exibe decorações de status Sync nos itens da árvore. |
@@ -18,7 +18,7 @@ Nota:
18
18
  2. Clique na aba **Plugins**
19
19
  3. Clique em **Plugins Folder**
20
20
 
21
- ![Abrir Plugins Folder](../../assets/screenshots/plugins_menu.png)
21
+ ![Abrir Plugins Folder](../../assets/screenshots/plugin/installation/plugins-menu.png)
22
22
 
23
23
  4. **Copie** `WeppyRobloxMCP.rbxm` para a pasta de Plugins
24
24
  5. **Reinicie o Roblox Studio**
@@ -27,7 +27,7 @@ Nota:
27
27
 
28
28
  Apos reiniciar, o botao **WROX** aparecera na aba Plugins.
29
29
 
30
- ![Botao WROX](../../assets/screenshots/weppy_plugin_toolbar.png)
30
+ ![Botao WROX](../../assets/screenshots/plugin/installation/toolbar-button.png)
31
31
 
32
32
  ## 4. Conectar ao Agente de IA
33
33
 
@@ -52,13 +52,13 @@ O servidor MCP deve estar instalado. Complete primeiro o guia do seu app de IA:
52
52
  3. Clique em **Connect**
53
53
  4. Quando aparecer **"Connected"**, esta pronto
54
54
 
55
- ![Tela Principal do Plugin](../../assets/screenshots/plugin_main.png)
55
+ ![Tela Principal do Plugin](../../assets/screenshots/plugin/installation/main-screen.png)
56
56
 
57
57
  ## 5. Configuracoes (Opcional)
58
58
 
59
59
  Use o botao de configuracoes no canto superior direito.
60
60
 
61
- ![Tela de Configuracoes](../../assets/screenshots/settings.png)
61
+ ![Tela de Configuracoes](../../assets/screenshots/plugin/installation/settings-screen.png)
62
62
 
63
63
  - **Conexao Automatica**
64
64
  - **Reconexao Automatica**
@@ -48,7 +48,7 @@ Você só precisa ativar a licença uma vez, no plugin ou no dashboard. As duas
48
48
  5. Se o status não atualizar imediatamente, clique em **Refresh**.
49
49
  6. Quando a ativação terminar, o status muda de Basic para Pro e os recursos Pro ficam disponíveis.
50
50
 
51
- ![Tela de ativação de licença no plugin](../assets/screenshots/license/license-plugin.png)
51
+ ![Tela de ativação de licença no plugin](../assets/screenshots/plugin/license/plugin-license-screen.png)
52
52
 
53
53
  ### Ativar no dashboard
54
54
 
@@ -58,7 +58,7 @@ Você só precisa ativar a licença uma vez, no plugin ou no dashboard. As duas
58
58
  4. Clique em **Activate License** para ativar a licença.
59
59
  5. Se necessário, use **Refresh License** para buscar o status mais recente.
60
60
 
61
- ![Tela de ativação de licença no dashboard](../assets/screenshots/license/license-dashboard.png)
61
+ ![Tela de ativação de licença no dashboard](../assets/screenshots/plugin/license/dashboard-license-screen.png)
62
62
 
63
63
  ### Depois da ativação
64
64
 
@@ -12,13 +12,13 @@ Sem Sync, a IA so enxerga trechos colados no chat. Com Sync ativo, ela trabalha
12
12
 
13
13
  ## Como funciona
14
14
 
15
- ![Fluxo de Sync — arvore do Studio espelhada em arquivos locais](../../assets/screenshots/sync.png)
15
+ ![Fluxo de Sync — arvore do Studio espelhada em arquivos locais](../../assets/screenshots/plugin/sync/sync-overview.png)
16
16
 
17
17
  1. Full Sync: espelho inicial da arvore/instancias do Studio para local
18
18
  2. Incremental Sync: atualizacao continua das mudancas novas
19
19
  3. Rastreamento de History/Status: ver o que mudou, quando e em qual direcao
20
20
 
21
- O caminho local padrao e `roblox-project-sync/place_{placeId}/explorer`.
21
+ Os dados de Sync ficam em `{projectRoot}/wrox-project-sync/place_{placeId}/explorer`.
22
22
 
23
23
  ### Explorar dados sincronizados no VSCode
24
24
 
@@ -109,7 +109,7 @@ No Pro, voce controla Direction e Apply Mode por tipo.
109
109
 
110
110
  Quando mudancas sao detectadas tanto no Studio quanto no local durante a sincronizacao bidirecional, um dialogo de resolucao de conflitos aparece.
111
111
 
112
- ![Local Changes Detected — opcoes de resolucao de conflitos (Studio Priority / Local Priority / Per-File)](../../assets/screenshots/sync_conflict.png)
112
+ ![Local Changes Detected — opcoes de resolucao de conflitos (Studio Priority / Local Priority / Per-File)](../../assets/screenshots/plugin/sync/sync-conflict.png)
113
113
 
114
114
  - **Studio Priority**: sobrescrever usando o estado do Studio como fonte de verdade
115
115
  - **Local Priority**: aplicar arquivos locais ao Studio
@@ -86,10 +86,10 @@ Edit --play--> Running --stop--> Edit
86
86
 
87
87
  ### 5. Escrita de relatorios
88
88
 
89
- Os relatorios e logs sao gravados em arquivos locais:
89
+ Os relatorios e logs sao gravados em `{projectRoot}/wrox-project-sync/place_XXXXX/tests/YYYYMMDD-HHmmss/`.
90
90
 
91
91
  ```text
92
- roblox-project-sync/place_XXXXX/tests/YYYYMMDD-HHmmss/
92
+ {projectRoot}/wrox-project-sync/place_XXXXX/tests/YYYYMMDD-HHmmss/
93
93
  ├── test-report.md
94
94
  └── test-log.txt
95
95
  ```
package/llms-full.txt CHANGED
@@ -495,7 +495,7 @@ Without Sync, AI only sees snippets pasted into chat. With Sync enabled, AI work
495
495
  2. Incremental Sync: continuous update of new changes
496
496
  3. History/Status tracking: inspect what changed, when, and in which direction
497
497
 
498
- Default local path is `roblox-project-sync/place_{placeId}/explorer`.
498
+ Sync data is stored under `{projectRoot}/wrox-project-sync/place_{placeId}/explorer`.
499
499
 
500
500
  ## Sync Capabilities
501
501
 
@@ -565,13 +565,13 @@ Set `DASHBOARD_AUTO_OPEN=false` to disable automatic opening.
565
565
  | **Tools** | Tool execution history, statistics, tier distribution |
566
566
  | **Settings** | License, log level, sync settings, language |
567
567
 
568
- ## Connection Levels
568
+ ## Connection States
569
569
 
570
- | Level | Condition | Available Pages |
571
- |:-----:|-----------|-----------------|
572
- | **L0** | Server not connected | Reconnection waiting screen only |
573
- | **L1** | Server connected, plugin not connected | Connection, Tools, Settings |
574
- | **L2** | Both server and plugin connected | All pages |
570
+ | State | Condition | Available Pages |
571
+ |-------|-----------|-----------------|
572
+ | **Server disconnected** | Server not connected | Reconnection waiting screen only |
573
+ | **Server connected** | Server connected, plugin not connected | Connection, Tools, Settings |
574
+ | **Studio connected** | Both server and plugin connected | All pages |
575
575
 
576
576
  ## Overview Page
577
577
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weppy/roblox-mcp",
3
- "version": "2.0.9",
3
+ "version": "2.1.0",
4
4
  "description": "MCP (Model Context Protocol) server for Roblox Studio integration - enables AI coding agents to interact with Roblox Studio in real-time",
5
5
  "main": "plugins/weppy-roblox-mcp/dist/index.js",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weppy-roblox-mcp",
3
3
  "description": "MCP server for Roblox Studio integration - AI-powered game development with specialized agents and skills",
4
- "version": "2.0.9",
4
+ "version": "2.1.0",
5
5
  "author": {
6
6
  "name": "hope1026"
7
7
  },
@@ -1 +1 @@
1
- import{r,a as R,u as M,b as B,c as D,j as e,T as m}from"./index-DWRH2iF4.js";import{I as V}from"./InfoLabel-DzXzzs6Q.js";import{D as F,P as G}from"./PropertyDiff-BZMdMzMr.js";function P(t){const s={scriptsModified:0,scriptsCreated:0,instancesCreated:0,instancesDeleted:0,instancesMoved:0,propertiesChanged:0,lightingChanged:!1,terrainChanged:!1,assetsInserted:0};for(const l of t)switch(l.category){case"script":l.changeType==="create"?s.scriptsCreated++:s.scriptsModified++;break;case"instance":l.changeType==="create"?s.instancesCreated++:l.changeType==="delete"?s.instancesDeleted++:l.changeType==="move"&&s.instancesMoved++;break;case"property":s.propertiesChanged++;break;case"lighting":s.lightingChanged=!0;break;case"terrain":s.terrainChanged=!0;break;case"asset":s.assetsInserted++;break}return s}function U(t){const[s,l]=r.useState(""),[a,d]=r.useState(""),[u,q]=r.useState(),[w,y]=r.useState("completed"),[E,I]=r.useState([]),[O,L]=r.useState([]),[f,x]=r.useState([]),[p,j]=r.useState(),[_,b]=r.useState(),[v,S]=r.useState(),[C,N]=r.useState({scriptsModified:0,scriptsCreated:0,instancesCreated:0,instancesDeleted:0,instancesMoved:0,propertiesChanged:0,lightingChanged:!1,terrainChanged:!1,assetsInserted:0}),[T,g]=r.useState(!0),[k,h]=r.useState(null),i=r.useCallback(async()=>{if(t){g(!0),h(null);try{const[c,o]=await Promise.all([R.get(`/api/dashboard/changelog/${t}`),R.get(`/api/dashboard/changelog/${t}/changes`)]);l(c.entryId),d(c.startTime),q(c.endTime),y(c.status),I(c.entries),L(c.failures),j(c.contextSummary),b(c.replayMetadata),S(c.verificationSummary),x(o.changes),N(P(o.changes))}catch(c){h(c instanceof Error?c.message:"Failed to load changelog detail")}finally{g(!1)}}},[t]);return r.useEffect(()=>{i()},[i]),{entryId:s,startTime:a,endTime:u,status:w,entries:E,failures:O,changes:f,changeSummary:C,contextSummary:p,replayMetadata:_,verificationSummary:v,loading:T,error:k,refresh:i}}const z={script:"📝",instance:"🧱",property:"🎨",lighting:"🌅",terrain:"⛰️",asset:"📦"};function H(t){return z[t]??"❓"}const J="_page_q2jbi_2",W="_header_q2jbi_10",Y="_backLink_q2jbi_16",Q="_headerTitle_q2jbi_29",X="_headerTime_q2jbi_37",Z="_statusActive_q2jbi_44",ee="_statusCompleted_q2jbi_49",te="_section_q2jbi_54",ne="_sectionTitle_q2jbi_61",ae="_summaryGrid_q2jbi_74",ie="_summaryCard_q2jbi_80",se="_summaryCardActive_q2jbi_94",ce="_summaryIcon_q2jbi_99",re="_summaryCount_q2jbi_105",oe="_summaryLabel_q2jbi_112",le="_contextGrid_q2jbi_121",de="_contextRow_q2jbi_127",me="_contextKey_q2jbi_133",ge="_contextValue_q2jbi_141",he="_timelineFilter_q2jbi_149",ue="_filterLabel_q2jbi_156",ye="_filterSelect_q2jbi_162",fe="_timeline_q2jbi_149",xe="_timelineEntry_q2jbi_182",pe="_timelineTime_q2jbi_199",je="_timelineIcon_q2jbi_207",_e="_timelineBody_q2jbi_212",be="_timelineSummary_q2jbi_217",ve="_timelineTarget_q2jbi_224",Se="_timelineConfidence_q2jbi_231",Ce="_confidenceExact_q2jbi_240",Ne="_confidencePartial_q2jbi_245",Te="_confidenceAfterOnly_q2jbi_250",ke="_confidenceIntentOnly_q2jbi_255",qe="_confidenceUnknown_q2jbi_260",we="_timelineExpanded_q2jbi_266",Ee="_empty_q2jbi_338",Ie="_loading_q2jbi_347",Oe="_error_q2jbi_356",n={page:J,header:W,backLink:Y,headerTitle:Q,headerTime:X,statusActive:Z,statusCompleted:ee,section:te,sectionTitle:ne,summaryGrid:ae,summaryCard:ie,summaryCardActive:se,summaryIcon:ce,summaryCount:re,summaryLabel:oe,contextGrid:le,contextRow:de,contextKey:me,contextValue:ge,timelineFilter:he,filterLabel:ue,filterSelect:ye,timeline:fe,timelineEntry:xe,timelineTime:pe,timelineIcon:je,timelineBody:_e,timelineSummary:be,timelineTarget:ve,timelineConfidence:Se,confidenceExact:Ce,confidencePartial:Ne,confidenceAfterOnly:Te,confidenceIntentOnly:ke,confidenceUnknown:qe,timelineExpanded:we,empty:Ee,loading:Ie,error:Oe},$=[{key:"script",icon:"📝",labelKey:"changelog.category.script"},{key:"instance",icon:"🧱",labelKey:"changelog.category.instance"},{key:"property",icon:"🎨",labelKey:"changelog.category.property"},{key:"lighting",icon:"🌅",labelKey:"changelog.category.lighting"},{key:"terrain",icon:"⛰️",labelKey:"changelog.category.terrain"},{key:"asset",icon:"📦",labelKey:"changelog.category.asset"}];function A(t){if(!t)return"--:--";const s=new Date(t);return`${String(s.getHours()).padStart(2,"0")}:${String(s.getMinutes()).padStart(2,"0")}`}function Le(t){if(!t)return"--:--:--";const s=new Date(t);return`${String(s.getHours()).padStart(2,"0")}:${String(s.getMinutes()).padStart(2,"0")}:${String(s.getSeconds()).padStart(2,"0")}`}function Ae(t,s){if(!t||!s)return"";const l=new Date(s).getTime()-new Date(t).getTime();return l<0?"":`${Math.round(l/6e4)}min`}function Ke(t){switch(t){case"exact":return n.confidenceExact;case"partial":return n.confidencePartial;case"after-only":return n.confidenceAfterOnly;case"intent-only":return n.confidenceIntentOnly;default:return n.confidenceUnknown}}function Re(t,s){switch(s){case"exact":return t("changelog.detail.confidence.exact","Exact");case"partial":return t("changelog.detail.confidence.partial","Partial");case"after-only":return t("changelog.detail.confidence.afterOnly","After only");case"intent-only":return t("changelog.detail.confidence.intentOnly","Intent only");default:return t("changelog.detail.confidence.unknown","Unknown")}}function $e(t,s){switch(s){case"exact":return t("changelog.detail.confidence.exact.tooltip","Both the before and after state were confirmed for this change.");case"partial":return t("changelog.detail.confidence.partial.tooltip","Only part of the before and after state could be confirmed for this change.");case"after-only":return t("changelog.detail.confidence.afterOnly.tooltip","Only the resulting state after the change could be confirmed.");case"intent-only":return t("changelog.detail.confidence.intentOnly.tooltip","Only the requested action was recorded, not the resulting state.");default:return t("changelog.detail.confidence.unknown.tooltip","This change could not be confidently classified from the available data.")}}function Fe(){var f,x,p,j,_,b,v,S,C,N,T,g,k,h;const{t}=M(),{id:s}=B(),l=D(),a=U(s),[d,u]=r.useState("all"),[q,w]=r.useState(null),y=r.useMemo(()=>[...d==="all"?a.changes:a.changes.filter(c=>c.category===d)].reverse(),[a.changes,d]);if(a.loading)return e.jsx("div",{className:n.loading,children:t("common.loading","Loading...")});if(a.error)return e.jsxs("div",{className:n.error,children:[a.error,e.jsx("br",{}),e.jsxs("span",{className:n.backLink,onClick:()=>l("/changelog"),children:["←"," ",t("changelog.detail.backToList","Back to list")]})]});const E=Ae(a.startTime,a.endTime),I=a.endTime?`${A(a.startTime)} → ${A(a.endTime)} (${E})`:`${A(a.startTime)} → ${t("changelog.card.inProgress","in progress")}`,O=!!((f=a.contextSummary)!=null&&f.intent||(x=a.contextSummary)!=null&&x.testScenario||(p=a.contextSummary)!=null&&p.expectedBehavior||(j=a.contextSummary)!=null&&j.observedBehavior),L=!!((_=a.verificationSummary)!=null&&_.label||(b=a.verificationSummary)!=null&&b.status||(v=a.verificationSummary)!=null&&v.testTimestamp);return e.jsxs("div",{className:n.page,children:[e.jsxs("div",{className:n.header,children:[e.jsxs("span",{className:n.backLink,onClick:()=>l("/changelog"),children:["←"," ",t("sidebar.changelog","Changelog")]}),e.jsx("span",{className:n.headerTitle,children:"|"}),e.jsx("span",{className:n.headerTime,children:I}),e.jsx(m,{text:a.status==="active"?t("changelog.card.active.tooltip","This session is still receiving new game changes."):t("changelog.card.completed.tooltip","This session has ended and no more changes are expected."),children:e.jsx("span",{className:a.status==="active"?n.statusActive:n.statusCompleted,children:a.status==="active"?t("changelog.card.active","Active"):t("changelog.card.completed","Completed")})})]}),e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.changeSummary.tooltip","Counts of extracted game changes grouped by category for this session."),children:e.jsx("span",{children:t("changelog.detail.changeSummary","Change Summary")})})}),e.jsx("div",{className:n.summaryGrid,children:$.map(i=>{const c=a.changeSummary;let o;switch(i.key){case"script":o=c.scriptsModified+c.scriptsCreated;break;case"instance":o=c.instancesCreated+c.instancesDeleted+c.instancesMoved;break;case"property":o=c.propertiesChanged;break;case"lighting":o=c.lightingChanged?1:0;break;case"terrain":o=c.terrainChanged?1:0;break;case"asset":o=c.assetsInserted;break;default:o=0}const K=d===i.key;return e.jsxs("div",{className:`${n.summaryCard} ${K?n.summaryCardActive:""}`,onClick:()=>u(K?"all":i.key),children:[e.jsx("span",{className:n.summaryIcon,children:i.icon}),e.jsx("div",{className:n.summaryCount,children:o}),e.jsx("div",{className:n.summaryLabel,children:t(i.labelKey,i.key)})]},i.key)})})]}),O&&e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.context.tooltip","Structured execution context captured for this changelog session."),children:e.jsx("span",{children:t("changelog.detail.context.title","Context Summary")})})}),e.jsxs("div",{className:n.contextGrid,children:[((S=a.contextSummary)==null?void 0:S.intent)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.card.sessionIntent","Session intent")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.intent})]}),((C=a.contextSummary)==null?void 0:C.testScenario)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("playtest.context.why","Why this test ran")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.testScenario})]}),((N=a.contextSummary)==null?void 0:N.expectedBehavior)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("playtest.context.expected","Expected")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.expectedBehavior})]}),((T=a.contextSummary)==null?void 0:T.observedBehavior)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("playtest.context.observed","Observed")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.observedBehavior})]})]})]}),L&&e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.verification.tooltip","Verification signals linked to this changelog session."),children:e.jsx("span",{children:t("changelog.detail.verification.title","Verification")})})}),e.jsxs("div",{className:n.contextGrid,children:[((g=a.verificationSummary)==null?void 0:g.label)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.detail.verification.label","Result")}),e.jsx("span",{className:n.contextValue,children:a.verificationSummary.label})]}),((k=a.verificationSummary)==null?void 0:k.status)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.detail.verification.status","Status")}),e.jsx("span",{className:n.contextValue,children:a.verificationSummary.status})]}),((h=a.verificationSummary)==null?void 0:h.testTimestamp)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.detail.verification.timestamp","Recorded at")}),e.jsx("span",{className:n.contextValue,children:a.verificationSummary.testTimestamp})]})]})]}),e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.changeTimeline.tooltip","Chronological list of extracted game changes for this session."),children:e.jsx("span",{children:t("changelog.detail.changeTimeline","Change Timeline")})})}),e.jsxs("div",{className:n.timelineFilter,children:[e.jsx("span",{className:n.filterLabel,children:e.jsx(V,{label:`${t("changelog.detail.filterCategory","Category")}:`,tooltip:t("changelog.detail.filterCategory.tooltip","Filter the timeline to a single change category.")})}),e.jsxs("select",{className:n.filterSelect,value:d,onChange:i=>u(i.target.value),children:[e.jsx("option",{value:"all",children:t("tools.filter.all","All")}),$.map(i=>e.jsxs("option",{value:i.key,children:[i.icon," ",t(i.labelKey,i.key)]},i.key))]})]}),y.length===0?e.jsx("div",{className:n.empty,children:t("changelog.detail.noChanges","No changes in this category")}):e.jsx("div",{className:n.timeline,children:y.map((i,c)=>{const o=q===c;return e.jsxs("div",{children:[e.jsxs("div",{className:n.timelineEntry,onClick:()=>w(o?null:c),children:[e.jsx("span",{className:n.timelineTime,children:Le(i.timestamp)}),e.jsx("span",{className:n.timelineIcon,children:H(i.category)}),e.jsxs("div",{className:n.timelineBody,children:[e.jsxs("div",{className:n.timelineSummary,children:[i.summary,e.jsx(m,{text:$e(t,i.confidence),children:e.jsx("span",{className:`${n.timelineConfidence} ${Ke(i.confidence)}`,children:Re(t,i.confidence)})})]}),e.jsx("div",{className:n.timelineTarget,children:i.target})]})]}),o&&e.jsx("div",{className:n.timelineExpanded,children:e.jsx(Me,{change:i})})]},c)})})]})]})}function Me({change:t}){return t.category==="script"&&(t.before||t.after)?e.jsx(F,{before:t.before,after:t.after}):t.category==="property"?e.jsx(G,{before:t.before,after:t.after}):t.details?e.jsx("pre",{style:{fontFamily:"var(--font-code)",fontSize:"11px",color:"var(--text-secondary)",margin:0,whiteSpace:"pre-wrap",wordBreak:"break-all"},children:JSON.stringify(t.details,null,2)}):e.jsx("div",{style:{fontFamily:"var(--font-code)",fontSize:"11px",color:"var(--text-muted)"},children:t.target})}export{Fe as Component};
1
+ import{r,a as R,u as M,b as B,c as D,j as e,T as m}from"./index-E1cuNPsJ.js";import{I as V}from"./InfoLabel-CBAqTqRy.js";import{D as F,P as G}from"./PropertyDiff-D34Apw3G.js";function P(t){const s={scriptsModified:0,scriptsCreated:0,instancesCreated:0,instancesDeleted:0,instancesMoved:0,propertiesChanged:0,lightingChanged:!1,terrainChanged:!1,assetsInserted:0};for(const l of t)switch(l.category){case"script":l.changeType==="create"?s.scriptsCreated++:s.scriptsModified++;break;case"instance":l.changeType==="create"?s.instancesCreated++:l.changeType==="delete"?s.instancesDeleted++:l.changeType==="move"&&s.instancesMoved++;break;case"property":s.propertiesChanged++;break;case"lighting":s.lightingChanged=!0;break;case"terrain":s.terrainChanged=!0;break;case"asset":s.assetsInserted++;break}return s}function U(t){const[s,l]=r.useState(""),[a,d]=r.useState(""),[u,q]=r.useState(),[w,y]=r.useState("completed"),[E,I]=r.useState([]),[O,L]=r.useState([]),[f,x]=r.useState([]),[p,j]=r.useState(),[_,b]=r.useState(),[v,S]=r.useState(),[C,N]=r.useState({scriptsModified:0,scriptsCreated:0,instancesCreated:0,instancesDeleted:0,instancesMoved:0,propertiesChanged:0,lightingChanged:!1,terrainChanged:!1,assetsInserted:0}),[T,g]=r.useState(!0),[k,h]=r.useState(null),i=r.useCallback(async()=>{if(t){g(!0),h(null);try{const[c,o]=await Promise.all([R.get(`/api/dashboard/changelog/${t}`),R.get(`/api/dashboard/changelog/${t}/changes`)]);l(c.entryId),d(c.startTime),q(c.endTime),y(c.status),I(c.entries),L(c.failures),j(c.contextSummary),b(c.replayMetadata),S(c.verificationSummary),x(o.changes),N(P(o.changes))}catch(c){h(c instanceof Error?c.message:"Failed to load changelog detail")}finally{g(!1)}}},[t]);return r.useEffect(()=>{i()},[i]),{entryId:s,startTime:a,endTime:u,status:w,entries:E,failures:O,changes:f,changeSummary:C,contextSummary:p,replayMetadata:_,verificationSummary:v,loading:T,error:k,refresh:i}}const z={script:"📝",instance:"🧱",property:"🎨",lighting:"🌅",terrain:"⛰️",asset:"📦"};function H(t){return z[t]??"❓"}const J="_page_q2jbi_2",W="_header_q2jbi_10",Y="_backLink_q2jbi_16",Q="_headerTitle_q2jbi_29",X="_headerTime_q2jbi_37",Z="_statusActive_q2jbi_44",ee="_statusCompleted_q2jbi_49",te="_section_q2jbi_54",ne="_sectionTitle_q2jbi_61",ae="_summaryGrid_q2jbi_74",ie="_summaryCard_q2jbi_80",se="_summaryCardActive_q2jbi_94",ce="_summaryIcon_q2jbi_99",re="_summaryCount_q2jbi_105",oe="_summaryLabel_q2jbi_112",le="_contextGrid_q2jbi_121",de="_contextRow_q2jbi_127",me="_contextKey_q2jbi_133",ge="_contextValue_q2jbi_141",he="_timelineFilter_q2jbi_149",ue="_filterLabel_q2jbi_156",ye="_filterSelect_q2jbi_162",fe="_timeline_q2jbi_149",xe="_timelineEntry_q2jbi_182",pe="_timelineTime_q2jbi_199",je="_timelineIcon_q2jbi_207",_e="_timelineBody_q2jbi_212",be="_timelineSummary_q2jbi_217",ve="_timelineTarget_q2jbi_224",Se="_timelineConfidence_q2jbi_231",Ce="_confidenceExact_q2jbi_240",Ne="_confidencePartial_q2jbi_245",Te="_confidenceAfterOnly_q2jbi_250",ke="_confidenceIntentOnly_q2jbi_255",qe="_confidenceUnknown_q2jbi_260",we="_timelineExpanded_q2jbi_266",Ee="_empty_q2jbi_338",Ie="_loading_q2jbi_347",Oe="_error_q2jbi_356",n={page:J,header:W,backLink:Y,headerTitle:Q,headerTime:X,statusActive:Z,statusCompleted:ee,section:te,sectionTitle:ne,summaryGrid:ae,summaryCard:ie,summaryCardActive:se,summaryIcon:ce,summaryCount:re,summaryLabel:oe,contextGrid:le,contextRow:de,contextKey:me,contextValue:ge,timelineFilter:he,filterLabel:ue,filterSelect:ye,timeline:fe,timelineEntry:xe,timelineTime:pe,timelineIcon:je,timelineBody:_e,timelineSummary:be,timelineTarget:ve,timelineConfidence:Se,confidenceExact:Ce,confidencePartial:Ne,confidenceAfterOnly:Te,confidenceIntentOnly:ke,confidenceUnknown:qe,timelineExpanded:we,empty:Ee,loading:Ie,error:Oe},$=[{key:"script",icon:"📝",labelKey:"changelog.category.script"},{key:"instance",icon:"🧱",labelKey:"changelog.category.instance"},{key:"property",icon:"🎨",labelKey:"changelog.category.property"},{key:"lighting",icon:"🌅",labelKey:"changelog.category.lighting"},{key:"terrain",icon:"⛰️",labelKey:"changelog.category.terrain"},{key:"asset",icon:"📦",labelKey:"changelog.category.asset"}];function A(t){if(!t)return"--:--";const s=new Date(t);return`${String(s.getHours()).padStart(2,"0")}:${String(s.getMinutes()).padStart(2,"0")}`}function Le(t){if(!t)return"--:--:--";const s=new Date(t);return`${String(s.getHours()).padStart(2,"0")}:${String(s.getMinutes()).padStart(2,"0")}:${String(s.getSeconds()).padStart(2,"0")}`}function Ae(t,s){if(!t||!s)return"";const l=new Date(s).getTime()-new Date(t).getTime();return l<0?"":`${Math.round(l/6e4)}min`}function Ke(t){switch(t){case"exact":return n.confidenceExact;case"partial":return n.confidencePartial;case"after-only":return n.confidenceAfterOnly;case"intent-only":return n.confidenceIntentOnly;default:return n.confidenceUnknown}}function Re(t,s){switch(s){case"exact":return t("changelog.detail.confidence.exact","Exact");case"partial":return t("changelog.detail.confidence.partial","Partial");case"after-only":return t("changelog.detail.confidence.afterOnly","After only");case"intent-only":return t("changelog.detail.confidence.intentOnly","Intent only");default:return t("changelog.detail.confidence.unknown","Unknown")}}function $e(t,s){switch(s){case"exact":return t("changelog.detail.confidence.exact.tooltip","Both the before and after state were confirmed for this change.");case"partial":return t("changelog.detail.confidence.partial.tooltip","Only part of the before and after state could be confirmed for this change.");case"after-only":return t("changelog.detail.confidence.afterOnly.tooltip","Only the resulting state after the change could be confirmed.");case"intent-only":return t("changelog.detail.confidence.intentOnly.tooltip","Only the requested action was recorded, not the resulting state.");default:return t("changelog.detail.confidence.unknown.tooltip","This change could not be confidently classified from the available data.")}}function Fe(){var f,x,p,j,_,b,v,S,C,N,T,g,k,h;const{t}=M(),{id:s}=B(),l=D(),a=U(s),[d,u]=r.useState("all"),[q,w]=r.useState(null),y=r.useMemo(()=>[...d==="all"?a.changes:a.changes.filter(c=>c.category===d)].reverse(),[a.changes,d]);if(a.loading)return e.jsx("div",{className:n.loading,children:t("common.loading","Loading...")});if(a.error)return e.jsxs("div",{className:n.error,children:[a.error,e.jsx("br",{}),e.jsxs("span",{className:n.backLink,onClick:()=>l("/changelog"),children:["←"," ",t("changelog.detail.backToList","Back to list")]})]});const E=Ae(a.startTime,a.endTime),I=a.endTime?`${A(a.startTime)} → ${A(a.endTime)} (${E})`:`${A(a.startTime)} → ${t("changelog.card.inProgress","in progress")}`,O=!!((f=a.contextSummary)!=null&&f.intent||(x=a.contextSummary)!=null&&x.testScenario||(p=a.contextSummary)!=null&&p.expectedBehavior||(j=a.contextSummary)!=null&&j.observedBehavior),L=!!((_=a.verificationSummary)!=null&&_.label||(b=a.verificationSummary)!=null&&b.status||(v=a.verificationSummary)!=null&&v.testTimestamp);return e.jsxs("div",{className:n.page,children:[e.jsxs("div",{className:n.header,children:[e.jsxs("span",{className:n.backLink,onClick:()=>l("/changelog"),children:["←"," ",t("sidebar.changelog","Changelog")]}),e.jsx("span",{className:n.headerTitle,children:"|"}),e.jsx("span",{className:n.headerTime,children:I}),e.jsx(m,{text:a.status==="active"?t("changelog.card.active.tooltip","This session is still receiving new game changes."):t("changelog.card.completed.tooltip","This session has ended and no more changes are expected."),children:e.jsx("span",{className:a.status==="active"?n.statusActive:n.statusCompleted,children:a.status==="active"?t("changelog.card.active","Active"):t("changelog.card.completed","Completed")})})]}),e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.changeSummary.tooltip","Counts of extracted game changes grouped by category for this session."),children:e.jsx("span",{children:t("changelog.detail.changeSummary","Change Summary")})})}),e.jsx("div",{className:n.summaryGrid,children:$.map(i=>{const c=a.changeSummary;let o;switch(i.key){case"script":o=c.scriptsModified+c.scriptsCreated;break;case"instance":o=c.instancesCreated+c.instancesDeleted+c.instancesMoved;break;case"property":o=c.propertiesChanged;break;case"lighting":o=c.lightingChanged?1:0;break;case"terrain":o=c.terrainChanged?1:0;break;case"asset":o=c.assetsInserted;break;default:o=0}const K=d===i.key;return e.jsxs("div",{className:`${n.summaryCard} ${K?n.summaryCardActive:""}`,onClick:()=>u(K?"all":i.key),children:[e.jsx("span",{className:n.summaryIcon,children:i.icon}),e.jsx("div",{className:n.summaryCount,children:o}),e.jsx("div",{className:n.summaryLabel,children:t(i.labelKey,i.key)})]},i.key)})})]}),O&&e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.context.tooltip","Structured execution context captured for this changelog session."),children:e.jsx("span",{children:t("changelog.detail.context.title","Context Summary")})})}),e.jsxs("div",{className:n.contextGrid,children:[((S=a.contextSummary)==null?void 0:S.intent)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.card.sessionIntent","Session intent")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.intent})]}),((C=a.contextSummary)==null?void 0:C.testScenario)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("playtest.context.why","Why this test ran")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.testScenario})]}),((N=a.contextSummary)==null?void 0:N.expectedBehavior)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("playtest.context.expected","Expected")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.expectedBehavior})]}),((T=a.contextSummary)==null?void 0:T.observedBehavior)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("playtest.context.observed","Observed")}),e.jsx("span",{className:n.contextValue,children:a.contextSummary.observedBehavior})]})]})]}),L&&e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.verification.tooltip","Verification signals linked to this changelog session."),children:e.jsx("span",{children:t("changelog.detail.verification.title","Verification")})})}),e.jsxs("div",{className:n.contextGrid,children:[((g=a.verificationSummary)==null?void 0:g.label)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.detail.verification.label","Result")}),e.jsx("span",{className:n.contextValue,children:a.verificationSummary.label})]}),((k=a.verificationSummary)==null?void 0:k.status)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.detail.verification.status","Status")}),e.jsx("span",{className:n.contextValue,children:a.verificationSummary.status})]}),((h=a.verificationSummary)==null?void 0:h.testTimestamp)&&e.jsxs("div",{className:n.contextRow,children:[e.jsx("span",{className:n.contextKey,children:t("changelog.detail.verification.timestamp","Recorded at")}),e.jsx("span",{className:n.contextValue,children:a.verificationSummary.testTimestamp})]})]})]}),e.jsxs("div",{className:n.section,children:[e.jsx("div",{className:n.sectionTitle,children:e.jsx(m,{text:t("changelog.detail.changeTimeline.tooltip","Chronological list of extracted game changes for this session."),children:e.jsx("span",{children:t("changelog.detail.changeTimeline","Change Timeline")})})}),e.jsxs("div",{className:n.timelineFilter,children:[e.jsx("span",{className:n.filterLabel,children:e.jsx(V,{label:`${t("changelog.detail.filterCategory","Category")}:`,tooltip:t("changelog.detail.filterCategory.tooltip","Filter the timeline to a single change category.")})}),e.jsxs("select",{className:n.filterSelect,value:d,onChange:i=>u(i.target.value),children:[e.jsx("option",{value:"all",children:t("tools.filter.all","All")}),$.map(i=>e.jsxs("option",{value:i.key,children:[i.icon," ",t(i.labelKey,i.key)]},i.key))]})]}),y.length===0?e.jsx("div",{className:n.empty,children:t("changelog.detail.noChanges","No changes in this category")}):e.jsx("div",{className:n.timeline,children:y.map((i,c)=>{const o=q===c;return e.jsxs("div",{children:[e.jsxs("div",{className:n.timelineEntry,onClick:()=>w(o?null:c),children:[e.jsx("span",{className:n.timelineTime,children:Le(i.timestamp)}),e.jsx("span",{className:n.timelineIcon,children:H(i.category)}),e.jsxs("div",{className:n.timelineBody,children:[e.jsxs("div",{className:n.timelineSummary,children:[i.summary,e.jsx(m,{text:$e(t,i.confidence),children:e.jsx("span",{className:`${n.timelineConfidence} ${Ke(i.confidence)}`,children:Re(t,i.confidence)})})]}),e.jsx("div",{className:n.timelineTarget,children:i.target})]})]}),o&&e.jsx("div",{className:n.timelineExpanded,children:e.jsx(Me,{change:i})})]},c)})})]})]})}function Me({change:t}){return t.category==="script"&&(t.before||t.after)?e.jsx(F,{before:t.before,after:t.after}):t.category==="property"?e.jsx(G,{before:t.before,after:t.after}):t.details?e.jsx("pre",{style:{fontFamily:"var(--font-code)",fontSize:"11px",color:"var(--text-secondary)",margin:0,whiteSpace:"pre-wrap",wordBreak:"break-all"},children:JSON.stringify(t.details,null,2)}):e.jsx("div",{style:{fontFamily:"var(--font-code)",fontSize:"11px",color:"var(--text-muted)"},children:t.target})}export{Fe as Component};
@@ -1 +1 @@
1
- import{r as i,a as P,D as O,u as D,j as e,T as I,c as V,i as U}from"./index-DWRH2iF4.js";import{C as G}from"./ConfirmModal-Db4rfrTo.js";import{u as H,T as Z,a as q}from"./tier-promo-config-CQFDWwo4.js";import{t as m}from"./TierPromo.module-BBX3CVXn.js";const z=10;function J(){const[t,p]=i.useState([]),[s,a]=i.useState(0),[u,f]=i.useState(!1),[r,v]=i.useState(!0),[l,C]=i.useState(0),[d,j]=i.useState("all"),b=i.useRef(null),x=i.useCallback(async(h,o)=>{v(!0);try{const S={limit:String(z),offset:String(h)};o!=="all"&&(S.status=o);const N=await P.get("/api/dashboard/changelog",S);p(N.entries),a(N.total),f(N.hasMore)}catch{p([]),a(0),f(!1)}finally{v(!1)}},[]),y=i.useCallback(()=>{x(l,d)},[x,l,d]),_=i.useCallback(async()=>{await P.post("/api/dashboard/changelog/clear"),p([]),a(0),f(!1)},[]);return i.useEffect(()=>{x(l,d)},[x,l,d]),i.useEffect(()=>{const h=new O;b.current=h,h.connect();const o=h.on("command",()=>{x(l,d)});return()=>{o(),h.disconnect(),b.current=null}},[x,l,d]),{entries:t,total:s,hasMore:u,loading:r,offset:l,statusFilter:d,setOffset:C,setStatusFilter:j,refresh:y,clear:_}}const K="_card_1n89u_2",Q="_header_1n89u_17",W="_statusBadge_1n89u_24",X="_statusDot_1n89u_35",Y="_active_1n89u_41",ee="_completed_1n89u_50",se="_timeRange_1n89u_58",te="_summaryList_1n89u_65",ae="_summaryItem_1n89u_71",ne="_summaryIcon_1n89u_80",oe="_summaryText_1n89u_86",ce="_progressBar_1n89u_91",ie="_progressFill_1n89u_99",re="_emptySummary_1n89u_112",le="_contextBlock_1n89u_120",de="_contextLabel_1n89u_129",ge="_contextValue_1n89u_137",n={card:K,header:Q,statusBadge:W,statusDot:X,active:Y,completed:ee,timeRange:se,summaryList:te,summaryItem:ae,summaryIcon:ne,summaryText:oe,progressBar:ce,progressFill:ie,emptySummary:re,contextBlock:le,contextLabel:de,contextValue:ge};function F(t){if(!t)return"--:--";const p=new Date(t);return`${String(p.getHours()).padStart(2,"0")}:${String(p.getMinutes()).padStart(2,"0")}`}function me({entry:t,onClick:p}){var S,N,k,B,L,w,A,M,R,E;const{t:s}=D(),a=t.changeSummary,u=t.status==="active",f=t.isBootstrapOnly===!0,r=[],v=a.scriptsModified+a.scriptsCreated;if(v>0){const g=[];a.scriptsModified>0&&g.push(`${a.scriptsModified} ${s("changelog.card.modified","modified")}`),a.scriptsCreated>0&&g.push(`${a.scriptsCreated} ${s("changelog.card.created","created")}`);const T=`${v} ${s("changelog.card.scripts","scripts")} ${g.join(", ")}`;r.push({icon:"📝",text:T,tooltip:s("changelog.card.scripts.tooltip","Script changes made in this session.")})}const l=a.instancesCreated+a.instancesDeleted+a.instancesMoved;if(l>0){const g=[];a.instancesCreated>0&&g.push(`${a.instancesCreated} ${s("changelog.card.created","created")}`),a.instancesDeleted>0&&g.push(`${a.instancesDeleted} ${s("changelog.card.deleted","deleted")}`),a.instancesMoved>0&&g.push(`${a.instancesMoved} ${s("changelog.card.moved","moved")}`);const T=`${l} ${s("changelog.card.instances","instances")} ${g.join(", ")}`;r.push({icon:"🧱",text:T,tooltip:s("changelog.card.instances.tooltip","Instance create, delete, move, or clone changes in this session.")})}a.propertiesChanged>0&&r.push({icon:"🎨",text:`${a.propertiesChanged} ${s("changelog.card.propertiesChanged","properties changed")}`,tooltip:s("changelog.card.propertiesChanged.tooltip","Property value changes recorded for this session.")}),a.lightingChanged&&r.push({icon:"🌅",text:s("changelog.card.lightingConfigured","Lighting configured"),tooltip:s("changelog.card.lightingConfigured.tooltip","Lighting or atmosphere settings changed in this session.")}),a.terrainChanged&&r.push({icon:"⛰️",text:s("changelog.card.terrainConfigured","Terrain configured"),tooltip:s("changelog.card.terrainConfigured.tooltip","Terrain data or terrain settings changed in this session.")}),a.assetsInserted>0&&r.push({icon:"📦",text:`${a.assetsInserted} ${s("changelog.card.assetsInserted","assets inserted")}`,tooltip:s("changelog.card.assetsInserted.tooltip","Assets inserted into the place during this session.")});const C=F(t.startTime),d=u?s("changelog.card.inProgress","in progress"):t.endTime?F(t.endTime):"--:--",j=f?s("changelog.card.bootstrapStatus","Bootstrap"):u?s("changelog.card.active","Active"):s("changelog.card.completed","Completed"),b=f?s("changelog.card.bootstrapStatus.tooltip","This session only contains the initial sync bootstrap snapshot."):u?s("changelog.card.active.tooltip","This session is still receiving new game changes."):s("changelog.card.completed.tooltip","This session has ended and no more changes are expected."),x=f?s("changelog.card.bootstrapSummary","Initial sync snapshot"):s("changelog.card.noChanges","No changes yet"),y=f?s("changelog.card.bootstrapSummary.tooltip","Initial file sync writes are collapsed into a single bootstrap snapshot row."):s("changelog.card.noChanges.tooltip","No game changes have been extracted for this session yet."),_=(N=(S=t.contextSummary)==null?void 0:S.intent)==null?void 0:N.trim(),h=((w=(L=(B=(k=t.contextSummary)==null?void 0:k.affectedAreas)==null?void 0:B[0])==null?void 0:L.label)==null?void 0:w.trim())||((M=(A=t.contextSummary)==null?void 0:A.testScenario)==null?void 0:M.trim()),o=(E=(R=t.verificationSummary)==null?void 0:R.label)==null?void 0:E.trim();return e.jsxs("div",{className:n.card,onClick:p,children:[e.jsxs("div",{className:n.header,children:[e.jsx(I,{text:b,children:e.jsxs("span",{className:`${n.statusBadge} ${u?n.active:n.completed}`,children:[e.jsx("span",{className:n.statusDot}),j]})}),e.jsxs("span",{className:n.timeRange,children:[C,"~",d]})]}),e.jsx("div",{className:n.summaryList,children:r.length>0?r.map((g,T)=>e.jsxs("div",{className:n.summaryItem,children:[e.jsx("span",{className:n.summaryIcon,children:g.icon}),e.jsx(I,{text:g.tooltip,children:e.jsx("span",{className:n.summaryText,children:g.text})})]},T)):e.jsx(I,{text:y,children:e.jsx("span",{className:n.emptySummary,style:{display:"block"},children:x})})}),_&&e.jsxs("div",{className:n.contextBlock,children:[e.jsx("span",{className:n.contextLabel,children:s("changelog.card.sessionIntent","Session intent")}),e.jsx("span",{className:n.contextValue,children:_})]}),!_&&h&&e.jsxs("div",{className:n.contextBlock,children:[e.jsx("span",{className:n.contextLabel,children:s("changelog.card.representativeArea","Representative area")}),e.jsx("span",{className:n.contextValue,children:h})]}),o&&e.jsxs("div",{className:n.contextBlock,children:[e.jsx("span",{className:n.contextLabel,children:s("changelog.card.verification","Verification")}),e.jsx("span",{className:n.contextValue,children:o})]}),u&&e.jsx("div",{className:n.progressBar,children:e.jsx("div",{className:n.progressFill})})]})}const he="_page_1srvj_2",pe="_limitNotice_1srvj_9",ue="_limitNoticeTitle_1srvj_18",fe="_limitNoticeText_1srvj_25",xe="_headerRow_1srvj_31",_e="_header_1srvj_31",ve="_clearButton_1srvj_48",je="_headerSub_1srvj_57",be="_filterTabs_1srvj_67",ye="_filterTab_1srvj_67",Ne="_filterTabActive_1srvj_90",Ce="_list_1srvj_96",Se="_empty_1srvj_103",Te="_loading_1srvj_112",$e="_pagination_1srvj_121",Ie="_pageInfo_1srvj_129",ke="_btn_1srvj_135",c={page:he,limitNotice:pe,limitNoticeTitle:ue,limitNoticeText:fe,headerRow:xe,header:_e,clearButton:ve,headerSub:je,filterTabs:be,filterTab:ye,filterTabActive:Ne,list:Ce,empty:Se,loading:Te,pagination:$e,pageInfo:Ie,btn:ke},$=10,Be=[{key:"all",label:"changelog.filter.all"},{key:"active",label:"changelog.filter.active"},{key:"completed",label:"changelog.filter.completed"}];function Re(){const{t}=D(),p=V(),s=J(),a=H(),{show:u}=U(),[f,r]=i.useState(!1),[v,l]=i.useState(!1),[C,d]=i.useState(!1),j=!a.loading&&a.tier==="basic",b=j?s.entries.slice(0,3):s.entries,x=!j&&s.total>$,y=b.length,_=s.total,h=async()=>{l(!0);try{await s.clear(),u(t("toast.clearSuccess","Cleared successfully"),"success"),r(!1)}catch{u(t("toast.clearFailed","Failed to clear data"),"error")}finally{l(!1)}};return e.jsxs("div",{className:c.page,children:[e.jsxs("div",{className:c.headerRow,children:[e.jsxs("h2",{className:c.header,children:[t("sidebar.changelog","Changelog"),e.jsx("span",{className:c.headerSub,children:t("changelog.subtitle","Game Change History")})]}),e.jsx("button",{className:c.clearButton,onClick:()=>r(!0),children:t("common.clear","Clear")})]}),e.jsx("div",{className:c.filterTabs,children:Be.map(o=>e.jsx("button",{className:`${c.filterTab} ${s.statusFilter===o.key?c.filterTabActive:""}`,onClick:()=>{s.setStatusFilter(o.key),s.setOffset(0)},children:t(o.label,o.key.charAt(0).toUpperCase()+o.key.slice(1))},o.key))}),s.loading&&s.entries.length===0&&e.jsx("div",{className:c.loading,children:t("common.loading","Loading...")}),!s.loading&&s.entries.length===0?e.jsx("div",{className:c.empty,children:t("changelog.empty","No changelog entries yet")}):e.jsx("div",{className:c.list,children:b.map(o=>e.jsx(me,{entry:o,onClick:()=>p(`/changelog/${o.entryId}`)},o.entryId))}),x&&e.jsxs("div",{className:c.pagination,children:[e.jsx("button",{className:c.btn,disabled:s.offset===0,onClick:()=>s.setOffset(Math.max(0,s.offset-$)),children:t("tools.page.prev","Prev")}),e.jsxs("span",{className:c.pageInfo,children:[s.offset+1,"–",Math.min(s.offset+$,s.total)," / ",s.total]}),e.jsx("button",{className:c.btn,disabled:!s.hasMore,onClick:()=>s.setOffset(s.offset+$),children:t("tools.page.next","Next")})]}),j&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:c.limitNotice,children:[e.jsx("div",{className:c.limitNoticeTitle,children:t("changelog.basic.limit.title","Basic preview shows the latest 3 sessions")}),e.jsx("div",{className:c.limitNoticeText,children:t("changelog.basic.limit.body","Upgrade to Pro to browse the full changelog timeline for this place.")})]}),e.jsxs("div",{className:m.progressPromo,children:[e.jsxs("div",{className:m.progressMain,children:[e.jsxs("div",{className:m.progressLabel,children:[e.jsx("span",{children:t("changelog.basic.metricLabel","Visible Changelog / Total")}),e.jsxs("span",{children:[y," / ",_]})]}),e.jsx("div",{className:m.progressBar,children:e.jsx("div",{className:m.progressFill,style:{width:`${_>0?Math.min(y/_*100,100):0}%`}})}),e.jsxs("div",{className:m.progressLabel,children:[e.jsxs("span",{children:[y," ",t("changelog.basic.visible","visible")]}),e.jsxs("span",{children:[_," ",t("changelog.basic.total","total")]})]})]}),e.jsx("span",{className:m.progressText,children:t("tier.banner.save","Save AI tokens with Pro!")}),e.jsxs("div",{className:m.actions,children:[e.jsx("button",{className:`${m.btn} ${m.btnOutline}`,onClick:()=>d(!0),children:t("tier.compare","Basic vs Pro")}),e.jsx("a",{className:`${m.btn} ${m.btnPrimary}`,href:Z.changelog,target:"_blank",rel:"noreferrer",children:t("tier.upgrade","Upgrade to Pro")})]})]})]}),e.jsx(G,{open:f,title:t("changelog.clear.title","Clear changelog?"),message:t("changelog.clear.message","This permanently removes the stored changelog for the current place."),cancelLabel:t("common.cancel","Cancel"),confirmLabel:t("common.clear","Clear"),loading:v,onCancel:()=>!v&&r(!1),onConfirm:h}),C&&e.jsx(q,{onClose:()=>d(!1)})]})}export{Re as Component};
1
+ import{r as i,a as P,D as O,u as D,j as e,T as I,c as V,i as U}from"./index-E1cuNPsJ.js";import{C as G}from"./ConfirmModal-BIJpNntn.js";import{u as H,T as Z,a as q}from"./tier-promo-config-BuGSENUv.js";import{t as m}from"./TierPromo.module-BBX3CVXn.js";const z=10;function J(){const[t,p]=i.useState([]),[s,a]=i.useState(0),[u,f]=i.useState(!1),[r,v]=i.useState(!0),[l,C]=i.useState(0),[d,j]=i.useState("all"),b=i.useRef(null),x=i.useCallback(async(h,o)=>{v(!0);try{const S={limit:String(z),offset:String(h)};o!=="all"&&(S.status=o);const N=await P.get("/api/dashboard/changelog",S);p(N.entries),a(N.total),f(N.hasMore)}catch{p([]),a(0),f(!1)}finally{v(!1)}},[]),y=i.useCallback(()=>{x(l,d)},[x,l,d]),_=i.useCallback(async()=>{await P.post("/api/dashboard/changelog/clear"),p([]),a(0),f(!1)},[]);return i.useEffect(()=>{x(l,d)},[x,l,d]),i.useEffect(()=>{const h=new O;b.current=h,h.connect();const o=h.on("command",()=>{x(l,d)});return()=>{o(),h.disconnect(),b.current=null}},[x,l,d]),{entries:t,total:s,hasMore:u,loading:r,offset:l,statusFilter:d,setOffset:C,setStatusFilter:j,refresh:y,clear:_}}const K="_card_1n89u_2",Q="_header_1n89u_17",W="_statusBadge_1n89u_24",X="_statusDot_1n89u_35",Y="_active_1n89u_41",ee="_completed_1n89u_50",se="_timeRange_1n89u_58",te="_summaryList_1n89u_65",ae="_summaryItem_1n89u_71",ne="_summaryIcon_1n89u_80",oe="_summaryText_1n89u_86",ce="_progressBar_1n89u_91",ie="_progressFill_1n89u_99",re="_emptySummary_1n89u_112",le="_contextBlock_1n89u_120",de="_contextLabel_1n89u_129",ge="_contextValue_1n89u_137",n={card:K,header:Q,statusBadge:W,statusDot:X,active:Y,completed:ee,timeRange:se,summaryList:te,summaryItem:ae,summaryIcon:ne,summaryText:oe,progressBar:ce,progressFill:ie,emptySummary:re,contextBlock:le,contextLabel:de,contextValue:ge};function F(t){if(!t)return"--:--";const p=new Date(t);return`${String(p.getHours()).padStart(2,"0")}:${String(p.getMinutes()).padStart(2,"0")}`}function me({entry:t,onClick:p}){var S,N,k,B,L,w,A,M,R,E;const{t:s}=D(),a=t.changeSummary,u=t.status==="active",f=t.isBootstrapOnly===!0,r=[],v=a.scriptsModified+a.scriptsCreated;if(v>0){const g=[];a.scriptsModified>0&&g.push(`${a.scriptsModified} ${s("changelog.card.modified","modified")}`),a.scriptsCreated>0&&g.push(`${a.scriptsCreated} ${s("changelog.card.created","created")}`);const T=`${v} ${s("changelog.card.scripts","scripts")} ${g.join(", ")}`;r.push({icon:"📝",text:T,tooltip:s("changelog.card.scripts.tooltip","Script changes made in this session.")})}const l=a.instancesCreated+a.instancesDeleted+a.instancesMoved;if(l>0){const g=[];a.instancesCreated>0&&g.push(`${a.instancesCreated} ${s("changelog.card.created","created")}`),a.instancesDeleted>0&&g.push(`${a.instancesDeleted} ${s("changelog.card.deleted","deleted")}`),a.instancesMoved>0&&g.push(`${a.instancesMoved} ${s("changelog.card.moved","moved")}`);const T=`${l} ${s("changelog.card.instances","instances")} ${g.join(", ")}`;r.push({icon:"🧱",text:T,tooltip:s("changelog.card.instances.tooltip","Instance create, delete, move, or clone changes in this session.")})}a.propertiesChanged>0&&r.push({icon:"🎨",text:`${a.propertiesChanged} ${s("changelog.card.propertiesChanged","properties changed")}`,tooltip:s("changelog.card.propertiesChanged.tooltip","Property value changes recorded for this session.")}),a.lightingChanged&&r.push({icon:"🌅",text:s("changelog.card.lightingConfigured","Lighting configured"),tooltip:s("changelog.card.lightingConfigured.tooltip","Lighting or atmosphere settings changed in this session.")}),a.terrainChanged&&r.push({icon:"⛰️",text:s("changelog.card.terrainConfigured","Terrain configured"),tooltip:s("changelog.card.terrainConfigured.tooltip","Terrain data or terrain settings changed in this session.")}),a.assetsInserted>0&&r.push({icon:"📦",text:`${a.assetsInserted} ${s("changelog.card.assetsInserted","assets inserted")}`,tooltip:s("changelog.card.assetsInserted.tooltip","Assets inserted into the place during this session.")});const C=F(t.startTime),d=u?s("changelog.card.inProgress","in progress"):t.endTime?F(t.endTime):"--:--",j=f?s("changelog.card.bootstrapStatus","Bootstrap"):u?s("changelog.card.active","Active"):s("changelog.card.completed","Completed"),b=f?s("changelog.card.bootstrapStatus.tooltip","This session only contains the initial sync bootstrap snapshot."):u?s("changelog.card.active.tooltip","This session is still receiving new game changes."):s("changelog.card.completed.tooltip","This session has ended and no more changes are expected."),x=f?s("changelog.card.bootstrapSummary","Initial sync snapshot"):s("changelog.card.noChanges","No changes yet"),y=f?s("changelog.card.bootstrapSummary.tooltip","Initial file sync writes are collapsed into a single bootstrap snapshot row."):s("changelog.card.noChanges.tooltip","No game changes have been extracted for this session yet."),_=(N=(S=t.contextSummary)==null?void 0:S.intent)==null?void 0:N.trim(),h=((w=(L=(B=(k=t.contextSummary)==null?void 0:k.affectedAreas)==null?void 0:B[0])==null?void 0:L.label)==null?void 0:w.trim())||((M=(A=t.contextSummary)==null?void 0:A.testScenario)==null?void 0:M.trim()),o=(E=(R=t.verificationSummary)==null?void 0:R.label)==null?void 0:E.trim();return e.jsxs("div",{className:n.card,onClick:p,children:[e.jsxs("div",{className:n.header,children:[e.jsx(I,{text:b,children:e.jsxs("span",{className:`${n.statusBadge} ${u?n.active:n.completed}`,children:[e.jsx("span",{className:n.statusDot}),j]})}),e.jsxs("span",{className:n.timeRange,children:[C,"~",d]})]}),e.jsx("div",{className:n.summaryList,children:r.length>0?r.map((g,T)=>e.jsxs("div",{className:n.summaryItem,children:[e.jsx("span",{className:n.summaryIcon,children:g.icon}),e.jsx(I,{text:g.tooltip,children:e.jsx("span",{className:n.summaryText,children:g.text})})]},T)):e.jsx(I,{text:y,children:e.jsx("span",{className:n.emptySummary,style:{display:"block"},children:x})})}),_&&e.jsxs("div",{className:n.contextBlock,children:[e.jsx("span",{className:n.contextLabel,children:s("changelog.card.sessionIntent","Session intent")}),e.jsx("span",{className:n.contextValue,children:_})]}),!_&&h&&e.jsxs("div",{className:n.contextBlock,children:[e.jsx("span",{className:n.contextLabel,children:s("changelog.card.representativeArea","Representative area")}),e.jsx("span",{className:n.contextValue,children:h})]}),o&&e.jsxs("div",{className:n.contextBlock,children:[e.jsx("span",{className:n.contextLabel,children:s("changelog.card.verification","Verification")}),e.jsx("span",{className:n.contextValue,children:o})]}),u&&e.jsx("div",{className:n.progressBar,children:e.jsx("div",{className:n.progressFill})})]})}const he="_page_1srvj_2",pe="_limitNotice_1srvj_9",ue="_limitNoticeTitle_1srvj_18",fe="_limitNoticeText_1srvj_25",xe="_headerRow_1srvj_31",_e="_header_1srvj_31",ve="_clearButton_1srvj_48",je="_headerSub_1srvj_57",be="_filterTabs_1srvj_67",ye="_filterTab_1srvj_67",Ne="_filterTabActive_1srvj_90",Ce="_list_1srvj_96",Se="_empty_1srvj_103",Te="_loading_1srvj_112",$e="_pagination_1srvj_121",Ie="_pageInfo_1srvj_129",ke="_btn_1srvj_135",c={page:he,limitNotice:pe,limitNoticeTitle:ue,limitNoticeText:fe,headerRow:xe,header:_e,clearButton:ve,headerSub:je,filterTabs:be,filterTab:ye,filterTabActive:Ne,list:Ce,empty:Se,loading:Te,pagination:$e,pageInfo:Ie,btn:ke},$=10,Be=[{key:"all",label:"changelog.filter.all"},{key:"active",label:"changelog.filter.active"},{key:"completed",label:"changelog.filter.completed"}];function Re(){const{t}=D(),p=V(),s=J(),a=H(),{show:u}=U(),[f,r]=i.useState(!1),[v,l]=i.useState(!1),[C,d]=i.useState(!1),j=!a.loading&&a.tier==="basic",b=j?s.entries.slice(0,3):s.entries,x=!j&&s.total>$,y=b.length,_=s.total,h=async()=>{l(!0);try{await s.clear(),u(t("toast.clearSuccess","Cleared successfully"),"success"),r(!1)}catch{u(t("toast.clearFailed","Failed to clear data"),"error")}finally{l(!1)}};return e.jsxs("div",{className:c.page,children:[e.jsxs("div",{className:c.headerRow,children:[e.jsxs("h2",{className:c.header,children:[t("sidebar.changelog","Changelog"),e.jsx("span",{className:c.headerSub,children:t("changelog.subtitle","Game Change History")})]}),e.jsx("button",{className:c.clearButton,onClick:()=>r(!0),children:t("common.clear","Clear")})]}),e.jsx("div",{className:c.filterTabs,children:Be.map(o=>e.jsx("button",{className:`${c.filterTab} ${s.statusFilter===o.key?c.filterTabActive:""}`,onClick:()=>{s.setStatusFilter(o.key),s.setOffset(0)},children:t(o.label,o.key.charAt(0).toUpperCase()+o.key.slice(1))},o.key))}),s.loading&&s.entries.length===0&&e.jsx("div",{className:c.loading,children:t("common.loading","Loading...")}),!s.loading&&s.entries.length===0?e.jsx("div",{className:c.empty,children:t("changelog.empty","No changelog entries yet")}):e.jsx("div",{className:c.list,children:b.map(o=>e.jsx(me,{entry:o,onClick:()=>p(`/changelog/${o.entryId}`)},o.entryId))}),x&&e.jsxs("div",{className:c.pagination,children:[e.jsx("button",{className:c.btn,disabled:s.offset===0,onClick:()=>s.setOffset(Math.max(0,s.offset-$)),children:t("tools.page.prev","Prev")}),e.jsxs("span",{className:c.pageInfo,children:[s.offset+1,"–",Math.min(s.offset+$,s.total)," / ",s.total]}),e.jsx("button",{className:c.btn,disabled:!s.hasMore,onClick:()=>s.setOffset(s.offset+$),children:t("tools.page.next","Next")})]}),j&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:c.limitNotice,children:[e.jsx("div",{className:c.limitNoticeTitle,children:t("changelog.basic.limit.title","Basic preview shows the latest 3 sessions")}),e.jsx("div",{className:c.limitNoticeText,children:t("changelog.basic.limit.body","Upgrade to Pro to browse the full changelog timeline for this place.")})]}),e.jsxs("div",{className:m.progressPromo,children:[e.jsxs("div",{className:m.progressMain,children:[e.jsxs("div",{className:m.progressLabel,children:[e.jsx("span",{children:t("changelog.basic.metricLabel","Visible Changelog / Total")}),e.jsxs("span",{children:[y," / ",_]})]}),e.jsx("div",{className:m.progressBar,children:e.jsx("div",{className:m.progressFill,style:{width:`${_>0?Math.min(y/_*100,100):0}%`}})}),e.jsxs("div",{className:m.progressLabel,children:[e.jsxs("span",{children:[y," ",t("changelog.basic.visible","visible")]}),e.jsxs("span",{children:[_," ",t("changelog.basic.total","total")]})]})]}),e.jsx("span",{className:m.progressText,children:t("tier.banner.save","Save AI tokens with Pro!")}),e.jsxs("div",{className:m.actions,children:[e.jsx("button",{className:`${m.btn} ${m.btnOutline}`,onClick:()=>d(!0),children:t("tier.compare","Basic vs Pro")}),e.jsx("a",{className:`${m.btn} ${m.btnPrimary}`,href:Z.changelog,target:"_blank",rel:"noreferrer",children:t("tier.upgrade","Upgrade to Pro")})]})]})]}),e.jsx(G,{open:f,title:t("changelog.clear.title","Clear changelog?"),message:t("changelog.clear.message","This permanently removes the stored changelog for the current place."),cancelLabel:t("common.cancel","Cancel"),confirmLabel:t("common.clear","Clear"),loading:v,onCancel:()=>!v&&r(!1),onConfirm:h}),C&&e.jsx(q,{onClose:()=>d(!1)})]})}export{Re as Component};
@@ -0,0 +1 @@
1
+ import{j as s,s as e}from"./index-E1cuNPsJ.js";function x({open:c,title:i,message:n,cancelLabel:t,confirmLabel:o,loading:a=!1,onCancel:l,onConfirm:r}){return c?s.jsx("div",{className:e.backdrop,onClick:a?void 0:l,children:s.jsxs("div",{className:e.modal,onClick:d=>d.stopPropagation(),children:[s.jsx("h2",{className:e.title,children:i}),s.jsx("p",{className:e.message,children:n}),s.jsxs("div",{className:e.actions,children:[s.jsx("button",{className:e.cancelButton,onClick:l,disabled:a,children:t}),s.jsx("button",{className:e.confirmButton,onClick:r,disabled:a,children:a?"...":o})]})]})}):null}export{x as C};
@@ -0,0 +1 @@
1
+ import{u as $,r,j as e,d as R,a as N,D as I,i as L,T as M}from"./index-E1cuNPsJ.js";import{I as a}from"./InfoLabel-CBAqTqRy.js";import{S as A}from"./StatusBadge-B-pYMpUG.js";import{C as E}from"./ConfirmModal-BIJpNntn.js";import{u as P,f as k}from"./useLiveUptime-qJDofPOo.js";const T="_container_1h084_2",H="_entry_1h084_14",B="_timestamp_1h084_21",D="_message_1h084_27",F="_warn_1h084_34",U="_error_1h084_39",G="_empty_1h084_44",v={container:T,entry:H,timestamp:B,message:D,warn:F,error:U,empty:G};function O(n){const t=new Date(n);return Number.isNaN(t.getTime())?n:`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`}function V({entries:n}){const{t}=$(),c=r.useRef(null);return r.useEffect(()=>{const l=c.current;l&&(l.scrollTop=l.scrollHeight)},[n.length]),e.jsx("div",{ref:c,className:v.container,children:n.length===0?e.jsx("div",{className:v.empty,children:t("connection.log.empty","No events yet")}):n.map((l,u)=>e.jsxs("div",{className:`${v.entry} ${l.type?v[l.type]:""}`,children:[e.jsx("span",{className:v.timestamp,children:O(l.timestamp)}),e.jsx("span",{className:v.message,children:l.message})]},u))})}const S=50;function q(){const n=new Date;return`${String(n.getHours()).padStart(2,"0")}:${String(n.getMinutes()).padStart(2,"0")}`}function X(){const{level:n,status:t,error:c}=R(),[l,u]=r.useState(null),[y,h]=r.useState([]),g=r.useRef(null),_=r.useCallback((i,f)=>{h(d=>{const p=[...d,{timestamp:q(),message:i,type:f}];return p.length>S?p.slice(-S):p})},[]),x=r.useCallback(async()=>{try{const i=await N.get("/connection-info");u(i)}catch{u(null)}},[]),j=r.useCallback(async()=>{try{const i=await N.get("/api/dashboard/connection-log");h(i.entries??[])}catch{h([])}},[]),C=r.useCallback(async()=>{await N.post("/api/dashboard/connection-log/clear"),h([])},[]);return r.useEffect(()=>{n!=="disconnected"&&t&&x(),j()},[n,t,x,j]),r.useEffect(()=>{const i=new I;g.current=i,i.connect();const f=i.on("connection",p=>{const m=p,b=m.status==="connected"?"connected":"disconnected";_(`Plugin ${b} — ${m.clientId}`,m.status==="connected"?"info":"warn")}),d=i.on("mcp_status",p=>{const m=p,b=m.status==="registered"?"registered":"unregistered";_(`MCP ${b} — ${m.aiClientName}`,m.status==="registered"?"info":"warn"),x()});return()=>{f(),d(),i.disconnect(),g.current=null}},[_,x]),{status:t,connectionInfo:l,connectionLog:y,level:n,error:c,clearConnectionLog:C}}const z="_page_12byi_2",J="_card_12byi_10",K="_disabled_12byi_18",Q="_cardHeader_12byi_24",W="_clearButton_12byi_37",Y="_serverGrid_12byi_47",Z="_statusRow_12byi_65",ee="_table_12byi_79",ne="_toggleBtn_12byi_104",te="_disconnected_12byi_119",se="_disconnectedActions_12byi_136",ce="_btn_12byi_143",oe="_emptyRow_12byi_161",s={page:z,card:J,disabled:K,cardHeader:Q,clearButton:W,serverGrid:Y,statusRow:Z,table:ee,toggleBtn:ne,disconnected:te,disconnectedActions:se,btn:ce,emptyRow:oe};function w(n,t){const c=Math.max(0,Math.floor((Date.now()-n)/1e3));return c<60?`${c}${t("connection.time.secondsAgo","s ago")}`:c<3600?`${Math.floor(c/60)}${t("connection.time.minutesAgo","m ago")}`:`${Math.floor(c/3600)}${t("connection.time.hoursAgo","h ago")}`}function pe(){var b;const{t:n}=$(),{status:t,connectionInfo:c,connectionLog:l,level:u,clearConnectionLog:y}=X(),{show:h}=L(),[g,_]=r.useState(!0),[x,j]=r.useState(!1),[C,i]=r.useState(!1),f=P(t==null?void 0:t.uptime),d=u==="disconnected",p=d?"offline":"online",m=async()=>{i(!0);try{await y(),h(n("toast.clearSuccess","Cleared successfully"),"success"),j(!1)}catch{h(n("toast.clearFailed","Failed to clear data"),"error")}finally{i(!1)}};return e.jsxs("div",{className:s.page,children:[e.jsxs("div",{className:s.card,children:[e.jsx("div",{className:s.cardHeader,children:n("connection.server.title","Server Status")}),d?e.jsxs("div",{className:s.disconnected,children:[e.jsx("h3",{children:n("level.l0.title")}),e.jsx("p",{children:n("level.l0.message")}),e.jsx("p",{children:n("common.reconnecting")}),e.jsxs("div",{className:s.disconnectedActions,children:[e.jsx("button",{className:s.btn,onClick:()=>window.location.reload(),children:n("connection.reconnect","Reconnect")}),e.jsx("button",{className:s.btn,onClick:()=>{window.location.hash="#/settings"},children:n("connection.checkSettings","Check Settings")})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:s.statusRow,children:e.jsx(A,{status:p})}),e.jsxs("dl",{className:s.serverGrid,children:[(t==null?void 0:t.version)&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:e.jsx(a,{label:n("connection.server.version","Version"),tooltip:n("connection.server.version.tooltip","Installed MCP server version")})}),e.jsx("dd",{children:e.jsx(M,{text:n("connection.server.version.tooltip","Installed MCP server version"),children:`v${t.version}`})})]}),(t==null?void 0:t.pid)!=null&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:e.jsx(a,{label:n("connection.server.pid","PID"),tooltip:n("connection.server.pid.tooltip","Operating system process identifier")})}),e.jsx("dd",{children:t.pid})]}),(t==null?void 0:t.uptime)!=null&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:e.jsx(a,{label:n("connection.server.uptime","Uptime"),tooltip:n("connection.server.uptime.tooltip","Time elapsed since the MCP server started")})}),e.jsx("dd",{children:k(f??t.uptime)})]}),(t==null?void 0:t.sessionId)&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:e.jsx(a,{label:n("connection.server.session","Session"),tooltip:n("connection.server.session.tooltip","Current MCP session identifier")})}),e.jsx("dd",{children:t.sessionId.slice(0,8)})]}),(c==null?void 0:c.serverExecutable)&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:e.jsx(a,{label:n("connection.server.exec","Exec"),tooltip:n("connection.server.exec.tooltip","Executable path used to launch the MCP server")})}),e.jsx("dd",{children:c.serverExecutable})]})]})]})]}),e.jsxs("div",{className:`${s.card} ${d?s.disabled:""}`,children:[e.jsxs("div",{className:s.cardHeader,children:[e.jsxs("span",{children:[n("connection.agents.title","AI Agents")," ","(",(c==null?void 0:c.mcpInstanceCount)??0,")"]}),e.jsx("button",{className:s.toggleBtn,onClick:()=>_(o=>!o),"aria-label":g?n("common.collapse","Collapse"):n("common.expand","Expand"),children:g?"▾":"▸"})]}),g&&e.jsxs("table",{className:s.table,children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:n("connection.agents.name","Agent")}),e.jsx("th",{children:e.jsx(a,{label:n("connection.server.pid","PID"),tooltip:n("connection.server.pid.tooltip","Operating system process identifier")})}),e.jsx("th",{children:e.jsx(a,{label:n("connection.agents.projectRoot","Project Root"),tooltip:n("connection.agents.projectRoot.tooltip","Authoritative project root for sync ownership")})}),e.jsx("th",{children:n("connection.agents.connected","Connected")})]})}),e.jsx("tbody",{children:c!=null&&c.mcpInstances&&c.mcpInstances.length>0?c.mcpInstances.map(o=>e.jsxs("tr",{children:[e.jsx("td",{children:o.aiClientName??n("connection.agents.unknown","Unknown")}),e.jsx("td",{children:o.pid}),e.jsx("td",{children:o.projectRoot??o.cwd??n("connection.agents.projectRoot.unresolved","Unresolved")}),e.jsx("td",{children:w(o.connectedAt,n)})]},o.instanceId)):e.jsx("tr",{children:e.jsx("td",{colSpan:4,className:s.emptyRow,children:n("connection.agents.none","No agents connected")})})})]})]}),e.jsxs("div",{className:`${s.card} ${d?s.disabled:""}`,children:[e.jsxs("div",{className:s.cardHeader,children:[n("connection.plugins.title","Plugins")," ","(",((b=t==null?void 0:t.pluginClients)==null?void 0:b.length)??0,")"]}),e.jsxs("table",{className:s.table,children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:n("connection.plugins.place","Place")}),e.jsx("th",{children:e.jsx(a,{label:n("connection.plugins.clientId","Client ID"),tooltip:n("connection.plugins.clientId.tooltip","Unique plugin client identifier for this Studio connection")})}),e.jsx("th",{children:e.jsx(a,{label:n("connection.plugins.lastSeen","Last Seen"),tooltip:n("connection.plugins.lastSeen.tooltip","Most recent heartbeat received from the plugin")})}),e.jsx("th",{children:e.jsx(a,{label:n("connection.plugins.version","Ver"),tooltip:n("connection.plugins.version.tooltip","Installed plugin version reported by Studio")})})]})}),e.jsx("tbody",{children:t!=null&&t.pluginClients&&t.pluginClients.length>0?t.pluginClients.map(o=>e.jsxs("tr",{children:[e.jsx("td",{children:o.placeName??o.projectName??"-"}),e.jsx("td",{children:o.clientId.slice(0,10)}),e.jsx("td",{children:w(o.lastSeen,n)}),e.jsx("td",{children:o.pluginVersion??"-"})]},o.clientId)):e.jsx("tr",{children:e.jsx("td",{colSpan:4,className:s.emptyRow,children:n("connection.plugins.none","No plugins connected")})})})]})]}),e.jsxs("div",{className:`${s.card} ${d?s.disabled:""}`,children:[e.jsxs("div",{className:s.cardHeader,children:[e.jsx("span",{children:n("connection.log.title","Connection Log")}),e.jsx("button",{className:s.clearButton,onClick:()=>j(!0),children:n("common.clear","Clear")})]}),e.jsx(V,{entries:l})]}),e.jsx(E,{open:x,title:n("connection.clear.title","Clear connection log?"),message:n("connection.clear.message","This permanently removes the stored connection log for the current project."),cancelLabel:n("common.cancel","Cancel"),confirmLabel:n("common.clear","Clear"),loading:C,onCancel:()=>!C&&j(!1),onConfirm:m})]})}export{pe as Component};
@@ -1 +1 @@
1
- import{j as r,m as e}from"./index-DWRH2iF4.js";function s({label:t,tooltip:o}){return r.jsx(e,{text:o,children:t})}export{s as I};
1
+ import{j as r,m as e}from"./index-E1cuNPsJ.js";function s({label:t,tooltip:o}){return r.jsx(e,{text:o,children:t})}export{s as I};