@skillcap/gdh 3.2.3 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +6 -5
  3. package/RELEASE-SPAN-UPDATE-CONTRACTS.json +125 -0
  4. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  5. package/node_modules/@gdh/adapters/dist/index.js +0 -2
  6. package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
  7. package/node_modules/@gdh/adapters/dist/skill-rendering.d.ts.map +1 -1
  8. package/node_modules/@gdh/adapters/dist/skill-rendering.js +26 -30
  9. package/node_modules/@gdh/adapters/dist/skill-rendering.js.map +1 -1
  10. package/node_modules/@gdh/adapters/package.json +7 -7
  11. package/node_modules/@gdh/authoring/dist/lsp.d.ts +12 -0
  12. package/node_modules/@gdh/authoring/dist/lsp.d.ts.map +1 -1
  13. package/node_modules/@gdh/authoring/dist/lsp.js +128 -3
  14. package/node_modules/@gdh/authoring/dist/lsp.js.map +1 -1
  15. package/node_modules/@gdh/authoring/package.json +2 -2
  16. package/node_modules/@gdh/cli/dist/bridge-session.d.ts.map +1 -1
  17. package/node_modules/@gdh/cli/dist/bridge-session.js +11 -25
  18. package/node_modules/@gdh/cli/dist/bridge-session.js.map +1 -1
  19. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  20. package/node_modules/@gdh/cli/dist/index.js +26 -537
  21. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  22. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  23. package/node_modules/@gdh/cli/dist/migrate.js +25 -0
  24. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  25. package/node_modules/@gdh/cli/package.json +11 -11
  26. package/node_modules/@gdh/core/dist/editor-operation-runner.d.ts.map +1 -1
  27. package/node_modules/@gdh/core/dist/editor-operation-runner.js +530 -1
  28. package/node_modules/@gdh/core/dist/editor-operation-runner.js.map +1 -1
  29. package/node_modules/@gdh/core/dist/index.d.ts +14 -10
  30. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  31. package/node_modules/@gdh/core/dist/index.js +16 -14
  32. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  33. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.d.ts +3 -0
  34. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.d.ts.map +1 -0
  35. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.js +91 -0
  36. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.js.map +1 -0
  37. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts +7 -2
  38. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts.map +1 -1
  39. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js +9 -3
  40. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js.map +1 -1
  41. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts +20 -9
  42. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts.map +1 -1
  43. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js +22 -10
  44. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js.map +1 -1
  45. package/node_modules/@gdh/core/dist/migrations/registry.d.ts +1 -1
  46. package/node_modules/@gdh/core/dist/migrations/registry.d.ts.map +1 -1
  47. package/node_modules/@gdh/core/dist/migrations/registry.js +2 -0
  48. package/node_modules/@gdh/core/dist/migrations/registry.js.map +1 -1
  49. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.d.ts +16 -0
  50. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.d.ts.map +1 -0
  51. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.js +89 -0
  52. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.js.map +1 -0
  53. package/node_modules/@gdh/core/package.json +1 -1
  54. package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
  55. package/node_modules/@gdh/docs/dist/guidance.js +29 -0
  56. package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
  57. package/node_modules/@gdh/docs/dist/templates/guidance/editor-bridge.md.tpl +4 -1
  58. package/node_modules/@gdh/docs/dist/templates/guidance/runtime.md.tpl +33 -0
  59. package/node_modules/@gdh/docs/package.json +2 -2
  60. package/node_modules/@gdh/editor/dist/index.d.ts +101 -0
  61. package/node_modules/@gdh/editor/dist/index.d.ts.map +1 -1
  62. package/node_modules/@gdh/editor/dist/index.js +166 -5
  63. package/node_modules/@gdh/editor/dist/index.js.map +1 -1
  64. package/node_modules/@gdh/editor/package.json +2 -2
  65. package/node_modules/@gdh/mcp/dist/bridge-tools.d.ts +3 -4
  66. package/node_modules/@gdh/mcp/dist/bridge-tools.d.ts.map +1 -1
  67. package/node_modules/@gdh/mcp/dist/bridge-tools.js +5 -77
  68. package/node_modules/@gdh/mcp/dist/bridge-tools.js.map +1 -1
  69. package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
  70. package/node_modules/@gdh/mcp/dist/index.js +832 -249
  71. package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
  72. package/node_modules/@gdh/mcp/package.json +8 -8
  73. package/node_modules/@gdh/observability/package.json +2 -2
  74. package/node_modules/@gdh/runtime/dist/bridge-broker.d.ts +9 -1
  75. package/node_modules/@gdh/runtime/dist/bridge-broker.d.ts.map +1 -1
  76. package/node_modules/@gdh/runtime/dist/bridge-broker.js +267 -52
  77. package/node_modules/@gdh/runtime/dist/bridge-broker.js.map +1 -1
  78. package/node_modules/@gdh/runtime/dist/bridge-surface.js +11 -3
  79. package/node_modules/@gdh/runtime/dist/bridge-surface.js.map +1 -1
  80. package/node_modules/@gdh/runtime/package.json +2 -2
  81. package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
  82. package/node_modules/@gdh/scan/dist/onboard.js +60 -180
  83. package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
  84. package/node_modules/@gdh/scan/package.json +3 -3
  85. package/node_modules/@gdh/verify/package.json +6 -6
  86. package/package.json +12 -12
@@ -58,6 +58,34 @@ func _dispatch_operation(operation: Dictionary) -> Dictionary:
58
58
  return _resource_properties(operation)
59
59
  "resource.set_property":
60
60
  return _resource_set_property(operation)
61
+ "animation.player.inspect":
62
+ return _animation_player_inspect(operation)
63
+ "animation.clip.create":
64
+ return _animation_clip_create(operation)
65
+ "animation.clip.read":
66
+ return _animation_clip_read(operation)
67
+ "animation.clip.delete":
68
+ return _animation_clip_delete(operation)
69
+ "animation.track.add":
70
+ return _animation_track_add(operation)
71
+ "animation.track.remove":
72
+ return _animation_track_remove(operation)
73
+ "animation.keyframe.upsert":
74
+ return _animation_keyframe_upsert(operation)
75
+ "animation.keyframe.delete":
76
+ return _animation_keyframe_delete(operation)
77
+ "animation.state_machine.create":
78
+ return _animation_state_machine_create(operation)
79
+ "animation.state_machine.read":
80
+ return _animation_state_machine_read(operation)
81
+ "animation.state_machine.node.upsert":
82
+ return _animation_state_machine_node_upsert(operation)
83
+ "animation.state_machine.node.delete":
84
+ return _animation_state_machine_node_delete(operation)
85
+ "animation.state_machine.transition.upsert":
86
+ return _animation_state_machine_transition_upsert(operation)
87
+ "animation.state_machine.transition.delete":
88
+ return _animation_state_machine_transition_delete(operation)
61
89
  "script.attach":
62
90
  return _script_attach(operation)
63
91
  "class.search":
@@ -314,6 +342,287 @@ func _resource_set_property(operation: Dictionary) -> Dictionary:
314
342
  return _failure("Failed to save resource: " + resource_path, ["resource_save_failed"])
315
343
  return _success("Resource property set.", {"resourcePath": resource_path, "propertyName": property_name})
316
344
 
345
+ func _animation_player_inspect(operation: Dictionary) -> Dictionary:
346
+ var loaded := _load_animation_player_context(operation)
347
+ if not loaded.get("ok", false):
348
+ return loaded
349
+ var player = loaded["player"]
350
+ var libraries := []
351
+ if player.has_method("get_animation_library_list"):
352
+ for library_name in player.get_animation_library_list():
353
+ var library = player.get_animation_library(library_name)
354
+ if library == null:
355
+ continue
356
+ var animations := []
357
+ for animation_name in library.get_animation_list():
358
+ var animation = library.get_animation(animation_name)
359
+ animations.append(_animation_clip_summary(str(library_name), str(animation_name), animation))
360
+ libraries.append({"name": str(library_name), "animations": animations})
361
+ var output := {
362
+ "scenePath": loaded["scenePath"],
363
+ "animationPlayerPath": loaded["animationPlayerPath"],
364
+ "class": player.get_class(),
365
+ "autoplay": str(player.get("autoplay")),
366
+ "defaultBlendTime": player.get("playback_default_blend_time"),
367
+ "libraries": libraries,
368
+ }
369
+ loaded["root"].queue_free()
370
+ return _success("Animation player inspected.", output)
371
+
372
+ func _animation_clip_create(operation: Dictionary) -> Dictionary:
373
+ var loaded := _load_animation_player_context(operation)
374
+ if not loaded.get("ok", false):
375
+ return loaded
376
+ var player = loaded["player"]
377
+ var library_name := str(operation.get("libraryName", ""))
378
+ var animation_name := str(operation.get("animationName", ""))
379
+ var library = _get_or_create_animation_library(player, library_name)
380
+ if library == null:
381
+ return _failure("Animation library could not be created: " + library_name, ["animation_library_unavailable"])
382
+ if library.has_animation(animation_name) and not bool(operation.get("overwrite", false)):
383
+ return _failure("Animation already exists: " + animation_name, ["animation_clip_exists"])
384
+ var animation := Animation.new()
385
+ animation.length = max(0.001, float(operation.get("length", 1.0)))
386
+ animation.step = max(0.001, float(operation.get("step", animation.step)))
387
+ animation.loop_mode = _animation_loop_mode(operation.get("loopMode", animation.loop_mode))
388
+ if library.has_animation(animation_name):
389
+ library.remove_animation(animation_name)
390
+ var add_err = library.add_animation(animation_name, animation)
391
+ if int(add_err) != OK:
392
+ return _failure("Animation could not be added: " + animation_name, ["animation_clip_add_failed"])
393
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
394
+ if not result.get("ok", false):
395
+ return result
396
+ return _success("Animation clip created.", {"scenePath": loaded["scenePath"], "animationPlayerPath": loaded["animationPlayerPath"], "libraryName": library_name, "animationName": animation_name, "length": animation.length, "loopMode": animation.loop_mode, "step": animation.step})
397
+
398
+ func _animation_clip_read(operation: Dictionary) -> Dictionary:
399
+ var loaded := _load_animation_player_context(operation)
400
+ if not loaded.get("ok", false):
401
+ return loaded
402
+ var resolved := _resolve_animation_clip(loaded["player"], operation)
403
+ if not resolved.get("ok", false):
404
+ loaded["root"].queue_free()
405
+ return resolved
406
+ var animation: Animation = resolved["animation"]
407
+ var include_keys := bool(operation.get("includeKeys", false))
408
+ var key_limit := int(operation.get("keyLimit", 200))
409
+ var tracks := []
410
+ for track_index in range(animation.get_track_count()):
411
+ tracks.append(_animation_track_summary(animation, track_index, include_keys, key_limit))
412
+ var output := _animation_clip_summary(str(resolved["libraryName"]), str(resolved["animationName"]), animation)
413
+ output["tracks"] = tracks
414
+ loaded["root"].queue_free()
415
+ return _success("Animation clip read.", output)
416
+
417
+ func _animation_clip_delete(operation: Dictionary) -> Dictionary:
418
+ var loaded := _load_animation_player_context(operation)
419
+ if not loaded.get("ok", false):
420
+ return loaded
421
+ var library_name := str(operation.get("libraryName", ""))
422
+ var animation_name := str(operation.get("animationName", ""))
423
+ var library = _get_animation_library(loaded["player"], library_name)
424
+ if library == null or not library.has_animation(animation_name):
425
+ return _failure("Animation clip not found: " + animation_name, ["animation_clip_not_found"])
426
+ library.remove_animation(animation_name)
427
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
428
+ if not result.get("ok", false):
429
+ return result
430
+ return _success("Animation clip deleted.", {"scenePath": loaded["scenePath"], "animationPlayerPath": loaded["animationPlayerPath"], "libraryName": library_name, "animationName": animation_name})
431
+
432
+ func _animation_track_add(operation: Dictionary) -> Dictionary:
433
+ var loaded := _load_animation_player_context(operation)
434
+ if not loaded.get("ok", false):
435
+ return loaded
436
+ var resolved := _resolve_animation_clip(loaded["player"], operation)
437
+ if not resolved.get("ok", false):
438
+ return resolved
439
+ var animation: Animation = resolved["animation"]
440
+ var track_type := _animation_track_type(operation.get("trackType", "value"))
441
+ if track_type < 0:
442
+ return _failure("Unsupported Animation track type: " + str(operation.get("trackType", "")), ["animation_track_type_unsupported"], _operation_help("animation.track.add"))
443
+ var at_position := int(operation.get("atPosition", -1))
444
+ var track_index := animation.add_track(track_type, at_position)
445
+ if track_index < 0:
446
+ return _failure("Animation track could not be added.", ["animation_track_add_failed"])
447
+ animation.track_set_path(track_index, NodePath(str(operation.get("trackPath", ""))))
448
+ if operation.has("interpolation"):
449
+ animation.track_set_interpolation_type(track_index, _animation_interpolation_type(operation.get("interpolation")))
450
+ if track_type == Animation.TYPE_VALUE and operation.has("updateMode"):
451
+ animation.value_track_set_update_mode(track_index, _animation_update_mode(operation.get("updateMode")))
452
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
453
+ if not result.get("ok", false):
454
+ return result
455
+ return _success("Animation track added.", {"scenePath": loaded["scenePath"], "animationPlayerPath": loaded["animationPlayerPath"], "animationName": str(resolved["animationName"]), "trackIndex": track_index, "trackType": track_type, "trackPath": str(animation.track_get_path(track_index))})
456
+
457
+ func _animation_track_remove(operation: Dictionary) -> Dictionary:
458
+ var loaded := _load_animation_player_context(operation)
459
+ if not loaded.get("ok", false):
460
+ return loaded
461
+ var resolved := _resolve_animation_clip(loaded["player"], operation)
462
+ if not resolved.get("ok", false):
463
+ return resolved
464
+ var animation: Animation = resolved["animation"]
465
+ var track_index := int(operation.get("trackIndex", -1))
466
+ if track_index < 0 or track_index >= animation.get_track_count():
467
+ return _failure("Animation track index is out of range.", ["animation_track_not_found"])
468
+ animation.remove_track(track_index)
469
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
470
+ if not result.get("ok", false):
471
+ return result
472
+ return _success("Animation track removed.", {"scenePath": loaded["scenePath"], "animationPlayerPath": loaded["animationPlayerPath"], "animationName": str(resolved["animationName"]), "trackIndex": track_index})
473
+
474
+ func _animation_keyframe_upsert(operation: Dictionary) -> Dictionary:
475
+ var loaded := _load_animation_player_context(operation)
476
+ if not loaded.get("ok", false):
477
+ return loaded
478
+ var resolved := _resolve_animation_clip(loaded["player"], operation)
479
+ if not resolved.get("ok", false):
480
+ return resolved
481
+ var animation: Animation = resolved["animation"]
482
+ var track_index := int(operation.get("trackIndex", -1))
483
+ if track_index < 0 or track_index >= animation.get_track_count():
484
+ return _failure("Animation track index is out of range.", ["animation_track_not_found"])
485
+ var time := float(operation.get("time", 0.0))
486
+ animation.track_remove_key_at_time(track_index, time)
487
+ var key_index := _animation_insert_key(animation, track_index, time, operation.get("value"), float(operation.get("transition", 1.0)))
488
+ if key_index < 0:
489
+ return _failure("Animation keyframe could not be inserted.", ["animation_keyframe_insert_failed"])
490
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
491
+ if not result.get("ok", false):
492
+ return result
493
+ return _success("Animation keyframe upserted.", {"scenePath": loaded["scenePath"], "animationPlayerPath": loaded["animationPlayerPath"], "animationName": str(resolved["animationName"]), "trackIndex": track_index, "keyIndex": key_index, "time": time})
494
+
495
+ func _animation_keyframe_delete(operation: Dictionary) -> Dictionary:
496
+ var loaded := _load_animation_player_context(operation)
497
+ if not loaded.get("ok", false):
498
+ return loaded
499
+ var resolved := _resolve_animation_clip(loaded["player"], operation)
500
+ if not resolved.get("ok", false):
501
+ return resolved
502
+ var animation: Animation = resolved["animation"]
503
+ var track_index := int(operation.get("trackIndex", -1))
504
+ if track_index < 0 or track_index >= animation.get_track_count():
505
+ return _failure("Animation track index is out of range.", ["animation_track_not_found"])
506
+ var key_index := int(operation.get("keyIndex", -1))
507
+ if key_index >= 0:
508
+ if key_index >= animation.track_get_key_count(track_index):
509
+ return _failure("Animation key index is out of range.", ["animation_keyframe_not_found"])
510
+ animation.track_remove_key(track_index, key_index)
511
+ elif operation.has("time"):
512
+ animation.track_remove_key_at_time(track_index, float(operation.get("time", 0.0)))
513
+ else:
514
+ return _failure("animation.keyframe.delete requires keyIndex or time.", ["animation_keyframe_selector_missing"])
515
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
516
+ if not result.get("ok", false):
517
+ return result
518
+ return _success("Animation keyframe deleted.", {"scenePath": loaded["scenePath"], "animationPlayerPath": loaded["animationPlayerPath"], "animationName": str(resolved["animationName"]), "trackIndex": track_index})
519
+
520
+ func _animation_state_machine_create(operation: Dictionary) -> Dictionary:
521
+ var scene_path := _res_path(str(operation.get("scenePath", "")))
522
+ var root = _load_scene_root(scene_path)
523
+ if root == null:
524
+ return _failure("Scene could not be loaded: " + scene_path, ["scene_load_failed"])
525
+ var tree_path := str(operation.get("animationTreePath", ""))
526
+ var tree = _find_node(root, tree_path)
527
+ if tree == null:
528
+ root.queue_free()
529
+ return _failure("AnimationTree not found: " + tree_path, ["animation_tree_not_found"])
530
+ if not (tree is AnimationTree):
531
+ root.queue_free()
532
+ return _failure("Node is not an AnimationTree: " + tree_path, ["animation_tree_invalid"])
533
+ var existing = tree.get("tree_root")
534
+ if existing != null and existing is AnimationNodeStateMachine and not bool(operation.get("overwrite", false)):
535
+ root.queue_free()
536
+ return _failure("AnimationTree already has an AnimationNodeStateMachine tree_root.", ["animation_state_machine_exists"])
537
+ var machine := AnimationNodeStateMachine.new()
538
+ _set_properties(machine, operation.get("properties", {}))
539
+ tree.set("tree_root", machine)
540
+ var result := _save_instantiated_scene(root, scene_path)
541
+ if not result.get("ok", false):
542
+ return result
543
+ return _success("Animation state machine created.", {"scenePath": scene_path, "animationTreePath": tree_path})
544
+
545
+ func _animation_state_machine_read(operation: Dictionary) -> Dictionary:
546
+ var loaded := _load_state_machine_context(operation)
547
+ if not loaded.get("ok", false):
548
+ return loaded
549
+ var output := _state_machine_summary(loaded["stateMachine"])
550
+ output["scenePath"] = loaded["scenePath"]
551
+ output["animationTreePath"] = loaded["animationTreePath"]
552
+ loaded["root"].queue_free()
553
+ return _success("Animation state machine read.", output)
554
+
555
+ func _animation_state_machine_node_upsert(operation: Dictionary) -> Dictionary:
556
+ var loaded := _load_state_machine_context(operation)
557
+ if not loaded.get("ok", false):
558
+ return loaded
559
+ var machine: AnimationNodeStateMachine = loaded["stateMachine"]
560
+ var state_name := str(operation.get("stateName", ""))
561
+ var graph_node = machine.get_node(state_name) if machine.has_node(state_name) else null
562
+ var created := false
563
+ if graph_node == null:
564
+ var node_type := str(operation.get("nodeType", "AnimationNodeAnimation"))
565
+ graph_node = _instantiate(node_type)
566
+ if graph_node == null or not (graph_node is AnimationNode):
567
+ return _failure("Unable to instantiate animation graph node type: " + node_type, ["animation_graph_node_type_invalid"])
568
+ machine.add_node(state_name, graph_node, _decode_value(operation.get("position", {"$vector2": {"x": 0, "y": 0}})))
569
+ created = true
570
+ _set_properties(graph_node, operation.get("properties", {}))
571
+ if operation.has("position"):
572
+ machine.set_node_position(state_name, _decode_value(operation.get("position")))
573
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
574
+ if not result.get("ok", false):
575
+ return result
576
+ return _success("Animation state-machine node upserted.", {"scenePath": loaded["scenePath"], "animationTreePath": loaded["animationTreePath"], "stateName": state_name, "created": created})
577
+
578
+ func _animation_state_machine_node_delete(operation: Dictionary) -> Dictionary:
579
+ var loaded := _load_state_machine_context(operation)
580
+ if not loaded.get("ok", false):
581
+ return loaded
582
+ var machine: AnimationNodeStateMachine = loaded["stateMachine"]
583
+ var state_name := str(operation.get("stateName", ""))
584
+ if not machine.has_node(state_name):
585
+ return _failure("Animation state-machine node not found: " + state_name, ["animation_graph_node_not_found"])
586
+ machine.remove_node(state_name)
587
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
588
+ if not result.get("ok", false):
589
+ return result
590
+ return _success("Animation state-machine node deleted.", {"scenePath": loaded["scenePath"], "animationTreePath": loaded["animationTreePath"], "stateName": state_name})
591
+
592
+ func _animation_state_machine_transition_upsert(operation: Dictionary) -> Dictionary:
593
+ var loaded := _load_state_machine_context(operation)
594
+ if not loaded.get("ok", false):
595
+ return loaded
596
+ var machine: AnimationNodeStateMachine = loaded["stateMachine"]
597
+ var from_state := str(operation.get("fromState", ""))
598
+ var to_state := str(operation.get("toState", ""))
599
+ if not machine.has_node(from_state) or not machine.has_node(to_state):
600
+ return _failure("Animation state-machine transition endpoints must both exist.", ["animation_graph_endpoint_not_found"])
601
+ if machine.has_transition(from_state, to_state):
602
+ machine.remove_transition(from_state, to_state)
603
+ var transition := AnimationNodeStateMachineTransition.new()
604
+ _set_properties(transition, operation.get("properties", {}))
605
+ machine.add_transition(from_state, to_state, transition)
606
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
607
+ if not result.get("ok", false):
608
+ return result
609
+ return _success("Animation state-machine transition upserted.", {"scenePath": loaded["scenePath"], "animationTreePath": loaded["animationTreePath"], "fromState": from_state, "toState": to_state})
610
+
611
+ func _animation_state_machine_transition_delete(operation: Dictionary) -> Dictionary:
612
+ var loaded := _load_state_machine_context(operation)
613
+ if not loaded.get("ok", false):
614
+ return loaded
615
+ var machine: AnimationNodeStateMachine = loaded["stateMachine"]
616
+ var from_state := str(operation.get("fromState", ""))
617
+ var to_state := str(operation.get("toState", ""))
618
+ if not machine.has_transition(from_state, to_state):
619
+ return _failure("Animation state-machine transition not found.", ["animation_graph_transition_not_found"])
620
+ machine.remove_transition(from_state, to_state)
621
+ var result := _save_instantiated_scene(loaded["root"], loaded["scenePath"])
622
+ if not result.get("ok", false):
623
+ return result
624
+ return _success("Animation state-machine transition deleted.", {"scenePath": loaded["scenePath"], "animationTreePath": loaded["animationTreePath"], "fromState": from_state, "toState": to_state})
625
+
317
626
  func _script_attach(operation: Dictionary) -> Dictionary:
318
627
  var scene_path := _res_path(str(operation.get("scenePath", "")))
319
628
  var root = _load_scene_root(scene_path)
@@ -451,6 +760,202 @@ func _batch_apply(operation: Dictionary) -> Dictionary:
451
760
  return _failure("Batch operation failed.", ["batch_child_failed", str(child_operation.get("kind", ""))])
452
761
  return _success("Batch applied.", {"results": results})
453
762
 
763
+ func _load_animation_player_context(operation: Dictionary) -> Dictionary:
764
+ var scene_path := _res_path(str(operation.get("scenePath", "")))
765
+ var root = _load_scene_root(scene_path)
766
+ if root == null:
767
+ return _failure("Scene could not be loaded: " + scene_path, ["scene_load_failed"])
768
+ var player_path := str(operation.get("animationPlayerPath", ""))
769
+ var player = _find_node(root, player_path)
770
+ if player == null:
771
+ root.queue_free()
772
+ return _failure("AnimationPlayer not found: " + player_path, ["animation_player_not_found"])
773
+ if not player.has_method("get_animation_library") or not player.has_method("get_animation_library_list"):
774
+ root.queue_free()
775
+ return _failure("Node is not an animation mixer: " + player_path, ["animation_player_invalid"])
776
+ return {"ok": true, "root": root, "scenePath": scene_path, "player": player, "animationPlayerPath": player_path}
777
+
778
+ func _get_animation_library(player, library_name: String):
779
+ if not player.has_method("has_animation_library") or not player.has_method("get_animation_library"):
780
+ return null
781
+ if player.has_animation_library(library_name):
782
+ return player.get_animation_library(library_name)
783
+ return null
784
+
785
+ func _get_or_create_animation_library(player, library_name: String):
786
+ var library = _get_animation_library(player, library_name)
787
+ if library != null:
788
+ return library
789
+ if not player.has_method("add_animation_library"):
790
+ return null
791
+ library = AnimationLibrary.new()
792
+ var err = player.add_animation_library(library_name, library)
793
+ if int(err) != OK:
794
+ return null
795
+ return library
796
+
797
+ func _resolve_animation_clip(player, operation: Dictionary) -> Dictionary:
798
+ var library_name := str(operation.get("libraryName", ""))
799
+ var animation_name := str(operation.get("animationName", ""))
800
+ var library = _get_animation_library(player, library_name)
801
+ if library == null:
802
+ return _failure("Animation library not found: " + library_name, ["animation_library_not_found"])
803
+ if not library.has_animation(animation_name):
804
+ return _failure("Animation clip not found: " + animation_name, ["animation_clip_not_found"])
805
+ return {"ok": true, "libraryName": library_name, "animationName": animation_name, "library": library, "animation": library.get_animation(animation_name)}
806
+
807
+ func _animation_clip_summary(library_name: String, animation_name: String, animation: Animation) -> Dictionary:
808
+ return {"libraryName": library_name, "animationName": animation_name, "length": animation.length, "loopMode": animation.loop_mode, "step": animation.step, "trackCount": animation.get_track_count()}
809
+
810
+ func _animation_track_summary(animation: Animation, track_index: int, include_keys: bool, key_limit: int) -> Dictionary:
811
+ var keys := []
812
+ var key_count := animation.track_get_key_count(track_index)
813
+ if include_keys:
814
+ var effective_limit := key_limit if key_limit > 0 else 200
815
+ for key_index in range(min(key_count, effective_limit)):
816
+ keys.append({
817
+ "keyIndex": key_index,
818
+ "time": animation.track_get_key_time(track_index, key_index),
819
+ "transition": animation.track_get_key_transition(track_index, key_index),
820
+ "value": _encode_animation_key_value(animation, track_index, key_index),
821
+ })
822
+ return {
823
+ "trackIndex": track_index,
824
+ "type": animation.track_get_type(track_index),
825
+ "path": str(animation.track_get_path(track_index)),
826
+ "enabled": animation.track_is_enabled(track_index),
827
+ "imported": animation.track_is_imported(track_index),
828
+ "interpolation": animation.track_get_interpolation_type(track_index),
829
+ "keyCount": key_count,
830
+ "keys": keys,
831
+ "keysTruncated": include_keys and key_count > keys.size(),
832
+ }
833
+
834
+ func _encode_animation_key_value(animation: Animation, track_index: int, key_index: int):
835
+ var track_type := animation.track_get_type(track_index)
836
+ if track_type == Animation.TYPE_ANIMATION:
837
+ return str(animation.animation_track_get_key_animation(track_index, key_index))
838
+ if track_type == Animation.TYPE_BEZIER:
839
+ return animation.bezier_track_get_key_value(track_index, key_index)
840
+ return _encode_value(animation.track_get_key_value(track_index, key_index))
841
+
842
+ func _animation_insert_key(animation: Animation, track_index: int, time: float, raw_value, transition: float) -> int:
843
+ var track_type := animation.track_get_type(track_index)
844
+ var value = _decode_value(raw_value)
845
+ if track_type == Animation.TYPE_ANIMATION:
846
+ return animation.animation_track_insert_key(track_index, time, str(raw_value))
847
+ if track_type == Animation.TYPE_BEZIER:
848
+ return animation.bezier_track_insert_key(track_index, time, float(raw_value))
849
+ if track_type == Animation.TYPE_POSITION_3D:
850
+ return animation.position_track_insert_key(track_index, time, value)
851
+ if track_type == Animation.TYPE_ROTATION_3D:
852
+ return animation.rotation_track_insert_key(track_index, time, value)
853
+ if track_type == Animation.TYPE_SCALE_3D:
854
+ return animation.scale_track_insert_key(track_index, time, value)
855
+ if track_type == Animation.TYPE_BLEND_SHAPE:
856
+ return animation.blend_shape_track_insert_key(track_index, time, float(raw_value))
857
+ return animation.track_insert_key(track_index, time, value, transition)
858
+
859
+ func _animation_track_type(value) -> int:
860
+ if typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT:
861
+ return int(value)
862
+ match str(value).to_lower():
863
+ "value", "type_value":
864
+ return Animation.TYPE_VALUE
865
+ "position_3d", "position3d", "type_position_3d":
866
+ return Animation.TYPE_POSITION_3D
867
+ "rotation_3d", "rotation3d", "type_rotation_3d":
868
+ return Animation.TYPE_ROTATION_3D
869
+ "scale_3d", "scale3d", "type_scale_3d":
870
+ return Animation.TYPE_SCALE_3D
871
+ "blend_shape", "blendshape", "type_blend_shape":
872
+ return Animation.TYPE_BLEND_SHAPE
873
+ "method", "type_method":
874
+ return Animation.TYPE_METHOD
875
+ "bezier", "type_bezier":
876
+ return Animation.TYPE_BEZIER
877
+ "audio", "type_audio":
878
+ return Animation.TYPE_AUDIO
879
+ "animation", "type_animation":
880
+ return Animation.TYPE_ANIMATION
881
+ _:
882
+ return -1
883
+
884
+ func _animation_interpolation_type(value) -> int:
885
+ if typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT:
886
+ return int(value)
887
+ match str(value).to_lower():
888
+ "nearest":
889
+ return Animation.INTERPOLATION_NEAREST
890
+ "cubic":
891
+ return Animation.INTERPOLATION_CUBIC
892
+ "linear_angle":
893
+ return Animation.INTERPOLATION_LINEAR_ANGLE
894
+ "cubic_angle":
895
+ return Animation.INTERPOLATION_CUBIC_ANGLE
896
+ _:
897
+ return Animation.INTERPOLATION_LINEAR
898
+
899
+ func _animation_update_mode(value) -> int:
900
+ if typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT:
901
+ return int(value)
902
+ match str(value).to_lower():
903
+ "discrete":
904
+ return Animation.UPDATE_DISCRETE
905
+ "capture":
906
+ return Animation.UPDATE_CAPTURE
907
+ _:
908
+ return Animation.UPDATE_CONTINUOUS
909
+
910
+ func _animation_loop_mode(value) -> int:
911
+ if typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT:
912
+ return int(value)
913
+ match str(value).to_lower():
914
+ "linear", "loop_linear":
915
+ return Animation.LOOP_LINEAR
916
+ "pingpong", "ping_pong", "loop_pingpong":
917
+ return Animation.LOOP_PINGPONG
918
+ _:
919
+ return Animation.LOOP_NONE
920
+
921
+ func _load_state_machine_context(operation: Dictionary) -> Dictionary:
922
+ var scene_path := _res_path(str(operation.get("scenePath", "")))
923
+ var root = _load_scene_root(scene_path)
924
+ if root == null:
925
+ return _failure("Scene could not be loaded: " + scene_path, ["scene_load_failed"])
926
+ var tree_path := str(operation.get("animationTreePath", ""))
927
+ var tree = _find_node(root, tree_path)
928
+ if tree == null:
929
+ root.queue_free()
930
+ return _failure("AnimationTree not found: " + tree_path, ["animation_tree_not_found"])
931
+ if not (tree is AnimationTree):
932
+ root.queue_free()
933
+ return _failure("Node is not an AnimationTree: " + tree_path, ["animation_tree_invalid"])
934
+ var state_machine = tree.get("tree_root")
935
+ if state_machine == null or not (state_machine is AnimationNodeStateMachine):
936
+ root.queue_free()
937
+ return _failure("AnimationTree tree_root is not an AnimationNodeStateMachine.", ["animation_state_machine_not_found"])
938
+ return {"ok": true, "root": root, "scenePath": scene_path, "tree": tree, "animationTreePath": tree_path, "stateMachine": state_machine}
939
+
940
+ func _state_machine_summary(machine: AnimationNodeStateMachine) -> Dictionary:
941
+ var nodes := []
942
+ for state_name in machine.get_node_list():
943
+ var graph_node = machine.get_node(state_name)
944
+ nodes.append({"name": str(state_name), "type": graph_node.get_class() if graph_node != null else "", "position": _encode_value(machine.get_node_position(state_name)), "properties": _selected_properties(graph_node, ["animation"]) if graph_node != null else {}})
945
+ var transitions := []
946
+ for index in range(machine.get_transition_count()):
947
+ var transition = machine.get_transition(index)
948
+ transitions.append({"index": index, "from": str(machine.get_transition_from(index)), "to": str(machine.get_transition_to(index)), "properties": _selected_properties(transition, ["advance_mode", "advance_condition", "xfade_time", "priority", "switch_mode"])})
949
+ return {"graphOffset": _encode_value(machine.get_graph_offset()), "nodes": nodes, "transitions": transitions}
950
+
951
+ func _selected_properties(object: Object, names: Array) -> Dictionary:
952
+ var values := {}
953
+ if object == null:
954
+ return values
955
+ for property_name in names:
956
+ values[str(property_name)] = _encode_value(object.get(str(property_name)))
957
+ return values
958
+
454
959
  func _load_scene_root(scene_path: String):
455
960
  var loaded = load(scene_path)
456
961
  if loaded == null or not (loaded is PackedScene):
@@ -492,6 +997,12 @@ func _decode_value(value):
492
997
  if value.has("$vector2"):
493
998
  var vector = value["$vector2"]
494
999
  return Vector2(float(vector.get("x", 0.0)), float(vector.get("y", 0.0)))
1000
+ if value.has("$vector3"):
1001
+ var vector = value["$vector3"]
1002
+ return Vector3(float(vector.get("x", 0.0)), float(vector.get("y", 0.0)), float(vector.get("z", 0.0)))
1003
+ if value.has("$quaternion"):
1004
+ var quaternion = value["$quaternion"]
1005
+ return Quaternion(float(quaternion.get("x", 0.0)), float(quaternion.get("y", 0.0)), float(quaternion.get("z", 0.0)), float(quaternion.get("w", 1.0)))
495
1006
  if value.has("$color"):
496
1007
  var color = value["$color"]
497
1008
  return Color(float(color.get("r", 0.0)), float(color.get("g", 0.0)), float(color.get("b", 0.0)), float(color.get("a", 1.0)))
@@ -504,6 +1015,10 @@ func _encode_value(value, depth := 0):
504
1015
  return {"$truncated": "max_depth", "text": str(value)}
505
1016
  if value is Vector2:
506
1017
  return {"$vector2": {"x": value.x, "y": value.y}}
1018
+ if value is Vector3:
1019
+ return {"$vector3": {"x": value.x, "y": value.y, "z": value.z}}
1020
+ if value is Quaternion:
1021
+ return {"$quaternion": {"x": value.x, "y": value.y, "z": value.z, "w": value.w}}
507
1022
  if value is Color:
508
1023
  return {"$color": {"r": value.r, "g": value.g, "b": value.b, "a": value.a}}
509
1024
  if value is Resource:
@@ -658,13 +1173,27 @@ func _operation_help(kind: String) -> Dictionary:
658
1173
  "node.inspect": {"kind": "node.inspect", "scenePath": "res://path/to/scene.tscn", "nodePath": ".", "includePropertyList": true, "propertyLimit": 40},
659
1174
  "resource.read": {"kind": "resource.read", "resourcePath": "res://path/to/resource.tres", "propertyNames": ["name"]},
660
1175
  "resource.properties": {"kind": "resource.properties", "resourcePath": "res://path/to/resource.tres"},
1176
+ "animation.player.inspect": {"kind": "animation.player.inspect", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer"},
1177
+ "animation.clip.create": {"kind": "animation.clip.create", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle", "length": 1.0},
1178
+ "animation.clip.read": {"kind": "animation.clip.read", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle", "includeKeys": true},
1179
+ "animation.clip.delete": {"kind": "animation.clip.delete", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle"},
1180
+ "animation.track.add": {"kind": "animation.track.add", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle", "trackType": "value", "trackPath": "Sprite2D:modulate:a"},
1181
+ "animation.track.remove": {"kind": "animation.track.remove", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle", "trackIndex": 0},
1182
+ "animation.keyframe.upsert": {"kind": "animation.keyframe.upsert", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle", "trackIndex": 0, "time": 0.0, "value": 1.0},
1183
+ "animation.keyframe.delete": {"kind": "animation.keyframe.delete", "scenePath": "res://path/to/scene.tscn", "animationPlayerPath": "AnimationPlayer", "animationName": "idle", "trackIndex": 0, "time": 0.0},
1184
+ "animation.state_machine.create": {"kind": "animation.state_machine.create", "scenePath": "res://path/to/scene.tscn", "animationTreePath": "AnimationTree"},
1185
+ "animation.state_machine.read": {"kind": "animation.state_machine.read", "scenePath": "res://path/to/scene.tscn", "animationTreePath": "AnimationTree"},
1186
+ "animation.state_machine.node.upsert": {"kind": "animation.state_machine.node.upsert", "scenePath": "res://path/to/scene.tscn", "animationTreePath": "AnimationTree", "stateName": "Idle", "nodeType": "AnimationNodeAnimation", "properties": {"animation": "idle"}},
1187
+ "animation.state_machine.node.delete": {"kind": "animation.state_machine.node.delete", "scenePath": "res://path/to/scene.tscn", "animationTreePath": "AnimationTree", "stateName": "Idle"},
1188
+ "animation.state_machine.transition.upsert": {"kind": "animation.state_machine.transition.upsert", "scenePath": "res://path/to/scene.tscn", "animationTreePath": "AnimationTree", "fromState": "Idle", "toState": "Run"},
1189
+ "animation.state_machine.transition.delete": {"kind": "animation.state_machine.transition.delete", "scenePath": "res://path/to/scene.tscn", "animationTreePath": "AnimationTree", "fromState": "Idle", "toState": "Run"},
661
1190
  "class.search": {"kind": "class.search", "query": "Label"},
662
1191
  "class.info": {"kind": "class.info", "className": "Label", "propertyQuery": "text"},
663
1192
  "editor.state": {"kind": "editor.state"},
664
1193
  }
665
1194
  var example = examples.get(kind, {"kind": "editor.state"})
666
1195
  return {
667
- "usage": "Use editor.operation.run with operation.kind, or prefer focused MCP tools: editor.state, editor.scene.tree, editor.node.inspect, editor.resource.read, editor.resource.properties, editor.class.search, editor.class.info.",
1196
+ "usage": "Use editor.operation.run with operation.kind, or prefer focused MCP tools such as editor.state, editor.scene.tree, editor.node.inspect, editor.resource.read, editor.resource.properties, editor.class.search, editor.class.info, and editor.animation.*.",
668
1197
  "helpCommand": "gdh editor operation run --help",
669
1198
  "examples": [example],
670
1199
  }
@@ -1 +1 @@
1
- {"version":3,"file":"editor-operation-runner.js","sourceRoot":"","sources":["../src/editor-operation-runner.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,MAAM,UAAU,2BAA2B;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4pBR,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oCAAoC;IAClD,MAAM,MAAM,GAAG,2BAA2B,EAAE,CAAC;IAC7C,MAAM,MAAM,GAAG,iEAAiE,CAAC;IACjF,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"editor-operation-runner.js","sourceRoot":"","sources":["../src/editor-operation-runner.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,MAAM,UAAU,2BAA2B;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6qCR,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oCAAoC;IAClD,MAAM,MAAM,GAAG,2BAA2B,EAAE,CAAC;IAC7C,MAAM,MAAM,GAAG,iEAAiE,CAAC;IACjF,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC"}