`;
}
tablecell(e) {
let t = this.parser.parseInline(e.tokens), n = e.header ? "th" : "td";
return (e.align ? `<${n} align="${e.align}">` : `<${n}>`) + t + `${n}>
`;
}
strong({ tokens: e }) {
return `${this.parser.parseInline(e)}`;
}
em({ tokens: e }) {
return `${this.parser.parseInline(e)}`;
}
codespan({ text: e }) {
return `${w(e, true)}`;
}
br(e) {
return " ";
}
del({ tokens: e }) {
return `${this.parser.parseInline(e)}`;
}
link({ href: e, title: t, tokens: n }) {
let r = this.parser.parseInline(n), i = X(e);
if (i === null) return r;
e = i;
let s = '" + r + "", s;
}
image({ href: e, title: t, text: n, tokens: r }) {
r && (n = this.parser.parseInline(r, this.parser.textRenderer));
let i = X(e);
if (i === null) return w(n);
e = i;
let s = `", s;
}
text(e) {
return "tokens" in e && e.tokens ? this.parser.parseInline(e.tokens) : "escaped" in e && e.escaped ? e.text : w(e.text);
}
};
var $ = class {
strong({ text: e }) {
return e;
}
em({ text: e }) {
return e;
}
codespan({ text: e }) {
return e;
}
del({ text: e }) {
return e;
}
html({ text: e }) {
return e;
}
text({ text: e }) {
return e;
}
link({ text: e }) {
return "" + e;
}
image({ text: e }) {
return "" + e;
}
br() {
return "";
}
checkbox({ raw: e }) {
return e;
}
};
var b = class u2 {
options;
renderer;
textRenderer;
constructor(e) {
this.options = e || T, this.options.renderer = this.options.renderer || new P(), this.renderer = this.options.renderer, this.renderer.options = this.options, this.renderer.parser = this, this.textRenderer = new $();
}
static parse(e, t) {
return new u2(t).parse(e);
}
static parseInline(e, t) {
return new u2(t).parseInline(e);
}
parse(e) {
let t = "";
for (let n = 0; n < e.length; n++) {
let r = e[n];
if (this.options.extensions?.renderers?.[r.type]) {
let s = r, a2 = this.options.extensions.renderers[s.type].call({ parser: this }, s);
if (a2 !== false || !["space", "hr", "heading", "code", "table", "blockquote", "list", "html", "def", "paragraph", "text"].includes(s.type)) {
t += a2 || "";
continue;
}
}
let i = r;
switch (i.type) {
case "space": {
t += this.renderer.space(i);
break;
}
case "hr": {
t += this.renderer.hr(i);
break;
}
case "heading": {
t += this.renderer.heading(i);
break;
}
case "code": {
t += this.renderer.code(i);
break;
}
case "table": {
t += this.renderer.table(i);
break;
}
case "blockquote": {
t += this.renderer.blockquote(i);
break;
}
case "list": {
t += this.renderer.list(i);
break;
}
case "checkbox": {
t += this.renderer.checkbox(i);
break;
}
case "html": {
t += this.renderer.html(i);
break;
}
case "def": {
t += this.renderer.def(i);
break;
}
case "paragraph": {
t += this.renderer.paragraph(i);
break;
}
case "text": {
t += this.renderer.text(i);
break;
}
default: {
let s = 'Token with "' + i.type + '" type was not found.';
if (this.options.silent) return console.error(s), "";
throw new Error(s);
}
}
}
return t;
}
parseInline(e, t = this.renderer) {
let n = "";
for (let r = 0; r < e.length; r++) {
let i = e[r];
if (this.options.extensions?.renderers?.[i.type]) {
let a2 = this.options.extensions.renderers[i.type].call({ parser: this }, i);
if (a2 !== false || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(i.type)) {
n += a2 || "";
continue;
}
}
let s = i;
switch (s.type) {
case "escape": {
n += t.text(s);
break;
}
case "html": {
n += t.html(s);
break;
}
case "link": {
n += t.link(s);
break;
}
case "image": {
n += t.image(s);
break;
}
case "checkbox": {
n += t.checkbox(s);
break;
}
case "strong": {
n += t.strong(s);
break;
}
case "em": {
n += t.em(s);
break;
}
case "codespan": {
n += t.codespan(s);
break;
}
case "br": {
n += t.br(s);
break;
}
case "del": {
n += t.del(s);
break;
}
case "text": {
n += t.text(s);
break;
}
default: {
let a2 = 'Token with "' + s.type + '" type was not found.';
if (this.options.silent) return console.error(a2), "";
throw new Error(a2);
}
}
}
return n;
}
};
var S = class {
options;
block;
constructor(e) {
this.options = e || T;
}
static passThroughHooks = /* @__PURE__ */ new Set(["preprocess", "postprocess", "processAllTokens", "emStrongMask"]);
static passThroughHooksRespectAsync = /* @__PURE__ */ new Set(["preprocess", "postprocess", "processAllTokens"]);
preprocess(e) {
return e;
}
postprocess(e) {
return e;
}
processAllTokens(e) {
return e;
}
emStrongMask(e) {
return e;
}
provideLexer() {
return this.block ? x.lex : x.lexInline;
}
provideParser() {
return this.block ? b.parse : b.parseInline;
}
};
var B = class {
defaults = L();
options = this.setOptions;
parse = this.parseMarkdown(true);
parseInline = this.parseMarkdown(false);
Parser = b;
Renderer = P;
TextRenderer = $;
Lexer = x;
Tokenizer = y;
Hooks = S;
constructor(...e) {
this.use(...e);
}
walkTokens(e, t) {
let n = [];
for (let r of e) switch (n = n.concat(t.call(this, r)), r.type) {
case "table": {
let i = r;
for (let s of i.header) n = n.concat(this.walkTokens(s.tokens, t));
for (let s of i.rows) for (let a2 of s) n = n.concat(this.walkTokens(a2.tokens, t));
break;
}
case "list": {
let i = r;
n = n.concat(this.walkTokens(i.items, t));
break;
}
default: {
let i = r;
this.defaults.extensions?.childTokens?.[i.type] ? this.defaults.extensions.childTokens[i.type].forEach((s) => {
let a2 = i[s].flat(1 / 0);
n = n.concat(this.walkTokens(a2, t));
}) : i.tokens && (n = n.concat(this.walkTokens(i.tokens, t)));
}
}
return n;
}
use(...e) {
let t = this.defaults.extensions || { renderers: {}, childTokens: {} };
return e.forEach((n) => {
let r = { ...n };
if (r.async = this.defaults.async || r.async || false, n.extensions && (n.extensions.forEach((i) => {
if (!i.name) throw new Error("extension name required");
if ("renderer" in i) {
let s = t.renderers[i.name];
s ? t.renderers[i.name] = function(...a2) {
let o = i.renderer.apply(this, a2);
return o === false && (o = s.apply(this, a2)), o;
} : t.renderers[i.name] = i.renderer;
}
if ("tokenizer" in i) {
if (!i.level || i.level !== "block" && i.level !== "inline") throw new Error("extension level must be 'block' or 'inline'");
let s = t[i.level];
s ? s.unshift(i.tokenizer) : t[i.level] = [i.tokenizer], i.start && (i.level === "block" ? t.startBlock ? t.startBlock.push(i.start) : t.startBlock = [i.start] : i.level === "inline" && (t.startInline ? t.startInline.push(i.start) : t.startInline = [i.start]));
}
"childTokens" in i && i.childTokens && (t.childTokens[i.name] = i.childTokens);
}), r.extensions = t), n.renderer) {
let i = this.defaults.renderer || new P(this.defaults);
for (let s in n.renderer) {
if (!(s in i)) throw new Error(`renderer '${s}' does not exist`);
if (["options", "parser"].includes(s)) continue;
let a2 = s, o = n.renderer[a2], l = i[a2];
i[a2] = (...p) => {
let c = o.apply(i, p);
return c === false && (c = l.apply(i, p)), c || "";
};
}
r.renderer = i;
}
if (n.tokenizer) {
let i = this.defaults.tokenizer || new y(this.defaults);
for (let s in n.tokenizer) {
if (!(s in i)) throw new Error(`tokenizer '${s}' does not exist`);
if (["options", "rules", "lexer"].includes(s)) continue;
let a2 = s, o = n.tokenizer[a2], l = i[a2];
i[a2] = (...p) => {
let c = o.apply(i, p);
return c === false && (c = l.apply(i, p)), c;
};
}
r.tokenizer = i;
}
if (n.hooks) {
let i = this.defaults.hooks || new S();
for (let s in n.hooks) {
if (!(s in i)) throw new Error(`hook '${s}' does not exist`);
if (["options", "block"].includes(s)) continue;
let a2 = s, o = n.hooks[a2], l = i[a2];
S.passThroughHooks.has(s) ? i[a2] = (p) => {
if (this.defaults.async && S.passThroughHooksRespectAsync.has(s)) return (async () => {
let g = await o.call(i, p);
return l.call(i, g);
})();
let c = o.call(i, p);
return l.call(i, c);
} : i[a2] = (...p) => {
if (this.defaults.async) return (async () => {
let g = await o.apply(i, p);
return g === false && (g = await l.apply(i, p)), g;
})();
let c = o.apply(i, p);
return c === false && (c = l.apply(i, p)), c;
};
}
r.hooks = i;
}
if (n.walkTokens) {
let i = this.defaults.walkTokens, s = n.walkTokens;
r.walkTokens = function(a2) {
let o = [];
return o.push(s.call(this, a2)), i && (o = o.concat(i.call(this, a2))), o;
};
}
this.defaults = { ...this.defaults, ...r };
}), this;
}
setOptions(e) {
return this.defaults = { ...this.defaults, ...e }, this;
}
lexer(e, t) {
return x.lex(e, t ?? this.defaults);
}
parser(e, t) {
return b.parse(e, t ?? this.defaults);
}
parseMarkdown(e) {
return (n, r) => {
let i = { ...r }, s = { ...this.defaults, ...i }, a2 = this.onError(!!s.silent, !!s.async);
if (this.defaults.async === true && i.async === false) return a2(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));
if (typeof n > "u" || n === null) return a2(new Error("marked(): input parameter is undefined or null"));
if (typeof n != "string") return a2(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(n) + ", string expected"));
if (s.hooks && (s.hooks.options = s, s.hooks.block = e), s.async) return (async () => {
let o = s.hooks ? await s.hooks.preprocess(n) : n, p = await (s.hooks ? await s.hooks.provideLexer() : e ? x.lex : x.lexInline)(o, s), c = s.hooks ? await s.hooks.processAllTokens(p) : p;
s.walkTokens && await Promise.all(this.walkTokens(c, s.walkTokens));
let h = await (s.hooks ? await s.hooks.provideParser() : e ? b.parse : b.parseInline)(c, s);
return s.hooks ? await s.hooks.postprocess(h) : h;
})().catch(a2);
try {
s.hooks && (n = s.hooks.preprocess(n));
let l = (s.hooks ? s.hooks.provideLexer() : e ? x.lex : x.lexInline)(n, s);
s.hooks && (l = s.hooks.processAllTokens(l)), s.walkTokens && this.walkTokens(l, s.walkTokens);
let c = (s.hooks ? s.hooks.provideParser() : e ? b.parse : b.parseInline)(l, s);
return s.hooks && (c = s.hooks.postprocess(c)), c;
} catch (o) {
return a2(o);
}
};
}
onError(e, t) {
return (n) => {
if (n.message += `
Please report this to https://github.com/markedjs/marked.`, e) {
let r = "
An error occurred:
" + w(n.message + "", true) + "
";
return t ? Promise.resolve(r) : r;
}
if (t) return Promise.reject(n);
throw n;
};
}
};
var _ = new B();
function d(u3, e) {
return _.parse(u3, e);
}
d.options = d.setOptions = function(u3) {
return _.setOptions(u3), d.defaults = _.defaults, Z(d.defaults), d;
};
d.getDefaults = L;
d.defaults = T;
d.use = function(...u3) {
return _.use(...u3), d.defaults = _.defaults, Z(d.defaults), d;
};
d.walkTokens = function(u3, e) {
return _.walkTokens(u3, e);
};
d.parseInline = _.parseInline;
d.Parser = b;
d.parser = b.parse;
d.Renderer = P;
d.TextRenderer = $;
d.Lexer = x;
d.lexer = x.lex;
d.Tokenizer = y;
d.Hooks = S;
d.parse = d;
var Dt = d.options;
var Ht = d.setOptions;
var Zt = d.use;
var Gt = d.walkTokens;
var Nt = d.parseInline;
var Ft = b.parse;
var jt = x.lex;
// frontend/js/pages/chat/SessionManager.js
import { nanoid as nanoid2 } from "https://cdn.jsdelivr.net/npm/nanoid/nanoid.js";
var LOCAL_STORAGE_KEY = "gemini_chat_state";
var SessionManager = class {
constructor() {
this.state = null;
}
/**
* Initializes the manager by loading state from localStorage or creating a default state.
*/
init() {
this._loadState();
}
// --- Public API for state access ---
getSessions() {
return this.state.sessions;
}
getCurrentSessionId() {
return this.state.currentSessionId;
}
getCurrentSession() {
return this.state.sessions.find((s) => s.id === this.state.currentSessionId);
}
// --- Public API for state mutation ---
/**
* Creates a new, empty session and sets it as the current one.
*/
createSession() {
const newSessionId = nanoid2();
const newSession = {
id: newSessionId,
name: "\u65B0\u4F1A\u8BDD",
systemPrompt: "",
messages: [],
modelConfig: { model: "gemini-2.0-flash-lite" },
params: { temperature: 0.7 }
};
this.state.sessions.unshift(newSession);
this.state.currentSessionId = newSessionId;
this._saveState();
}
/**
* Switches the current session to the one with the given ID.
* @param {string} sessionId The ID of the session to switch to.
*/
switchSession(sessionId) {
if (this.state.currentSessionId === sessionId) return;
this.state.currentSessionId = sessionId;
this._saveState();
}
/**
* Deletes a session by its ID.
* @param {string} sessionId The ID of the session to delete.
*/
deleteSession(sessionId) {
this.state.sessions = this.state.sessions.filter((s) => s.id !== sessionId);
if (this.state.currentSessionId === sessionId) {
this.state.currentSessionId = this.state.sessions[0]?.id || null;
if (!this.state.currentSessionId) {
this._createInitialState();
}
}
this._saveState();
}
/**
* [NEW] Clears all messages from the currently active session.
*/
clearCurrentSession() {
const currentSession = this.getCurrentSession();
if (currentSession) {
currentSession.messages = [];
this._saveState();
}
}
/**
* Adds a message to the current session and updates the session name if it's the first message.
* @param {object} message The message object to add.
* @returns {object} The session that was updated.
*/
addMessage(message) {
const currentSession = this.getCurrentSession();
if (currentSession) {
if (currentSession.messages.length === 0 && message.role === "user") {
currentSession.name = message.content.substring(0, 30);
}
currentSession.messages.push(message);
this._saveState();
return currentSession;
}
return null;
}
deleteMessage(messageId) {
const currentSession = this.getCurrentSession();
if (currentSession) {
const messageIndex = currentSession.messages.findIndex((m2) => m2.id === messageId);
if (messageIndex > -1) {
currentSession.messages.splice(messageIndex, 1);
this._saveState();
console.log(`Message ${messageId} deleted.`);
}
}
}
truncateMessagesAfter(messageId) {
const currentSession = this.getCurrentSession();
if (currentSession) {
const messageIndex = currentSession.messages.findIndex((m2) => m2.id === messageId);
if (messageIndex > -1 && messageIndex < currentSession.messages.length - 1) {
currentSession.messages.splice(messageIndex + 1);
this._saveState();
console.log(`Truncated messages after ${messageId}.`);
}
}
}
// --- Private persistence methods ---
_saveState() {
try {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.state));
} catch (error) {
console.error("Failed to save session state:", error);
}
}
_loadState() {
try {
const stateString = localStorage.getItem(LOCAL_STORAGE_KEY);
if (stateString) {
this.state = JSON.parse(stateString);
} else {
this._createInitialState();
}
} catch (error) {
console.error("Failed to load session state, creating initial state:", error);
this._createInitialState();
}
}
_createInitialState() {
const initialSessionId = nanoid2();
this.state = {
sessions: [{
id: initialSessionId,
name: "\u65B0\u4F1A\u8BDD",
systemPrompt: "",
messages: [],
modelConfig: { model: "gemini-2.0-flash-lite" },
params: { temperature: 0.7 }
}],
currentSessionId: initialSessionId,
settings: {}
};
this._saveState();
}
};
// frontend/js/pages/chat/chatSettings.js
var ChatSettings = class {
constructor(elements) {
this.elements = {};
this.elements.root = elements;
this._initScopedDOMElements();
this.elements.quickSettingsPanel.style.gridTemplateRows = "0fr";
this.elements.sessionParamsPanel.style.gridTemplateRows = "0fr";
}
// [NEW] A dedicated method to find elements within their specific panels
_initScopedDOMElements() {
this.elements.quickSettingsPanel = this.elements.root.quickSettingsPanel;
this.elements.sessionParamsPanel = this.elements.root.sessionParamsPanel;
this.elements.toggleQuickSettingsBtn = this.elements.root.toggleQuickSettingsBtn;
this.elements.toggleSessionParamsBtn = this.elements.root.toggleSessionParamsBtn;
this.elements.btnGroups = this.elements.quickSettingsPanel.querySelectorAll(".btn-group");
this.elements.directRoutingOptions = this.elements.quickSettingsPanel.querySelector("#direct-routing-options");
this.elements.temperatureSlider = this.elements.sessionParamsPanel.querySelector("#temperature-slider");
this.elements.temperatureValue = this.elements.sessionParamsPanel.querySelector("#temperature-value");
this.elements.contextSlider = this.elements.sessionParamsPanel.querySelector("#context-slider");
this.elements.contextValue = this.elements.sessionParamsPanel.querySelector("#context-value");
}
init() {
if (!this.elements.toggleQuickSettingsBtn) {
console.warn("ChatSettings: Aborting initialization, required elements not found.");
return;
}
this._initPanelToggleListeners();
this._initButtonGroupListeners();
this._initSliderListeners();
}
_initPanelToggleListeners() {
this.elements.toggleQuickSettingsBtn.addEventListener(
"click",
() => this._togglePanel(this.elements.quickSettingsPanel, this.elements.toggleQuickSettingsBtn)
);
this.elements.toggleSessionParamsBtn.addEventListener(
"click",
() => this._togglePanel(this.elements.sessionParamsPanel, this.elements.toggleSessionParamsBtn)
);
}
_initButtonGroupListeners() {
this.elements.btnGroups.forEach((group) => {
group.addEventListener("click", (e) => {
const button = e.target.closest(".btn-group-item");
if (!button) return;
group.querySelectorAll(".btn-group-item").forEach((btn) => btn.removeAttribute("data-active"));
button.setAttribute("data-active", "true");
if (button.dataset.group === "routing-mode") {
this._handleRoutingModeChange(button.dataset.value);
}
});
});
}
_initSliderListeners() {
if (this.elements.temperatureSlider) {
this.elements.temperatureSlider.addEventListener("input", () => {
this.elements.temperatureValue.textContent = parseFloat(this.elements.temperatureSlider.value).toFixed(1);
});
}
if (this.elements.contextSlider) {
this.elements.contextSlider.addEventListener("input", () => {
this.elements.contextValue.textContent = `${this.elements.contextSlider.value}k`;
});
}
}
_handleRoutingModeChange(selectedValue) {
if (this.elements.directRoutingOptions) {
if (selectedValue === "direct") {
this.elements.directRoutingOptions.classList.remove("hidden");
} else {
this.elements.directRoutingOptions.classList.add("hidden");
}
}
}
_togglePanel(panel, button) {
const isExpanded = panel.hasAttribute("data-expanded");
this.elements.quickSettingsPanel.removeAttribute("data-expanded");
this.elements.sessionParamsPanel.removeAttribute("data-expanded");
this.elements.toggleQuickSettingsBtn.removeAttribute("data-active");
this.elements.toggleSessionParamsBtn.removeAttribute("data-active");
this.elements.quickSettingsPanel.style.gridTemplateRows = "0fr";
this.elements.sessionParamsPanel.style.gridTemplateRows = "0fr";
if (!isExpanded) {
panel.setAttribute("data-expanded", "true");
button.setAttribute("data-active", "true");
panel.style.gridTemplateRows = "1fr";
}
}
};
// frontend/js/pages/chat/index.js
d.use({ breaks: true, gfm: true });
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var ChatPage = class {
constructor() {
this.sessionManager = new SessionManager();
this.isStreaming = false;
this.elements = {};
this.initialized = false;
this.searchTerm = "";
this.settingsManager = null;
}
init() {
if (!document.querySelector('[data-page-id="chat"]')) {
return;
}
this.sessionManager.init();
this.initialized = true;
this._initDOMElements();
this._initComponents();
this._initEventListeners();
this._render();
console.log("ChatPage initialized. Session management is delegated.", this.sessionManager.state);
}
_initDOMElements() {
this.elements.chatScrollContainer = document.getElementById("chat-scroll-container");
this.elements.chatMessagesContainer = document.getElementById("chat-messages-container");
this.elements.messageForm = document.getElementById("message-form");
this.elements.messageInput = document.getElementById("message-input");
this.elements.sendBtn = document.getElementById("send-btn");
this.elements.newSessionBtn = document.getElementById("new-session-btn");
this.elements.sessionListContainer = document.getElementById("session-list-container");
this.elements.chatHeaderTitle = document.querySelector(".chat-header-title");
this.elements.clearSessionBtn = document.getElementById("clear-session-btn");
this.elements.sessionSearchInput = document.getElementById("session-search-input");
this.elements.toggleQuickSettingsBtn = document.getElementById("toggle-quick-settings");
this.elements.toggleSessionParamsBtn = document.getElementById("toggle-session-params");
this.elements.quickSettingsPanel = document.getElementById("quick-settings-panel");
this.elements.sessionParamsPanel = document.getElementById("session-params-panel");
this.elements.directRoutingOptions = document.getElementById("direct-routing-options");
this.elements.btnGroups = document.querySelectorAll(".btn-group");
this.elements.temperatureSlider = document.getElementById("temperature-slider");
this.elements.temperatureValue = document.getElementById("temperature-value");
this.elements.contextSlider = document.getElementById("context-slider");
this.elements.contextValue = document.getElementById("context-value");
this.elements.groupSelectContainer = document.getElementById("group-select-container");
}
// [NEW] A dedicated method for initializing complex UI components
_initComponents() {
if (this.elements.groupSelectContainer) {
new CustomSelectV2(this.elements.groupSelectContainer);
}
}
_initEventListeners() {
this.settingsManager = new ChatSettings(this.elements);
this.settingsManager.init();
this.elements.messageForm.addEventListener("submit", (e) => {
e.preventDefault();
this._handleSendMessage();
});
this.elements.messageInput.addEventListener("keydown", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
this._handleSendMessage();
}
});
this.elements.messageInput.addEventListener("input", () => this._autoResizeTextarea());
this.elements.newSessionBtn.addEventListener("click", () => {
this.sessionManager.createSession();
this._render();
this.elements.messageInput.focus();
});
this.elements.sessionListContainer.addEventListener("click", (e) => {
const sessionItem = e.target.closest("[data-session-id]");
const deleteBtn = e.target.closest(".delete-session-btn");
if (deleteBtn) {
e.preventDefault();
const sessionId = deleteBtn.closest("[data-session-id]").dataset.sessionId;
this._handleDeleteSession(sessionId);
} else if (sessionItem) {
e.preventDefault();
const sessionId = sessionItem.dataset.sessionId;
this.sessionManager.switchSession(sessionId);
this._render();
this.elements.messageInput.focus();
}
});
this.elements.clearSessionBtn.addEventListener("click", () => this._handleClearSession());
this.elements.sessionSearchInput.addEventListener("input", (e) => {
this.searchTerm = e.target.value.trim();
this._renderSessionList();
});
this.elements.chatMessagesContainer.addEventListener("click", (e) => {
const messageElement = e.target.closest("[data-message-id]");
if (!messageElement) return;
const messageId = messageElement.dataset.messageId;
const copyBtn = e.target.closest(".action-copy");
const deleteBtn = e.target.closest(".action-delete");
const retryBtn = e.target.closest(".action-retry");
if (copyBtn) {
this._handleCopyMessage(messageId);
} else if (deleteBtn) {
this._handleDeleteMessage(messageId, e.target);
} else if (retryBtn) {
this._handleRetryMessage(messageId);
}
});
}
_handleCopyMessage(messageId) {
const currentSession = this.sessionManager.getCurrentSession();
if (!currentSession) return;
const message = currentSession.messages.find((m2) => m2.id === messageId);
if (!message || !message.content) {
console.error("Message content not found for copying.");
return;
}
let textToCopy = message.content;
if (textToCopy.includes("<") && textToCopy.includes(">")) {
const tempDiv = document.createElement("div");
tempDiv.innerHTML = textToCopy;
textToCopy = tempDiv.textContent || tempDiv.innerText || "";
}
navigator.clipboard.writeText(textToCopy).then(() => {
Swal.fire({
toast: true,
position: "top-end",
icon: "success",
title: "\u5DF2\u590D\u5236",
showConfirmButton: false,
timer: 1500,
customClass: {
popup: `${document.documentElement.classList.contains("dark") ? "swal2-dark" : ""}`
}
});
}).catch((err) => {
console.error("Failed to copy text: ", err);
Swal.fire({
toast: true,
position: "top-end",
icon: "error",
title: "\u590D\u5236\u5931\u8D25",
showConfirmButton: false,
timer: 1500,
customClass: {
popup: `${document.documentElement.classList.contains("dark") ? "swal2-dark" : ""}`
}
});
});
}
_handleDeleteMessage(messageId, targetElement) {
const existingPopover = document.getElementById("delete-confirmation-popover");
if (existingPopover) {
existingPopover.remove();
}
const popover = document.createElement("div");
popover.id = "delete-confirmation-popover";
popover.className = "absolute z-50 p-3 w-45 border border-border rounded-md shadow-lg bg-background text-popover-foreground flex flex-col items-center";
popover.innerHTML = `