fix(gui): improve multi-agent-per-session handling
Session selection now auto-selects first agent: - Clicking a session row selects its first agent (not just the session) - Auto-select at startup now picks first agent of first session - Fixes issue where clicking session would clear agent and show empty chat Improved UI feedback: - Header shows 'Session / Agent' name instead of just state - State indicator gray when no agent selected - Placeholder message when no agent selected: 'Select an agent to view chat' - Chat ListView hidden when no agent (shows placeholder instead) - Input already disabled with 'No agent' placeholder (existing behavior) Cleanup: - Remove dead declaration reconcileAgentConnections(const QVariantList&)
This commit is contained in:
parent
28de16dbf1
commit
414143955f
63
gui/main.qml
63
gui/main.qml
|
|
@ -627,7 +627,22 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
if (renamingIndex >= 0) return
|
if (renamingIndex >= 0) return
|
||||||
if (type === "session") {
|
if (type === "session") {
|
||||||
ollie.setActiveSessionId(id)
|
// Find first agent in this session and select it
|
||||||
|
var foundAgent = null
|
||||||
|
for (var i = 0; i < sessionTreeModel.count; ++i) {
|
||||||
|
var item = sessionTreeModel.get(i)
|
||||||
|
if (item.type === "agent" && item.parentId === id) {
|
||||||
|
foundAgent = item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (foundAgent) {
|
||||||
|
// Select the first agent
|
||||||
|
activateAgent(id, foundAgent.agentId)
|
||||||
|
} else {
|
||||||
|
// No agents - just select session (clears agent)
|
||||||
|
ollie.setActiveSessionId(id)
|
||||||
|
}
|
||||||
toggleExpand(parent.modelIndex)
|
toggleExpand(parent.modelIndex)
|
||||||
} else if (type === "agent") {
|
} else if (type === "agent") {
|
||||||
activateAgent(parentId, agentId)
|
activateAgent(parentId, agentId)
|
||||||
|
|
@ -773,15 +788,44 @@ ApplicationWindow {
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// Header
|
// Header - shows active session/agent and state
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: 6
|
Layout.margins: 6
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
|
// Active session/agent label
|
||||||
|
Label {
|
||||||
|
text: {
|
||||||
|
var sessionName = ""
|
||||||
|
var agentName = ""
|
||||||
|
for (var i = 0; i < sessionTreeModel.count; ++i) {
|
||||||
|
var item = sessionTreeModel.get(i)
|
||||||
|
if (item.type === "session" && item.id === ollie.activeSessionId) {
|
||||||
|
sessionName = item.displayName || item.id
|
||||||
|
}
|
||||||
|
if (item.type === "agent" && item.agentId === ollie.activeAgentId && item.parentId === ollie.activeSessionId) {
|
||||||
|
agentName = item.displayName || item.agentId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sessionName && agentName) {
|
||||||
|
return sessionName + " / " + agentName
|
||||||
|
} else if (sessionName) {
|
||||||
|
return sessionName + " (no agent)"
|
||||||
|
}
|
||||||
|
return "(no session)"
|
||||||
|
}
|
||||||
|
font.bold: true
|
||||||
|
font.pointSize: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// State indicator
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 10; height: 10; radius: 5
|
width: 10; height: 10; radius: 5
|
||||||
color: {
|
color: {
|
||||||
|
if (!ollie.activeAgentId) return "#9e9e9e" // gray when no agent
|
||||||
if (ollie.activeState === "idle") return "#4caf50"
|
if (ollie.activeState === "idle") return "#4caf50"
|
||||||
if (ollie.activeState.startsWith("calling")) return "#ff9800"
|
if (ollie.activeState.startsWith("calling")) return "#ff9800"
|
||||||
return "#2196f3"
|
return "#2196f3"
|
||||||
|
|
@ -789,12 +833,10 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: ollie.activeState
|
text: ollie.activeAgentId ? ollie.activeState : ""
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.85 : 11
|
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.85 : 11
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillWidth: true }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle { Layout.fillWidth: true; height: 1; color: theme.midColor }
|
Rectangle { Layout.fillWidth: true; height: 1; color: theme.midColor }
|
||||||
|
|
@ -805,6 +847,16 @@ ApplicationWindow {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
// Placeholder when no agent selected
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: ollie.activeSessionId ? "Select an agent to view chat" : "Select a session"
|
||||||
|
color: theme.midColor
|
||||||
|
font.italic: true
|
||||||
|
font.pixelSize: theme.fontSize > 0 ? theme.fontSize : 12
|
||||||
|
visible: !ollie.activeAgentId
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: chatView
|
id: chatView
|
||||||
model: chatModel
|
model: chatModel
|
||||||
|
|
@ -812,6 +864,7 @@ ApplicationWindow {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 4
|
anchors.margins: 4
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
visible: ollie.activeAgentId !== ""
|
||||||
|
|
||||||
onCountChanged: {
|
onCountChanged: {
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
|
|
|
||||||
|
|
@ -330,9 +330,19 @@ void Ollie9pClient::refreshSessions()
|
||||||
reconcileAgentConnections();
|
reconcileAgentConnections();
|
||||||
emit sessionsChanged();
|
emit sessionsChanged();
|
||||||
|
|
||||||
// Auto-select first session if none active
|
// Auto-select first session and its first agent if none active
|
||||||
if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) {
|
if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) {
|
||||||
setActiveSessionId(m_sessions.first().toMap()["id"].toString());
|
const QVariantMap firstSession = m_sessions.first().toMap();
|
||||||
|
const QString sid = firstSession.value("id").toString();
|
||||||
|
const QVariantList agents = firstSession.value("agents").toList();
|
||||||
|
if (!agents.isEmpty()) {
|
||||||
|
// Select first agent of first session
|
||||||
|
const QString aid = agents.first().toMap().value("id").toString();
|
||||||
|
switchAgent(sid, aid);
|
||||||
|
} else {
|
||||||
|
// No agents - just select the session
|
||||||
|
setActiveSessionId(sid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
QList<AgentRecord> readAgentRecords(const QString &sessionId);
|
QList<AgentRecord> readAgentRecords(const QString &sessionId);
|
||||||
void reconcileAgentConnections(const QVariantList &sessions);
|
|
||||||
void setAgentConnected(const QString &key, bool connected);
|
void setAgentConnected(const QString &key, bool connected);
|
||||||
QString agentKey(const QString &sessionId, const QString &agentId) const;
|
QString agentKey(const QString &sessionId, const QString &agentId) const;
|
||||||
Q_INVOKABLE void setActiveAgentId(const QString &agentId);
|
Q_INVOKABLE void setActiveAgentId(const QString &agentId);
|
||||||
|
|
|
||||||
Reference in New Issue