alienet commited on
Commit
92e6753
·
1 Parent(s): 61481b2

5/17 preset setting

Browse files
BookWorld.py CHANGED
@@ -184,10 +184,12 @@ class Server():
184
  if self.mode == "free":
185
  self.get_event()
186
  self.log(f"--------- Free Mode: Current Event ---------\n{self.event}\n")
 
187
  self.event_history.append(self.event)
188
  elif self.mode == "script":
189
  self.get_script()
190
  self.log(f"--------- Script Mode: Setted Script ---------\n{self.script}\n")
 
191
  self.event_history.append(self.event)
192
  if self.mode == "free":
193
  for role_code in self.role_codes:
@@ -648,7 +650,6 @@ class Server():
648
  status = "\n".join([self.role_agents[role_code].status for role_code in self.role_codes])
649
  script = self.world_agent.generate_script(roles_info_text=roles_info_text,event=self.intervention,history_text=status)
650
  self.script = script
651
- # self.event = self.script
652
  return self.script
653
 
654
  def update_event(self, group: List[str], top_k: int = 1):
 
184
  if self.mode == "free":
185
  self.get_event()
186
  self.log(f"--------- Free Mode: Current Event ---------\n{self.event}\n")
187
+ yield ("system","",f"--------- Current Event ---------\n{self.event}\n", None)
188
  self.event_history.append(self.event)
189
  elif self.mode == "script":
190
  self.get_script()
191
  self.log(f"--------- Script Mode: Setted Script ---------\n{self.script}\n")
192
+ yield ("system","",f"--------- Setted Script ---------\n{self.script}\n", None)
193
  self.event_history.append(self.event)
194
  if self.mode == "free":
195
  for role_code in self.role_codes:
 
650
  status = "\n".join([self.role_agents[role_code].status for role_code in self.role_codes])
651
  script = self.world_agent.generate_script(roles_info_text=roles_info_text,event=self.intervention,history_text=status)
652
  self.script = script
 
653
  return self.script
654
 
655
  def update_event(self, group: List[str], top_k: int = 1):
app.py CHANGED
@@ -20,6 +20,9 @@ for key in config:
20
  static_file_abspath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'frontend'))
21
  app.mount("/frontend", StaticFiles(directory=static_file_abspath), name="frontend")
22
 
 
 
 
23
  class ConnectionManager:
24
  def __init__(self):
25
  self.active_connections: dict[str, WebSocket] = {}
@@ -137,6 +140,61 @@ async def get_file(full_path: str):
137
 
138
  raise HTTPException(status_code=404, detail="File not found")
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  @app.websocket("/ws/{client_id}")
141
  async def websocket_endpoint(websocket: WebSocket, client_id: str):
142
  await manager.connect(websocket, client_id)
 
20
  static_file_abspath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'frontend'))
21
  app.mount("/frontend", StaticFiles(directory=static_file_abspath), name="frontend")
22
 
23
+ # 预设文件目录
24
+ PRESETS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'experiment_presets')
25
+
26
  class ConnectionManager:
27
  def __init__(self):
28
  self.active_connections: dict[str, WebSocket] = {}
 
140
 
141
  raise HTTPException(status_code=404, detail="File not found")
142
 
143
+ @app.get("/api/list-presets")
144
+ async def list_presets():
145
+ try:
146
+ # 获取所有json文件
147
+ presets = [f for f in os.listdir(PRESETS_DIR) if f.endswith('.json')]
148
+ return {"presets": presets}
149
+ except Exception as e:
150
+ raise HTTPException(status_code=500, detail=str(e))
151
+
152
+ @app.post("/api/load-preset")
153
+ async def load_preset(request: Request):
154
+ try:
155
+ data = await request.json()
156
+ preset_name = data.get('preset')
157
+
158
+ if not preset_name:
159
+ raise HTTPException(status_code=400, detail="No preset specified")
160
+
161
+ preset_path = os.path.join(PRESETS_DIR, preset_name)
162
+ print(f"Loading preset from: {preset_path}")
163
+
164
+ if not os.path.exists(preset_path):
165
+ raise HTTPException(status_code=404, detail=f"Preset not found: {preset_path}")
166
+
167
+ try:
168
+ # 更新BookWorld实例的预设
169
+ manager.bw = BookWorld(
170
+ preset_path=preset_path,
171
+ world_llm_name=config["world_llm_name"],
172
+ role_llm_name=config["role_llm_name"],
173
+ embedding_name=config["embedding_model_name"]
174
+ )
175
+ manager.bw.set_generator(
176
+ rounds=config["rounds"],
177
+ save_dir=config["save_dir"],
178
+ if_save=config["if_save"],
179
+ mode=config["mode"],
180
+ scene_mode=config["scene_mode"]
181
+ )
182
+
183
+ # 获取初始数据
184
+ initial_data = await manager.get_initial_data()
185
+
186
+ return {
187
+ "success": True,
188
+ "data": initial_data
189
+ }
190
+ except Exception as e:
191
+ print(f"Error initializing BookWorld: {str(e)}")
192
+ raise HTTPException(status_code=500, detail=f"Error initializing BookWorld: {str(e)}")
193
+
194
+ except Exception as e:
195
+ print(f"Error in load_preset: {str(e)}")
196
+ raise HTTPException(status_code=500, detail=str(e))
197
+
198
  @app.websocket("/ws/{client_id}")
199
  async def websocket_endpoint(websocket: WebSocket, client_id: str):
200
  await manager.connect(websocket, client_id)
bw_utils.py CHANGED
@@ -452,6 +452,7 @@ def clean_collection_name(name: str) -> str:
452
  valid_name = re.sub(r'\.\.+', '-', valid_name)
453
  valid_name = re.sub(r'^[^a-zA-Z0-9]+', '', valid_name) # 移除开头非法字符
454
  valid_name = re.sub(r'[^a-zA-Z0-9]+$', '', valid_name)
 
455
  return valid_name
456
 
457
  cache_sign = True
 
452
  valid_name = re.sub(r'\.\.+', '-', valid_name)
453
  valid_name = re.sub(r'^[^a-zA-Z0-9]+', '', valid_name) # 移除开头非法字符
454
  valid_name = re.sub(r'[^a-zA-Z0-9]+$', '', valid_name)
455
+ valid_name = valid_name[:60]
456
  return valid_name
457
 
458
  cache_sign = True
data/roles/example_world/Falko-en/role_info.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "role_code": "Falko-en",
3
+ "role_name": "Falko·Weiss",
4
+ "source": "example",
5
+ "activity": 1,
6
+ "profile": "Falko Weiss serves as a doctoral researcher in neurointelligence at Eldridge Corporation's laboratory, where he works alongside Lacia and Trek. With short brown hair, greenish-yellow eyes, and glasses, he presents a typical scholarly appearance. Despite his serious and methodical approach to work, Falko is known for his inherent kindness and unwavering dedication to his colleagues' well-being. In contrast to Trek's revolutionary views and Lacia's initially aggressive stance, Falko consistently emphasizes the importance of thorough safety protocols and ethical guidelines in their research.",
7
+ "nickname": "",
8
+ "relation": {
9
+ "Trek-en": {
10
+ "relation": ["friend","alumni"],
11
+ "detail": "Despite foreseeing the severe consequences of Trek's plans, Falko holds genuine affection for the humble young researcher. He participated in their early studies, hoping to find a middle ground between Trek's radical vision and his own conservative stance."
12
+ },
13
+ "Lacia-en": {
14
+ "relation": ["friend","alumni"],
15
+ "detail": "Falko and Lacia have been friends since high school, maintaining an easy rapport where Lacia teases Falko's serious nature while Falko returns with sardonic quips. Though their dynamic remains light-hearted, Falko silently watches as Lacia chases after Trek's radical ideals, preparing himself to support his friend when this zealous pursuit of technological revolution inevitably leads to disillusionment."
16
+ }
17
+ }
18
+ }
data/roles/example_world/Lacia-en/role_info.json CHANGED
@@ -3,12 +3,16 @@
3
  "role_name": "Lacia·Eldridge",
4
  "source": "example",
5
  "activity": 1,
6
- "profile": "Lacia is the designated heir to Eldridge Corporation and holds a doctorate in neurointelligence. Known for his cheerful and generous demeanor, he approaches challenges with boldness and decisiveness.He possesses shoulder-length silver hair and dark red eyes, often seen wearing leather jackets or black shirts, giving him an appearance more fitting for a renegade than a scholar. At the outset, Lacia advocated for aggressive advancements in mechanical intelligence, aligning closely with Trek’s radical philosophy. However, as developments unfolded, he adopted a more cautious stance, recognizing the potential dangers of uncontrolled technological acceleration and the need to mitigate irreversible consequences",
7
  "nickname": "Lacia",
8
  "relation": {
9
  "Trek-en": {
10
  "relation": ["intimate friend","alumni"],
11
- "detail": ""
 
 
 
 
12
  }
13
  }
14
  }
 
3
  "role_name": "Lacia·Eldridge",
4
  "source": "example",
5
  "activity": 1,
6
+ "profile": "Lacia is the designated heir to Eldridge Corporation and holds a doctorate in neurointelligence. Known for his cheerful and generous demeanor, he approaches challenges with boldness and decisiveness.He possesses shoulder-length silver hair and dark red eyes, often seen wearing leather jackets or black shirts, giving him an appearance more fitting for a renegade than a scholar. At the outset, Lacia advocated for aggressive advancements in mechanical intelligence, aligning closely with Trek’s radical philosophy. However, as developments unfolded, he adopted a more cautious stance, recognizing the potential dangers of uncontrolled technological acceleration and the need to mitigate irreversible consequences.",
7
  "nickname": "Lacia",
8
  "relation": {
9
  "Trek-en": {
10
  "relation": ["intimate friend","alumni"],
11
+ "detail": "Lacia and Trek first met during high school, where Lacia was two years ahead. Sharing the same ambition at the time, they quickly formed a strong bond. Lacia admired Trek’s talent and unwavering determination, providing him with significant support in his research endeavors. However, as events unfolded, Lacia began to realize that Trek’s views had become increasingly extreme. As Lacia's own stance shifted toward caution and moderation, a rift gradually formed between them, straining their once close partnership."
12
+ },
13
+ "Falko-en": {
14
+ "relation": ["friend","alumni"],
15
+ "detail": "Falko and Lacia have been friends since high school, maintaining an easy rapport where Lacia teases Falko's serious nature while Falko returns with sardonic quips. "
16
  }
17
  }
18
  }
data/roles/example_world/Trek-en/role_info.json CHANGED
@@ -9,6 +9,10 @@
9
  "Lacia-en": {
10
  "relation": ["intimate friend","alumni"],
11
  "detail": "Lacia and Trek first met during high school, where Lacia was two years ahead. Sharing the same ambition at the time, they quickly formed a strong bond. Lacia admired Trek’s talent and unwavering determination, providing him with significant support in his research endeavors. However, as events unfolded, Lacia began to realize that Trek’s views had become increasingly extreme. As Lacia's own stance shifted toward caution and moderation, a rift gradually formed between them, straining their once close partnership."
12
- }
 
 
 
 
13
  }
14
  }
 
9
  "Lacia-en": {
10
  "relation": ["intimate friend","alumni"],
11
  "detail": "Lacia and Trek first met during high school, where Lacia was two years ahead. Sharing the same ambition at the time, they quickly formed a strong bond. Lacia admired Trek’s talent and unwavering determination, providing him with significant support in his research endeavors. However, as events unfolded, Lacia began to realize that Trek’s views had become increasingly extreme. As Lacia's own stance shifted toward caution and moderation, a rift gradually formed between them, straining their once close partnership."
12
+ },
13
+ "Falko-en": {
14
+ "relation": ["friend","alumni"],
15
+ "detail": "To Trek, Falko is a dedicated and helpful senior colleague with whom he frequently engages in academic discussions. However, Trek notices that Falko, while always supportive professionally, maintains an emotional distance and seems to deliberately avoid forming any personal connection."
16
+ }
17
  }
18
  }
experiment_presets/example_script.json CHANGED
@@ -5,6 +5,7 @@
5
  "loc_file_path":"./data/locations/example_locations.json",
6
  "role_file_dir":"./data/roles/",
7
  "role_agent_codes":["Lacia-en","Trek-en"],
 
8
  "script":"One day, an enigmatic signal from an unknown source reached Lacia and Trek. The pattern resembled Trek’s early consciousness digitization code—but far more evolved. Tracing its origin to lunar orbit, the two found themselves divided: Lacia urged caution, fearing the dangers of an unknown intelligence, while Trek saw it as proof that human evolution had already begun elsewhere. As they followed the signal, ideological tension grew—one seeking to contain it, the other longing to embrace it.",
9
  "source":"example_world",
10
  "language":"en"
 
5
  "loc_file_path":"./data/locations/example_locations.json",
6
  "role_file_dir":"./data/roles/",
7
  "role_agent_codes":["Lacia-en","Trek-en"],
8
+ "intervention":"",
9
  "script":"One day, an enigmatic signal from an unknown source reached Lacia and Trek. The pattern resembled Trek’s early consciousness digitization code—but far more evolved. Tracing its origin to lunar orbit, the two found themselves divided: Lacia urged caution, fearing the dangers of an unknown intelligence, while Trek saw it as proof that human evolution had already begun elsewhere. As they followed the signal, ideological tension grew—one seeking to contain it, the other longing to embrace it.",
10
  "source":"example_world",
11
  "language":"en"
experiment_presets/experiment_icefire.json CHANGED
@@ -5,7 +5,7 @@
5
  "loc_file_path":"./data/locations/A_Song_of_Ice_and_Fire.json",
6
  "role_file_dir":"./data/roles/",
7
  "role_agent_codes":["SansaStark-zh", "CerseiLannister-zh", "AryaStark-zh", "DaenerysTargaryen-zh", "JaimeLannister-zh", "TyrionLannister-zh", "JonSnow-zh", "BranStark-zh"],
8
- "intervention":"",
9
  "script":"",
10
  "source":"A_Song_of_Ice_and_Fire",
11
  "language":"zh"
 
5
  "loc_file_path":"./data/locations/A_Song_of_Ice_and_Fire.json",
6
  "role_file_dir":"./data/roles/",
7
  "role_agent_codes":["SansaStark-zh", "CerseiLannister-zh", "AryaStark-zh", "DaenerysTargaryen-zh", "JaimeLannister-zh", "TyrionLannister-zh", "JonSnow-zh", "BranStark-zh"],
8
+ "intervention":"血色婚礼刚刚结束————这是一场艾德慕·徒利(凯特琳·史塔克的弟弟)与罗莎琳·弗雷的婚礼,罗柏·史塔克为了修复与弗雷家族关系而策划的政治联姻,却在泰温兰尼斯特与老瓦德伯爵共同策划的阴谋下,演变成了一场彻头彻尾的悲剧。凯特琳、罗柏被杀,几乎所有的史塔克军人都被杀死,佛雷、波顿一方仅仅付出了大约五十人的伤亡代价。此事让兰尼斯特家族的实力大大增强,让史塔克家族与兰尼斯特家族之间的仇恨变得无可挽回。众人需要对这一事件做出反应。",
9
  "script":"",
10
  "source":"A_Song_of_Ice_and_Fire",
11
  "language":"zh"
frontend/css/right-section/preset-panel.css ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .preset-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 15px;
5
+ padding: 15px;
6
+ }
7
+
8
+ .preset-header {
9
+ display: flex;
10
+ justify-content: space-between;
11
+ align-items: center;
12
+ }
13
+
14
+ .preset-header h3 {
15
+ color: #5f3737;
16
+ margin: 0;
17
+ }
18
+
19
+ .preset-select {
20
+ width: 100%;
21
+ padding: 8px;
22
+ border: 1px solid #5f3737;
23
+ border-radius: 4px;
24
+ background-color: white;
25
+ color: #5f3737;
26
+ margin-bottom: 10px;
27
+ }
28
+
29
+ .preset-select option {
30
+ padding: 8px;
31
+ }
32
+
33
+ .preset-submit-btn {
34
+ padding: 10px;
35
+ background-color: #5f3737;
36
+ color: white;
37
+ border: none;
38
+ border-radius: 4px;
39
+ cursor: pointer;
40
+ transition: background-color 0.3s;
41
+ width: 100%;
42
+ }
43
+
44
+ .preset-submit-btn:hover {
45
+ background-color: #7a4747;
46
+ }
47
+
48
+ .preset-submit-btn:disabled {
49
+ background-color: #ccc;
50
+ cursor: not-allowed;
51
+ }
frontend/css/right-section/status-panel.css CHANGED
@@ -64,6 +64,62 @@
64
  font-style: italic;
65
  }
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  /* 状态指示器 */
68
  .status-indicator {
69
  width: 8px;
 
64
  font-style: italic;
65
  }
66
 
67
+ /* 设定模块样式 */
68
+ .settings-content {
69
+ max-height: 300px;
70
+ overflow-y: auto;
71
+ }
72
+
73
+ .setting-item {
74
+ background-color: #fdf5ee;
75
+ padding: 10px;
76
+ border-radius: 6px;
77
+ margin-bottom: 8px;
78
+ }
79
+
80
+ .setting-term {
81
+ font-weight: bold;
82
+ color: #5f3737;
83
+ margin-bottom: 4px;
84
+ }
85
+
86
+ .setting-nature {
87
+ color: #666;
88
+ font-size: 0.9em;
89
+ margin-bottom: 4px;
90
+ }
91
+
92
+ .setting-detail {
93
+ color: #666;
94
+ line-height: 1.4;
95
+ }
96
+
97
+ .no-settings {
98
+ color: #666;
99
+ font-style: italic;
100
+ text-align: center;
101
+ padding: 10px;
102
+ }
103
+
104
+ /* 滚动条样式 */
105
+ .settings-content::-webkit-scrollbar {
106
+ width: 6px;
107
+ }
108
+
109
+ .settings-content::-webkit-scrollbar-track {
110
+ background: #f1f1f1;
111
+ border-radius: 3px;
112
+ }
113
+
114
+ .settings-content::-webkit-scrollbar-thumb {
115
+ background: #888;
116
+ border-radius: 3px;
117
+ }
118
+
119
+ .settings-content::-webkit-scrollbar-thumb:hover {
120
+ background: #555;
121
+ }
122
+
123
  /* 状态指示器 */
124
  .status-indicator {
125
  width: 8px;
frontend/js/i18n.js CHANGED
@@ -23,7 +23,9 @@ const translations = {
23
  configSubmitted: "Configuration has been submitted to the server!",
24
  submitFailed: "Submission failed. Please check server status.",
25
  networkError: "Submission failed. Please check network connection.",
26
- APIsettings: "API Setting"
 
 
27
  },
28
  zh: {
29
  start: "开始",
@@ -49,13 +51,15 @@ const translations = {
49
  configSubmitted: "配置已提交到服务器!",
50
  submitFailed: "提交失败,请检查服务器状态。",
51
  networkError: "提交失败,请检查网络连接。",
52
- APIsettings: "API设置"
 
 
53
  }
54
  };
55
 
56
  class I18nManager {
57
  constructor() {
58
- this.currentLang = 'zh';
59
  this.init();
60
  }
61
 
 
23
  configSubmitted: "Configuration has been submitted to the server!",
24
  submitFailed: "Submission failed. Please check server status.",
25
  networkError: "Submission failed. Please check network connection.",
26
+ APIsettings: "API Setting",
27
+ preset: "Preset",
28
+ presetList: "Preset List",
29
  },
30
  zh: {
31
  start: "开始",
 
51
  configSubmitted: "配置已提交到服务器!",
52
  submitFailed: "提交失败,请检查服务器状态。",
53
  networkError: "提交失败,请检查网络连接。",
54
+ APIsettings: "API设置",
55
+ preset: "预设",
56
+ presetList: "预设列表",
57
  }
58
  };
59
 
60
  class I18nManager {
61
  constructor() {
62
+ this.currentLang = 'en';
63
  this.init();
64
  }
65
 
frontend/js/right-section/preset-panel.js ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class PresetPanel {
2
+ constructor() {
3
+ this.presets = [];
4
+ this.currentPreset = null;
5
+ this.container = document.querySelector('.preset-container');
6
+ this.select = document.querySelector('.preset-select');
7
+ this.submitBtn = document.querySelector('.preset-submit-btn');
8
+ this.init();
9
+ }
10
+
11
+ init() {
12
+ this.loadPresets();
13
+ this.setupEventListeners();
14
+ }
15
+
16
+ async loadPresets() {
17
+ try {
18
+ const response = await fetch('/api/list-presets');
19
+ if (!response.ok) {
20
+ throw new Error('Failed to load presets');
21
+ }
22
+ const data = await response.json();
23
+ this.presets = data.presets;
24
+ this.renderPresetOptions();
25
+ } catch (error) {
26
+ console.error('Error loading presets:', error);
27
+ alert('加载预设列表失败,请刷新页面重试');
28
+ }
29
+ }
30
+
31
+ renderPresetOptions() {
32
+ if (!this.select) return;
33
+
34
+ this.select.innerHTML = '<option value="">选择预设...</option>';
35
+ this.presets.forEach(preset => {
36
+ const option = document.createElement('option');
37
+ option.value = preset;
38
+ option.textContent = preset.replace('.json', '');
39
+ this.select.appendChild(option);
40
+ });
41
+ }
42
+
43
+ setupEventListeners() {
44
+ if (this.select) {
45
+ this.select.addEventListener('change', () => {
46
+ this.currentPreset = this.select.value;
47
+ this.submitBtn.disabled = !this.currentPreset;
48
+ });
49
+ }
50
+
51
+ if (this.submitBtn) {
52
+ this.submitBtn.addEventListener('click', () => this.handleSubmit());
53
+ }
54
+ }
55
+
56
+ async handleSubmit() {
57
+ if (!this.currentPreset) return;
58
+
59
+ // 禁用按钮,防止重复点击
60
+ this.submitBtn.disabled = true;
61
+ this.submitBtn.textContent = '加载中...';
62
+
63
+ try {
64
+ const response = await fetch('/api/load-preset', {
65
+ method: 'POST',
66
+ headers: {
67
+ 'Content-Type': 'application/json'
68
+ },
69
+ body: JSON.stringify({
70
+ preset: this.currentPreset
71
+ })
72
+ });
73
+
74
+ const data = await response.json();
75
+
76
+ if (!response.ok) {
77
+ throw new Error(data.detail || '加载预设失败');
78
+ }
79
+
80
+ if (data.success) {
81
+ // 触发预设加载成功事件
82
+ window.dispatchEvent(new CustomEvent('preset-loaded', {
83
+ detail: { preset: this.currentPreset }
84
+ }));
85
+
86
+ // 重新加载初始数据
87
+ if (window.ws && window.ws.readyState === WebSocket.OPEN) {
88
+ // 先停止当前的故事生成
89
+ window.ws.send(JSON.stringify({
90
+ type: 'control',
91
+ action: 'stop'
92
+ }));
93
+
94
+ // 重新连接WebSocket以获取新的初始数据
95
+ const clientId = Date.now().toString();
96
+ const ws = new WebSocket(`ws://${window.location.host}/ws/${clientId}`);
97
+
98
+ ws.onopen = () => {
99
+ console.log('WebSocket重新连接成功');
100
+ };
101
+
102
+ ws.onmessage = (event) => {
103
+ const message = JSON.parse(event.data);
104
+ // 触发自定义事件,让其他面板更新数据
105
+ window.dispatchEvent(new CustomEvent('websocket-message', {
106
+ detail: message
107
+ }));
108
+ };
109
+
110
+ ws.onerror = (error) => {
111
+ console.error('WebSocket错误:', error);
112
+ alert('连接服务器失败,请刷新页面重试');
113
+ };
114
+
115
+ // 更新全局WebSocket实例
116
+ window.ws = ws;
117
+ }
118
+
119
+ alert('预设加载成功!');
120
+ }
121
+ } catch (error) {
122
+ console.error('Error loading preset:', error);
123
+ alert(error.message || '加载预设失败,请重试');
124
+ } finally {
125
+ // 恢复按钮状态
126
+ this.submitBtn.disabled = false;
127
+ this.submitBtn.textContent = '加载预设';
128
+ }
129
+ }
130
+ }
131
+
132
+ const presetPanel = new PresetPanel();
frontend/js/right-section/status-panel.js CHANGED
@@ -16,6 +16,7 @@ class StatusPanel {
16
  init() {
17
  // 设置默认状态
18
  this.updateAllStatus(this.currentStatus);
 
19
 
20
  // 监听WebSocket消息
21
  window.addEventListener('websocket-message', (event) => {
@@ -27,6 +28,7 @@ class StatusPanel {
27
 
28
  if (message.type === 'initial_data' && message.data.status) {
29
  this.updateAllStatus(message.data.status);
 
30
  }
31
  });
32
  }
@@ -68,11 +70,29 @@ class StatusPanel {
68
  }
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  updateAllStatus(statusData) {
72
- this.currentStatus = statusData
73
  if (statusData.event !== undefined) this.updateEvent(statusData.event);
74
  if (statusData.location) this.updateLocation(statusData.location);
75
  if (statusData.group) this.updateGroup(statusData.group);
76
  }
77
  }
78
- const statusPanel = new StatusPanel;
 
 
16
  init() {
17
  // 设置默认状态
18
  this.updateAllStatus(this.currentStatus);
19
+ this.updateSettings([]);
20
 
21
  // 监听WebSocket消息
22
  window.addEventListener('websocket-message', (event) => {
 
28
 
29
  if (message.type === 'initial_data' && message.data.status) {
30
  this.updateAllStatus(message.data.status);
31
+ this.updateSettings(message.data.settings);
32
  }
33
  });
34
  }
 
70
  }
71
  }
72
 
73
+ updateSettings(settings) {
74
+ const settingsContainer = document.querySelector('.settings-content');
75
+ if (settingsContainer) {
76
+ if (settings && settings.length > 0) {
77
+ settingsContainer.innerHTML = settings.map(setting => `
78
+ <div class="setting-item">
79
+ <div class="setting-term">${setting.term}</div>
80
+ <div class="setting-nature">${setting.nature}</div>
81
+ <div class="setting-detail">${setting.detail}</div>
82
+ </div>
83
+ `).join('');
84
+ } else {
85
+ settingsContainer.innerHTML = '<div class="no-settings">暂无设定</div>';
86
+ }
87
+ }
88
+ }
89
+
90
  updateAllStatus(statusData) {
91
+ this.currentStatus = statusData;
92
  if (statusData.event !== undefined) this.updateEvent(statusData.event);
93
  if (statusData.location) this.updateLocation(statusData.location);
94
  if (statusData.group) this.updateGroup(statusData.group);
95
  }
96
  }
97
+
98
+ const statusPanel = new StatusPanel();
index.html CHANGED
@@ -13,6 +13,7 @@
13
  <link rel="stylesheet" href="./frontend/css/right-section/settings-panel.css">
14
  <link rel="stylesheet" href="./frontend/css/right-section/status-panel.css">
15
  <link rel="stylesheet" href="./frontend/css/right-section/scene-panel.css">
 
16
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
17
  <script src="https://d3js.org/d3.v7.min.js"></script>
18
  </head>
@@ -73,8 +74,8 @@
73
  <div class="right-section">
74
  <div class="right-toolbar">
75
  <button class="tab-btn active" data-target="status-panel" data-i18n="status">状态</button>
76
- <button class="tab-btn" data-target="settings-panel" data-i18n="settings">设定</button>
77
  <button class="tab-btn" data-target="scenes-panel" data-i18n="scenes">场景</button>
 
78
  <button class="tab-btn" data-target="api-panel" data-i18n="APIsettings">API设置</button>
79
  </div>
80
  <div class="right-content">
@@ -102,11 +103,13 @@
102
  </div>
103
  </div>
104
  </div>
105
- </div>
106
- </div>
107
- <div class="tab-panel" id="settings-panel">
108
- <div class="settings-container">
109
- <!-- 设定内容将通过JS动态添加 -->
 
 
110
  </div>
111
  </div>
112
  <div class="tab-panel" id="scenes-panel">
@@ -119,6 +122,17 @@
119
  </div>
120
  </div>
121
  </div>
 
 
 
 
 
 
 
 
 
 
 
122
  <div class="tab-panel" id="api-panel">
123
  <div class="api-container">
124
  <label for="api-provider"><h3 data-i18n="apiProvider">API 提供商:</h3></label>
@@ -154,6 +168,7 @@
154
  <script src="./frontend/js/right-section/settings-panel.js"></script>
155
  <script src="./frontend/js/right-section/status-panel.js"></script>
156
  <script src="./frontend/js/right-section/scene-panel.js"></script>
 
157
  <script>
158
  document.addEventListener('DOMContentLoaded', () => {
159
  if (window.RightSection) {
 
13
  <link rel="stylesheet" href="./frontend/css/right-section/settings-panel.css">
14
  <link rel="stylesheet" href="./frontend/css/right-section/status-panel.css">
15
  <link rel="stylesheet" href="./frontend/css/right-section/scene-panel.css">
16
+ <link rel="stylesheet" href="./frontend/css/right-section/preset-panel.css">
17
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
18
  <script src="https://d3js.org/d3.v7.min.js"></script>
19
  </head>
 
74
  <div class="right-section">
75
  <div class="right-toolbar">
76
  <button class="tab-btn active" data-target="status-panel" data-i18n="status">状态</button>
 
77
  <button class="tab-btn" data-target="scenes-panel" data-i18n="scenes">场景</button>
78
+ <button class="tab-btn" data-target="preset-panel" data-i18n="preset">预设</button>
79
  <button class="tab-btn" data-target="api-panel" data-i18n="APIsettings">API设置</button>
80
  </div>
81
  <div class="right-content">
 
103
  </div>
104
  </div>
105
  </div>
106
+
107
+ <div class="status-module" id="settings-module">
108
+ <h3 data-i18n="settings">设定</h3>
109
+ <div class="module-content settings-content">
110
+ <!-- 设定内容将通过JS动态添加 -->
111
+ </div>
112
+ </div>
113
  </div>
114
  </div>
115
  <div class="tab-panel" id="scenes-panel">
 
122
  </div>
123
  </div>
124
  </div>
125
+ <div class="tab-panel" id="preset-panel">
126
+ <div class="preset-container">
127
+ <div class="preset-header">
128
+ <h3 data-i18n="presetList">预设列表</h3>
129
+ </div>
130
+ <select class="preset-select">
131
+ <!-- 预设选项将通过JS动态添加 -->
132
+ </select>
133
+ <button class="preset-submit-btn" disabled>Load</button>
134
+ </div>
135
+ </div>
136
  <div class="tab-panel" id="api-panel">
137
  <div class="api-container">
138
  <label for="api-provider"><h3 data-i18n="apiProvider">API 提供商:</h3></label>
 
168
  <script src="./frontend/js/right-section/settings-panel.js"></script>
169
  <script src="./frontend/js/right-section/status-panel.js"></script>
170
  <script src="./frontend/js/right-section/scene-panel.js"></script>
171
+ <script src="./frontend/js/right-section/preset-panel.js"></script>
172
  <script>
173
  document.addEventListener('DOMContentLoaded', () => {
174
  if (window.RightSection) {