auto-coder-web 0.1.6__tar.gz → 0.1.8__tar.gz

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 (38) hide show
  1. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/PKG-INFO +4 -2
  2. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/README.md +1 -0
  3. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/auto_coder_runner.py +51 -6
  4. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/proxy.py +71 -1
  5. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/terminal.py +124 -111
  6. auto_coder_web-0.1.8/src/auto_coder_web/version.py +1 -0
  7. auto_coder_web-0.1.8/src/auto_coder_web/web/asset-manifest.json +15 -0
  8. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/index.html +1 -1
  9. auto_coder_web-0.1.8/src/auto_coder_web/web/static/css/main.7f8664fc.css +6 -0
  10. auto_coder_web-0.1.8/src/auto_coder_web/web/static/css/main.7f8664fc.css.map +1 -0
  11. auto_coder_web-0.1.6/src/auto_coder_web/web/static/js/main.80a0dc24.js → auto_coder_web-0.1.8/src/auto_coder_web/web/static/js/main.5b5d887a.js +3 -3
  12. auto_coder_web-0.1.8/src/auto_coder_web/web/static/js/main.5b5d887a.js.map +1 -0
  13. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web.egg-info/PKG-INFO +4 -2
  14. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web.egg-info/SOURCES.txt +5 -5
  15. auto_coder_web-0.1.8/src/auto_coder_web.egg-info/requires.txt +8 -0
  16. auto_coder_web-0.1.6/src/auto_coder_web/version.py +0 -1
  17. auto_coder_web-0.1.6/src/auto_coder_web/web/asset-manifest.json +0 -15
  18. auto_coder_web-0.1.6/src/auto_coder_web/web/static/css/main.d3950267.css +0 -6
  19. auto_coder_web-0.1.6/src/auto_coder_web/web/static/css/main.d3950267.css.map +0 -1
  20. auto_coder_web-0.1.6/src/auto_coder_web/web/static/js/main.80a0dc24.js.map +0 -1
  21. auto_coder_web-0.1.6/src/auto_coder_web.egg-info/requires.txt +0 -5
  22. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/setup.cfg +0 -0
  23. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/setup.py +0 -0
  24. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/__init__.py +0 -0
  25. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/file_group.py +0 -0
  26. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/file_manager.py +0 -0
  27. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/json_file_storage.py +0 -0
  28. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/favicon.ico +0 -0
  29. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/logo192.png +0 -0
  30. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/logo512.png +0 -0
  31. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/manifest.json +0 -0
  32. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/robots.txt +0 -0
  33. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/static/js/453.8ab44547.chunk.js +0 -0
  34. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web/web/static/js/453.8ab44547.chunk.js.map +0 -0
  35. /auto_coder_web-0.1.6/src/auto_coder_web/web/static/js/main.80a0dc24.js.LICENSE.txt → /auto_coder_web-0.1.8/src/auto_coder_web/web/static/js/main.5b5d887a.js.LICENSE.txt +0 -0
  36. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web.egg-info/dependency_links.txt +0 -0
  37. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web.egg-info/entry_points.txt +0 -0
  38. {auto_coder_web-0.1.6 → auto_coder_web-0.1.8}/src/auto_coder_web.egg-info/top_level.txt +0 -0
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto_coder_web
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: auto-coder.web: A Python Project
5
5
  Author: allwefantasy
6
6
  Classifier: Programming Language :: Python :: 3.9
7
7
  Classifier: Programming Language :: Python :: 3.10
8
8
  Classifier: Programming Language :: Python :: 3.11
9
9
  Description-Content-Type: text/markdown
10
- Requires-Dist: auto-coder==0.1.195
10
+ Requires-Dist: auto-coder
11
11
  Requires-Dist: aiofiles
12
12
  Requires-Dist: psutil
13
13
  Requires-Dist: sse-starlette
14
14
  Requires-Dist: websockets
15
+ Requires-Dist: pywinpty; sys_platform == "win32"
15
16
 
16
17
  # auto-coder.web
17
18
 
@@ -45,6 +46,7 @@ docker run \
45
46
  -e BASE_URL=https://api.deepseek.com/v1 \
46
47
  -e API_KEY=$MODEL_DEEPSEEK_TOKEN \
47
48
  -e MODEL=deepseek-chat \
49
+ -p 8006:8006 \
48
50
  -p 8007:8007 \
49
51
  -p 8265:8265 \
50
52
  -v <你的项目>:/app/work \
@@ -30,6 +30,7 @@ docker run \
30
30
  -e BASE_URL=https://api.deepseek.com/v1 \
31
31
  -e API_KEY=$MODEL_DEEPSEEK_TOKEN \
32
32
  -e MODEL=deepseek-chat \
33
+ -p 8006:8006 \
33
34
  -p 8007:8007 \
34
35
  -p 8265:8265 \
35
36
  -v <你的项目>:/app/work \
@@ -100,7 +100,8 @@ class AutoCoderRunner:
100
100
  if group_name not in self.memory["current_files"]["groups"]:
101
101
  return None
102
102
  del self.memory["current_files"]["groups"][group_name]
103
- del self.memory["current_files"]["groups_info"][group_name]
103
+ if group_name in self.memory["current_files"]["groups_info"]:
104
+ del self.memory["current_files"]["groups_info"][group_name]
104
105
  self.save_memory()
105
106
  return {"message": f"Removed group: {group_name}"}
106
107
 
@@ -186,7 +187,7 @@ class AutoCoderRunner:
186
187
  matched_files.append(abs_path)
187
188
  else:
188
189
  is_added = False
189
- for root, dirs, files in os.walk(project_root):
190
+ for root, dirs, files in os.walk(project_root, followlinks=True):
190
191
  dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
191
192
  if pattern in files:
192
193
  matched_files.append(os.path.join(root, pattern))
@@ -351,6 +352,9 @@ class AutoCoderRunner:
351
352
  self.memory["conversation"].append({"role": "user", "content": query})
352
353
  conf = self.memory.get("conf", {})
353
354
  current_files = self.memory["current_files"]["files"]
355
+ current_groups = self.memory["current_files"].get("current_groups", [])
356
+ groups = self.memory["current_files"].get("groups", {})
357
+ groups_info = self.memory["current_files"].get("groups_info", {})
354
358
  request_id = str(uuid.uuid4())
355
359
 
356
360
  def process():
@@ -377,6 +381,19 @@ class AutoCoderRunner:
377
381
  converted_value = self.convert_config_value(key, value)
378
382
  if converted_value is not None:
379
383
  yaml_config[key] = converted_value
384
+
385
+ if current_groups:
386
+ active_groups_context = "下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\n"
387
+ for group in current_groups:
388
+ group_files = groups.get(group, [])
389
+ query_prefix = groups_info.get(group, {}).get("query_prefix", "")
390
+ active_groups_context += f"组名: {group}\n"
391
+ active_groups_context += f"文件列表:\n"
392
+ for file in group_files:
393
+ active_groups_context += f"- {file}\n"
394
+ active_groups_context += f"组描述: {query_prefix}\n\n"
395
+
396
+ yaml_config["context"] = active_groups_context + "\n"
380
397
 
381
398
  yaml_content = self.convert_yaml_config_to_str(yaml_config)
382
399
  execute_file = os.path.join("actions", latest_yaml_file)
@@ -502,7 +519,7 @@ class AutoCoderRunner:
502
519
 
503
520
  def execute_ask():
504
521
  cmd = ["agent", "chat", "--file",
505
- execute_file, "--request_id", request_id]
522
+ execute_file, "--request_id", request_id, "--skip_events"]
506
523
  if is_new:
507
524
  cmd.append("--new_session")
508
525
  auto_coder_main(cmd)
@@ -571,11 +588,11 @@ class AutoCoderRunner:
571
588
 
572
589
  if "Successfully reverted changes" in result:
573
590
  os.remove(file_path)
574
- return {"message": "Reverted the last chat action successfully"}
591
+ return {"message": "Reverted the last chat action successfully", "status": True}
575
592
  else:
576
- return {"message": result}
593
+ return {"message": result, "status": False}
577
594
  else:
578
- return {"message": "No previous chat action found to revert."}
595
+ return {"message": "No previous chat action found to revert.", "status": False}
579
596
 
580
597
  async def index_build(self) -> Dict[str, str]:
581
598
  request_id = str(uuid.uuid4())
@@ -702,3 +719,31 @@ query: |
702
719
  docs.append(f.read())
703
720
 
704
721
  return docs
722
+
723
+ def get_last_yaml_info(self) -> Dict[str, Any]:
724
+ last_yaml_file = get_last_yaml_file("actions")
725
+ if last_yaml_file:
726
+ file_path = os.path.join("actions", last_yaml_file)
727
+ try:
728
+ with open(file_path, 'r') as f:
729
+ yaml_content = yaml.safe_load(f)
730
+ return {
731
+ "status": True,
732
+ "file_name": last_yaml_file,
733
+ "content": yaml_content
734
+ }
735
+ except yaml.YAMLError as e:
736
+ return {
737
+ "status": False,
738
+ "message": f"Error parsing YAML file: {str(e)}"
739
+ }
740
+ except Exception as e:
741
+ return {
742
+ "status": False,
743
+ "message": f"Error reading YAML file: {str(e)}"
744
+ }
745
+ else:
746
+ return {
747
+ "status": False,
748
+ "message": "No previous chat action found."
749
+ }
@@ -25,7 +25,7 @@ from autocoder.utils.log_capture import LogCapture
25
25
  from typing import Optional, Dict, List, Any
26
26
  from .terminal import terminal_manager
27
27
  from autocoder.common import AutoCoderArgs
28
-
28
+ import json
29
29
 
30
30
  class EventGetRequest(BaseModel):
31
31
  request_id: str
@@ -48,6 +48,11 @@ class CompletionResponse(BaseModel):
48
48
  completions: List[CompletionItem]
49
49
 
50
50
 
51
+ class ChatList(BaseModel):
52
+ name: str
53
+ messages: List[Dict[str, Any]]
54
+
55
+
51
56
  def check_environment():
52
57
  """Check and initialize the required environment"""
53
58
  console = Console()
@@ -539,6 +544,71 @@ class ProxyServer:
539
544
  async def get_terminal_logs(request_id: str):
540
545
  return self.auto_coder_runner.get_logs(request_id)
541
546
 
547
+ @self.app.get("/api/last-yaml")
548
+ async def get_last_yaml():
549
+ """Get information about the last YAML file"""
550
+ return JSONResponse(content=self.auto_coder_runner.get_last_yaml_info())
551
+
552
+ @self.app.post("/api/chat-lists/save")
553
+ async def save_chat_list(chat_list: ChatList):
554
+ try:
555
+ chat_lists_dir = os.path.join(".auto-coder","auto-coder.web", "chat-lists")
556
+ os.makedirs(chat_lists_dir, exist_ok=True)
557
+
558
+ file_path = os.path.join(chat_lists_dir, f"{chat_list.name}.json")
559
+ async with aiofiles.open(file_path, 'w') as f:
560
+ await f.write(json.dumps({"messages": chat_list.messages}, indent=2))
561
+ return {"status": "success", "message": f"Chat list {chat_list.name} saved successfully"}
562
+ except Exception as e:
563
+ raise HTTPException(status_code=500, detail=str(e))
564
+
565
+ @self.app.get("/api/chat-lists")
566
+ async def get_chat_lists():
567
+ try:
568
+ chat_lists_dir = os.path.join(".auto-coder","auto-coder.web", "chat-lists")
569
+ os.makedirs(chat_lists_dir, exist_ok=True)
570
+
571
+ # Get files with their modification times
572
+ chat_lists = []
573
+ for file in os.listdir(chat_lists_dir):
574
+ if file.endswith('.json'):
575
+ file_path = os.path.join(chat_lists_dir, file)
576
+ mod_time = os.path.getmtime(file_path)
577
+ chat_lists.append((file[:-5], mod_time)) # Store tuple of (name, mod_time)
578
+
579
+ # Sort by modification time (newest first)
580
+ chat_lists.sort(key=lambda x: x[1], reverse=True)
581
+
582
+ # Return only the chat list names
583
+ return {"chat_lists": [name for name, _ in chat_lists]}
584
+ except Exception as e:
585
+ raise HTTPException(status_code=500, detail=str(e))
586
+
587
+ @self.app.get("/api/chat-lists/{name}")
588
+ async def get_chat_list(name: str):
589
+ try:
590
+ file_path = os.path.join(".auto-coder","auto-coder.web", "chat-lists", f"{name}.json")
591
+ if not os.path.exists(file_path):
592
+ raise HTTPException(status_code=404, detail=f"Chat list {name} not found")
593
+
594
+ async with aiofiles.open(file_path, 'r') as f:
595
+ content = await f.read()
596
+ return json.loads(content)
597
+ except Exception as e:
598
+ raise HTTPException(status_code=500, detail=str(e))
599
+
600
+ @self.app.delete("/api/chat-lists/{name}")
601
+ async def delete_chat_list(name: str):
602
+ try:
603
+ file_path = os.path.join(".auto-coder","auto-coder.web", "chat-lists", f"{name}.json")
604
+ if not os.path.exists(file_path):
605
+ raise HTTPException(status_code=404, detail=f"Chat list {name} not found")
606
+
607
+ os.remove(file_path)
608
+ return {"status": "success", "message": f"Chat list {name} deleted successfully"}
609
+ except Exception as e:
610
+ raise HTTPException(status_code=500, detail=str(e))
611
+
542
612
 
543
613
  def main():
544
614
  parser = argparse.ArgumentParser(description="Proxy Server")
@@ -1,84 +1,90 @@
1
1
  from fastapi import WebSocket
2
2
  import asyncio
3
3
  import websockets
4
- import pty
5
4
  import os
6
5
  import json
7
6
  import struct
8
- import fcntl
9
- import termios
10
7
  import signal
11
8
  from typing import Dict, Optional
12
9
  import threading
13
10
  import select
14
11
  import psutil
15
12
  import time
13
+ import sys
14
+ import platform
16
15
 
16
+ # 为不同平台选择合适的终端库
17
+ if platform.system() == 'Windows':
18
+ import winpty
19
+ else:
20
+ import pty
21
+ import fcntl
22
+ import termios
17
23
 
18
24
  class TerminalSession:
19
- def __init__(self, websocket: WebSocket, shell: str = '/bin/bash'):
25
+ def __init__(self, websocket: WebSocket, shell: str = None):
20
26
  self.websocket = websocket
21
- self.shell = shell
27
+ # 根据平台选择默认shell
28
+ if shell is None:
29
+ if platform.system() == 'Windows':
30
+ self.shell = 'cmd.exe'
31
+ else:
32
+ self.shell = '/bin/bash'
33
+ else:
34
+ self.shell = shell
35
+
22
36
  self.fd: Optional[int] = None
23
37
  self.pid: Optional[int] = None
24
38
  self.running = False
25
39
  self._lock = threading.Lock()
26
40
  self._last_heartbeat = time.time()
41
+ self.platform = platform.system()
42
+ self.pty = None # 用于Windows的winpty实例
27
43
 
28
44
  async def start(self):
29
45
  """Start the terminal session"""
30
- # Fork a new process for the shell
31
- self.pid, self.fd = pty.fork()
32
-
33
- if self.pid == 0: # Child process
34
- # Execute the shell
35
- env = os.environ.copy()
36
- env["TERM"] = "xterm-256color"
37
- os.execvpe(self.shell, [self.shell], env)
38
- else: # Parent process
39
- self.running = True
40
- asyncio.create_task(self._handle_io())
41
- # await self._handle_io()
42
-
43
- def resize(self, rows: int, cols: int):
44
- """Resize the terminal"""
45
- if self.fd is not None:
46
- # Get the current window size
47
- size = struct.pack("HHHH", rows, cols, 0, 0)
48
- # Set new window size
49
- fcntl.ioctl(self.fd, termios.TIOCSWINSZ, size)
50
-
51
- async def _send_heartbeat(self):
52
- while self.running:
46
+ if self.platform == 'Windows':
47
+ # Windows下使用winpty
53
48
  try:
54
- # Shorter heartbeat timeout
55
- if time.time() - self._last_heartbeat > 15: # Reduced from 30 to 15 seconds
56
- print(
57
- "No heartbeat received for 15 seconds, initiating reconnection")
58
- self.running = False
59
- try:
60
- await self.websocket.close(code=1000, reason="Heartbeat timeout")
61
- except Exception:
62
- pass
63
- break
64
-
65
- if self.websocket.client_state.CONNECTED:
66
- await self.websocket.send_json({"type": "heartbeat"})
67
- else:
68
- print("WebSocket disconnected, stopping heartbeat")
69
- break
70
-
71
- await asyncio.sleep(5) # Reduced from 15 to 5 seconds
72
-
73
- except websockets.exceptions.ConnectionClosed:
74
- print("Connection closed normally during heartbeat")
75
- break
49
+ self.pty = winpty.PTY(
50
+ cols=80,
51
+ rows=24
52
+ )
53
+ # 在Windows下,pid就是process.pid
54
+ self.pid = self.pty.spawn(self.shell)
55
+ self.fd = self.pty.fd # winpty提供了类似的文件描述符
56
+ self.running = True
57
+ asyncio.create_task(self._handle_io())
76
58
  except Exception as e:
77
- print(f"Error in heartbeat: {str(e)}")
78
- if not self.running:
79
- break
80
- await asyncio.sleep(1) # Brief pause before retry
81
- continue
59
+ print(f"Failed to start Windows terminal: {e}")
60
+ raise
61
+ else:
62
+ # Unix系统使用原有的pty
63
+ self.pid, self.fd = pty.fork()
64
+
65
+ if self.pid == 0: # Child process
66
+ # Execute the shell
67
+ env = os.environ.copy()
68
+ env["TERM"] = "xterm-256color"
69
+ os.execvpe(self.shell, [self.shell], env)
70
+ else: # Parent process
71
+ self.running = True
72
+ asyncio.create_task(self._handle_io())
73
+
74
+ def resize(self, rows: int, cols: int):
75
+ """Resize the terminal"""
76
+ if not self.running:
77
+ return
78
+
79
+ if self.platform == 'Windows':
80
+ if self.pty:
81
+ self.pty.set_size(rows, cols)
82
+ else:
83
+ if self.fd is not None:
84
+ # Get the current window size
85
+ size = struct.pack("HHHH", rows, cols, 0, 0)
86
+ # Set new window size
87
+ fcntl.ioctl(self.fd, termios.TIOCSWINSZ, size)
82
88
 
83
89
  async def _handle_io(self):
84
90
  """Handle I/O between PTY and WebSocket"""
@@ -87,18 +93,29 @@ class TerminalSession:
87
93
  def _read_pty(fd, size=1024):
88
94
  """Synchronous PTY read with timeout"""
89
95
  try:
90
- r, _, _ = select.select([fd], [], [], 0.1)
91
- if r:
92
- try:
93
- data = os.read(fd, size)
94
- return data if data else None # 返回None表示EOF
95
- except (OSError, EOFError) as e:
96
- print(f"Error reading PTY: {e}")
97
- return None
98
- return b'' # 没有数据可读但不是错误
96
+ if self.platform == 'Windows':
97
+ # Windows下使用winpty的读取方法
98
+ if self.pty:
99
+ try:
100
+ data = self.pty.read(size)
101
+ return data.encode('utf-8') if isinstance(data, str) else data
102
+ except Exception as e:
103
+ print(f"Error reading from winpty: {e}")
104
+ return None
105
+ else:
106
+ # Unix系统使用select
107
+ r, _, _ = select.select([fd], [], [], 0.1)
108
+ if r:
109
+ try:
110
+ data = os.read(fd, size)
111
+ return data if data else None
112
+ except (OSError, EOFError) as e:
113
+ print(f"Error reading PTY: {e}")
114
+ return None
115
+ return b''
99
116
  except Exception as e:
100
117
  print(f"Fatal error in PTY read: {e}")
101
- return None # 严重错误
118
+ return None
102
119
 
103
120
  async def _safe_send(data: bytes):
104
121
  """Safely send data to websocket with error handling"""
@@ -113,8 +130,8 @@ class TerminalSession:
113
130
  print(f"WebSocket send error: {e}")
114
131
  return False
115
132
 
116
- read_errors = 0 # 跟踪连续读取错误
117
- MAX_READ_ERRORS = 3 # 最大允许的连续读取错误
133
+ read_errors = 0
134
+ MAX_READ_ERRORS = 3
118
135
 
119
136
  try:
120
137
  while self.running:
@@ -126,23 +143,21 @@ class TerminalSession:
126
143
  if not self.running:
127
144
  break
128
145
 
129
- if data is None: # 严重错误或EOF
146
+ if data is None:
130
147
  read_errors += 1
131
148
  if read_errors >= MAX_READ_ERRORS:
132
- print(
133
- f"Too many PTY read errors ({read_errors}), stopping")
149
+ print(f"Too many PTY read errors ({read_errors}), stopping")
134
150
  break
135
- await asyncio.sleep(0.1) # 错误后短暂等待
151
+ await asyncio.sleep(0.1)
136
152
  continue
137
153
 
138
- read_errors = 0 # 重置错误计数
154
+ read_errors = 0
139
155
 
140
- if data: # 有实际数据
141
- print(f"Received data from PTY: {data.decode('utf-8', errors='replace')}")
156
+ if data:
142
157
  if not await _safe_send(data):
143
158
  break
144
159
 
145
- await asyncio.sleep(0.001) # 防止CPU过度使用
160
+ await asyncio.sleep(0.001)
146
161
 
147
162
  except Exception as e:
148
163
  print(f"IO handling error: {e}")
@@ -154,45 +169,48 @@ class TerminalSession:
154
169
  except Exception as e:
155
170
  print(f"Fatal error in IO handling: {e}")
156
171
  finally:
157
- self.running = False # 确保设置运行状态
172
+ self.running = False
158
173
  print("IO handling stopped")
159
- # 确保清理
160
- if self.fd is not None:
161
- try:
162
- os.close(self.fd)
163
- except:
164
- pass
165
- self.fd = None
174
+ self.cleanup()
166
175
 
167
176
  def write(self, data: str):
168
177
  """Write data to the terminal"""
169
- if self.fd is not None:
170
- try:
171
- print(f"Writing data to pty: {data}")
172
- encoded_data = data.encode('utf-8')
173
- bytes_written = os.write(self.fd, encoded_data)
174
- if bytes_written != len(encoded_data):
175
- print(
176
- f"Warning: Not all bytes written. Expected {len(encoded_data)}, wrote {bytes_written}")
177
- except Exception as e:
178
- print(f"Error writing to terminal: {e}")
178
+ if not self.running:
179
+ return
180
+
181
+ try:
182
+ encoded_data = data.encode('utf-8')
183
+ if self.platform == 'Windows':
184
+ if self.pty:
185
+ self.pty.write(data) # winpty接受字符串输入
186
+ else:
187
+ if self.fd is not None:
188
+ os.write(self.fd, encoded_data)
189
+ except Exception as e:
190
+ print(f"Error writing to terminal: {e}")
179
191
 
180
192
  def cleanup(self):
181
193
  """Clean up the terminal session"""
182
194
  print("Cleaning up terminal session...")
183
195
  self.running = False
184
- if self.pid:
185
- try:
186
- os.kill(self.pid, signal.SIGTERM)
187
- except ProcessLookupError:
188
- pass
189
- if self.fd is not None:
190
- try:
191
- os.close(self.fd)
192
- except OSError:
193
- # File descriptor may already be closed
194
- pass
195
-
196
+
197
+ if self.platform == 'Windows':
198
+ if self.pty:
199
+ try:
200
+ self.pty.close()
201
+ except Exception as e:
202
+ print(f"Error closing winpty: {e}")
203
+ else:
204
+ if self.pid:
205
+ try:
206
+ os.kill(self.pid, signal.SIGTERM)
207
+ except ProcessLookupError:
208
+ pass
209
+ if self.fd is not None:
210
+ try:
211
+ os.close(self.fd)
212
+ except OSError:
213
+ pass
196
214
 
197
215
  class TerminalManager:
198
216
  def __init__(self):
@@ -200,9 +218,6 @@ class TerminalManager:
200
218
 
201
219
  async def create_session(self, websocket: WebSocket, session_id: str):
202
220
  """Create a new terminal session"""
203
-
204
- # if self.sessions:
205
- # return list(self.sessions.values())[-1]
206
221
  if session_id in self.sessions:
207
222
  await self.close_session(session_id)
208
223
 
@@ -237,7 +252,6 @@ class TerminalManager:
237
252
  else:
238
253
  session.write(data)
239
254
  except json.JSONDecodeError:
240
- # 如果不是JSON,就当作普通输入处理
241
255
  session.write(data)
242
256
  except RuntimeError as e:
243
257
  if "WebSocket is not connected" in str(e):
@@ -255,5 +269,4 @@ class TerminalManager:
255
269
  await self.close_session(session_id)
256
270
  raise
257
271
 
258
-
259
- terminal_manager = TerminalManager()
272
+ terminal_manager = TerminalManager()
@@ -0,0 +1 @@
1
+ __version__ = "0.1.8"
@@ -0,0 +1,15 @@
1
+ {
2
+ "files": {
3
+ "main.css": "/static/css/main.7f8664fc.css",
4
+ "main.js": "/static/js/main.5b5d887a.js",
5
+ "static/js/453.8ab44547.chunk.js": "/static/js/453.8ab44547.chunk.js",
6
+ "index.html": "/index.html",
7
+ "main.7f8664fc.css.map": "/static/css/main.7f8664fc.css.map",
8
+ "main.5b5d887a.js.map": "/static/js/main.5b5d887a.js.map",
9
+ "453.8ab44547.chunk.js.map": "/static/js/453.8ab44547.chunk.js.map"
10
+ },
11
+ "entrypoints": [
12
+ "static/css/main.7f8664fc.css",
13
+ "static/js/main.5b5d887a.js"
14
+ ]
15
+ }
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.80a0dc24.js"></script><link href="/static/css/main.d3950267.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.5b5d887a.js"></script><link href="/static/css/main.7f8664fc.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
@@ -0,0 +1,6 @@
1
+ /*
2
+ ! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com
3
+ */body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{border:0;height:0;left:-9999em;margin:0;opacity:0;overflow:hidden;padding:0;position:absolute;resize:none;top:0;white-space:nowrap;width:0;z-index:-5}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;bottom:0;cursor:default;left:0;overflow-y:scroll;position:absolute;right:0;top:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{left:0;position:absolute;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;left:-9999em;line-height:normal;position:absolute;top:0;visibility:hidden}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{bottom:0;color:#0000;left:0;pointer-events:none;position:absolute;right:0;top:0;z-index:10}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{height:1px;left:-9999px;overflow:hidden;position:absolute;width:1px}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:double underline;text-decoration:double underline}.xterm-underline-3{-webkit-text-decoration:wavy underline;text-decoration:wavy underline}.xterm-underline-4{-webkit-text-decoration:dotted underline;text-decoration:dotted underline}.xterm-underline-5{-webkit-text-decoration:dashed underline;text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{position:absolute;z-index:6}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{pointer-events:none;position:absolute;right:0;top:0;z-index:8}.xterm-decoration-top{position:relative;z-index:2}.split-container{display:flex;height:100%;width:100%}.gutter{position:relative}.gutter.gutter-horizontal{cursor:col-resize}.gutter:after{background-color:#9ca3af;border-radius:2px;content:"";height:20px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:4px}.gutter:hover:after{background-color:#f3f4f6}.split-container>div{height:100%;min-width:0;overflow:hidden}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*
4
+ ! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com
5
+ */*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.-right-4{right:-1rem}.left-2{left:.5rem}.top-1\/2{top:50%}.top-2{top:.5rem}.z-10{z-index:10}.z-50{z-index:50}.my-3{margin-bottom:.75rem;margin-top:.75rem}.-ml-1{margin-left:-.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.block{display:block}.flex{display:flex}.hidden{display:none}.h-2{height:.5rem}.h-4{height:1rem}.h-48{height:12rem}.h-\[1px\]{height:1px}.h-full{height:100%}.h-screen{height:100vh}.min-h-\[180px\]{min-height:180px}.w-2{width:.5rem}.w-4{width:1rem}.w-48{width:12rem}.w-60{width:15rem}.w-64{width:16rem}.w-80{width:20rem}.w-96{width:24rem}.w-\[50px\]{width:50px}.w-full{width:100%}.max-w-\[80\%\]{max-width:80%}.flex-1{flex:1 1}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.animate-bounce{animation:bounce 1s infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-0{border-width:0}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}.border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.bg-\[\#1e1e1e\]{--tw-bg-opacity:1;background-color:rgb(30 30 30/var(--tw-bg-opacity))}.bg-\[\#1f1f1f\]{--tw-bg-opacity:1;background-color:rgb(31 31 31/var(--tw-bg-opacity))}.bg-\[\#2d2d2d\]{--tw-bg-opacity:1;background-color:rgb(45 45 45/var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-gray-700\/50{background-color:#37415180}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-800\/60{background-color:#1f293799}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from:#3b82f6 var(--tw-gradient-from-position);--tw-gradient-to:#3b82f600 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-indigo-600{--tw-gradient-to:#4f46e5 var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-2\.5{padding-bottom:.625rem;padding-top:.625rem}.pl-4{padding-left:1rem}.pt-2{padding-top:.5rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xs{font-size:.625rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}.text-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-blue-500\/20{--tw-shadow-color:#3b82f633;--tw-shadow:var(--tw-shadow-colored)}.shadow-gray-500\/20{--tw-shadow-color:#6b728033;--tw-shadow:var(--tw-shadow-colored)}.shadow-indigo-500\/20{--tw-shadow-color:#6366f133;--tw-shadow:var(--tw-shadow-colored)}.outline-none{outline:2px solid #0000;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.delay-100{transition-delay:.1s}.delay-200{transition-delay:.2s}.duration-300{transition-duration:.3s}body,html{background-color:#1a1a1a;margin:0}#root,body,html{height:100%}::-webkit-scrollbar{height:8px;width:8px}::-webkit-scrollbar-track{background:#2d2d2d}::-webkit-scrollbar-thumb{background:#888;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#555}.custom-select .ant-select-selector{background-color:#374151!important;border-color:#4b5563!important;color:#fff!important}.custom-select .ant-select-selection-placeholder{color:#9ca3af!important}.custom-select .ant-select-selection-item{background-color:#4b5563!important;border-color:#6b7280!important;color:#fff!important}.custom-select .ant-select-dropdown{background-color:#374151!important;border:1px solid #4b5563!important}.custom-select .ant-select-item{color:#fff!important}.custom-select .ant-select-item-option-active{background-color:#4b5563!important}.custom-select .ant-select-item-option-selected{background-color:#6b7280!important}.custom-select .ant-select-selection-search input{color:#fff!important}.custom-select .ant-select-clear{background-color:initial!important;color:#9ca3af!important}.custom-select .ant-select-arrow{color:#9ca3af!important}.split{display:flex;flex-direction:row}.gutter{background-color:#4b5563;cursor:col-resize}.gutter:hover{background-color:#6b7280}.custom-input{background-color:#1f2937!important;border-color:#4b5563!important;color:#e5e7eb!important}.custom-input:focus,.custom-input:hover{border-color:#6366f1!important;box-shadow:0 0 0 2px #6366f133!important}.custom-input input{background-color:initial!important;color:#e5e7eb!important}.custom-input .anticon{color:#9ca3af!important}.dark-mode-table,.dark-mode-table .ant-table{background-color:initial!important}.dark-mode-table .ant-table-thead>tr>th{background-color:#374151!important}.dark-mode-table .ant-table-tbody>tr>td,.dark-mode-table .ant-table-thead>tr>th{border-bottom:1px solid #4b5563!important;color:#fff!important}.dark-mode-table .ant-table-tbody>tr:hover>td{background-color:#4b5563!important}.dark-mode-table .ant-table-row-expand-icon{background-color:#4b5563!important;border-color:#6b7280!important;color:#fff!important}.dark-mode-table .nested-table{margin:0!important;padding:0!important}.dark-mode-table .nested-table .ant-table-tbody>tr>td{background-color:#1f2937!important}.dark-mode-table .nested-table .ant-table-tbody>tr:hover>td{background-color:#374151!important}.hover\:-translate-y-0\.5:hover{--tw-translate-y:-0.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.hover\:bg-gray-700\/80:hover{background-color:#374151cc}.hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}.hover\:from-blue-600:hover{--tw-gradient-from:#2563eb var(--tw-gradient-from-position);--tw-gradient-to:#2563eb00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-indigo-700:hover{--tw-gradient-to:#4338ca var(--tw-gradient-to-position)}.hover\:text-red-500:hover{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-lg:hover,.hover\:shadow-sm:hover{box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.hover\:shadow-blue-500\/30:hover{--tw-shadow-color:#3b82f64d;--tw-shadow:var(--tw-shadow-colored)}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:#111827}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:visible{visibility:visible}
6
+ /*# sourceMappingURL=main.7f8664fc.css.map*/