@weppy/roblox-mcp 2.2.2 → 2.3.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 (45) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +17 -0
  3. package/README.md +3 -5
  4. package/docs/en/installation/README.md +2 -2
  5. package/docs/es/README.md +1 -3
  6. package/docs/es/installation/README.md +2 -2
  7. package/docs/id/README.md +0 -2
  8. package/docs/id/installation/README.md +2 -2
  9. package/docs/installer/assets/index-B4Gp7BPj.js +63 -0
  10. package/docs/installer/assets/index-B7mvmOPt.css +1 -0
  11. package/docs/installer/index.html +14 -0
  12. package/docs/installer/manifest.webmanifest +15 -0
  13. package/docs/installer/sw.js +7 -0
  14. package/docs/installer/wrox-icon.png +0 -0
  15. package/docs/ja/README.md +0 -2
  16. package/docs/ja/installation/README.md +1 -1
  17. package/docs/ko/README.md +3 -5
  18. package/docs/ko/installation/README.md +2 -2
  19. package/docs/pt-br/README.md +1 -3
  20. package/docs/pt-br/installation/README.md +2 -2
  21. package/install.ps1 +434 -4
  22. package/install.sh +439 -4
  23. package/llms-full.txt +14 -2
  24. package/llms.txt +9 -3
  25. package/package.json +1 -1
  26. package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
  27. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogDetailPage-D7eMrarv.js → ChangelogDetailPage-CGK59Jsx.js} +1 -1
  28. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogPage-DFCCRyyK.js → ChangelogPage-oNm6ratx.js} +1 -1
  29. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConfirmModal-BmRJ2JXZ.js → ConfirmModal-Dtak3Vnq.js} +1 -1
  30. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConnectionPage-CiaCY026.js → ConnectionPage-CjLtImxr.js} +1 -1
  31. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{InfoLabel-CCDWZLC9.js → InfoLabel-CLvjiyTG.js} +1 -1
  32. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{OverviewPage-BHpt3LI2.js → OverviewPage-BdF0Ve7h.js} +1 -1
  33. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PlaytestPage-CNwwI5Ro.js → PlaytestPage-cQMWlAOS.js} +1 -1
  34. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PropertyDiff-DIplDn-J.js → PropertyDiff-BnOZxkTS.js} +1 -1
  35. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SettingsPage-CPqQYZPN.js → SettingsPage-C-QX0AY-.js} +1 -1
  36. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{StatusBadge-C8VKAPpk.js → StatusBadge-A9U9m2LQ.js} +1 -1
  37. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SyncPage-DTSKbpio.js → SyncPage-BAS0cXRM.js} +1 -1
  38. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierComparison-7ofkPwj3.js → TierComparison-BA_L4c9p.js} +1 -1
  39. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierPromoProgress-SnRUjAPh.js → TierPromoProgress-Dq6ofjr2.js} +1 -1
  40. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ToolsPage-CrdNh3D9.js → ToolsPage-C_tMIyix.js} +1 -1
  41. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{index-DGGmfli1.js → index-OH9mpHwW.js} +2 -2
  42. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{useLiveUptime-BnXeLpOw.js → useLiveUptime-Df1ECedb.js} +1 -1
  43. package/plugins/weppy-roblox-mcp/dashboard/dist/index.html +1 -1
  44. package/plugins/weppy-roblox-mcp/dist/index.js +63 -65
  45. package/plugins/weppy-roblox-mcp/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
package/install.sh CHANGED
@@ -117,7 +117,7 @@ try {
117
117
  ' >/dev/null 2>&1
118
118
  }
119
119
 
120
- # Add MCP server to Antigravity config (flat top-level keys, no mcpServers wrapper)
120
+ # Antigravity 설정에 canonical mcpServers 래퍼로 MCP 서버를 추가하고 legacy flat key를 정리
121
121
  add_antigravity_mcp_config() {
122
122
  local config_path="$1"
123
123
  local parent_dir
@@ -128,7 +128,20 @@ const fs = require("fs");
128
128
  const configPath = process.env.MCP_CONFIG_PATH;
129
129
  let config = {};
130
130
  try { config = JSON.parse(fs.readFileSync(configPath, "utf8")); } catch {}
131
- config["weppy-roblox-mcp"] = { command: "npx", args: ["-y", "@weppy/roblox-mcp"] };
131
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
132
+ config = {};
133
+ }
134
+ const mcpServers = config.mcpServers;
135
+ if (mcpServers !== undefined && (typeof mcpServers !== "object" || mcpServers === null || Array.isArray(mcpServers))) {
136
+ throw new Error("Antigravity mcpServers must be an object");
137
+ }
138
+ const next = { ...config };
139
+ delete next["weppy-roblox-mcp"];
140
+ next.mcpServers = {
141
+ ...(mcpServers || {}),
142
+ "weppy-roblox-mcp": { command: "npx", args: ["-y", "@weppy/roblox-mcp"] }
143
+ };
144
+ config = next;
132
145
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
133
146
  '
134
147
  }
@@ -141,9 +154,24 @@ is_antigravity_mcp_configured() {
141
154
  MCP_CONFIG_PATH="$config_path" node -e '
142
155
  const fs = require("fs");
143
156
  const configPath = process.env.MCP_CONFIG_PATH;
157
+ function isJsonObject(value) {
158
+ return typeof value === "object" && value !== null && !Array.isArray(value);
159
+ }
160
+ function hasExpectedCommandShape(value) {
161
+ return (
162
+ isJsonObject(value) &&
163
+ value.command === "npx" &&
164
+ Array.isArray(value.args) &&
165
+ value.args.length === 2 &&
166
+ value.args[0] === "-y" &&
167
+ value.args[1] === "@weppy/roblox-mcp"
168
+ );
169
+ }
144
170
  try {
145
171
  const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
146
- process.exit(config?.["weppy-roblox-mcp"] ? 0 : 1);
172
+ const canonical = config?.mcpServers?.["weppy-roblox-mcp"];
173
+ const hasLegacyFlatKey = Object.prototype.hasOwnProperty.call(config, "weppy-roblox-mcp");
174
+ process.exit(hasExpectedCommandShape(canonical) && !hasLegacyFlatKey ? 0 : 1);
147
175
  } catch {
148
176
  process.exit(1);
149
177
  }
@@ -154,7 +182,414 @@ is_codex_config_configured() {
154
182
  local config_path="$1"
155
183
 
156
184
  [ -f "$config_path" ] || return 1
157
- grep -Eq '^[[:space:]]*\[mcp_servers\.weppy-roblox-mcp\][[:space:]]*$' "$config_path"
185
+ MCP_CODEX_CONFIG_PATH="$config_path" node <<'NODE' >/dev/null 2>&1
186
+ const fs = require("fs");
187
+
188
+ const configPath = process.env.MCP_CODEX_CONFIG_PATH;
189
+ const serverName = "weppy-roblox-mcp";
190
+ const expectedCommand = "npx";
191
+ const expectedArgs = ["-y", "@weppy/roblox-mcp"];
192
+ const headerPattern = new RegExp(
193
+ "^\\s*\\[\\s*mcp_servers\\." + serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*\\]\\s*(?:#.*)?$",
194
+ );
195
+
196
+ function stripCommentOutsideStrings(line) {
197
+ let inSingle = false;
198
+ let inDouble = false;
199
+ let escaped = false;
200
+
201
+ for (let index = 0; index < line.length; index += 1) {
202
+ const char = line[index];
203
+
204
+ if (char === '"' && !inSingle && !escaped) {
205
+ inDouble = !inDouble;
206
+ } else if (char === "'" && !inDouble && !escaped) {
207
+ inSingle = !inSingle;
208
+ } else if (char === "#" && !inSingle && !inDouble) {
209
+ return line.slice(0, index).trimEnd();
210
+ }
211
+
212
+ escaped = char === "\\" && !escaped;
213
+ if (char !== "\\") {
214
+ escaped = false;
215
+ }
216
+ }
217
+
218
+ return line.trimEnd();
219
+ }
220
+
221
+ function countTripleQuoteToggles(line, quote) {
222
+ let count = 0;
223
+ let inSingle = false;
224
+ let inDouble = false;
225
+ let escaped = false;
226
+
227
+ for (let index = 0; index < line.length; index += 1) {
228
+ const char = line[index] ?? "";
229
+ const nextThree = line.slice(index, index + 3);
230
+ const isOutsideStrings = !inSingle && !inDouble;
231
+
232
+ if (isOutsideStrings && nextThree === quote.repeat(3)) {
233
+ count += 1;
234
+ index += 2;
235
+ escaped = false;
236
+ continue;
237
+ }
238
+
239
+ if (char === '"' && !inSingle && !escaped) {
240
+ inDouble = !inDouble;
241
+ } else if (char === "'" && !inDouble && !escaped) {
242
+ inSingle = !inSingle;
243
+ } else if (char === "#" && !inSingle && !inDouble) {
244
+ break;
245
+ }
246
+
247
+ escaped = char === "\\" && !escaped;
248
+ if (char !== "\\") {
249
+ escaped = false;
250
+ }
251
+ }
252
+
253
+ return count;
254
+ }
255
+
256
+ function advanceTripleQuoteState(line, state) {
257
+ const next = { ...state };
258
+ const tripleDoubleCount = countTripleQuoteToggles(line, '"');
259
+ const tripleSingleCount = countTripleQuoteToggles(line, "'");
260
+
261
+ if (!next.inTripleSingle && tripleDoubleCount % 2 === 1) {
262
+ next.inTripleDouble = !next.inTripleDouble;
263
+ }
264
+
265
+ if (!next.inTripleDouble && tripleSingleCount % 2 === 1) {
266
+ next.inTripleSingle = !next.inTripleSingle;
267
+ }
268
+
269
+ return next;
270
+ }
271
+
272
+ function isTomlTableHeaderLine(line) {
273
+ const normalized = stripCommentOutsideStrings(line).trim();
274
+
275
+ if (normalized.length === 0) {
276
+ return false;
277
+ }
278
+
279
+ return /^\[\[.*\]\]$/.test(normalized) || /^\[.*\]$/.test(normalized);
280
+ }
281
+
282
+ function findAllCodexBlocks(source) {
283
+ const lines = source.split("\n");
284
+ const blocks = [];
285
+ let activeLines = null;
286
+ let state = {
287
+ inTripleDouble: false,
288
+ inTripleSingle: false,
289
+ };
290
+
291
+ for (const line of lines) {
292
+ const isHeaderCandidate = !state.inTripleDouble && !state.inTripleSingle && isTomlTableHeaderLine(line);
293
+ const isCodexHeader = isHeaderCandidate && headerPattern.test(line);
294
+
295
+ if (isCodexHeader) {
296
+ if (activeLines !== null) {
297
+ blocks.push(activeLines.join("\n").trim());
298
+ }
299
+ activeLines = [line];
300
+ } else if (activeLines !== null && isHeaderCandidate) {
301
+ blocks.push(activeLines.join("\n").trim());
302
+ activeLines = null;
303
+ } else if (activeLines !== null) {
304
+ activeLines.push(line);
305
+ }
306
+
307
+ state = advanceTripleQuoteState(line, state);
308
+ }
309
+
310
+ if (activeLines !== null) {
311
+ blocks.push(activeLines.join("\n").trim());
312
+ }
313
+
314
+ return blocks;
315
+ }
316
+
317
+ function parseStringAssignment(value, key) {
318
+ const match = new RegExp("^\\s*" + key + "\\s*=\\s*([\"'])([^\"']+)\\1\\s*$").exec(value);
319
+ return match ? match[2] : null;
320
+ }
321
+
322
+ function parseTomlStringArray(value) {
323
+ const match = /^\s*args\s*=\s*\[(.*)\]\s*$/ms.exec(value.trim());
324
+
325
+ if (match === null) {
326
+ return null;
327
+ }
328
+
329
+ const body = match[1] ?? "";
330
+ const values = [];
331
+ let cursor = 0;
332
+ let expectValue = true;
333
+
334
+ while (cursor < body.length) {
335
+ while (cursor < body.length && /\s/.test(body[cursor] ?? "")) {
336
+ cursor += 1;
337
+ }
338
+
339
+ if (cursor >= body.length) {
340
+ break;
341
+ }
342
+
343
+ if (!expectValue) {
344
+ if (body[cursor] !== ",") {
345
+ return null;
346
+ }
347
+ cursor += 1;
348
+ expectValue = true;
349
+ continue;
350
+ }
351
+
352
+ const quote = body[cursor];
353
+ if (quote !== '"' && quote !== "'") {
354
+ return null;
355
+ }
356
+
357
+ cursor += 1;
358
+ let token = "";
359
+ let escaped = false;
360
+
361
+ while (cursor < body.length) {
362
+ const char = body[cursor] ?? "";
363
+
364
+ if (char === quote && !escaped) {
365
+ cursor += 1;
366
+ values.push(token);
367
+ break;
368
+ }
369
+
370
+ token += char;
371
+ escaped = char === "\\" && !escaped;
372
+ if (char !== "\\") {
373
+ escaped = false;
374
+ }
375
+ cursor += 1;
376
+ }
377
+
378
+ if (cursor > body.length) {
379
+ return null;
380
+ }
381
+
382
+ expectValue = false;
383
+ }
384
+
385
+ const leftover = body.slice(cursor).trim();
386
+ if (leftover === ",") {
387
+ return values;
388
+ }
389
+
390
+ return leftover.length === 0 ? values : null;
391
+ }
392
+
393
+ function collectArrayLines(lines, startIndex) {
394
+ const collected = [stripCommentOutsideStrings(lines[startIndex] ?? "")];
395
+ let bracketDepth = 0;
396
+ let inSingle = false;
397
+ let inDouble = false;
398
+ let escaped = false;
399
+
400
+ for (let lineIndex = startIndex; lineIndex < lines.length; lineIndex += 1) {
401
+ const sanitized = stripCommentOutsideStrings(lines[lineIndex] ?? "");
402
+ if (lineIndex !== startIndex) {
403
+ collected.push(sanitized);
404
+ }
405
+
406
+ for (let index = 0; index < sanitized.length; index += 1) {
407
+ const char = sanitized[index] ?? "";
408
+
409
+ if (char === '"' && !inSingle && !escaped) {
410
+ inDouble = !inDouble;
411
+ } else if (char === "'" && !inDouble && !escaped) {
412
+ inSingle = !inSingle;
413
+ } else if (!inSingle && !inDouble) {
414
+ if (char === "[") {
415
+ bracketDepth += 1;
416
+ } else if (char === "]") {
417
+ bracketDepth -= 1;
418
+ }
419
+ }
420
+
421
+ escaped = char === "\\" && !escaped;
422
+ if (char !== "\\") {
423
+ escaped = false;
424
+ }
425
+ }
426
+
427
+ if (bracketDepth <= 0) {
428
+ return {
429
+ nextIndex: lineIndex,
430
+ text: collected.join("\n"),
431
+ };
432
+ }
433
+ }
434
+
435
+ return null;
436
+ }
437
+
438
+ function parseCodexBlock(blockContent) {
439
+ const lines = blockContent.split("\n");
440
+ let command = null;
441
+ let args = null;
442
+ let hasConflict = false;
443
+ let inTripleDouble = false;
444
+ let inTripleSingle = false;
445
+
446
+ for (let index = 1; index < lines.length; index += 1) {
447
+ const line = lines[index] ?? "";
448
+ const sanitized = stripCommentOutsideStrings(line);
449
+ const trimmed = sanitized.trim();
450
+
451
+ if (inTripleDouble) {
452
+ if (countTripleQuoteToggles(sanitized, '"') % 2 === 1) {
453
+ inTripleDouble = false;
454
+ }
455
+ continue;
456
+ }
457
+
458
+ if (inTripleSingle) {
459
+ if (countTripleQuoteToggles(sanitized, "'") % 2 === 1) {
460
+ inTripleSingle = false;
461
+ }
462
+ continue;
463
+ }
464
+
465
+ if (countTripleQuoteToggles(sanitized, '"') % 2 === 1) {
466
+ inTripleDouble = true;
467
+ continue;
468
+ }
469
+
470
+ if (countTripleQuoteToggles(sanitized, "'") % 2 === 1) {
471
+ inTripleSingle = true;
472
+ continue;
473
+ }
474
+
475
+ if (trimmed.length === 0) {
476
+ continue;
477
+ }
478
+
479
+ if (/^command\s*=/.test(trimmed)) {
480
+ const parsedCommand = parseStringAssignment(trimmed, "command");
481
+ if (command !== null || parsedCommand === null) {
482
+ hasConflict = true;
483
+ } else {
484
+ command = parsedCommand;
485
+ }
486
+ continue;
487
+ }
488
+
489
+ if (/^args\s*=/.test(trimmed)) {
490
+ const collected = collectArrayLines(lines, index);
491
+ const parsedArgs = collected === null ? null : parseTomlStringArray(collected.text);
492
+
493
+ if (args !== null || parsedArgs === null || collected === null) {
494
+ hasConflict = true;
495
+ } else {
496
+ args = parsedArgs;
497
+ index = collected.nextIndex;
498
+ }
499
+ }
500
+ }
501
+
502
+ return {
503
+ args,
504
+ command,
505
+ hasConflict,
506
+ };
507
+ }
508
+
509
+ function isStructurallySafe(source) {
510
+ let bracketDepth = 0;
511
+ let braceDepth = 0;
512
+ let inSingle = false;
513
+ let inDouble = false;
514
+ let escaped = false;
515
+ let tripleState = {
516
+ inTripleDouble: false,
517
+ inTripleSingle: false,
518
+ };
519
+
520
+ for (const line of source.split("\n")) {
521
+ tripleState = advanceTripleQuoteState(line, tripleState);
522
+
523
+ for (let index = 0; index < line.length; index += 1) {
524
+ const char = line[index] ?? "";
525
+
526
+ if (!inSingle && !inDouble && char === "#") {
527
+ break;
528
+ }
529
+
530
+ if (char === '"' && !inSingle && !escaped) {
531
+ inDouble = !inDouble;
532
+ } else if (char === "'" && !inDouble && !escaped) {
533
+ inSingle = !inSingle;
534
+ } else if (!inSingle && !inDouble) {
535
+ if (char === "[") {
536
+ bracketDepth += 1;
537
+ } else if (char === "]") {
538
+ bracketDepth -= 1;
539
+ if (bracketDepth < 0) {
540
+ return false;
541
+ }
542
+ } else if (char === "{") {
543
+ braceDepth += 1;
544
+ } else if (char === "}") {
545
+ braceDepth -= 1;
546
+ if (braceDepth < 0) {
547
+ return false;
548
+ }
549
+ }
550
+ }
551
+
552
+ escaped = char === "\\" && !escaped;
553
+ if (char !== "\\") {
554
+ escaped = false;
555
+ }
556
+ }
557
+ }
558
+
559
+ return (
560
+ !tripleState.inTripleDouble &&
561
+ !tripleState.inTripleSingle &&
562
+ bracketDepth === 0 &&
563
+ braceDepth === 0 &&
564
+ !inSingle &&
565
+ !inDouble
566
+ );
567
+ }
568
+
569
+ try {
570
+ const source = fs.readFileSync(configPath, "utf8");
571
+ if (!isStructurallySafe(source)) {
572
+ process.exit(1);
573
+ }
574
+
575
+ const blocks = findAllCodexBlocks(source);
576
+ if (blocks.length !== 1) {
577
+ process.exit(1);
578
+ }
579
+
580
+ const parsed = parseCodexBlock(blocks[0]);
581
+ const isConfigured =
582
+ !parsed.hasConflict &&
583
+ parsed.command === expectedCommand &&
584
+ Array.isArray(parsed.args) &&
585
+ parsed.args.length === expectedArgs.length &&
586
+ parsed.args.every((entry, index) => entry === expectedArgs[index]);
587
+
588
+ process.exit(isConfigured ? 0 : 1);
589
+ } catch {
590
+ process.exit(1);
591
+ }
592
+ NODE
158
593
  }
159
594
 
160
595
  resolve_optional_cli_command() {
package/llms-full.txt CHANGED
@@ -21,9 +21,21 @@ Roblox Studio Plugin ← Executes commands inside Studio
21
21
 
22
22
  When an AI app says "Create a blue part", the MCP server converts this request, and the Roblox Studio plugin actually creates the part.
23
23
 
24
- ## One-Line Install (Recommended)
24
+ ## Web Installer (Recommended)
25
25
 
26
- Installs the MCP server, Roblox Studio plugin, and registers with your AI apps in one step:
26
+ Open the installer page in Chrome, Edge, or Arc for one-click setup:
27
+
28
+ https://hope1026.github.io/weppy-roblox-mcp/installer/
29
+
30
+ It writes the supported AI app config for `npx -y @weppy/roblox-mcp` first.
31
+
32
+ After you reopen the AI app, the first MCP run fetches the package through `npx` and auto-installs the Roblox Studio plugin.
33
+
34
+ For Safari or Firefox, use the installer page as a guide or continue with the manual install below.
35
+
36
+ ## One-Line Install (Fallback)
37
+
38
+ Advanced users can install everything in one command:
27
39
 
28
40
  ```bash
29
41
  # macOS / Linux
package/llms.txt CHANGED
@@ -21,7 +21,11 @@ terrain, and more — inside a live Studio session.
21
21
 
22
22
  ## Installation
23
23
 
24
- **One-line install** (installs MCP server, Roblox Studio plugin, and registers with AI apps):
24
+ **Web installer** (recommended one-click setup for Chrome, Edge, and Arc):
25
+
26
+ https://hope1026.github.io/weppy-roblox-mcp/installer/
27
+
28
+ **One-line install** (advanced fallback):
25
29
 
26
30
  ```bash
27
31
  # macOS / Linux
@@ -31,8 +35,10 @@ curl -fsSL https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/inst
31
35
  irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1 | iex
32
36
  ```
33
37
 
34
- Or manually:
35
- 1. Install Roblox Studio plugin (bridge between Studio and AI)
38
+ Safari / Firefox users should follow the installer page guide or open the installation docs.
39
+
40
+ Manual alternative:
41
+ 1. Install Roblox Studio plugin via the installation guide
36
42
  2. Register MCP server: `npx -y @weppy/roblox-mcp`
37
43
 
38
44
  ## Tiers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weppy/roblox-mcp",
3
- "version": "2.2.2",
3
+ "version": "2.3.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.2.2",
4
+ "version": "2.3.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-DGGmfli1.js";import{I as V}from"./InfoLabel-CCDWZLC9.js";import{D as F,P as G}from"./PropertyDiff-DIplDn-J.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-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};