feat: shared-page message merging + UI reorganization
Shared Page: - Merge consecutive assistant text messages (same iteration) - Add iteration field to thread API response - Unified rendering for single/thread messages UI Reorganization (manual changes): - Session + Agent forms: collapsible 'Weitere Einstellungen' section - Agent defaults: tool_choice unchecked, web_search 2025, model opus-4-6 - Profile: default-path field hidden, divider removed - Form spacing adjustments, .more toggle style
This commit is contained in:
1 parent
4e1ed81f37
commit
4ebbeaa818
6 files changed
+133
-96
No files matched your search
@@ -126,7 +126,8 @@ public static class SharedEndpoint
|
|||||||
m.Id,
|
m.Id,
|
||||||
m.Role,
|
m.Role,
|
||||||
m.Content,
|
m.Content,
|
||||||
m.CreatedAt
|
m.CreatedAt,
|
||||||
|
m.Iteration
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@
|
|||||||
margin-top: var(--box-spacing);
|
margin-top: var(--box-spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.more {
|
||||||
|
margin: var(--box-spacing) auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar-section {
|
.avatar-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -55,7 +60,7 @@
|
|||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--badge-spacing);
|
gap: var(--badge-spacing);
|
||||||
margin-block: var(--box-spacing) calc(var(--page-spacing) * 4);
|
margin-block: var(--box-spacing) calc(var(--page-spacing) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Custom Commands ── */
|
/* ── Custom Commands ── */
|
||||||
@@ -211,12 +216,6 @@ label.skill-item {
|
|||||||
color: var(--dangerous);
|
color: var(--dangerous);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-divider {
|
|
||||||
border: none;
|
|
||||||
border-top: var(--border-width) solid var(--f2-border);
|
|
||||||
margin: var(--box-spacing) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-btn {
|
.danger-btn {
|
||||||
color: var(--dangerous);
|
color: var(--dangerous);
|
||||||
border-color: currentcolor;
|
border-color: currentcolor;
|
||||||
|
|||||||
+81
-72
@@ -108,34 +108,38 @@
|
|||||||
</div>
|
</div>
|
||||||
<label>Name</label>
|
<label>Name</label>
|
||||||
<input type="text" id="sessionName" placeholder="Name der Session">
|
<input type="text" id="sessionName" placeholder="Name der Session">
|
||||||
<label>Beschreibung</label>
|
|
||||||
<input id="sessionDescription" placeholder="Kurze Beschreibung (optional)">
|
|
||||||
<label>Agent</label>
|
<label>Agent</label>
|
||||||
<select id="agentSelect"></select>
|
<select id="agentSelect"></select>
|
||||||
<label>Workspace</label>
|
<div class="more">
|
||||||
<div class="input-with-btn">
|
<a>Weitere Einstellungen</a>
|
||||||
<input type="text" id="sessionWorkspace" placeholder="Absoluter Pfad zum Arbeitsverzeichnis (optional)">
|
<div hidden>
|
||||||
<button type="button" id="sessionWorkspaceBrowse" class="icon-btn"><i class="fa fa-folder"></i></button>
|
<label>Beschreibung</label>
|
||||||
</div>
|
<input id="sessionDescription" placeholder="Kurze Beschreibung (optional)">
|
||||||
<label>Kontext</label>
|
<label>Workspace</label>
|
||||||
<textarea id="sessionContext" rows="4" placeholder="Zusätzlicher Kontext für den System-Prompt (optional)"></textarea>
|
<div class="input-with-btn">
|
||||||
<label>Benutzerdefinierte Befehle</label>
|
<input type="text" id="sessionWorkspace" placeholder="Absoluter Pfad zum Arbeitsverzeichnis (optional)">
|
||||||
<div class="custom-commands" id="customCommandsList"></div>
|
<button type="button" id="sessionWorkspaceBrowse" class="icon-btn"><i class="fa fa-folder"></i></button>
|
||||||
<button type="button" id="addCommandBtn" class="add-cmd-btn">+ Befehl hinzufügen</button>
|
</div>
|
||||||
|
<label>Kontext</label>
|
||||||
|
<textarea id="sessionContext" rows="4" placeholder="Zusätzlicher Kontext für den System-Prompt (optional)"></textarea>
|
||||||
|
<label>Benutzerdefinierte Befehle</label>
|
||||||
|
<div class="custom-commands" id="customCommandsList"></div>
|
||||||
|
<button type="button" id="addCommandBtn" class="add-cmd-btn">+ Befehl hinzufügen</button>
|
||||||
|
|
||||||
<label><i class="fa fa-paper-plane"></i> <b>Telegram Bot</b></label>
|
<label><i class="fa fa-paper-plane"></i> <b>Telegram Bot</b></label>
|
||||||
<span>Erstelle einen Bot via <a href="https://t.me/BotFather" target="_blank">@BotFather</a></span>
|
<span>Erstelle einen Bot via <a href="https://t.me/BotFather" target="_blank">@BotFather</a></span>
|
||||||
<label>Bot-Token</label>
|
<label>Bot-Token</label>
|
||||||
<input type="text" id="sessionTelegramToken" placeholder="123456:ABC-DEF..." autocomplete="off">
|
<input type="text" id="sessionTelegramToken" placeholder="123456:ABC-DEF..." autocomplete="off">
|
||||||
<label>Webhook-Secret</label>
|
<label>Webhook-Secret</label>
|
||||||
<input type="text" id="sessionTelegramSecret" readonly style="opacity: var(--semi-transparent); ">
|
<input type="text" id="sessionTelegramSecret" readonly style="opacity: var(--semi-transparent); ">
|
||||||
<label>Status</label>
|
<label>Status</label>
|
||||||
<div id="sessionTelegramStatusBox" class="telegram-status-box">Kein Status verfügbar</div>
|
<div id="sessionTelegramStatusBox" class="telegram-status-box">Kein Status verfügbar</div>
|
||||||
<div class="telegram-actions">
|
<div class="telegram-actions">
|
||||||
<button type="button" id="sessionTelegramRegisterBtn" hidden>Webhook registrieren</button>
|
<button type="button" id="sessionTelegramRegisterBtn" hidden>Webhook registrieren</button>
|
||||||
<button type="button" id="sessionTelegramDeleteBtn" class="danger-btn" hidden>Webhook entfernen</button>
|
<button type="button" id="sessionTelegramDeleteBtn" class="danger-btn" hidden>Webhook entfernen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button id="sessionSaveBtn">Speichern</button>
|
<button id="sessionSaveBtn">Speichern</button>
|
||||||
<button id="sessionCancelBtn">Abbrechen</button>
|
<button id="sessionCancelBtn">Abbrechen</button>
|
||||||
@@ -162,57 +166,62 @@
|
|||||||
</div>
|
</div>
|
||||||
<label>Name</label>
|
<label>Name</label>
|
||||||
<input type="text" id="agentName" placeholder="Name des Agents">
|
<input type="text" id="agentName" placeholder="Name des Agents">
|
||||||
<label>Beschreibung</label>
|
|
||||||
<input type="text" id="agentDescription" placeholder="Kurze Beschreibung (optional)">
|
|
||||||
<label>Model</label>
|
<label>Model</label>
|
||||||
<select id="agentModel">
|
<select id="agentModel">
|
||||||
<option value="sonnet-4-5">Sonnet 4.5</option>
|
<option value="sonnet-4-5">Sonnet 4.5</option>
|
||||||
<option value="sonnet-4-6">Sonnet 4.6</option>
|
<option value="sonnet-4-6">Sonnet 4.6</option>
|
||||||
<option value="opus-4-5">Opus 4.5</option>
|
<option value="opus-4-5">Opus 4.5</option>
|
||||||
<option value="opus-4-6">Opus 4.6</option>
|
<option value="opus-4-6" selected>Opus 4.6</option>
|
||||||
<option value="haiku-4-5">Haiku 4.5</option>
|
<option value="haiku-4-5">Haiku 4.5</option>
|
||||||
</select>
|
</select>
|
||||||
<label class="checkbox-label">
|
<div class="more">
|
||||||
<input type="checkbox" id="agentToolChoice" checked>
|
<a>Weitere Einstellungen</a>
|
||||||
Tool-Nutzung erzwingen <span class="checkbox-hint">(tool_choice: any)</span>
|
<div hidden>
|
||||||
</label>
|
<label>Beschreibung</label>
|
||||||
<label>Git-Commit-Verhalten</label>
|
<input type="text" id="agentDescription" placeholder="Kurze Beschreibung (optional)">
|
||||||
<select id="agentAutoCommit">
|
<label class="checkbox-label">
|
||||||
<option value="">Nicht erwähnen</option>
|
<input type="checkbox" id="agentToolChoice">
|
||||||
<option value="mention">Anmerken (nur auf Anweisung)</option>
|
Tool-Nutzung erzwingen <span class="checkbox-hint">(tool_choice: any)</span>
|
||||||
<option value="enforce">Erzwingen (nach jeder Änderung)</option>
|
</label>
|
||||||
<option value="forbid">Verbieten (strikt verboten)</option>
|
<label>Git-Commit-Verhalten</label>
|
||||||
</select>
|
<select id="agentAutoCommit">
|
||||||
<label>Ego</label>
|
<option value="">Nicht erwähnen</option>
|
||||||
<label class="checkbox-label">
|
<option value="mention">Anmerken (nur auf Anweisung)</option>
|
||||||
<input type="checkbox" id="agentUseSoul" checked>
|
<option value="enforce">Erzwingen (nach jeder Änderung)</option>
|
||||||
Hat eine Seele <span class="checkbox-hint">(SOUL.md im System-Prompt)</span>
|
<option value="forbid">Verbieten (strikt verboten)</option>
|
||||||
</label>
|
</select>
|
||||||
<label class="checkbox-label">
|
<label>Ego</label>
|
||||||
<input type="checkbox" id="agentUseIdentity" checked>
|
<label class="checkbox-label">
|
||||||
Hat eine Identität <span class="checkbox-hint">(IDENTITY.md im System-Prompt)</span>
|
<input type="checkbox" id="agentUseSoul">
|
||||||
</label>
|
Hat eine Seele <span class="checkbox-hint">(SOUL.md im System-Prompt)</span>
|
||||||
<label class="checkbox-label">
|
</label>
|
||||||
<input type="checkbox" id="agentUseUserInfo" checked>
|
<label class="checkbox-label">
|
||||||
Kennt sein Gegenüber <span class="checkbox-hint">(Benutzer-Persona im System-Prompt)</span>
|
<input type="checkbox" id="agentUseIdentity">
|
||||||
</label>
|
Hat eine Identität <span class="checkbox-hint">(IDENTITY.md im System-Prompt)</span>
|
||||||
<label class="checkbox-label">
|
</label>
|
||||||
<input type="checkbox" id="agentUseMemory" checked>
|
<label class="checkbox-label">
|
||||||
Zugriff auf Erinnerungen <span class="checkbox-hint">(Memory-Tools verfügbar)</span>
|
<input type="checkbox" id="agentUseUserInfo">
|
||||||
</label>
|
Kennt sein Gegenüber <span class="checkbox-hint">(Benutzer-Persona im System-Prompt)</span>
|
||||||
<label>Web-Suche</label>
|
</label>
|
||||||
<select id="agentWebSearchVersion">
|
<label class="checkbox-label">
|
||||||
<option value="">Deaktiviert</option>
|
<input type="checkbox" id="agentUseMemory">
|
||||||
<option value="web_search_20250305">Aktiviert - Version 2025</option>
|
Zugriff auf Erinnerungen <span class="checkbox-hint">(Memory-Tools verfügbar)</span>
|
||||||
<option value="web_search_20260209">Aktiviert - Version 2026</option>
|
</label>
|
||||||
</select>
|
<label>Web-Suche</label>
|
||||||
<label>Skills</label>
|
<select id="agentWebSearchVersion">
|
||||||
<div id="agentSkillsSection" hidden>
|
<option value="">Deaktiviert</option>
|
||||||
<div class="skill-list" id="agentSkillList"></div>
|
<option value="web_search_20250305" selected>Aktiviert - Version 2025</option>
|
||||||
|
<option value="web_search_20260209">Aktiviert - Version 2026</option>
|
||||||
|
</select>
|
||||||
|
<label>Skills</label>
|
||||||
|
<div id="agentSkillsSection" hidden>
|
||||||
|
<div class="skill-list" id="agentSkillList"></div>
|
||||||
|
</div>
|
||||||
|
<label>Benutzerdefinierte Befehle</label>
|
||||||
|
<div class="custom-commands" id="agentCommandsList"></div>
|
||||||
|
<button type="button" id="agentAddCommandBtn" class="add-cmd-btn">+ Befehl hinzufügen</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label>Benutzerdefinierte Befehle</label>
|
|
||||||
<div class="custom-commands" id="agentCommandsList"></div>
|
|
||||||
<button type="button" id="agentAddCommandBtn" class="add-cmd-btn">+ Befehl hinzufügen</button>
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button id="agentSaveBtn">Speichern</button>
|
<button id="agentSaveBtn">Speichern</button>
|
||||||
<button id="agentCancelBtn">Abbrechen</button>
|
<button id="agentCancelBtn">Abbrechen</button>
|
||||||
@@ -397,8 +406,8 @@
|
|||||||
<input type="text" id="profileUsername" disabled>
|
<input type="text" id="profileUsername" disabled>
|
||||||
<label>Anzeigename</label>
|
<label>Anzeigename</label>
|
||||||
<input type="text" id="profileDisplayName" placeholder="Anzeigename">
|
<input type="text" id="profileDisplayName" placeholder="Anzeigename">
|
||||||
<label>Standard-Pfad</label>
|
<label hidden>Standard-Pfad</label>
|
||||||
<input type="text" id="profileDefaultPath" disabled placeholder="Wird vom Administrator festgelegt">
|
<input type="text" id="profileDefaultPath" disabled placeholder="Wird vom Administrator festgelegt" hidden>
|
||||||
<label>Persona</label>
|
<label>Persona</label>
|
||||||
<span class="setting-hint">Wird im System-Prompt verwendet, wenn der Agent "Kennt sein Gegenüber" aktiviert hat.</span>
|
<span class="setting-hint">Wird im System-Prompt verwendet, wenn der Agent "Kennt sein Gegenüber" aktiviert hat.</span>
|
||||||
<textarea id="profilePersona" rows="8" placeholder="Beschreibe dich selbst – Name, Rolle, Präferenzen, Kommunikationsstil..."></textarea>
|
<textarea id="profilePersona" rows="8" placeholder="Beschreibe dich selbst – Name, Rolle, Präferenzen, Kommunikationsstil..."></textarea>
|
||||||
@@ -406,7 +415,6 @@
|
|||||||
<button id="profileSaveBtn">Speichern</button>
|
<button id="profileSaveBtn">Speichern</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-message" id="profileMessage" hidden></div>
|
<div class="profile-message" id="profileMessage" hidden></div>
|
||||||
<hr class="profile-divider">
|
|
||||||
<h2>Passwort ändern</h2>
|
<h2>Passwort ändern</h2>
|
||||||
<label>Aktuelles Passwort</label>
|
<label>Aktuelles Passwort</label>
|
||||||
<input type="password" id="profileCurrentPassword" placeholder="Aktuelles Passwort" autocomplete="current-password">
|
<input type="password" id="profileCurrentPassword" placeholder="Aktuelles Passwort" autocomplete="current-password">
|
||||||
@@ -418,8 +426,9 @@
|
|||||||
<button id="profilePasswordBtn">Passwort ändern</button>
|
<button id="profilePasswordBtn">Passwort ändern</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-message" id="profilePasswordMessage" hidden></div>
|
<div class="profile-message" id="profilePasswordMessage" hidden></div>
|
||||||
<hr class="profile-divider">
|
<div class="form-actions">
|
||||||
<button id="profileLogoutBtn" class="danger-btn">Abmelden</button>
|
<button id="profileLogoutBtn" class="danger-btn">Abmelden</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ registerPage('agent', {
|
|||||||
const addCommandBtn = document.getElementById('agentAddCommandBtn');
|
const addCommandBtn = document.getElementById('agentAddCommandBtn');
|
||||||
const skillsSection = document.getElementById('agentSkillsSection');
|
const skillsSection = document.getElementById('agentSkillsSection');
|
||||||
const skillList = document.getElementById('agentSkillList');
|
const skillList = document.getElementById('agentSkillList');
|
||||||
|
const more = document.querySelector('.more');
|
||||||
|
|
||||||
_agentAvatarData = null;
|
_agentAvatarData = null;
|
||||||
|
|
||||||
@@ -120,6 +121,13 @@ registerPage('agent', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
more.addEventListener('click', () => {
|
||||||
|
const content = more.querySelector('[hidden]');
|
||||||
|
while (content.firstChild)
|
||||||
|
more.parentNode.insertBefore(content.firstChild, more);
|
||||||
|
more.remove();
|
||||||
|
});
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
saveBtn.addEventListener('click', async () => {
|
saveBtn.addEventListener('click', async () => {
|
||||||
const data = {
|
const data = {
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ registerPage('session', {
|
|||||||
const deleteBtn = document.getElementById('sessionDeleteBtn');
|
const deleteBtn = document.getElementById('sessionDeleteBtn');
|
||||||
const commandsList = document.getElementById('customCommandsList');
|
const commandsList = document.getElementById('customCommandsList');
|
||||||
const addCommandBtn = document.getElementById('addCommandBtn');
|
const addCommandBtn = document.getElementById('addCommandBtn');
|
||||||
|
const more = document.querySelector('.more');
|
||||||
|
|
||||||
_sessionImageData = null;
|
_sessionImageData = null;
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
@@ -125,6 +126,13 @@ registerPage('session', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
more.addEventListener('click', () => {
|
||||||
|
const content = more.querySelector('[hidden]');
|
||||||
|
while (content.firstChild)
|
||||||
|
more.parentNode.insertBefore(content.firstChild, more);
|
||||||
|
more.remove();
|
||||||
|
});
|
||||||
|
|
||||||
// Workspace Browse
|
// Workspace Browse
|
||||||
const workspaceBrowse = document.getElementById('sessionWorkspaceBrowse');
|
const workspaceBrowse = document.getElementById('sessionWorkspaceBrowse');
|
||||||
if (workspaceBrowse) {
|
if (workspaceBrowse) {
|
||||||
|
|||||||
@@ -13,23 +13,18 @@ registerPage('shared', {
|
|||||||
const data = await apiGetShared(token);
|
const data = await apiGetShared(token);
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
let messages = [];
|
||||||
|
|
||||||
if (data.messages && data.messages.length > 0) {
|
if (data.messages && data.messages.length > 0) {
|
||||||
// Mehrere Nachrichten (Thread mit Kontext)
|
messages = data.messages;
|
||||||
data.messages.forEach(msg => {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.className = 'msg ' + (msg.role === 'user' ? 'user' : 'assistant') + ' markdown';
|
|
||||||
if (msg.role === 'user') {
|
|
||||||
renderUserContent(div, msg.content);
|
|
||||||
} else {
|
|
||||||
div.innerHTML = renderMarkdown(msg.content);
|
|
||||||
parseFluentEmojis(div);
|
|
||||||
wrapKreaGalleries(div);
|
|
||||||
}
|
|
||||||
container.appendChild(div);
|
|
||||||
});
|
|
||||||
} else if (data.message) {
|
} else if (data.message) {
|
||||||
// Einzelne Nachricht
|
messages = [data.message];
|
||||||
const msg = data.message;
|
}
|
||||||
|
|
||||||
|
// Aufeinanderfolgende Assistant-Text-Messages mergen (wie im Chat)
|
||||||
|
messages = _mergeConsecutiveTexts(messages);
|
||||||
|
|
||||||
|
for (const msg of messages) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'msg ' + (msg.role === 'user' ? 'user' : 'assistant') + ' markdown';
|
div.className = 'msg ' + (msg.role === 'user' ? 'user' : 'assistant') + ' markdown';
|
||||||
if (msg.role === 'user') {
|
if (msg.role === 'user') {
|
||||||
@@ -57,3 +52,20 @@ registerPage('shared', {
|
|||||||
},
|
},
|
||||||
destroy: () => {}
|
destroy: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function _mergeConsecutiveTexts(msgs) {
|
||||||
|
const result = [];
|
||||||
|
for (const msg of msgs) {
|
||||||
|
if (msg.role === 'assistant') {
|
||||||
|
const prev = result[result.length - 1];
|
||||||
|
if (prev && prev.role === 'assistant'
|
||||||
|
&& prev.iteration === msg.iteration) {
|
||||||
|
prev.content = (prev.content || '') + ' ' + (msg.content || '');
|
||||||
|
if (msg.createdAt) prev.createdAt = msg.createdAt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(Object.assign({}, msg));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user