feedback-mcp 1.0.64__py3-none-any.whl

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.

Potentially problematic release.


This version of feedback-mcp might be problematic. Click here for more details.

command.py ADDED
@@ -0,0 +1,636 @@
1
+ """
2
+ 指令管理模块
3
+ 包含指令的增删改查、UI组件等所有相关功能
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ import json
9
+ from typing import Optional, List, Dict, Any
10
+
11
+ try:
12
+ from PySide6.QtWidgets import (
13
+ QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QScrollArea,
14
+ QGridLayout, QRadioButton, QButtonGroup, QPushButton, QFrame,
15
+ QLabel, QSizePolicy, QDialog
16
+ )
17
+ from PySide6.QtCore import Qt, Signal
18
+ except ImportError:
19
+ from PyQt5.QtWidgets import (
20
+ QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QScrollArea,
21
+ QGridLayout, QRadioButton, QButtonGroup, QPushButton, QFrame,
22
+ QLabel, QSizePolicy, QDialog
23
+ )
24
+ from PyQt5.QtCore import Qt, pyqtSignal as Signal
25
+
26
+ # 导入指令对话框
27
+ try:
28
+ import add_command_dialog
29
+ AddCommandDialog = add_command_dialog.AddCommandDialog
30
+ EditCommandDialog = add_command_dialog.EditCommandDialog
31
+ except ImportError:
32
+ try:
33
+ from add_command_dialog import AddCommandDialog, EditCommandDialog
34
+ except ImportError:
35
+ print("Warning: 无法导入命令对话框组件")
36
+ AddCommandDialog = None
37
+ EditCommandDialog = None
38
+
39
+ # 导入路径配置模块
40
+ try:
41
+ from path_config import get_path_config
42
+ PATH_CONFIG_AVAILABLE = True
43
+ except ImportError:
44
+ PATH_CONFIG_AVAILABLE = False
45
+
46
+ # 导入调试日志模块
47
+ try:
48
+ from debug_logger import get_debug_logger
49
+ DEBUG_LOG_AVAILABLE = True
50
+ except ImportError:
51
+ DEBUG_LOG_AVAILABLE = False
52
+
53
+
54
+ class CommandManager:
55
+ """指令数据管理类"""
56
+
57
+ def __init__(self, project_path: str):
58
+ self.project_path = project_path
59
+ # 获取路径配置实例
60
+ if PATH_CONFIG_AVAILABLE:
61
+ self.path_config = get_path_config()
62
+ else:
63
+ self.path_config = None
64
+ # 命令缓存
65
+ self._cache = {}
66
+ self._cache_enabled = False
67
+
68
+ def enable_cache(self):
69
+ """启用缓存模式"""
70
+ self._cache_enabled = True
71
+ self._cache.clear()
72
+
73
+ def disable_cache(self):
74
+ """禁用缓存模式"""
75
+ self._cache_enabled = False
76
+ self._cache.clear()
77
+
78
+ def load_project_commands(self) -> List[Dict[str, Any]]:
79
+ """加载项目指令(.cursor/rules/目录)"""
80
+ if self._cache_enabled and 'project' in self._cache:
81
+ return self._cache['project']
82
+
83
+ commands = []
84
+ if not self.project_path:
85
+ return commands
86
+
87
+ if self.path_config:
88
+ prompts_dir = self.path_config.get_project_commands_dir(self.project_path)
89
+ if prompts_dir:
90
+ commands = self._load_commands_from_dir(prompts_dir, "项目")
91
+
92
+ if self._cache_enabled:
93
+ self._cache['project'] = commands
94
+ return commands
95
+
96
+ def load_personal_commands(self) -> List[Dict[str, Any]]:
97
+ """个人指令已移除,返回空列表"""
98
+ return []
99
+
100
+ def load_plugin_commands(self) -> List[Dict[str, Any]]:
101
+ """加载插件指令(从已启用插件的commands目录)"""
102
+ if self._cache_enabled and 'plugin' in self._cache:
103
+ return self._cache['plugin']
104
+
105
+ commands = []
106
+
107
+ if not self.path_config:
108
+ return commands
109
+
110
+ try:
111
+ # 1. 读取 installed_plugins.json
112
+ plugins_config_path = self.path_config.get_plugins_config_path()
113
+ if not os.path.exists(plugins_config_path):
114
+ if DEBUG_LOG_AVAILABLE:
115
+ logger = get_debug_logger()
116
+ logger.log("插件配置文件不存在", "WARN")
117
+ return commands
118
+
119
+ with open(plugins_config_path, 'r', encoding='utf-8') as f:
120
+ plugins_data = json.load(f)
121
+
122
+ installed_plugins = plugins_data.get('plugins', {})
123
+
124
+ # 2. 读取 settings.json 获取启用状态
125
+ settings_path = self.path_config.get_settings_path()
126
+ enabled_plugins = {}
127
+ if os.path.exists(settings_path):
128
+ with open(settings_path, 'r', encoding='utf-8') as f:
129
+ settings_data = json.load(f)
130
+ enabled_plugins = settings_data.get('enabledPlugins', {})
131
+
132
+ # 3. 遍历已启用的插件
133
+ for plugin_id, plugin_info in installed_plugins.items():
134
+ # 只加载已启用的插件
135
+ if not enabled_plugins.get(plugin_id, False):
136
+ continue
137
+
138
+ # plugin_info 可能是数组(新格式)或对象(旧格式)
139
+ if isinstance(plugin_info, list) and len(plugin_info) > 0:
140
+ install_path = plugin_info[0].get('installPath')
141
+ else:
142
+ install_path = plugin_info.get('installPath')
143
+ if not install_path or not os.path.exists(install_path):
144
+ if DEBUG_LOG_AVAILABLE:
145
+ logger = get_debug_logger()
146
+ logger.log(f"插件路径不存在: {plugin_id}", "WARN")
147
+ continue
148
+
149
+ # 4. 扫描插件的 commands/ 目录
150
+ commands_dir = os.path.join(install_path, 'commands')
151
+ if not os.path.exists(commands_dir):
152
+ continue
153
+
154
+ # 5. 复用 _load_commands_from_dir 方法
155
+ plugin_commands = self._load_commands_from_dir(commands_dir, "插件")
156
+
157
+ # 6. 为每个指令添加插件标识
158
+ plugin_name = plugin_id.split('@')[0] if '@' in plugin_id else plugin_id
159
+ for cmd in plugin_commands:
160
+ cmd['plugin_id'] = plugin_id
161
+ cmd['plugin_name'] = plugin_name
162
+ # 修改 title 格式:title@plugin_name
163
+ original_title = cmd['title']
164
+ cmd['title'] = f"{original_title}@{plugin_name}"
165
+
166
+ commands.extend(plugin_commands)
167
+
168
+ except json.JSONDecodeError as e:
169
+ sys.stderr.write(f"Error parsing plugin config JSON: {e}\n")
170
+ except Exception as e:
171
+ sys.stderr.write(f"Error loading plugin commands: {e}\n")
172
+
173
+ if self._cache_enabled:
174
+ self._cache['plugin'] = commands
175
+ return commands
176
+
177
+ def _load_commands_from_dir(self, prompts_dir: str, path_type: str) -> List[Dict[str, Any]]:
178
+ """从指定目录加载指令(支持递归读取子目录)"""
179
+ commands = []
180
+
181
+ if DEBUG_LOG_AVAILABLE:
182
+ logger = get_debug_logger()
183
+ logger.log(f"_load_commands_from_dir: 目录={prompts_dir}, 类型={path_type}", "LOAD")
184
+
185
+ if not os.path.exists(prompts_dir):
186
+ if DEBUG_LOG_AVAILABLE:
187
+ logger = get_debug_logger()
188
+ logger.log_error("目录扫描", f"目录不存在: {prompts_dir}")
189
+ return commands
190
+
191
+ # 递归函数来读取目录和子目录
192
+ def scan_directory(dir_path: str, relative_path: str = "") -> None:
193
+ try:
194
+ entries = os.listdir(dir_path)
195
+
196
+ if DEBUG_LOG_AVAILABLE and dir_path == prompts_dir:
197
+ logger = get_debug_logger()
198
+ md_files = [f for f in entries if f.endswith('.md')]
199
+ logger.log_dir_scan(dir_path, entries, md_files)
200
+
201
+ for entry in entries:
202
+ entry_path = os.path.join(dir_path, entry)
203
+
204
+ if os.path.isdir(entry_path):
205
+ # 递归读取子目录
206
+ new_relative = os.path.join(relative_path, entry) if relative_path else entry
207
+ scan_directory(entry_path, new_relative)
208
+ elif entry.endswith('.md'):
209
+ try:
210
+ with open(entry_path, 'r', encoding='utf-8') as f:
211
+ content = f.read().strip()
212
+
213
+ if not content:
214
+ continue
215
+
216
+ # 生成标题:使用冒号分隔的格式
217
+ file_title = entry.replace('.md', '')
218
+ if relative_path:
219
+ # 将路径分隔符替换为冒号
220
+ path_parts = relative_path.replace(os.sep, ':')
221
+ title = f"{path_parts}:{file_title}"
222
+ else:
223
+ title = file_title
224
+
225
+ description = ""
226
+ main_content = content
227
+ globs = ""
228
+ always_apply = False
229
+ argument_hint = "" # 新增:参数提示字段
230
+
231
+ if content.startswith('---'):
232
+ # 分离frontmatter和内容
233
+ parts = content.split('---', 2)
234
+ if len(parts) >= 3:
235
+ frontmatter = parts[1].strip()
236
+ main_content = parts[2].strip()
237
+
238
+ # 解析frontmatter中的各个字段
239
+ for line in frontmatter.split('\n'):
240
+ line = line.strip()
241
+ if line.startswith('title:'):
242
+ extracted_title = line.split('title:', 1)[1].strip()
243
+ if extracted_title: # 如果frontmatter中有title,使用它但保留路径前缀
244
+ if relative_path:
245
+ path_parts = relative_path.replace(os.sep, ':')
246
+ title = f"{path_parts}:{extracted_title}"
247
+ else:
248
+ title = extracted_title
249
+ elif line.startswith('description:'):
250
+ description = line.split('description:', 1)[1].strip()
251
+ elif line.startswith('argument-hint:'):
252
+ argument_hint = line.split('argument-hint:', 1)[1].strip()
253
+ elif line.startswith('globs:'):
254
+ globs = line.split('globs:', 1)[1].strip()
255
+ elif line.startswith('alwaysApply:'):
256
+ always_apply_str = line.split('alwaysApply:', 1)[1].strip().lower()
257
+ always_apply = always_apply_str in ('true', 'yes', '1')
258
+ else:
259
+ # 没有frontmatter的文件,使用文件名作为描述,内容作为主体
260
+ description = f"来自文件: {os.path.join(relative_path, entry) if relative_path else entry}"
261
+
262
+ # 只有当有实际内容时才添加指令
263
+ if main_content:
264
+ commands.append({
265
+ 'title': title,
266
+ 'content': main_content,
267
+ 'description': description,
268
+ 'filename': entry,
269
+ 'path_type': path_type,
270
+ 'full_path': entry_path,
271
+ 'globs': globs,
272
+ 'always_apply': always_apply,
273
+ 'argument_hint': argument_hint, # 新增:参数提示
274
+ 'relative_path': relative_path # 添加相对路径信息
275
+ })
276
+ except Exception as e:
277
+ # Error loading file - log to stderr instead of stdout
278
+ sys.stderr.write(f"Error loading {entry} from {path_type}: {e}\n")
279
+ except Exception as e:
280
+ sys.stderr.write(f"Error scanning directory {dir_path}: {e}\n")
281
+
282
+ # 开始递归扫描
283
+ try:
284
+ scan_directory(prompts_dir)
285
+ except Exception as e:
286
+ # Error reading directory - log to stderr instead of stdout
287
+ sys.stderr.write(f"Error reading {path_type} prompts directory: {e}\n")
288
+
289
+ return commands
290
+
291
+
292
+ class CommandTabWidget(QTabWidget):
293
+ """指令选项卡组件"""
294
+ command_executed = Signal(str) # 指令执行信号
295
+
296
+ def __init__(self, project_path: str, parent=None):
297
+ super().__init__(parent)
298
+ self.project_path = project_path
299
+ self.command_manager = CommandManager(project_path)
300
+
301
+ # 设置大小策略:优先按内容大小,但允许收缩
302
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
303
+ # 限制TabWidget的最大高度为200px,但允许按内容自适应
304
+ self.setMaximumHeight(200)
305
+
306
+ self._create_tabs()
307
+ self._setup_tab_change_handler()
308
+ # 设置初始按钮显示
309
+ self._on_tab_changed(0)
310
+ # 确保按钮容器正确初始化
311
+ self._ensure_button_container_initialized()
312
+
313
+ def _create_tabs(self):
314
+ """创建项目指令和个人指令选项卡"""
315
+ # 项目指令选项卡
316
+ self.project_tab = ProjectCommandTab(self.project_path, self.command_manager, self)
317
+ self.project_tab.command_executed.connect(self.command_executed.emit)
318
+ self.project_tab.commands_changed.connect(self._on_commands_changed)
319
+ self.addTab(self.project_tab, "🏢 项目指令")
320
+
321
+ # 个人指令选项卡
322
+ self.personal_tab = PersonalCommandTab(self.project_path, self.command_manager, self)
323
+ self.personal_tab.command_executed.connect(self.command_executed.emit)
324
+ self.personal_tab.commands_changed.connect(self._on_commands_changed)
325
+ self.addTab(self.personal_tab, "👤 个人指令")
326
+
327
+
328
+ def _setup_tab_change_handler(self):
329
+ """设置选项卡切换处理"""
330
+ self.currentChanged.connect(self._on_tab_changed)
331
+
332
+ def _on_tab_changed(self, index):
333
+ """选项卡切换时更新按钮"""
334
+ print(f"🔄 选项卡切换到索引: {index}")
335
+
336
+ # 获取当前选项卡
337
+ current_tab = self.widget(index)
338
+ if current_tab and hasattr(current_tab, 'get_button_container'):
339
+ button_container = current_tab.get_button_container()
340
+ if button_container:
341
+ # 确保按钮容器可见
342
+ button_container.setVisible(True)
343
+ self.setCornerWidget(button_container, Qt.TopRightCorner)
344
+ print(f"✅ 设置按钮容器成功,选项卡: {self.tabText(index)}")
345
+ else:
346
+ print(f"⚠️ 当前选项卡 '{self.tabText(index)}' 没有按钮容器,保持现有按钮")
347
+ # 如果当前选项卡没有按钮容器,不要清空现有的cornerWidget
348
+ # 这样可以保持之前有按钮的选项卡的按钮仍然可见
349
+ else:
350
+ print(f"❌ 选项卡 '{self.tabText(index)}' 没有get_button_container方法")
351
+
352
+ def _on_commands_changed(self):
353
+ """指令变化时的处理"""
354
+ # 刷新所有选项卡的数据
355
+ self.project_tab.refresh_commands()
356
+ self.personal_tab.refresh_commands()
357
+
358
+ def refresh_all_commands(self):
359
+ """刷新所有指令"""
360
+ self.project_tab.refresh_commands()
361
+ self.personal_tab.refresh_commands()
362
+
363
+ def _ensure_button_container_initialized(self):
364
+ """确保按钮容器正确初始化"""
365
+ # 强制设置第一个有按钮容器的选项卡的按钮
366
+ for i in range(self.count()):
367
+ tab = self.widget(i)
368
+ if tab and hasattr(tab, 'get_button_container'):
369
+ button_container = tab.get_button_container()
370
+ if button_container:
371
+ button_container.setVisible(True)
372
+ self.setCornerWidget(button_container, Qt.TopRightCorner)
373
+ print(f"✅ 初始化按钮容器成功,选项卡索引: {i}")
374
+ break
375
+
376
+
377
+ class BaseCommandTab(QWidget):
378
+ """指令选项卡基类"""
379
+ command_executed = Signal(str) # 指令执行信号
380
+ commands_changed = Signal() # 指令变化信号
381
+
382
+ def __init__(self, project_path: str, command_manager: CommandManager, command_type: str, parent=None):
383
+ super().__init__(parent)
384
+ self.project_path = project_path
385
+ self.command_manager = command_manager
386
+ self.command_type = command_type # "项目" 或 "个人"
387
+ self.commands = []
388
+ self.command_button_group = QButtonGroup()
389
+ self.command_button_group.setExclusive(False) # Allow deselection
390
+ self.command_radios = []
391
+ self.button_container = None
392
+
393
+ self._create_ui()
394
+ self._create_button_container()
395
+ self.refresh_commands()
396
+
397
+ def _create_ui(self):
398
+ """创建UI"""
399
+ layout = QVBoxLayout(self)
400
+ layout.setContentsMargins(0, 0, 0, 0)
401
+ layout.setSpacing(5)
402
+
403
+ # 创建主内容区域
404
+ self.content_widget = QWidget()
405
+ self.content_layout = QVBoxLayout(self.content_widget)
406
+ self.content_layout.setContentsMargins(0, 0, 0, 0)
407
+ self.content_layout.setSpacing(5)
408
+
409
+ layout.addWidget(self.content_widget)
410
+
411
+ def _create_button_container(self):
412
+ """创建按钮容器 - 已禁用所有管理按钮"""
413
+ # 不再创建添加、编辑按钮,用户需直接编辑 .md 文件来管理指令
414
+ self.button_container = None
415
+
416
+ def get_button_container(self):
417
+ """获取按钮容器 - 已禁用"""
418
+ return None # 不再提供按钮容器
419
+
420
+ def _load_commands(self) -> List[Dict[str, Any]]:
421
+ """加载指令(子类实现)"""
422
+ raise NotImplementedError
423
+
424
+ def _get_default_command_type_for_dialog(self) -> str:
425
+ """获取对话框的默认指令类型(子类实现)"""
426
+ raise NotImplementedError
427
+
428
+ def refresh_commands(self):
429
+ """刷新指令列表"""
430
+ # 清除旧的UI组件
431
+ for i in reversed(range(self.content_layout.count())):
432
+ child = self.content_layout.itemAt(i).widget()
433
+ if child:
434
+ child.setParent(None)
435
+
436
+ # 清除按钮组
437
+ self.command_radios.clear()
438
+
439
+ # 重新加载指令
440
+ self.commands = self._load_commands()
441
+
442
+ # 编辑按钮已移除,不再更新状态
443
+
444
+ if self.commands:
445
+ self._create_command_list()
446
+ else:
447
+ self._create_empty_state()
448
+
449
+ def _create_command_list(self):
450
+ """创建指令列表"""
451
+ # 创建滚动区域
452
+ scroll_area = QScrollArea()
453
+ scroll_area.setWidgetResizable(True)
454
+ scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
455
+ scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
456
+ scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
457
+
458
+ commands_widget = QWidget()
459
+ commands_layout = QGridLayout(commands_widget)
460
+ commands_layout.setContentsMargins(5, 5, 5, 5)
461
+ commands_layout.setSpacing(5)
462
+
463
+ # 计算网格布局
464
+ total_commands = len(self.commands)
465
+ columns = 2 # 两列布局
466
+
467
+ for i, command in enumerate(self.commands):
468
+ row = i // columns
469
+ col = i % columns
470
+
471
+ # Create frame for each command item
472
+ command_frame = QFrame()
473
+ command_item_layout = QHBoxLayout(command_frame)
474
+ command_item_layout.setContentsMargins(5, 2, 5, 2)
475
+
476
+ # Radio button
477
+ radio = QRadioButton(command['title'])
478
+ radio.setProperty('command_index', i)
479
+ radio.clicked.connect(lambda checked, r=radio: self._handle_radio_click(r))
480
+ # 设置选中效果样式,与插件指令保持一致
481
+ radio.setStyleSheet("""
482
+ QRadioButton {
483
+ padding: 4px 8px;
484
+ border-radius: 3px;
485
+ }
486
+ QRadioButton:checked {
487
+ background-color: rgba(0, 120, 212, 0.15);
488
+ }
489
+ QRadioButton:hover {
490
+ background-color: rgba(0, 120, 212, 0.08);
491
+ }
492
+ QRadioButton::indicator {
493
+ width: 14px;
494
+ height: 14px;
495
+ border-radius: 7px;
496
+ border: 2px solid #666666;
497
+ }
498
+ QRadioButton::indicator:checked {
499
+ background-color: #0078d4;
500
+ border-color: #0078d4;
501
+ }
502
+ QRadioButton::indicator:hover {
503
+ border-color: #0078d4;
504
+ }
505
+ """)
506
+ self.command_button_group.addButton(radio)
507
+ self.command_radios.append(radio)
508
+
509
+ # Button container
510
+ button_layout = QHBoxLayout()
511
+ button_layout.setSpacing(10)
512
+
513
+ # Execute button
514
+ execute_btn = QPushButton("▶️")
515
+ execute_btn.setMaximumSize(30, 30)
516
+ execute_btn.setProperty('command_index', i)
517
+ execute_btn.clicked.connect(lambda checked, idx=i: self._execute_command(idx))
518
+ execute_btn.setToolTip("立即执行")
519
+ execute_btn.setStyleSheet("""
520
+ QPushButton {
521
+ background: transparent;
522
+ border: none;
523
+ font-size: 16px;
524
+ }
525
+ QPushButton:hover {
526
+ background-color: rgba(76, 175, 80, 0.1);
527
+ border-radius: 3px;
528
+ }
529
+ QPushButton:pressed {
530
+ background-color: rgba(76, 175, 80, 0.2);
531
+ border-radius: 3px;
532
+ }
533
+ """)
534
+ button_layout.addWidget(execute_btn)
535
+
536
+ # Add to frame layout
537
+ command_item_layout.addWidget(radio)
538
+ command_item_layout.addStretch()
539
+ command_item_layout.addLayout(button_layout)
540
+
541
+ # Add frame to grid layout
542
+ commands_layout.addWidget(command_frame, row, col)
543
+
544
+ commands_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
545
+ scroll_area.setWidget(commands_widget)
546
+ self.content_layout.addWidget(scroll_area)
547
+
548
+ def _create_empty_state(self):
549
+ """创建空状态提示"""
550
+ empty_widget = QWidget()
551
+ empty_layout = QVBoxLayout(empty_widget)
552
+ empty_layout.setContentsMargins(20, 20, 20, 20)
553
+
554
+ empty_layout.addStretch()
555
+
556
+ # 主提示标签
557
+ empty_label = QLabel(f"💡 暂无{self.command_type}指令")
558
+ empty_label.setAlignment(Qt.AlignCenter)
559
+ empty_label.setStyleSheet("""
560
+ QLabel {
561
+ font-size: 16px;
562
+ font-weight: bold;
563
+ color: #666666;
564
+ margin-bottom: 10px;
565
+ }
566
+ """)
567
+ empty_layout.addWidget(empty_label)
568
+
569
+ # 操作提示标签
570
+ help_label = QLabel("点击右上角 ➕ 按钮添加您的第一个指令")
571
+ help_label.setAlignment(Qt.AlignCenter)
572
+ help_label.setStyleSheet("""
573
+ QLabel {
574
+ font-size: 12px;
575
+ color: #888888;
576
+ margin-top: 5px;
577
+ }
578
+ """)
579
+ empty_layout.addWidget(help_label)
580
+
581
+ empty_layout.addStretch()
582
+
583
+ self.content_layout.addWidget(empty_widget)
584
+
585
+ def _handle_radio_click(self, radio_button):
586
+ """处理radio按钮点击"""
587
+ if radio_button.isChecked():
588
+ # 取消其他radio的选中状态
589
+ for other_radio in self.command_radios:
590
+ if other_radio != radio_button and other_radio.isChecked():
591
+ other_radio.setChecked(False)
592
+
593
+ # 编辑按钮已移除,不再更新状态
594
+
595
+ def _execute_command(self, command_index: int):
596
+ """执行指令"""
597
+ if 0 <= command_index < len(self.commands):
598
+ command_content = self.commands[command_index]['content']
599
+ if command_content:
600
+ self.command_executed.emit(command_content)
601
+
602
+ # 添加/编辑指令方法已移除
603
+ # 用户需要直接编辑 .md 文件来管理指令
604
+
605
+
606
+ class ProjectCommandTab(BaseCommandTab):
607
+ """项目指令选项卡"""
608
+
609
+ def __init__(self, project_path: str, command_manager: CommandManager, parent=None):
610
+ super().__init__(project_path, command_manager, "项目", parent)
611
+
612
+ def _load_commands(self) -> List[Dict[str, Any]]:
613
+ """加载项目指令"""
614
+ return self.command_manager.load_project_commands()
615
+
616
+ def _get_default_command_type_for_dialog(self) -> str:
617
+ """获取对话框的默认指令类型"""
618
+ return "project"
619
+
620
+
621
+
622
+
623
+ class PersonalCommandTab(BaseCommandTab):
624
+ """个人指令选项卡"""
625
+
626
+ def __init__(self, project_path: str, command_manager: CommandManager, parent=None):
627
+ super().__init__(project_path, command_manager, "个人", parent)
628
+
629
+ def _load_commands(self) -> List[Dict[str, Any]]:
630
+ """加载个人指令"""
631
+ return self.command_manager.load_personal_commands()
632
+
633
+ def _get_default_command_type_for_dialog(self) -> str:
634
+ """获取对话框的默认指令类型"""
635
+ return "private"
636
+
components/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """
2
+ 通用组件模块
3
+
4
+ 包含可复用的UI组件。
5
+ """
6
+
7
+ from .feedback_text_edit import FeedbackTextEdit
8
+ from .markdown_display import MarkdownDisplayWidget
9
+ from .command_popup import CommandPopup
10
+
11
+ __all__ = [
12
+ 'FeedbackTextEdit',
13
+ 'MarkdownDisplayWidget',
14
+ 'CommandPopup'
15
+ ]