228 lines
7.1 KiB
Python
228 lines
7.1 KiB
Python
from collections.abc import Callable
|
|
from dataclasses import dataclass, field
|
|
|
|
from cli.tui.copy import TUI_COPY
|
|
from cli.tui.search import PageFilterOption, PageSearchContext
|
|
from cli.utils import SHELL_TEXT_FORMATTER
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DetailSectionSnapshot:
|
|
"""
|
|
TUI 详情页单个分区快照。
|
|
|
|
:param title: 分区标题
|
|
:param status: 分区状态
|
|
:param lines: 分区正文文本行
|
|
"""
|
|
|
|
title: str
|
|
status: str
|
|
lines: list[str]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DetailPageSnapshot:
|
|
"""
|
|
TUI 详情页聚合快照。
|
|
|
|
:param title: 页面标题
|
|
:param subtitle: 页面副标题
|
|
:param sections: 页面分区列表
|
|
:param search: 当前页面搜索上下文
|
|
"""
|
|
|
|
title: str
|
|
subtitle: str
|
|
sections: list[DetailSectionSnapshot]
|
|
search: PageSearchContext | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BrowserRecordSnapshot:
|
|
"""
|
|
TUI 浏览页单条记录快照。
|
|
|
|
:param key: 记录唯一标识
|
|
:param title: 记录标题
|
|
:param status: 记录状态
|
|
:param summary: 记录摘要
|
|
:param metadata_lines: 记录元信息文本行
|
|
:param detail_sections: 记录联动详情分区列表
|
|
"""
|
|
|
|
key: str
|
|
title: str
|
|
status: str
|
|
summary: str
|
|
metadata_lines: list[str]
|
|
detail_sections: list[DetailSectionSnapshot]
|
|
detail_loader: Callable[[], list[DetailSectionSnapshot]] | None = None
|
|
_cached_detail_sections: tuple[DetailSectionSnapshot, ...] | None = field(
|
|
default=None,
|
|
init=False,
|
|
repr=False,
|
|
compare=False,
|
|
)
|
|
|
|
def resolve_detail_sections(self) -> list[DetailSectionSnapshot]:
|
|
"""
|
|
获取当前记录详情分区,必要时按需加载并缓存。
|
|
|
|
:return: 详情分区列表
|
|
"""
|
|
if self.detail_loader is None:
|
|
return self.detail_sections
|
|
if self._cached_detail_sections is None:
|
|
object.__setattr__(self, '_cached_detail_sections', tuple(self.detail_loader()))
|
|
return list(self._cached_detail_sections)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BrowserPageSnapshot:
|
|
"""
|
|
TUI 浏览页聚合快照。
|
|
|
|
:param title: 页面标题
|
|
:param subtitle: 页面副标题
|
|
:param records: 可浏览记录列表
|
|
:param shared_sections: 全局共享分区列表
|
|
:param filters: 页面可用筛选项
|
|
:param active_filter_key: 当前激活筛选键
|
|
:param search: 当前页面搜索上下文
|
|
"""
|
|
|
|
title: str
|
|
subtitle: str
|
|
records: list[BrowserRecordSnapshot]
|
|
shared_sections: list[DetailSectionSnapshot]
|
|
filters: list[PageFilterOption] = field(default_factory=list)
|
|
active_filter_key: str | None = None
|
|
search: PageSearchContext | None = None
|
|
|
|
|
|
class TuiAdapterModelRenderService:
|
|
"""
|
|
TUI 适配层模型渲染服务。
|
|
|
|
该对象集中封装结果负载摘要提取,以及空态、加载态、失败态文本行
|
|
的统一构建逻辑,避免这些模块级辅助函数继续散落扩张。
|
|
"""
|
|
|
|
@staticmethod
|
|
def extract_payload_message(payload: dict[str, object] | None) -> str:
|
|
"""
|
|
提取结果负载中的可读消息。
|
|
|
|
:param payload: 标准结果负载
|
|
:return: 摘要消息文本
|
|
"""
|
|
if not isinstance(payload, dict):
|
|
return '-'
|
|
if payload.get('message'):
|
|
return SHELL_TEXT_FORMATTER.truncate_text(payload.get('message', '-'), 120)
|
|
if payload.get('error'):
|
|
return SHELL_TEXT_FORMATTER.truncate_text(payload.get('error', '-'), 120)
|
|
return '-'
|
|
|
|
@staticmethod
|
|
def build_empty_lines(
|
|
*,
|
|
empty_label: str,
|
|
empty_value: str,
|
|
detail: str,
|
|
suggestion: str = TUI_COPY.build_empty_state_suggestion(),
|
|
) -> list[str]:
|
|
"""
|
|
构建统一的空态文本行。
|
|
|
|
:param empty_label: 空态主字段名称
|
|
:param empty_value: 空态主字段值
|
|
:param detail: 空态说明
|
|
:param suggestion: 建议操作
|
|
:return: 空态文本行列表
|
|
"""
|
|
return [
|
|
TUI_COPY.build_state_section_title('status'),
|
|
f'{empty_label}: {empty_value}',
|
|
'',
|
|
TUI_COPY.build_state_section_title('detail'),
|
|
detail,
|
|
'',
|
|
TUI_COPY.build_state_section_title('suggestion'),
|
|
suggestion,
|
|
]
|
|
|
|
@staticmethod
|
|
def build_loading_lines(
|
|
*,
|
|
loading_label: str,
|
|
loading_value: str,
|
|
detail: str,
|
|
suggestion: str = TUI_COPY.build_loading_state_suggestion(),
|
|
) -> list[str]:
|
|
"""
|
|
构建统一的加载中文本行。
|
|
|
|
:param loading_label: 加载态主字段名称
|
|
:param loading_value: 加载态主字段值
|
|
:param detail: 加载说明
|
|
:param suggestion: 建议操作
|
|
:return: 加载态文本行列表
|
|
"""
|
|
return [
|
|
TUI_COPY.build_state_section_title('status'),
|
|
f'{loading_label}: {loading_value}',
|
|
'',
|
|
TUI_COPY.build_state_section_title('detail'),
|
|
detail,
|
|
'',
|
|
TUI_COPY.build_state_section_title('suggestion'),
|
|
suggestion,
|
|
]
|
|
|
|
def build_failure_lines(
|
|
self,
|
|
payload: dict[str, object] | None,
|
|
*,
|
|
empty_label: str,
|
|
empty_value: str,
|
|
) -> list[str]:
|
|
"""
|
|
为失败状态构建统一的可读文本行。
|
|
|
|
:param payload: 标准结果负载
|
|
:param empty_label: 主字段标签
|
|
:param empty_value: 主字段兜底值
|
|
:return: 失败状态文本行列表
|
|
"""
|
|
lines = [
|
|
TUI_COPY.build_state_section_title('status'),
|
|
f'{empty_label}: {empty_value}',
|
|
'',
|
|
TUI_COPY.build_state_section_title('error'),
|
|
]
|
|
if not isinstance(payload, dict):
|
|
lines.append('结果消息: -')
|
|
lines.extend(
|
|
['', TUI_COPY.build_state_section_title('suggestion'), TUI_COPY.build_failure_state_suggestion()]
|
|
)
|
|
return lines
|
|
lines.append(f'结果消息: {self.extract_payload_message(payload)}')
|
|
detail_lines: list[str] = []
|
|
if payload.get('hint'):
|
|
detail_lines.append(f'建议提示: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("hint", "-"), 120)}')
|
|
if payload.get('error'):
|
|
detail_lines.append(f'错误信息: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("error", "-"), 120)}')
|
|
if payload.get('stderr'):
|
|
detail_lines.append(f'标准错误: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("stderr", "-"), 120)}')
|
|
if payload.get('stdout'):
|
|
detail_lines.append(f'标准输出: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("stdout", "-"), 120)}')
|
|
if detail_lines:
|
|
lines.extend(['', TUI_COPY.build_state_section_title('diagnostic'), *detail_lines])
|
|
lines.extend(['', TUI_COPY.build_state_section_title('suggestion'), TUI_COPY.build_failure_state_suggestion()])
|
|
return lines
|
|
|
|
|
|
TUI_ADAPTER_MODEL_RENDERER = TuiAdapterModelRenderService()
|