prompt_objects 0.3.0 → 0.4.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/Gemfile.lock +31 -29
  4. data/exe/prompt_objects +161 -0
  5. data/frontend/package-lock.json +123 -0
  6. data/frontend/package.json +4 -0
  7. data/frontend/src/App.tsx +58 -51
  8. data/frontend/src/canvas/CanvasView.tsx +113 -0
  9. data/frontend/src/canvas/ForceLayout.ts +115 -0
  10. data/frontend/src/canvas/SceneManager.ts +587 -0
  11. data/frontend/src/canvas/canvasStore.ts +47 -0
  12. data/frontend/src/canvas/constants.ts +95 -0
  13. data/frontend/src/canvas/controls/CameraControls.ts +98 -0
  14. data/frontend/src/canvas/edges/MessageArc.ts +149 -0
  15. data/frontend/src/canvas/inspector/InspectorPanel.tsx +31 -0
  16. data/frontend/src/canvas/inspector/POInspector.tsx +262 -0
  17. data/frontend/src/canvas/inspector/ToolCallInspector.tsx +67 -0
  18. data/frontend/src/canvas/nodes/PONode.ts +249 -0
  19. data/frontend/src/canvas/nodes/ToolCallNode.ts +116 -0
  20. data/frontend/src/canvas/types.ts +24 -0
  21. data/frontend/src/components/ChatPanel.tsx +13 -5
  22. data/frontend/src/components/Header.tsx +13 -1
  23. data/frontend/src/hooks/useWebSocket.ts +246 -189
  24. data/frontend/src/index.css +48 -0
  25. data/frontend/src/store/index.ts +19 -0
  26. data/frontend/src/types/index.ts +3 -0
  27. data/lib/prompt_objects/connectors/mcp.rb +3 -6
  28. data/lib/prompt_objects/environment.rb +8 -0
  29. data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +1 -2
  30. data/lib/prompt_objects/mcp/tools/list_prompt_objects.rb +1 -2
  31. data/lib/prompt_objects/primitives/list_files.rb +1 -2
  32. data/lib/prompt_objects/prompt_object.rb +25 -7
  33. data/lib/prompt_objects/server/app.rb +9 -0
  34. data/lib/prompt_objects/server/public/assets/index-6y64NXFy.css +1 -0
  35. data/lib/prompt_objects/server/public/assets/index-xvyeb-5Z.js +4345 -0
  36. data/lib/prompt_objects/server/public/index.html +2 -2
  37. data/prompt_objects.gemspec +1 -1
  38. data/tools/thread-explorer.html +1043 -0
  39. metadata +18 -4
  40. data/lib/prompt_objects/server/public/assets/index-Bkme6COu.css +0 -1
  41. data/lib/prompt_objects/server/public/assets/index-CQ7lVDF_.js +0 -77
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 676489da9fefa02b4d187f99137469c6cc0011d99e7e43b72574c0d57b19c8a7
4
- data.tar.gz: acd7c61e837ac00485cb8286cf95caa3d2103c82d07aa5fef7ce49185a3a0f6a
3
+ metadata.gz: 92cf8b8492d42dee0adb37a4fb41692e6b228ab7a364f9176444c60cd81eb63d
4
+ data.tar.gz: 45e8828d53f7b9c4893fb6374535bbb099c77e92371a3e111bda07d9e35db93e
5
5
  SHA512:
6
- metadata.gz: dda01aa39c36088e3433894b004994b4a24b735bd6b59bb6ae510943be0f6f83931d1e45fa34051b4f49a7243b606b7ac71b8790f8e5701f9652373dff29d756
7
- data.tar.gz: e02fb7e206ced06445391d81012a61d790277f4fc0f46343e3e0391f46da58aafd3a047abd13147e0c2e8fe150284b5333759f27ab1037ccb6a7e8020d18316a
6
+ metadata.gz: 123244bee715f744b7b377bbf4e2912ed28cbb966ce6b92bb7d43a5ea053bf2460e7bb10c35acf09ab5f4cfa8f0afeeb78198be62acfbde44b86f3c5f0559deb
7
+ data.tar.gz: 15ee859102e87e971df1e38b756d776a0f913148554974fc2fe9ae79b12853cf9028793aadfaabb92aa1d74961739c97a51c258c0e9e2a38cbf77fb5f931b46d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to PromptObjects are documented in this file.
4
4
 
5
+ ## [0.4.0] - 2026-02-11
6
+
7
+ ### Added
8
+
9
+ - **Spatial Canvas** — Three.js 2D visualization at `/canvas` showing POs as glowing hexagonal nodes with force-directed layout, tool call diamonds, animated message arcs with traveling particles, and click-to-inspect side panels. Real-time updates from the same WebSocket feed as the dashboard. Zoom, pan, and keyboard shortcuts (F to fit, Escape to deselect).
10
+ - **PO-to-PO delegation broadcasting** — Server now broadcasts `po_delegation_started` and `po_delegation_completed` WebSocket events when one PO calls another. Delegated POs show as active in both the canvas (cyan glow, "called by X" status) and dashboard views. Replaces client-side inference from message history scanning.
11
+ - **Ruby 4 support** — CI now tests against Ruby 4. Fixed empty required parameter handling for compatibility. Thanks to [@radanskoric](https://github.com/radanskoric) for the contribution! ([#2](https://github.com/works-on-your-machine/prompt_objects/pull/2))
12
+
13
+ ### Fixed
14
+
15
+ - **WebSocket reconnection lifecycle** — Fixed duplicate connections on page refresh caused by zombie `onclose` handlers. Added socket identity guards, close-before-reconnect, and `handleMessageRef` pattern to prevent stale closures.
16
+ - **Stale state on disconnect** — PO statuses now reset to idle when WebSocket disconnects, preventing stuck "thinking" indicators and stale streaming content.
17
+ - **Chat input locked on disconnect** — Chat input is now only disabled when the PO is busy AND connected. Shows "Reconnecting..." indicator when disconnected instead of permanently locking.
18
+ - **Tool calls not appearing on canvas** — Tool call visualization now extracts from PO message history instead of looking for a format bus messages don't use.
19
+
20
+ ## [0.3.1] - 2025-02-08
21
+
22
+ ### Added
23
+
24
+ - **Thread Explorer** — Standalone HTML visualizer for exploring conversation thread exports. Three views: sequence diagram (swim lanes showing PO communication), timeline (flat chronological event list), and detail panel (full conversation with tool calls). Includes token cost bar, search, per-PO filtering, breadcrumb navigation, and structural event highlighting.
25
+ - **`explore` CLI command** — Open Thread Explorer from the command line. `prompt_objects explore <env>` lists root threads and opens the most recent; `--session ID` targets a specific thread. Data is embedded directly so it opens ready to go.
26
+
27
+ ### Fixed
28
+
29
+ - Delegation messages now correctly show the calling PO's name instead of "human". Previously `context.current_capability` resolved to the target PO (matching its own name), causing a fallback to "human". Now uses `context.calling_po`.
30
+ - CLI integration tests skip gracefully in CI when no LLM API key is available, instead of failing.
31
+
5
32
  ## [0.3.0] - 2025-02-05
6
33
 
7
34
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prompt_objects (0.3.0)
4
+ prompt_objects (0.4.0)
5
5
  anthropic (~> 1.0)
6
6
  async-websocket (~> 0.28)
7
7
  falcon (~> 0.50)
@@ -17,24 +17,25 @@ GEM
17
17
  specs:
18
18
  addressable (2.8.8)
19
19
  public_suffix (>= 2.0.2, < 8.0)
20
- anthropic (1.16.3)
20
+ anthropic (1.19.0)
21
+ cgi
21
22
  connection_pool
22
23
  ast (2.4.3)
23
- async (2.35.2)
24
+ async (2.36.0)
24
25
  console (~> 1.29)
25
26
  fiber-annotation
26
27
  io-event (~> 1.11)
27
28
  metrics (~> 0.12)
28
29
  traces (~> 0.18)
29
- async-container (0.27.7)
30
+ async-container (0.30.0)
30
31
  async (~> 2.22)
31
- async-container-supervisor (0.9.2)
32
+ async-container-supervisor (0.9.3)
32
33
  async-service
33
34
  io-endpoint
34
35
  memory (~> 0.7)
35
36
  memory-leak (~> 0.5)
36
37
  process-metrics
37
- async-http (0.94.0)
38
+ async-http (0.94.2)
38
39
  async (>= 2.10.2)
39
40
  async-pool (~> 0.11)
40
41
  io-endpoint (~> 0.14)
@@ -49,9 +50,9 @@ GEM
49
50
  async-http (~> 0.56)
50
51
  async-pool (0.11.1)
51
52
  async (>= 2.0)
52
- async-service (0.16.0)
53
+ async-service (0.19.0)
53
54
  async
54
- async-container (~> 0.16)
55
+ async-container (~> 0.29)
55
56
  string-format (~> 0.2)
56
57
  async-websocket (0.30.0)
57
58
  async-http (~> 0.76)
@@ -63,6 +64,7 @@ GEM
63
64
  samovar (~> 2.1)
64
65
  base64 (0.3.0)
65
66
  bigdecimal (4.0.1)
67
+ cgi (0.5.1)
66
68
  connection_pool (3.0.2)
67
69
  console (1.34.2)
68
70
  fiber-annotation
@@ -74,20 +76,20 @@ GEM
74
76
  reline (>= 0.3.8)
75
77
  erb (6.0.1)
76
78
  event_stream_parser (1.0.0)
77
- falcon (0.53.1)
79
+ falcon (0.54.2)
78
80
  async
79
81
  async-container (~> 0.20)
80
82
  async-container-supervisor (~> 0.6)
81
83
  async-http (~> 0.75)
82
84
  async-http-cache (~> 0.4)
83
- async-service (~> 0.16)
85
+ async-service (~> 0.19)
84
86
  bundler
85
87
  localhost (~> 1.1)
86
88
  openssl (>= 3.0)
87
89
  protocol-http (~> 0.31)
88
90
  protocol-rack (~> 0.7)
89
91
  samovar (~> 2.3)
90
- faraday (2.14.0)
92
+ faraday (2.14.1)
91
93
  faraday-net_http (>= 2.0, < 3.5)
92
94
  json
93
95
  logger
@@ -103,29 +105,29 @@ GEM
103
105
  fiber-storage (1.0.1)
104
106
  front_matter_parser (1.0.1)
105
107
  io-console (0.8.2)
106
- io-endpoint (0.16.0)
108
+ io-endpoint (0.17.2)
107
109
  io-event (1.14.2)
108
110
  io-stream (0.11.1)
109
- irb (1.16.0)
111
+ irb (1.17.0)
110
112
  pp (>= 0.6.0)
113
+ prism (>= 1.3.0)
111
114
  rdoc (>= 4.0.0)
112
115
  reline (>= 0.4.2)
113
- json (2.18.0)
116
+ json (2.18.1)
114
117
  json-schema (6.1.0)
115
118
  addressable (~> 2.8)
116
119
  bigdecimal (>= 3.1, < 5)
117
- json_rpc_handler (0.1.1)
118
120
  language_server-protocol (3.17.0.5)
119
121
  lint_roller (1.1.0)
120
- listen (3.9.0)
122
+ listen (3.10.0)
123
+ logger
121
124
  rb-fsevent (~> 0.10, >= 0.10.3)
122
125
  rb-inotify (~> 0.9, >= 0.9.10)
123
- localhost (1.6.0)
126
+ localhost (1.7.0)
124
127
  logger (1.7.0)
125
128
  mapping (1.1.3)
126
- mcp (0.4.0)
129
+ mcp (0.6.0)
127
130
  json-schema (>= 4.1)
128
- json_rpc_handler (~> 0.1)
129
131
  memory (0.12.0)
130
132
  bake (~> 0.15)
131
133
  console
@@ -139,25 +141,25 @@ GEM
139
141
  uri (>= 0.11.1)
140
142
  openssl (4.0.0)
141
143
  parallel (1.27.0)
142
- parser (3.3.10.0)
144
+ parser (3.3.10.1)
143
145
  ast (~> 2.4.1)
144
146
  racc
145
147
  pp (0.6.3)
146
148
  prettyprint
147
149
  prettyprint (0.2.0)
148
- prism (1.7.0)
150
+ prism (1.9.0)
149
151
  process-metrics (0.8.0)
150
152
  console (~> 1.8)
151
153
  json (~> 2)
152
154
  samovar (~> 2.1)
153
155
  protocol-hpack (1.5.1)
154
- protocol-http (0.58.0)
155
- protocol-http1 (0.36.0)
156
+ protocol-http (0.59.0)
157
+ protocol-http1 (0.37.0)
156
158
  protocol-http (~> 0.58)
157
- protocol-http2 (0.23.0)
159
+ protocol-http2 (0.24.0)
158
160
  protocol-hpack (~> 1.4)
159
161
  protocol-http (~> 0.47)
160
- protocol-rack (0.21.0)
162
+ protocol-rack (0.21.1)
161
163
  io-stream (>= 0.10)
162
164
  protocol-http (~> 0.58)
163
165
  rack (>= 1.0)
@@ -167,7 +169,7 @@ GEM
167
169
  psych (5.3.1)
168
170
  date
169
171
  stringio
170
- public_suffix (7.0.1)
172
+ public_suffix (7.0.2)
171
173
  racc (1.8.1)
172
174
  rack (3.2.4)
173
175
  rainbow (3.1.1)
@@ -175,14 +177,14 @@ GEM
175
177
  rb-fsevent (0.11.2)
176
178
  rb-inotify (0.11.1)
177
179
  ffi (~> 1.0)
178
- rdoc (7.0.3)
180
+ rdoc (7.2.0)
179
181
  erb
180
182
  psych (>= 4.0.0)
181
183
  tsort
182
184
  regexp_parser (2.11.3)
183
185
  reline (0.6.3)
184
186
  io-console (~> 0.5)
185
- rubocop (1.82.1)
187
+ rubocop (1.84.1)
186
188
  json (~> 2.3)
187
189
  language_server-protocol (~> 3.17.0.2)
188
190
  lint_roller (~> 1.1.0)
@@ -190,7 +192,7 @@ GEM
190
192
  parser (>= 3.3.0.2)
191
193
  rainbow (>= 2.2.2, < 4.0)
192
194
  regexp_parser (>= 2.9.3, < 3.0)
193
- rubocop-ast (>= 1.48.0, < 2.0)
195
+ rubocop-ast (>= 1.49.0, < 2.0)
194
196
  ruby-progressbar (~> 1.7)
195
197
  unicode-display_width (>= 2.4.0, < 4.0)
196
198
  rubocop-ast (1.49.0)
data/exe/prompt_objects CHANGED
@@ -285,6 +285,7 @@ def print_main_help
285
285
  serve <env> Run environment as a web server
286
286
  message <env> <po> "text" Send a message to a PO and print the response
287
287
  events <env> [--session ID] Show recent events from the message bus
288
+ explore [env] [--session ID] Open Thread Explorer to visualize conversations
288
289
  repl [name] [objects_dir] Start interactive REPL with a prompt object
289
290
  help Show this help message
290
291
 
@@ -297,6 +298,7 @@ def print_main_help
297
298
  prompt_objects serve my-env --open # Start and open browser
298
299
  prompt_objects message my-env solver "Hello" # Send a message
299
300
  prompt_objects message my-env solver "Hello" --json # JSON output
301
+ prompt_objects explore my-env # Visualize conversations
300
302
  HELP
301
303
  end
302
304
 
@@ -920,6 +922,163 @@ def print_events(events)
920
922
  end
921
923
  end
922
924
 
925
+ # === Explore Command ===
926
+
927
+ def run_explore(args)
928
+ options = { session_id: nil }
929
+ positional = []
930
+ skip_next = false
931
+
932
+ args.each_with_index do |arg, i|
933
+ if skip_next
934
+ skip_next = false
935
+ next
936
+ end
937
+
938
+ case arg
939
+ when "--session"
940
+ options[:session_id] = args[i + 1]
941
+ skip_next = true
942
+ when "--help", "-h"
943
+ print_explore_help
944
+ exit 0
945
+ else
946
+ positional << arg
947
+ end
948
+ end
949
+
950
+ explorer_html = File.expand_path("../tools/thread-explorer.html", __dir__)
951
+
952
+ # No args: just open the empty visualizer
953
+ unless positional[0]
954
+ open_in_browser("file://#{explorer_html}")
955
+ puts "Opened Thread Explorer in browser"
956
+ exit 0
957
+ end
958
+
959
+ env_path = resolve_environment(positional[0])
960
+ unless env_path
961
+ $stderr.puts "Error: environment '#{positional[0]}' not found"
962
+ exit 1
963
+ end
964
+
965
+ db_path = File.join(env_path, "sessions.db")
966
+ unless File.exist?(db_path)
967
+ $stderr.puts "No session data found (sessions.db not found)"
968
+ $stderr.puts "Opening empty Thread Explorer..."
969
+ open_in_browser("file://#{explorer_html}")
970
+ exit 0
971
+ end
972
+
973
+ store = PromptObjects::Session::Store.new(db_path)
974
+
975
+ if options[:session_id]
976
+ # Export a specific thread
977
+ export_and_open(store, options[:session_id], explorer_html)
978
+ else
979
+ # List sessions and let user choose, or export the most recent root thread
980
+ # Query root sessions directly (list_sessions requires po_name)
981
+ rows = store.instance_variable_get(:@db).execute(<<~SQL)
982
+ SELECT * FROM sessions
983
+ WHERE thread_type IS NULL OR thread_type = 'root'
984
+ ORDER BY created_at ASC
985
+ SQL
986
+ root_sessions = rows.map { |r| { id: r["id"], po_name: r["po_name"], name: r["name"], thread_type: r["thread_type"], created_at: r["created_at"] } }
987
+
988
+ if root_sessions.empty?
989
+ $stderr.puts "No root sessions found"
990
+ open_in_browser("file://#{explorer_html}")
991
+ exit 0
992
+ end
993
+
994
+ # Show available sessions
995
+ puts "Root threads:"
996
+ root_sessions.last(10).each_with_index do |s, i|
997
+ name = s[:name] || "(unnamed)"
998
+ po = s[:po_name] || "?"
999
+ time = s[:created_at]
1000
+ time_str = time.is_a?(Time) ? time.strftime("%Y-%m-%d %H:%M") : time.to_s[0, 16]
1001
+ puts " #{i + 1}. [#{po}] #{name} (#{time_str}) #{s[:id]}"
1002
+ end
1003
+
1004
+ puts
1005
+ puts "Opening most recent thread..."
1006
+ export_and_open(store, root_sessions.last[:id], explorer_html)
1007
+ end
1008
+
1009
+ store.close
1010
+ end
1011
+
1012
+ def export_and_open(store, session_id, explorer_html)
1013
+ data = store.export_thread_tree_json(session_id)
1014
+ unless data
1015
+ $stderr.puts "Error: session '#{session_id}' not found"
1016
+ exit 1
1017
+ end
1018
+
1019
+ json_str = JSON.generate(data)
1020
+
1021
+ # Read the explorer HTML and inject the data as auto-load
1022
+ html_content = File.read(explorer_html)
1023
+ inject_script = <<~JS
1024
+ <script>
1025
+ // Auto-load embedded data
1026
+ window.addEventListener('DOMContentLoaded', function() {
1027
+ var data = #{json_str};
1028
+ if (typeof loadData === 'function') loadData(data);
1029
+ });
1030
+ </script>
1031
+ JS
1032
+
1033
+ # Insert before </body>
1034
+ html_content = html_content.sub("</body>", "#{inject_script}</body>")
1035
+
1036
+ # Write to temp file
1037
+ require "tempfile"
1038
+ tmp = Tempfile.new(["thread-explorer-", ".html"])
1039
+ tmp.write(html_content)
1040
+ tmp.close
1041
+
1042
+ open_in_browser("file://#{tmp.path}")
1043
+ puts "Opened Thread Explorer for session #{session_id}"
1044
+
1045
+ # Keep temp file around briefly so the browser can load it
1046
+ sleep 2
1047
+ end
1048
+
1049
+ def open_in_browser(url)
1050
+ case RUBY_PLATFORM
1051
+ when /darwin/
1052
+ system("open", url)
1053
+ when /linux/
1054
+ system("xdg-open", url)
1055
+ when /mswin|mingw|cygwin/
1056
+ system("start", url)
1057
+ else
1058
+ $stderr.puts "Open this URL in your browser: #{url}"
1059
+ end
1060
+ end
1061
+
1062
+ def print_explore_help
1063
+ puts <<~HELP
1064
+ Usage: prompt_objects explore [env] [options]
1065
+
1066
+ Open the Thread Explorer to visualize conversation threads.
1067
+
1068
+ Arguments:
1069
+ env Environment name or path (optional)
1070
+
1071
+ Options:
1072
+ --session ID Open a specific thread by session ID
1073
+ --help, -h Show this help message
1074
+
1075
+ Examples:
1076
+ prompt_objects explore # Open empty explorer
1077
+ prompt_objects explore my-env # Open most recent thread
1078
+ prompt_objects explore my-env --session abc123 # Open specific thread
1079
+ HELP
1080
+ end
1081
+
923
1082
  # === Main Entry Point ===
924
1083
 
925
1084
  def run_env(args)
@@ -947,6 +1106,8 @@ def main
947
1106
  run_message(args)
948
1107
  when "events"
949
1108
  run_events(args)
1109
+ when "explore"
1110
+ run_explore(args)
950
1111
  when "help", "--help", "-h"
951
1112
  print_main_help
952
1113
  else
@@ -10,16 +10,20 @@
10
10
  "dependencies": {
11
11
  "@types/react-syntax-highlighter": "^15.5.13",
12
12
  "clsx": "^2.1.1",
13
+ "d3-force": "^3.0.0",
13
14
  "react": "^18.3.1",
14
15
  "react-dom": "^18.3.1",
15
16
  "react-markdown": "^10.1.0",
16
17
  "react-syntax-highlighter": "^16.1.0",
17
18
  "remark-gfm": "^4.0.1",
19
+ "three": "^0.182.0",
18
20
  "zustand": "^5.0.0"
19
21
  },
20
22
  "devDependencies": {
23
+ "@types/d3-force": "^3.0.10",
21
24
  "@types/react": "^18.3.12",
22
25
  "@types/react-dom": "^18.3.1",
26
+ "@types/three": "^0.182.0",
23
27
  "@vitejs/plugin-react": "^4.3.4",
24
28
  "autoprefixer": "^10.4.20",
25
29
  "postcss": "^8.4.49",
@@ -332,6 +336,13 @@
332
336
  "node": ">=6.9.0"
333
337
  }
334
338
  },
339
+ "node_modules/@dimforge/rapier3d-compat": {
340
+ "version": "0.12.0",
341
+ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
342
+ "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
343
+ "dev": true,
344
+ "license": "Apache-2.0"
345
+ },
335
346
  "node_modules/@esbuild/aix-ppc64": {
336
347
  "version": "0.25.12",
337
348
  "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
@@ -1219,6 +1230,13 @@
1219
1230
  "win32"
1220
1231
  ]
1221
1232
  },
1233
+ "node_modules/@tweenjs/tween.js": {
1234
+ "version": "23.1.3",
1235
+ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
1236
+ "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
1237
+ "dev": true,
1238
+ "license": "MIT"
1239
+ },
1222
1240
  "node_modules/@types/babel__core": {
1223
1241
  "version": "7.20.5",
1224
1242
  "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -1264,6 +1282,13 @@
1264
1282
  "@babel/types": "^7.28.2"
1265
1283
  }
1266
1284
  },
1285
+ "node_modules/@types/d3-force": {
1286
+ "version": "3.0.10",
1287
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
1288
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
1289
+ "dev": true,
1290
+ "license": "MIT"
1291
+ },
1267
1292
  "node_modules/@types/debug": {
1268
1293
  "version": "4.1.12",
1269
1294
  "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -1353,12 +1378,42 @@
1353
1378
  "@types/react": "*"
1354
1379
  }
1355
1380
  },
1381
+ "node_modules/@types/stats.js": {
1382
+ "version": "0.17.4",
1383
+ "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
1384
+ "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
1385
+ "dev": true,
1386
+ "license": "MIT"
1387
+ },
1388
+ "node_modules/@types/three": {
1389
+ "version": "0.182.0",
1390
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz",
1391
+ "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==",
1392
+ "dev": true,
1393
+ "license": "MIT",
1394
+ "dependencies": {
1395
+ "@dimforge/rapier3d-compat": "~0.12.0",
1396
+ "@tweenjs/tween.js": "~23.1.3",
1397
+ "@types/stats.js": "*",
1398
+ "@types/webxr": ">=0.5.17",
1399
+ "@webgpu/types": "*",
1400
+ "fflate": "~0.8.2",
1401
+ "meshoptimizer": "~0.22.0"
1402
+ }
1403
+ },
1356
1404
  "node_modules/@types/unist": {
1357
1405
  "version": "3.0.3",
1358
1406
  "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
1359
1407
  "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
1360
1408
  "license": "MIT"
1361
1409
  },
1410
+ "node_modules/@types/webxr": {
1411
+ "version": "0.5.24",
1412
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
1413
+ "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
1414
+ "dev": true,
1415
+ "license": "MIT"
1416
+ },
1362
1417
  "node_modules/@ungap/structured-clone": {
1363
1418
  "version": "1.3.0",
1364
1419
  "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
@@ -1386,6 +1441,13 @@
1386
1441
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1387
1442
  }
1388
1443
  },
1444
+ "node_modules/@webgpu/types": {
1445
+ "version": "0.1.69",
1446
+ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz",
1447
+ "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==",
1448
+ "dev": true,
1449
+ "license": "BSD-3-Clause"
1450
+ },
1389
1451
  "node_modules/any-promise": {
1390
1452
  "version": "1.3.0",
1391
1453
  "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -1705,6 +1767,47 @@
1705
1767
  "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1706
1768
  "license": "MIT"
1707
1769
  },
1770
+ "node_modules/d3-dispatch": {
1771
+ "version": "3.0.1",
1772
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
1773
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
1774
+ "license": "ISC",
1775
+ "engines": {
1776
+ "node": ">=12"
1777
+ }
1778
+ },
1779
+ "node_modules/d3-force": {
1780
+ "version": "3.0.0",
1781
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
1782
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
1783
+ "license": "ISC",
1784
+ "dependencies": {
1785
+ "d3-dispatch": "1 - 3",
1786
+ "d3-quadtree": "1 - 3",
1787
+ "d3-timer": "1 - 3"
1788
+ },
1789
+ "engines": {
1790
+ "node": ">=12"
1791
+ }
1792
+ },
1793
+ "node_modules/d3-quadtree": {
1794
+ "version": "3.0.1",
1795
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
1796
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
1797
+ "license": "ISC",
1798
+ "engines": {
1799
+ "node": ">=12"
1800
+ }
1801
+ },
1802
+ "node_modules/d3-timer": {
1803
+ "version": "3.0.1",
1804
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
1805
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
1806
+ "license": "ISC",
1807
+ "engines": {
1808
+ "node": ">=12"
1809
+ }
1810
+ },
1708
1811
  "node_modules/debug": {
1709
1812
  "version": "4.4.3",
1710
1813
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1911,6 +2014,13 @@
1911
2014
  "url": "https://github.com/sponsors/wooorm"
1912
2015
  }
1913
2016
  },
2017
+ "node_modules/fflate": {
2018
+ "version": "0.8.2",
2019
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
2020
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
2021
+ "dev": true,
2022
+ "license": "MIT"
2023
+ },
1914
2024
  "node_modules/fill-range": {
1915
2025
  "version": "7.1.1",
1916
2026
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -2624,6 +2734,13 @@
2624
2734
  "node": ">= 8"
2625
2735
  }
2626
2736
  },
2737
+ "node_modules/meshoptimizer": {
2738
+ "version": "0.22.0",
2739
+ "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz",
2740
+ "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==",
2741
+ "dev": true,
2742
+ "license": "MIT"
2743
+ },
2627
2744
  "node_modules/micromark": {
2628
2745
  "version": "4.0.2",
2629
2746
  "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
@@ -4006,6 +4123,12 @@
4006
4123
  "node": ">=0.8"
4007
4124
  }
4008
4125
  },
4126
+ "node_modules/three": {
4127
+ "version": "0.182.0",
4128
+ "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz",
4129
+ "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==",
4130
+ "license": "MIT"
4131
+ },
4009
4132
  "node_modules/tinyglobby": {
4010
4133
  "version": "0.2.15",
4011
4134
  "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -12,16 +12,20 @@
12
12
  "dependencies": {
13
13
  "@types/react-syntax-highlighter": "^15.5.13",
14
14
  "clsx": "^2.1.1",
15
+ "d3-force": "^3.0.0",
15
16
  "react": "^18.3.1",
16
17
  "react-dom": "^18.3.1",
17
18
  "react-markdown": "^10.1.0",
18
19
  "react-syntax-highlighter": "^16.1.0",
19
20
  "remark-gfm": "^4.0.1",
21
+ "three": "^0.182.0",
20
22
  "zustand": "^5.0.0"
21
23
  },
22
24
  "devDependencies": {
25
+ "@types/d3-force": "^3.0.10",
23
26
  "@types/react": "^18.3.12",
24
27
  "@types/react-dom": "^18.3.1",
28
+ "@types/three": "^0.182.0",
25
29
  "@vitejs/plugin-react": "^4.3.4",
26
30
  "autoprefixer": "^10.4.20",
27
31
  "postcss": "^8.4.49",