Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion TeXmacs/progs/kernel/gui/kbd-define.scm
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,17 @@
(cond ((null? l) l)
((ahash-ref lazy-keyboard-done (cdar l)) (lazy-keyboard-force-do (cdr l)))
((or lazy-force-all? (texmacs-in-mode? (caar l)))
(module-provide (cdar l))
(let ((start (texmacs-time)))
(module-provide (cdar l))
(debug-message "debug-std"
(string-append "lazy-keyboard-force: loaded "
(object->string (cdar l))
" in "
(number->string (- (texmacs-time) start))
" ms\n"
) ;string-append
) ;debug-message
) ;let
(ahash-set! lazy-keyboard-done (cdar l) #t)
(lazy-keyboard-force-do (cdr l))
) ;
Expand Down
34 changes: 34 additions & 0 deletions devel/1182.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# [1182] LLM Chat 标签页打开耗时打点

## 1 任务相关的代码文件
- `src/Plugins/Qt/qt_chat_controller.cpp` — ChatController::createView 及各 scheme 调用点
- `src/Plugins/Qt/qt_chat_tab_widget.cpp/.hpp` — QTChatTabWidget,首次 paintEvent 打点
- `src/Plugins/Qt/qt_tm_widget.cpp` — sync_chat_tab_mode,show+focus 打点
- `TeXmacs/progs/kernel/gui/kbd-define.scm` — lazy-keyboard-force 逐模块加载计时

## 2 改动记录

### 2.1 点击 LLM 标签页全链路耗时打点(2026-08-07)

**What**:在点击 Chat 标签页 → 界面首次绘制的全链路加 `debug_std` 计时日志
(`-d std` 开启)。

**Why**:实测点击 LLM 标签页到界面显示约 400ms,需要定位耗时分布。

**How**:
- `ChatController::createView`:分 7 段计时(use-modules / load-all /
build-infos / new-widget / connect / activate / misc + total)
- `ChatController::ensureNewConversation`:create-panel / sync-dark-style /
load-input-styles / activate-panel 分段
- `ChatController::getOrCreatePanel`、`loadSessionContent`:scheme 调用计时
- `QTChatTabWidget`:新增 `paintEvent` override + `armFirstPaintLog`,
首次绘制时输出 show→paint 耗时(界面真正可见时刻)
- `sync_chat_tab_mode`:show_widget_in_layout + setFocus 计时
- `kbd-define.scm`:`lazy-keyboard-force-do` 每个 `module-provide` 计时,
覆盖 update_menus 之后 lazy kbd 模块加载的空档

**实测分布**(点击到显示 ~400ms):
- `use-modules (llm chat-loader)` ~57ms
- scheme init(sync-dark-style + load-input-styles)~81ms
- `update_menus` ~97ms
- 其余为 lazy kbd 模块加载与首次 paint
43 changes: 43 additions & 0 deletions src/Plugins/Qt/qt_chat_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "new_buffer.hpp"
#include "s7_tm.hpp"
#include "scheme.hpp"
#include "tm_debug.hpp"
#include "tm_timer.hpp"

#include <QApplication>
#include <QDir>
Expand Down Expand Up @@ -54,10 +56,13 @@ ChatController::destroyView () {

QWidget*
ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) {
time_t t0= texmacs_time ();
// 1. Load session metadata
// llm 插件按 idle 延迟初始化,新建 Chat 标签页时其 scheme 模块可能尚未加载
eval ("(use-modules (llm chat-loader))");
time_t t1= texmacs_time ();
call ("chat-persist-load-all");
time_t t2= texmacs_time ();
cout << "[chat-persist] ChatController: restored "
<< sessionManager_.sessionCount () << " session metadatas" << LF;

Expand All @@ -72,10 +77,12 @@ ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) {
else {
initialId= sessionManager_.firstActiveSessionId ();
}
time_t t3= texmacs_time ();

// 3. 创建 View,Sidebar 构造时就有数据
view_= new QTChatTabWidget (infos, initialId, parent);
view_->setParentTmWidget (tm);
time_t t4= texmacs_time ();

// 连接 Sidebar 信号
ChatSidebar* sb= view_->sidebar ();
Expand Down Expand Up @@ -128,12 +135,14 @@ ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) {
}

// 4. 激活初始会话(按需创建 Panel)
time_t t5= texmacs_time ();
if (!is_empty (initialId)) {
activateSession (initialId);
}
else {
ensureNewConversation ();
}
time_t t6= texmacs_time ();

// 5. 恢复当前模型(使用激活的会话)
if (!is_empty (initialId)) {
Expand All @@ -149,6 +158,16 @@ ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) {
return view_->contentWidget ();
});

if (DEBUG_STD) {
time_t t7= texmacs_time ();
debug_std << "[chat-tab] createView: use-modules=" << (t1 - t0)
<< "ms, load-all=" << (t2 - t1) << "ms, build-infos=" << (t3 - t2)
<< "ms, new-widget=" << (t4 - t3) << "ms, connect=" << (t5 - t4)
<< "ms, activate=" << (t6 - t5) << "ms, misc=" << (t7 - t6)
<< "ms, total=" << (t7 - t0) << "ms\n";
}
view_->armFirstPaintLog (texmacs_time ());

return view_;
}

Expand Down Expand Up @@ -486,8 +505,13 @@ ChatController::loadSessionContent (ChatConversationPanel* panel) {
// 只在非归档会话且内容未加载时才加载
if (s->archived) return;

time_t t0= texmacs_time ();
call ("chat-persist-load-session-content", panel->sessionId (),
object (s->defaultExpandCount));
if (DEBUG_STD) {
debug_std << "[chat-tab] loadSessionContent: scheme-load="
<< (texmacs_time () - t0) << "ms\n";
}

// 检查消息 buffer 是否非空,若非空则进入会话模式并滚动到底部
tree msgBody= get_buffer_body (
Expand Down Expand Up @@ -595,15 +619,19 @@ ChatController::ensureNewConversation () {
}

// 创建新会话
time_t t0 = texmacs_time ();
string sid = sessionManager_.createSession ();
ChatConversationPanel* panel= view_->createPanel (sid);
if (!panel) return;
time_t t1= texmacs_time ();

sessionManager_.setPanel (sid, panel);
sessionManager_.setModel (sid, currentModel_);

call ("chat-tab-sync-dark-style!", sid);
time_t t1b= texmacs_time ();
call ("chat-tab-load-input-styles!", sid);
time_t t2= texmacs_time ();

if (panel->sessionTitle ()) panel->sessionTitle ()->hide ();

Expand All @@ -612,6 +640,15 @@ ChatController::ensureNewConversation () {

view_->activatePanel (panel);
view_->sidebar ()->setActiveItem ("");

if (DEBUG_STD) {
time_t t3= texmacs_time ();
debug_std << "[chat-tab] ensureNewConversation: create-panel=" << (t1 - t0)
<< "ms, sync-dark-style=" << (t1b - t1)
<< "ms, load-input-styles=" << (t2 - t1b)
<< "ms, activate-panel=" << (t3 - t2) << "ms, total=" << (t3 - t0)
<< "ms\n";
}
}

/**
Expand All @@ -629,13 +666,19 @@ ChatController::getOrCreatePanel (const string& sessionId) {
if (s->panel) return static_cast<ChatConversationPanel*> (s->panel);

// 按需创建面板
time_t t0 = texmacs_time ();
ChatConversationPanel* panel= view_->createPanel (sessionId);
if (!panel) return nullptr;
time_t t1= texmacs_time ();

sessionManager_.setPanel (sessionId, panel);

call ("chat-tab-sync-dark-style!", sessionId);
call ("chat-tab-init-session!", sessionId, s->model);
if (DEBUG_STD) {
debug_std << "[chat-tab] getOrCreatePanel: create-panel=" << (t1 - t0)
<< "ms, scheme-init=" << (texmacs_time () - t1) << "ms\n";
}

// 连接 Panel 的信号
connectPanelSignals (panel);
Expand Down
13 changes: 13 additions & 0 deletions src/Plugins/Qt/qt_chat_tab_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "qt_utilities.hpp"
#include "qt_widget.hpp"
#include "s7_tm.hpp"
#include "tm_debug.hpp"
#include "tm_timer.hpp"
#include "tm_window.hpp"

#include <moebius/tree_label.hpp>
Expand Down Expand Up @@ -1672,6 +1674,17 @@ QTChatTabWidget::setGlobalSidebarCollapsed (bool collapsed) {
* QTChatTabWidget 事件处理
******************************************************************************/

void
QTChatTabWidget::paintEvent (QPaintEvent* event) {
if (firstPaintPending_) {
firstPaintPending_= false;
if (DEBUG_STD)
debug_std << "[chat-tab] first paint: show->paint="
<< (texmacs_time () - createFinishTime_) << "ms\n";
}
QWidget::paintEvent (event);
}

void
QTChatTabWidget::keyPressEvent (QKeyEvent* event) {
// Ctrl/Cmd+J:关闭 AI 聊天侧边栏(仅在 dock 模式下触发)
Expand Down
12 changes: 12 additions & 0 deletions src/Plugins/Qt/qt_chat_tab_widget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <QList>
#include <QMap>
#include <QWidget>
#include <ctime>

#include "widget.hpp"

Expand Down Expand Up @@ -398,6 +399,12 @@ class QTChatTabWidget : public QWidget {
// ---- 供外部组件访问 ----
QWidget* contentWidget () const { return contentWidget_; }

/// 标记 createView 完成,等待首次 paint 打点(性能定位用)
void armFirstPaintLog (time_t createFinishTime) {
firstPaintPending_= true;
createFinishTime_ = createFinishTime;
}

signals:
void cancelRequested (const string& sessionId);
void newChatRequested ();
Expand All @@ -409,6 +416,8 @@ class QTChatTabWidget : public QWidget {
void keyReleaseEvent (QKeyEvent* event) override;
/// 事件过滤器
bool eventFilter (QObject* watched, QEvent* event) override;
/// 首次绘制时打点(用于测量点击标签页到界面可见的耗时)
void paintEvent (QPaintEvent* event) override;

private:
/// 构建左侧侧边栏布局
Expand Down Expand Up @@ -441,6 +450,9 @@ class QTChatTabWidget : public QWidget {
qt_tm_widget_rep* parentTmWidget_= nullptr; ///< 关联的 TeXmacs widget

static bool globalSidebarCollapsed_; ///< 全局记忆的侧边栏折叠状态

bool firstPaintPending_= false; ///< 是否等待首次绘制打点
time_t createFinishTime_ = 0; ///< createView 完成时刻(ms)
};

#endif // QT_CHAT_TAB_WIDGET_HPP
6 changes: 6 additions & 0 deletions src/Plugins/Qt/qt_tm_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
#include "qt_renderer.hpp"
#include "qt_tm_widget.hpp"
#include "qt_utilities.hpp"
#include "tm_debug.hpp"
#include "tm_timer.hpp"

bool in_presentation_mode ();

Expand Down Expand Up @@ -1259,8 +1261,12 @@ qt_tm_widget_rep::sync_chat_tab_mode () {
chatContentWidget=
get_chat_controller ()->createView (centralwidget (), this);
}
time_t t_show0= texmacs_time ();
show_widget_in_layout (chatContentWidget, layout);
chatContentWidget->setFocus (Qt::OtherFocusReason);
if (DEBUG_STD)
debug_std << "[chat-tab] sync_chat_tab_mode: show+focus="
<< (texmacs_time () - t_show0) << "ms\n";
}
else {
// Show normal editor view only when no special tab mode is active
Expand Down
Loading