@weppy/roblox-mcp 2.3.0 → 2.3.1

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 (73) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.github/workflows/install-test.yml +108 -0
  3. package/CHANGELOG.md +14 -0
  4. package/README.md +14 -8
  5. package/docs/en/installation/README.md +2 -2
  6. package/docs/en/installation/roblox-explorer.md +13 -3
  7. package/docs/en/installation/roblox-plugin.md +42 -31
  8. package/docs/en/pro-upgrade.md +26 -12
  9. package/docs/en/sync/luau-lsp.md +41 -0
  10. package/docs/en/sync/overview.md +4 -1
  11. package/docs/es/README.md +13 -5
  12. package/docs/es/installation/README.md +2 -2
  13. package/docs/es/pro-upgrade.md +26 -12
  14. package/docs/es/sync/luau-lsp.md +41 -0
  15. package/docs/es/sync/overview.md +4 -1
  16. package/docs/id/README.md +13 -5
  17. package/docs/id/installation/README.md +2 -2
  18. package/docs/id/pro-upgrade.md +26 -12
  19. package/docs/id/sync/luau-lsp.md +41 -0
  20. package/docs/id/sync/overview.md +4 -1
  21. package/docs/installer/assets/index-Bz0amd7x.js +63 -0
  22. package/docs/installer/assets/index-ei4lRUa6.css +1 -0
  23. package/docs/installer/index.html +2 -2
  24. package/docs/ja/README.md +15 -9
  25. package/docs/ja/installation/README.md +2 -2
  26. package/docs/ja/pro-upgrade.md +26 -12
  27. package/docs/ja/sync/luau-lsp.md +41 -0
  28. package/docs/ja/sync/overview.md +4 -1
  29. package/docs/ko/README.md +14 -8
  30. package/docs/ko/installation/README.md +2 -2
  31. package/docs/ko/installation/roblox-explorer.md +13 -3
  32. package/docs/ko/installation/roblox-plugin.md +42 -31
  33. package/docs/ko/pro-upgrade.md +26 -12
  34. package/docs/ko/sync/luau-lsp.md +41 -0
  35. package/docs/ko/sync/overview.md +4 -1
  36. package/docs/pt-br/README.md +13 -5
  37. package/docs/pt-br/installation/README.md +2 -2
  38. package/docs/pt-br/pro-upgrade.md +26 -12
  39. package/docs/pt-br/sync/luau-lsp.md +41 -0
  40. package/docs/pt-br/sync/overview.md +4 -1
  41. package/docs/troubleshooting.md +1 -1
  42. package/install.ps1 +36 -96
  43. package/install.sh +22 -67
  44. package/llms.txt +1 -1
  45. package/package.json +1 -1
  46. package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
  47. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogDetailPage-CGK59Jsx.js → ChangelogDetailPage-ITTDURna.js} +1 -1
  48. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogPage-oNm6ratx.js → ChangelogPage-DjVot-60.js} +1 -1
  49. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConfirmModal-Dtak3Vnq.js → ConfirmModal-B1q8BGeA.js} +1 -1
  50. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConnectionPage-9bG71eB1.css +1 -0
  51. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConnectionPage-D4y36l03.js +1 -0
  52. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{InfoLabel-CLvjiyTG.js → InfoLabel-COMRAIq0.js} +1 -1
  53. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{OverviewPage-BdF0Ve7h.js → OverviewPage-DojgIxVT.js} +1 -1
  54. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PlaytestPage-cQMWlAOS.js → PlaytestPage-CkEvf6XW.js} +1 -1
  55. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/PropertyDiff-r9iLxfi8.js +6 -0
  56. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SettingsPage-C-QX0AY-.js → SettingsPage-CEs6Ob3d.js} +1 -1
  57. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{StatusBadge-A9U9m2LQ.js → StatusBadge-DHhSWmAT.js} +1 -1
  58. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/SyncPage-ifjnfsNg.js +4 -0
  59. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierComparison-BA_L4c9p.js → TierComparison-C29caZ6C.js} +1 -1
  60. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierPromoProgress-Dq6ofjr2.js → TierPromoProgress-BdEtTxkK.js} +1 -1
  61. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ToolsPage-C_tMIyix.js → ToolsPage-BOIC0ngW.js} +1 -1
  62. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/index-CQYn3Wfp.js +129 -0
  63. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{useLiveUptime-Df1ECedb.js → useLiveUptime-Dco8Aiiz.js} +1 -1
  64. package/plugins/weppy-roblox-mcp/dashboard/dist/index.html +1 -1
  65. package/plugins/weppy-roblox-mcp/dist/index.js +86 -81
  66. package/plugins/weppy-roblox-mcp/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
  67. package/docs/installer/assets/index-B4Gp7BPj.js +0 -63
  68. package/docs/installer/assets/index-B7mvmOPt.css +0 -1
  69. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConnectionPage-CN3LYLAT.css +0 -1
  70. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ConnectionPage-CjLtImxr.js +0 -1
  71. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/PropertyDiff-BnOZxkTS.js +0 -6
  72. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/SyncPage-BAS0cXRM.js +0 -4
  73. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/index-OH9mpHwW.js +0 -129
@@ -25,9 +25,16 @@ A IA pode controlar os playtests do Roblox Studio diretamente. Ela pode iniciar
25
25
  - "Escreve um teste que verifica se o SpawnLocation está acima do chão e executa."
26
26
  - "Valida que o script que acabei de mudar funciona sem erros no playtest."
27
27
 
28
- ### Capacidades avançadas mais amplas
28
+ ### Mais recursos exclusivos do Pro
29
29
 
30
- Geração de terrain, busca de assets, análise espacial, animação, áudio e automação em escala de produção.
30
+ O Pro desbloqueia recursos adicionais além do fluxo de trabalho principal.
31
+
32
+ - **Operações em massa** — Criar, modificar ou excluir múltiplas instâncias em uma única solicitação
33
+ - **Geração de terrain** — Preencher com bloco, esfera, cilindro, cunha e substituir materiais
34
+ - **Busca/inserção de assets** — Pesquisar no marketplace do Roblox e inserir assets diretamente
35
+ - **Análise espacial** — Raycast, encontrar chão, busca de área plana, detecção de colisão
36
+ - **Controle de ambiente** — Iluminação, atmosfera, céu e hora do dia
37
+ - **Áudio/Animação** — Reprodução de som e carregar/reproduzir animações
31
38
 
32
39
  ## Comprar e ativar
33
40
 
@@ -70,13 +77,20 @@ Você só precisa ativar a licença uma vez, no plugin ou no dashboard. As duas
70
77
 
71
78
  | Recurso | Basic | Pro |
72
79
  |---------|:-----:|:---:|
73
- | Gerenciamento de Script, Instance, Property | ✅ Acesso total | ✅ Acesso total |
74
- | Selection, Tag, Camera, Log | ✅ Acesso total | Acesso total |
75
- | Direção do Sync | Studio Local (unidirecional) | Bidirecional |
76
- | Sync Direction por tipo | | ✅ Scripts / Values / Containers / Data / Services |
77
- | Apply Mode por tipo | | ✅ Auto / Manual |
78
- | Histórico de mudanças | | ✅ |
79
- | Sync multi-place | | ✅ Até 3 places com armazenamento isolado |
80
- | Controle de Playtest (Reproduzir/Parar/Pausar/Retomar) | | |
81
- | Cobertura avançada de tools | Conjunto base | Conjunto avançado mais amplo |
82
- | Eficiência de tokens IA | Padrão | Melhor com ações em massa / de alto impacto |
80
+ | Criação/edição de scripts | ✅ | ✅ |
81
+ | Criar/excluir/mover instâncias | | ✅ + operações em massa |
82
+ | Ler/modificar propriedades | | + alterações em massa |
83
+ | Seleção, tags, busca | | ✅ |
84
+ | Controle de câmera | | ✅ |
85
+ | Monitoramento de logs | | ✅ |
86
+ | Execução de código Luau | ✅ | |
87
+ | Sincronização de projeto | Studio → Local unidirecional | Bidirecional + direção/modo por tipo |
88
+ | Sync multi-place | | Até 3 Places simultaneamente |
89
+ | Histórico de mudanças | | Rastrear mudanças antes de aplicar |
90
+ | Controle de playtest | — | Reproduzir / parar / pausar / retomar + testes automáticos |
91
+ | Geração/edição de terrain | — | Bloco, esfera, cilindro, cunha, substituição de material |
92
+ | Busca/inserção de assets | — | Pesquisar no marketplace do Roblox e inserir diretamente |
93
+ | Análise espacial | — | Raycast, encontrar chão, detecção de colisão |
94
+ | Controle de ambiente | — | Iluminação, atmosfera, céu, hora do dia |
95
+ | Áudio / Animação | — | Reprodução de som, carregar/reproduzir animações |
96
+ | Eficiência de tokens IA | Ações individuais | Menos chamadas com ações em massa |
@@ -0,0 +1,41 @@
1
+ # Usar `luau-lsp` com WROX Sync
2
+
3
+ O WROX Sync pode gerar automaticamente os arquivos sourcemap que o `luau-lsp` precisa, para que voce habilite recursos de editor com contexto de Roblox sem montar um projeto Rojo separado.
4
+
5
+ ## O que o WROX grava
6
+
7
+ Depois que o Full Sync termina, o WROX grava:
8
+
9
+ - Place sourcemap: `wrox-project-sync/place_<id>/sourcemap.json`
10
+ - Arquivo representativo da raiz: `wrox-project-sync/sourcemap.json`
11
+
12
+ Para a maioria dos usuarios, o caminho recomendado e `wrox-project-sync/sourcemap.json`.
13
+
14
+ ## O que melhora
15
+
16
+ Quando o `luau-lsp` usa o sourcemap do WROX, melhora:
17
+
18
+ - Autocomplete de `game.*`
19
+ - Navegacao entre scripts sincronizados
20
+ - Resolucao de `require` entre scripts sincronizados
21
+
22
+ ## Configuracao recomendada
23
+
24
+ 1. Execute o Full Sync uma vez para que o WROX crie `wrox-project-sync/sourcemap.json`.
25
+ 2. Aponte a configuracao de sourcemap do `luau-lsp` no seu editor para `wrox-project-sync/sourcemap.json`.
26
+ 3. Se o seu cliente puder desativar a geracao automatica com Rojo, defina `luau-lsp.sourcemap.autogenerate` como `false`.
27
+
28
+ Exemplo de configuracao no VSCode:
29
+
30
+ ```json
31
+ {
32
+ "luau-lsp.sourcemap.enabled": true,
33
+ "luau-lsp.sourcemap.autogenerate": false,
34
+ "luau-lsp.sourcemap.sourcemapFile": "wrox-project-sync/sourcemap.json"
35
+ }
36
+ ```
37
+
38
+ ## Observacao sobre Multi-place
39
+
40
+ `wrox-project-sync/sourcemap.json` acompanha o place representativo atual do projeto.
41
+ Se voce precisar fixar um place especifico, aponte o `luau-lsp` diretamente para `wrox-project-sync/place_<id>/sourcemap.json`.
@@ -19,6 +19,8 @@ Sem Sync, a IA so enxerga trechos colados no chat. Com Sync ativo, ela trabalha
19
19
  3. Rastreamento de History/Status: ver o que mudou, quando e em qual direcao
20
20
 
21
21
  Os dados de Sync ficam em `{projectRoot}/wrox-project-sync/place_{placeId}/explorer`.
22
+ Alem disso, o WROX grava um sourcemap por place em `{projectRoot}/wrox-project-sync/place_{placeId}/sourcemap.json` e mantem o arquivo representativo recomendado na raiz em `{projectRoot}/wrox-project-sync/sourcemap.json`.
23
+ Para integracoes de editor como `luau-lsp`, o caminho da raiz e o recomendado. Os passos de configuracao estao em [Usar `luau-lsp` com WROX Sync](./luau-lsp.md).
22
24
 
23
25
  ### Explorar dados sincronizados no VSCode
24
26
 
@@ -146,5 +148,6 @@ Nomes que contem `~` sao escapados como `~~` (ex: `Part~2` → `Part~~2/`). Regr
146
148
 
147
149
  ## Documentos relacionados
148
150
 
151
+ - [Usar `luau-lsp` com WROX Sync](./luau-lsp.md)
149
152
  - [Cobertura de ferramentas (Tools Overview)](../tools/overview.md)
150
- - [Guia de upgrade Pro](../pro-upgrade.md)
153
+ - [Guia de upgrade Pro](https://weppy-web.pages.dev/en/plans)
@@ -19,7 +19,7 @@
19
19
  ## "Pro feature required" error
20
20
 
21
21
  The action you're requesting requires the Pro tier.
22
- See [Pro Upgrade Guide](en/pro-upgrade.md) for details.
22
+ See [Pro Upgrade Guide](https://weppy-web.pages.dev/en/plans) for details.
23
23
 
24
24
  ## Sync not working
25
25
 
package/install.ps1 CHANGED
@@ -4,17 +4,15 @@
4
4
  # Usage:
5
5
  # irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1 | iex
6
6
  #
7
- # Interactive 3 steps:
8
- # [1/3] MCP server install (npm)
9
- # [2/3] Roblox Studio Plugin install (.rbxm)
10
- # [3/3] Register MCP with AI apps (user selection)
7
+ # Interactive 2 steps:
8
+ # [1/2] Setup install Roblox Studio Plugin via npx
9
+ # [2/2] Register MCP with AI apps (user selection)
11
10
  #
12
11
 
13
12
  $ErrorActionPreference = "Stop"
14
13
  $script:InstallLogPath = Join-Path ([System.IO.Path]::GetTempPath()) ("wrox-install-{0:yyyyMMdd-HHmmss}.log" -f (Get-Date))
15
14
  $script:TranscriptStarted = $false
16
15
  $script:NpmCommandPath = $null
17
- $script:NpmGlobalPrefix = $null
18
16
 
19
17
  # ── Utilities ──
20
18
  function Write-Step($step, $msg) { Write-Host "`n[$step] $msg" -ForegroundColor Cyan -NoNewline; Write-Host "" }
@@ -50,6 +48,10 @@ trap {
50
48
  }
51
49
 
52
50
  function Confirm-Action($prompt) {
51
+ if ($env:CI -eq 'true') {
52
+ Write-Host "$prompt (Y/n): Y"
53
+ return $true
54
+ }
53
55
  $reply = Read-Host "$prompt (Y/n)"
54
56
  if ([string]::IsNullOrWhiteSpace($reply)) { $reply = "Y" }
55
57
  return $reply -match '^[Yy]'
@@ -69,30 +71,6 @@ function Resolve-NpmCommand() {
69
71
  return $script:NpmCommandPath
70
72
  }
71
73
 
72
- function Get-NpmGlobalPrefix() {
73
- if ($script:NpmGlobalPrefix) {
74
- return $script:NpmGlobalPrefix
75
- }
76
-
77
- $script:NpmGlobalPrefix = (Invoke-Npm prefix -g 2>$null | Out-String).Trim()
78
- return $script:NpmGlobalPrefix
79
- }
80
-
81
- function Invoke-Npm {
82
- param(
83
- [Parameter(ValueFromRemainingArguments = $true)]
84
- [string[]]$Args
85
- )
86
-
87
- $npmCommandPath = Resolve-NpmCommand
88
- $output = & $npmCommandPath @Args
89
- if ($LASTEXITCODE -ne 0) {
90
- throw "npm $($Args -join ' ') failed with exit code $LASTEXITCODE"
91
- }
92
-
93
- return $output
94
- }
95
-
96
74
  function Resolve-OptionalCliCommand($commandName) {
97
75
  $resolvedCommand = Get-Command $commandName -ErrorAction SilentlyContinue
98
76
  if ($resolvedCommand) {
@@ -100,11 +78,6 @@ function Resolve-OptionalCliCommand($commandName) {
100
78
  }
101
79
 
102
80
  $candidatePaths = @()
103
- $npmPrefix = Get-NpmGlobalPrefix
104
- if ($npmPrefix) {
105
- $candidatePaths += (Join-Path $npmPrefix "$commandName.cmd")
106
- $candidatePaths += (Join-Path $npmPrefix $commandName)
107
- }
108
81
 
109
82
  if ($env:APPDATA) {
110
83
  $appDataNpmDir = Join-Path $env:APPDATA 'npm'
@@ -195,7 +168,7 @@ function Test-CodexConfigConfigured($configPath) {
195
168
 
196
169
  $env:MCP_CODEX_CONFIG_PATH = $configPath
197
170
  try {
198
- node -e @"
171
+ node -e @'
199
172
  const fs = require('fs');
200
173
 
201
174
  const configPath = process.env.MCP_CODEX_CONFIG_PATH;
@@ -602,7 +575,7 @@ try {
602
575
  } catch {
603
576
  process.exit(1);
604
577
  }
605
- "@
578
+ '@
606
579
  return $LASTEXITCODE -eq 0
607
580
  }
608
581
  finally {
@@ -630,11 +603,6 @@ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
630
603
  }
631
604
  }
632
605
 
633
- function Test-LfsPointer($filePath) {
634
- if (-not (Test-Path $filePath)) { return $false }
635
- return Select-String -Path $filePath -Pattern "git-lfs.github.com/spec/v1" -Quiet
636
- }
637
-
638
606
  # ── Header ──
639
607
  Write-Host ""
640
608
  Write-Host "WROX Installer" -ForegroundColor White -BackgroundColor DarkCyan
@@ -655,71 +623,37 @@ catch {
655
623
  }
656
624
 
657
625
  # ═══════════════════════════════════
658
- # [1/3] MCP server install
626
+ # [1/2] Setup Roblox Studio Plugin
659
627
  # ═══════════════════════════════════
660
- Write-Step "1/3" "Install @weppy/roblox-mcp via npm"
628
+ Write-Step "1/2" "Setup Roblox Studio Plugin"
661
629
 
662
- if (Confirm-Action " Run npm install -g @weppy/roblox-mcp?") {
630
+ if (Confirm-Action " Run npx -y @weppy/roblox-mcp --setup?") {
663
631
  try {
664
- Invoke-Npm install -g "@weppy/roblox-mcp"
665
- Write-Ok "Installed @weppy/roblox-mcp"
666
- }
667
- catch {
668
- Abort-Install "npm install failed: $_"
669
- }
670
- }
671
- else {
672
- Write-Warn "MCP server install skipped"
673
- }
674
-
675
- # ═══════════════════════════════════
676
- # [2/3] Roblox Studio Plugin install
677
- # ═══════════════════════════════════
678
- Write-Step "2/3" "Install Roblox Studio Plugin"
679
-
680
- $pluginsDir = Join-Path $env:LOCALAPPDATA "Roblox\Plugins"
681
- $pluginName = "WeppyRobloxMCP.rbxm"
682
-
683
- # Search for .rbxm in npm global path
684
- $npmPrefix = Get-NpmGlobalPrefix
685
- $bundledPlugin = $null
686
- $searchPaths = @(
687
- (Join-Path $npmPrefix "node_modules\@weppy\roblox-mcp\plugins\weppy-roblox-mcp\roblox-plugin\$pluginName"),
688
- (Join-Path $npmPrefix "node_modules\@weppy\roblox-mcp\roblox-plugin\$pluginName")
689
- )
690
- foreach ($p in $searchPaths) {
691
- if (Test-Path $p) {
692
- $bundledPlugin = $p
693
- break
694
- }
695
- }
696
-
697
- if ($bundledPlugin) {
698
- if (Test-LfsPointer $bundledPlugin) {
699
- Abort-Install "Bundled plugin payload is invalid (Git LFS pointer detected). Install the plugin from the GitHub release ZIP instead."
700
- }
701
-
702
- Write-Host " → $pluginsDir\$pluginName"
703
- if (Confirm-Action " Copy plugin to Roblox Plugins folder?") {
704
- if (-not (Test-Path $pluginsDir)) {
705
- New-Item -ItemType Directory -Path $pluginsDir -Force | Out-Null
632
+ $npmCommandPath = Resolve-NpmCommand
633
+ $npmDir = Split-Path $npmCommandPath -Parent
634
+ $npxPath = Join-Path $npmDir "npx.cmd"
635
+ if (-not (Test-Path $npxPath)) {
636
+ $npxPath = "npx"
637
+ }
638
+ & $npxPath -y "@weppy/roblox-mcp" --setup
639
+ if ($LASTEXITCODE -ne 0) {
640
+ Write-Warn "Setup encountered a warning (non-blocking)"
641
+ } else {
642
+ Write-Ok "Setup complete"
706
643
  }
707
- Copy-Item $bundledPlugin -Destination (Join-Path $pluginsDir $pluginName) -Force
708
- Write-Ok "Plugin installed → $pluginsDir\$pluginName"
709
644
  }
710
- else {
711
- Write-Warn "Plugin install skipped"
645
+ catch {
646
+ Write-Warn "Setup encountered a warning: $_"
712
647
  }
713
648
  }
714
649
  else {
715
- Write-Warn "Bundled plugin file not found"
716
- Write-Info "Will be installed automatically on first MCP server run"
650
+ Write-Warn "Setup skipped"
717
651
  }
718
652
 
719
653
  # ═══════════════════════════════════
720
- # [3/3] Register MCP with AI apps
654
+ # [2/2] Register MCP with AI apps
721
655
  # ═══════════════════════════════════
722
- Write-Step "3/3" "Register MCP with AI apps"
656
+ Write-Step "2/2" "Register MCP with AI apps"
723
657
  Write-Host " Automatic registration: Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity"
724
658
 
725
659
  $detectedNames = @()
@@ -833,7 +767,12 @@ else {
833
767
  }
834
768
 
835
769
  Write-Host ""
836
- $selection = Read-Host " Select apps to register (comma-separated, 'a' for all, 'n' to skip)"
770
+ if ($env:CI -eq 'true') {
771
+ Write-Host " Select apps to register (comma-separated, 'a' for all, 'n' to skip): a"
772
+ $selection = 'a'
773
+ } else {
774
+ $selection = Read-Host " Select apps to register (comma-separated, 'a' for all, 'n' to skip)"
775
+ }
837
776
  if ([string]::IsNullOrWhiteSpace($selection)) { $selection = "n" }
838
777
 
839
778
  $selectedIndices = @()
@@ -866,7 +805,8 @@ else {
866
805
  elseif ($claudeCodeCliCommand) {
867
806
  & $claudeCodeCliCommand mcp add weppy-roblox-mcp -- npx -y "@weppy/roblox-mcp"
868
807
  if ($LASTEXITCODE -ne 0) {
869
- throw 'claude mcp add failed'
808
+ # CLI 실패 (Windows에서 -- 파싱 문제 등) JSON config에 직접 쓰기로 폴백
809
+ Add-McpToConfig $claudeGlobalConfig
870
810
  }
871
811
  Write-Ok "Registered: $appName"
872
812
  }
package/install.sh CHANGED
@@ -5,10 +5,9 @@
5
5
  # Usage:
6
6
  # curl -fsSL https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.sh | bash
7
7
  #
8
- # Interactive 3 steps:
9
- # [1/3] MCP server install (npm)
10
- # [2/3] Roblox Studio Plugin install (.rbxm)
11
- # [3/3] Register MCP with AI apps (user selection)
8
+ # Interactive 2 steps:
9
+ # [1/2] Setup install Roblox Studio Plugin via npx
10
+ # [2/2] Register MCP with AI apps (user selection)
12
11
  #
13
12
 
14
13
  set -euo pipefail
@@ -74,6 +73,10 @@ trap 'handle_install_error "${LINENO}" "$BASH_COMMAND"' ERR
74
73
  confirm() {
75
74
  local prompt="$1"
76
75
  local reply
76
+ if [ "${CI:-}" = "true" ]; then
77
+ printf "%s (Y/n): Y\n" "$prompt"
78
+ return 0
79
+ fi
77
80
  printf "%s (Y/n): " "$prompt"
78
81
  read -r reply </dev/tty
79
82
  reply="${reply:-Y}"
@@ -610,16 +613,6 @@ resolve_optional_cli_command() {
610
613
  return 1
611
614
  }
612
615
 
613
- is_lfs_pointer() {
614
- local file_path="$1"
615
-
616
- if [ ! -f "$file_path" ]; then
617
- return 1
618
- fi
619
-
620
- grep -q "git-lfs.github.com/spec/v1" "$file_path"
621
- }
622
-
623
616
  # ── Header ──
624
617
  # shellcheck disable=SC2059
625
618
  printf "\n${BOLD}WROX Installer${NC}\n"
@@ -643,67 +636,24 @@ fi
643
636
  success "Node.js $(node -v) detected"
644
637
 
645
638
  # ═══════════════════════════════════
646
- # [1/3] MCP server install
647
- # ═══════════════════════════════════
648
- step "1/3" "Install @weppy/roblox-mcp via npm"
649
-
650
- if confirm " Run npm install -g @weppy/roblox-mcp?"; then
651
- if npm install -g @weppy/roblox-mcp; then
652
- success "Installed @weppy/roblox-mcp"
653
- else
654
- fail "npm install failed"
655
- exit 1
656
- fi
657
- else
658
- warn "MCP server install skipped"
659
- fi
660
-
661
- # ═══════════════════════════════════
662
- # [2/3] Roblox Studio Plugin install
639
+ # [1/2] Setup Roblox Studio Plugin
663
640
  # ═══════════════════════════════════
664
- step "2/3" "Install Roblox Studio Plugin"
665
-
666
- PLUGINS_DIR="$HOME/Documents/Roblox/Plugins"
667
- PLUGIN_NAME="WeppyRobloxMCP.rbxm"
668
-
669
- # Search for .rbxm in npm global prefix
670
- NPM_PREFIX=$(npm prefix -g 2>/dev/null)
671
- BUNDLED_PLUGIN=""
672
-
673
- # Search for .rbxm in npm global path
674
- for search_dir in \
675
- "${NPM_PREFIX}/lib/node_modules/@weppy/roblox-mcp/plugins/weppy-roblox-mcp/roblox-plugin" \
676
- "${NPM_PREFIX}/lib/node_modules/@weppy/roblox-mcp/roblox-plugin"; do
677
- if [ -f "${search_dir}/${PLUGIN_NAME}" ]; then
678
- BUNDLED_PLUGIN="${search_dir}/${PLUGIN_NAME}"
679
- break
680
- fi
681
- done
682
-
683
- if [ -n "$BUNDLED_PLUGIN" ]; then
684
- if is_lfs_pointer "$BUNDLED_PLUGIN"; then
685
- fail "Bundled plugin payload is invalid (Git LFS pointer detected)"
686
- info "Install the plugin from the GitHub release ZIP instead"
687
- exit 1
688
- fi
641
+ step "1/2" "Setup Roblox Studio Plugin"
689
642
 
690
- printf " %s/%s\n" "$PLUGINS_DIR" "$PLUGIN_NAME"
691
- if confirm " Copy plugin to Roblox Plugins folder?"; then
692
- mkdir -p "$PLUGINS_DIR"
693
- cp "$BUNDLED_PLUGIN" "$PLUGINS_DIR/$PLUGIN_NAME"
694
- success "Plugin installed → $PLUGINS_DIR/$PLUGIN_NAME"
643
+ if confirm " Run npx -y @weppy/roblox-mcp --setup?"; then
644
+ if npx -y @weppy/roblox-mcp --setup; then
645
+ success "Setup complete"
695
646
  else
696
- warn "Plugin install skipped"
647
+ warn "Setup encountered a warning (non-blocking)"
697
648
  fi
698
649
  else
699
- warn "Bundled plugin file not found"
700
- info "Will be installed automatically on first MCP server run"
650
+ warn "Setup skipped"
701
651
  fi
702
652
 
703
653
  # ═══════════════════════════════════
704
- # [3/3] Register MCP with AI apps
654
+ # [2/2] Register MCP with AI apps
705
655
  # ═══════════════════════════════════
706
- step "3/3" "Register MCP with AI apps"
656
+ step "2/2" "Register MCP with AI apps"
707
657
  printf " Automatic registration: Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity\n"
708
658
 
709
659
  MCP_COMMAND='npx -y @weppy/roblox-mcp'
@@ -811,7 +761,12 @@ else
811
761
  # shellcheck disable=SC2059
812
762
  printf "\n Select apps to register ${DIM}(comma-separated, 'a' for all, 'n' to skip)${NC}\n"
813
763
  printf " → "
814
- read -r selection </dev/tty
764
+ if [ "${CI:-}" = "true" ]; then
765
+ selection="a"
766
+ printf "a\n"
767
+ else
768
+ read -r selection </dev/tty
769
+ fi
815
770
  selection="${selection:-n}"
816
771
 
817
772
  # Parse selection
package/llms.txt CHANGED
@@ -55,7 +55,7 @@ Manual alternative:
55
55
  - WROX Dashboard guide: https://github.com/hope1026/weppy-roblox-mcp/blob/main/docs/en/dashboard/overview.md
56
56
  - Troubleshooting: https://github.com/hope1026/weppy-roblox-mcp/blob/main/docs/troubleshooting.md
57
57
  - Compatibility: https://github.com/hope1026/weppy-roblox-mcp/blob/main/docs/compatibility.md
58
- - Pro upgrade: https://github.com/hope1026/weppy-roblox-mcp/blob/main/docs/en/pro-upgrade.md
58
+ - Pro upgrade: https://weppy-web.pages.dev/en/plans
59
59
 
60
60
  ## Requirements
61
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weppy/roblox-mcp",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
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.3.0",
4
+ "version": "2.3.1",
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-OH9mpHwW.js";import{I as V}from"./InfoLabel-CLvjiyTG.js";import{D as F,P as G}from"./PropertyDiff-BnOZxkTS.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-CQYn3Wfp.js";import{I as V}from"./InfoLabel-COMRAIq0.js";import{D as F,P as G}from"./PropertyDiff-r9iLxfi8.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 D,D as O,u as F,j as e,T as I,c as V,i as U}from"./index-OH9mpHwW.js";import{C as G}from"./ConfirmModal-Dtak3Vnq.js";import{u as H,T as Z,D as W,a as q}from"./TierComparison-BA_L4c9p.js";import{t as d}from"./TierPromo.module-CAoUYgIx.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),[g,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 D.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,g)},[x,l,g]),_=i.useCallback(async()=>{await D.post("/api/dashboard/changelog/clear"),p([]),a(0),f(!1)},[]);return i.useEffect(()=>{x(l,g)},[x,l,g]),i.useEffect(()=>{const h=new O;b.current=h,h.connect();const o=h.on("command",()=>{x(l,g)});return()=>{o(),h.disconnect(),b.current=null}},[x,l,g]),{entries:t,total:s,hasMore:u,loading:r,offset:l,statusFilter:g,setOffset:C,setStatusFilter:j,refresh:y,clear:_}}const K="_card_1n89u_2",Q="_header_1n89u_17",X="_statusBadge_1n89u_24",Y="_statusDot_1n89u_35",ee="_active_1n89u_41",se="_completed_1n89u_50",te="_timeRange_1n89u_58",ae="_summaryList_1n89u_65",ne="_summaryItem_1n89u_71",oe="_summaryIcon_1n89u_80",ce="_summaryText_1n89u_86",ie="_progressBar_1n89u_91",re="_progressFill_1n89u_99",le="_emptySummary_1n89u_112",de="_contextBlock_1n89u_120",ge="_contextLabel_1n89u_129",me="_contextValue_1n89u_137",n={card:K,header:Q,statusBadge:X,statusDot:Y,active:ee,completed:se,timeRange:te,summaryList:ae,summaryItem:ne,summaryIcon:oe,summaryText:ce,progressBar:ie,progressFill:re,emptySummary:le,contextBlock:de,contextLabel:ge,contextValue:me};function E(t){if(!t)return"--:--";const p=new Date(t);return`${String(p.getHours()).padStart(2,"0")}:${String(p.getMinutes()).padStart(2,"0")}`}function he({entry:t,onClick:p}){var S,N,k,B,L,w,A,M,R,P;const{t:s}=F(),a=t.changeSummary,u=t.status==="active",f=t.isBootstrapOnly===!0,r=[],v=a.scriptsModified+a.scriptsCreated;if(v>0){const m=[];a.scriptsModified>0&&m.push(`${a.scriptsModified} ${s("changelog.card.modified","modified")}`),a.scriptsCreated>0&&m.push(`${a.scriptsCreated} ${s("changelog.card.created","created")}`);const T=`${v} ${s("changelog.card.scripts","scripts")} ${m.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 m=[];a.instancesCreated>0&&m.push(`${a.instancesCreated} ${s("changelog.card.created","created")}`),a.instancesDeleted>0&&m.push(`${a.instancesDeleted} ${s("changelog.card.deleted","deleted")}`),a.instancesMoved>0&&m.push(`${a.instancesMoved} ${s("changelog.card.moved","moved")}`);const T=`${l} ${s("changelog.card.instances","instances")} ${m.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=E(t.startTime),g=u?s("changelog.card.inProgress","in progress"):t.endTime?E(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=(P=(R=t.verificationSummary)==null?void 0:R.label)==null?void 0:P.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,"~",g]})]}),e.jsx("div",{className:n.summaryList,children:r.length>0?r.map((m,T)=>e.jsxs("div",{className:n.summaryItem,children:[e.jsx("span",{className:n.summaryIcon,children:m.icon}),e.jsx(I,{text:m.tooltip,children:e.jsx("span",{className:n.summaryText,children:m.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 pe="_page_1srvj_2",ue="_limitNotice_1srvj_9",fe="_limitNoticeTitle_1srvj_18",xe="_limitNoticeText_1srvj_25",_e="_headerRow_1srvj_31",ve="_header_1srvj_31",je="_clearButton_1srvj_48",be="_headerSub_1srvj_57",ye="_filterTabs_1srvj_67",Ne="_filterTab_1srvj_67",Ce="_filterTabActive_1srvj_90",Se="_list_1srvj_96",Te="_empty_1srvj_103",$e="_loading_1srvj_112",Ie="_pagination_1srvj_121",ke="_pageInfo_1srvj_129",Be="_btn_1srvj_135",c={page:pe,limitNotice:ue,limitNoticeTitle:fe,limitNoticeText:xe,headerRow:_e,header:ve,clearButton:je,headerSub:be,filterTabs:ye,filterTab:Ne,filterTabActive:Ce,list:Se,empty:Te,loading:$e,pagination:Ie,pageInfo:ke,btn:Be},$=10,Le=[{key:"all",label:"changelog.filter.all"},{key:"active",label:"changelog.filter.active"},{key:"completed",label:"changelog.filter.completed"}];function Pe(){const{t}=F(),p=V(),s=J(),a=H(),{show:u}=U(),[f,r]=i.useState(!1),[v,l]=i.useState(!1),[C,g]=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:Le.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(he,{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:d.progressPromoWrap,children:[e.jsxs("div",{className:d.progressPromo,children:[e.jsxs("div",{className:d.progressMain,children:[e.jsxs("div",{className:d.progressLabel,children:[e.jsx("span",{children:t("changelog.basic.metricLabel","Visible Changelog / Total")}),e.jsxs("span",{children:[y," / ",_]})]}),e.jsx("div",{className:d.progressBar,children:e.jsx("div",{className:d.progressFill,style:{width:`${_>0?Math.min(y/_*100,100):0}%`}})}),e.jsxs("div",{className:d.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:d.progressText,children:t("tier.banner.save","Save AI tokens with Pro!")}),e.jsxs("div",{className:d.actions,children:[e.jsx("button",{className:`${d.btn} ${d.btnOutline}`,onClick:()=>g(!0),children:t("tier.compare","Basic vs Pro")}),e.jsx("a",{className:`${d.btn} ${d.btnPrimary}`,href:Z.changelog,target:"_blank",rel:"noreferrer",children:t("tier.upgrade","Upgrade to Pro")})]})]}),e.jsx(W,{variant:"centered"})]})]}),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:()=>g(!1)})]})}export{Pe as Component};
1
+ import{r as i,a as D,D as O,u as F,j as e,T as I,c as V,i as U}from"./index-CQYn3Wfp.js";import{C as G}from"./ConfirmModal-B1q8BGeA.js";import{u as H,T as Z,D as W,a as q}from"./TierComparison-C29caZ6C.js";import{t as d}from"./TierPromo.module-CAoUYgIx.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),[g,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 D.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,g)},[x,l,g]),_=i.useCallback(async()=>{await D.post("/api/dashboard/changelog/clear"),p([]),a(0),f(!1)},[]);return i.useEffect(()=>{x(l,g)},[x,l,g]),i.useEffect(()=>{const h=new O;b.current=h,h.connect();const o=h.on("command",()=>{x(l,g)});return()=>{o(),h.disconnect(),b.current=null}},[x,l,g]),{entries:t,total:s,hasMore:u,loading:r,offset:l,statusFilter:g,setOffset:C,setStatusFilter:j,refresh:y,clear:_}}const K="_card_1n89u_2",Q="_header_1n89u_17",X="_statusBadge_1n89u_24",Y="_statusDot_1n89u_35",ee="_active_1n89u_41",se="_completed_1n89u_50",te="_timeRange_1n89u_58",ae="_summaryList_1n89u_65",ne="_summaryItem_1n89u_71",oe="_summaryIcon_1n89u_80",ce="_summaryText_1n89u_86",ie="_progressBar_1n89u_91",re="_progressFill_1n89u_99",le="_emptySummary_1n89u_112",de="_contextBlock_1n89u_120",ge="_contextLabel_1n89u_129",me="_contextValue_1n89u_137",n={card:K,header:Q,statusBadge:X,statusDot:Y,active:ee,completed:se,timeRange:te,summaryList:ae,summaryItem:ne,summaryIcon:oe,summaryText:ce,progressBar:ie,progressFill:re,emptySummary:le,contextBlock:de,contextLabel:ge,contextValue:me};function E(t){if(!t)return"--:--";const p=new Date(t);return`${String(p.getHours()).padStart(2,"0")}:${String(p.getMinutes()).padStart(2,"0")}`}function he({entry:t,onClick:p}){var S,N,k,B,L,w,A,M,R,P;const{t:s}=F(),a=t.changeSummary,u=t.status==="active",f=t.isBootstrapOnly===!0,r=[],v=a.scriptsModified+a.scriptsCreated;if(v>0){const m=[];a.scriptsModified>0&&m.push(`${a.scriptsModified} ${s("changelog.card.modified","modified")}`),a.scriptsCreated>0&&m.push(`${a.scriptsCreated} ${s("changelog.card.created","created")}`);const T=`${v} ${s("changelog.card.scripts","scripts")} ${m.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 m=[];a.instancesCreated>0&&m.push(`${a.instancesCreated} ${s("changelog.card.created","created")}`),a.instancesDeleted>0&&m.push(`${a.instancesDeleted} ${s("changelog.card.deleted","deleted")}`),a.instancesMoved>0&&m.push(`${a.instancesMoved} ${s("changelog.card.moved","moved")}`);const T=`${l} ${s("changelog.card.instances","instances")} ${m.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=E(t.startTime),g=u?s("changelog.card.inProgress","in progress"):t.endTime?E(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=(P=(R=t.verificationSummary)==null?void 0:R.label)==null?void 0:P.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,"~",g]})]}),e.jsx("div",{className:n.summaryList,children:r.length>0?r.map((m,T)=>e.jsxs("div",{className:n.summaryItem,children:[e.jsx("span",{className:n.summaryIcon,children:m.icon}),e.jsx(I,{text:m.tooltip,children:e.jsx("span",{className:n.summaryText,children:m.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 pe="_page_1srvj_2",ue="_limitNotice_1srvj_9",fe="_limitNoticeTitle_1srvj_18",xe="_limitNoticeText_1srvj_25",_e="_headerRow_1srvj_31",ve="_header_1srvj_31",je="_clearButton_1srvj_48",be="_headerSub_1srvj_57",ye="_filterTabs_1srvj_67",Ne="_filterTab_1srvj_67",Ce="_filterTabActive_1srvj_90",Se="_list_1srvj_96",Te="_empty_1srvj_103",$e="_loading_1srvj_112",Ie="_pagination_1srvj_121",ke="_pageInfo_1srvj_129",Be="_btn_1srvj_135",c={page:pe,limitNotice:ue,limitNoticeTitle:fe,limitNoticeText:xe,headerRow:_e,header:ve,clearButton:je,headerSub:be,filterTabs:ye,filterTab:Ne,filterTabActive:Ce,list:Se,empty:Te,loading:$e,pagination:Ie,pageInfo:ke,btn:Be},$=10,Le=[{key:"all",label:"changelog.filter.all"},{key:"active",label:"changelog.filter.active"},{key:"completed",label:"changelog.filter.completed"}];function Pe(){const{t}=F(),p=V(),s=J(),a=H(),{show:u}=U(),[f,r]=i.useState(!1),[v,l]=i.useState(!1),[C,g]=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:Le.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(he,{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:d.progressPromoWrap,children:[e.jsxs("div",{className:d.progressPromo,children:[e.jsxs("div",{className:d.progressMain,children:[e.jsxs("div",{className:d.progressLabel,children:[e.jsx("span",{children:t("changelog.basic.metricLabel","Visible Changelog / Total")}),e.jsxs("span",{children:[y," / ",_]})]}),e.jsx("div",{className:d.progressBar,children:e.jsx("div",{className:d.progressFill,style:{width:`${_>0?Math.min(y/_*100,100):0}%`}})}),e.jsxs("div",{className:d.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:d.progressText,children:t("tier.banner.save","Save AI tokens with Pro!")}),e.jsxs("div",{className:d.actions,children:[e.jsx("button",{className:`${d.btn} ${d.btnOutline}`,onClick:()=>g(!0),children:t("tier.compare","Basic vs Pro")}),e.jsx("a",{className:`${d.btn} ${d.btnPrimary}`,href:Z.changelog,target:"_blank",rel:"noreferrer",children:t("tier.upgrade","Upgrade to Pro")})]})]}),e.jsx(W,{variant:"centered"})]})]}),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:()=>g(!1)})]})}export{Pe as Component};