feat(chat,todo): 小埃同学对话窗口与待办窗口

Chat:
- DTK2 窗口:左侧会话列表 + 右侧对话区 + 输入框
- DeepSeek / 自定义 API,SSE 流式输出,首字即显示
- 会话持久化到 chat.json,新建/切换/标题自动生成
- 设置对话框:API Key、API 地址、模型、系统提示,含开通指引
- 拖放:左侧空白=新建对话,左侧会话=指定会话,右侧对话区=当前会话
- Ctrl+Enter 发送,错误以消息形式回显

Todo:
- 待办窗口(layer-shell,不抢焦点、不进 dock),复选框/删除/新建
- 待办持久化 todo.json,--todo 打开,独立 desktop 入口

其他:
- desktop 入口改为打开对话窗口,--chat 参数
- 单实例消息支持 show/todo/chat
This commit is contained in:
2026-10-30 20:12:31 +08:00
parent 150a49af2f
commit e3c8da1a3f
12 changed files with 1363 additions and 10 deletions
+34 -6
View File
@@ -3,9 +3,12 @@
#include "app.h"
#include "chat/chatwindow.h"
#include "config.h"
#include "portal/portalclient.h"
#include "staging/stagingmodel.h"
#include "todo/todomodel.h"
#include "todo/todowindow.h"
#include "ui/portalpanel.h"
#include <QLocalServer>
@@ -19,7 +22,7 @@ App::App(QObject *parent)
{
}
bool App::init(bool daemon, bool forceShow)
bool App::init(bool daemon, bool forceShow, bool showTodo, bool showChat)
{
Config::instance()->init();
m_daemon = daemon;
@@ -29,7 +32,7 @@ bool App::init(bool daemon, bool forceShow)
QLocalSocket probe;
probe.connectToServer(serverName);
if (probe.waitForConnected(200)) {
probe.write("show");
probe.write(showChat ? "chat" : (showTodo ? "todo" : "show"));
probe.waitForBytesWritten(200);
probe.disconnectFromServer();
return false;
@@ -41,7 +44,12 @@ bool App::init(bool daemon, bool forceShow)
connect(m_server, &QLocalServer::newConnection, this, [this]() {
while (QLocalSocket *socket = m_server->nextPendingConnection()) {
connect(socket, &QLocalSocket::readyRead, this, [this, socket]() {
if (socket->readAll().contains("show")) {
const QByteArray data = socket->readAll();
if (data.contains("chat")) {
showChatWindow();
} else if (data.contains("todo")) {
showTodoWindow();
} else if (data.contains("show")) {
showPanel();
}
});
@@ -80,6 +88,12 @@ bool App::init(bool daemon, bool forceShow)
}
});
if (showChat) {
showChatWindow();
}
if (showTodo) {
showTodoWindow();
}
if (!daemon || forceShow || !m_model->isEmpty()) {
showPanel();
}
@@ -94,9 +108,7 @@ void App::ensurePanel()
}
m_panel = new PortalPanel(m_model);
connect(m_panel, &PortalPanel::openChatRequested, this, []() {
// TODO(M4): open the AI See chat window.
});
connect(m_panel, &PortalPanel::openChatRequested, this, &App::showChatWindow);
}
void App::showPanel()
@@ -104,3 +116,19 @@ void App::showPanel()
ensurePanel();
m_panel->popup();
}
void App::showChatWindow()
{
if (!m_chatWindow) {
m_chatWindow = new ChatWindow;
}
m_chatWindow->activateForAsk();
}
void App::showTodoWindow()
{
if (!m_todoWindow) {
m_todoWindow = new TodoWindow(TodoModel::instance());
}
m_todoWindow->popup();
}