feat: personalize and stream mentor chat

This commit is contained in:
leefer
2026-07-23 19:24:57 +08:00
parent 771882ebf0
commit 95789c837c
11 changed files with 876 additions and 104 deletions
+294 -33
View File
@@ -65,6 +65,9 @@ const state = {
mentorQuery: "",
mentorGrade: "all",
mentorDirectoryOpen: false,
mentorSortMode: false,
mentorSavingPreferences: false,
mentorController: null,
heavenSetup: null,
heavenManualData: null,
personalField: null,
@@ -554,6 +557,7 @@ function bindEvents() {
});
document.querySelector("#closeMentorDirectory").addEventListener("click", () => toggleMentorDirectory(false));
document.querySelector("#mentorDirectoryBackdrop").addEventListener("click", () => toggleMentorDirectory(false));
document.querySelector("#mentorSortToggle").addEventListener("click", toggleMentorSortMode);
document.querySelector("#mentorSearchInput").addEventListener("input", (event) => {
state.mentorQuery = event.target.value.trim().toLocaleLowerCase("zh-CN");
renderMentorDirectory();
@@ -2045,6 +2049,13 @@ async function loadMentorSetup(force = false) {
const query = new URLSearchParams({ trade_date: elements.tradeDate.value });
const payload = await apiRequest(`/api/mentors/setup?${query}`);
payload.requestedDate = requestedDate;
if (!payload.preferences_configured) {
payload.mentors.sort((first, second) => {
if (Boolean(first.private) !== Boolean(second.private)) return first.private ? -1 : 1;
return String(first.name || "").localeCompare(String(second.name || ""), "zh-CN");
});
payload.mentors.forEach((mentor, index) => { mentor.sort_order = index; });
}
state.mentorSetup = payload;
const selectedExists = payload.mentors.some((item) => item.id === state.selectedMentorId);
state.selectedMentorId = selectedExists ? state.selectedMentorId : payload.mentors[0]?.id || "";
@@ -2075,6 +2086,7 @@ function renderMentorDirectory() {
const mentors = state.mentorSetup?.mentors || [];
const query = state.mentorQuery;
const filtered = mentors.filter((mentor) => {
if (state.mentorSortMode) return true;
if (state.mentorGrade !== "all" && mentor.evidence?.grade !== state.mentorGrade) return false;
if (!query) return true;
const haystack = [
@@ -2086,28 +2098,180 @@ function renderMentorDirectory() {
...(mentor.focus || []),
].filter(Boolean).join(" ").toLocaleLowerCase("zh-CN");
return haystack.includes(query);
}).sort((first, second) => {
if (Boolean(first.private) !== Boolean(second.private)) return first.private ? -1 : 1;
return String(first.name || "").localeCompare(String(second.name || ""), "zh-CN");
});
setText("mentorCount", filtered.length === mentors.length ? `${mentors.length}` : `${filtered.length} / ${mentors.length}`);
const sortToggle = document.querySelector("#mentorSortToggle");
sortToggle.classList.toggle("active", state.mentorSortMode);
sortToggle.setAttribute("aria-pressed", String(state.mentorSortMode));
sortToggle.querySelector("span").textContent = state.mentorSortMode ? "完成" : "整理";
document.querySelector("#mentorSortHint").hidden = !state.mentorSortMode;
document.querySelector("#mentorSearchInput").disabled = state.mentorSortMode;
document.querySelectorAll("[data-mentor-grade]").forEach((button) => {
button.disabled = state.mentorSortMode;
});
const container = document.querySelector("#mentorList");
container.innerHTML = filtered.map((mentor) => `
<button type="button" class="mentor-option ${mentor.id === state.selectedMentorId ? "active" : ""}" data-mentor-id="${escapeHtml(mentor.id)}" aria-pressed="${mentor.id === state.selectedMentorId}">
<span class="mentor-option-copy">
<strong>${escapeHtml(mentor.name)}</strong>
<em title="${escapeHtml(mentor.description || "")}">${escapeHtml(mentor.description || mentor.tagline || "思维模型")}</em>
</span>
<span class="mentor-option-badges">${renderMentorBadges(mentor)}</span>
</button>
`).join("");
container.classList.toggle("is-sorting", state.mentorSortMode);
container.innerHTML = filtered.map((mentor) => {
const group = mentors.filter((item) => Boolean(item.pinned) === Boolean(mentor.pinned));
const groupIndex = group.findIndex((item) => item.id === mentor.id);
return `
<article class="mentor-option ${mentor.id === state.selectedMentorId ? "active" : ""} ${mentor.pinned ? "is-pinned" : ""}"
data-mentor-card="${escapeHtml(mentor.id)}" draggable="${state.mentorSortMode && !state.mentorSavingPreferences}">
<button type="button" class="mentor-option-main" data-mentor-id="${escapeHtml(mentor.id)}" aria-pressed="${mentor.id === state.selectedMentorId}" ${state.mentorLoading ? "disabled" : ""}>
<span class="mentor-option-copy">
<strong>${escapeHtml(mentor.name)}</strong>
<em title="${escapeHtml(mentor.description || "")}">${escapeHtml(mentor.description || mentor.tagline || "思维模型")}</em>
</span>
<span class="mentor-option-badges">${renderMentorBadges(mentor)}</span>
</button>
<span class="mentor-option-tools">
<button type="button" class="mentor-pin-button ${mentor.pinned ? "active" : ""}" data-mentor-pin="${escapeHtml(mentor.id)}"
aria-label="${mentor.pinned ? "取消置顶" : "置顶"}${escapeHtml(mentor.name)}" title="${mentor.pinned ? "取消置顶" : "置顶"}" ${state.mentorSavingPreferences ? "disabled" : ""}>
<i data-lucide="pin"></i>
</button>
${state.mentorSortMode ? `
<button type="button" class="mentor-order-button" data-mentor-move="up" data-mentor-target="${escapeHtml(mentor.id)}" aria-label="上移${escapeHtml(mentor.name)}" title="上移" ${groupIndex <= 0 || state.mentorSavingPreferences ? "disabled" : ""}><i data-lucide="chevron-up"></i></button>
<button type="button" class="mentor-order-button" data-mentor-move="down" data-mentor-target="${escapeHtml(mentor.id)}" aria-label="下移${escapeHtml(mentor.name)}" title="下移" ${groupIndex >= group.length - 1 || state.mentorSavingPreferences ? "disabled" : ""}><i data-lucide="chevron-down"></i></button>
` : ""}
</span>
</article>
`;
}).join("");
document.querySelector("#mentorListEmpty").hidden = filtered.length > 0;
document.querySelectorAll("[data-mentor-id]").forEach((button) => {
button.addEventListener("click", () => selectMentor(button.dataset.mentorId));
});
document.querySelectorAll("[data-mentor-pin]").forEach((button) => {
button.addEventListener("click", () => toggleMentorPin(button.dataset.mentorPin));
});
document.querySelectorAll("[data-mentor-move]").forEach((button) => {
button.addEventListener("click", () => moveMentor(button.dataset.mentorTarget, button.dataset.mentorMove));
});
document.querySelectorAll("[data-mentor-card]").forEach((card) => {
card.addEventListener("dragstart", handleMentorDragStart);
card.addEventListener("dragover", handleMentorDragOver);
card.addEventListener("drop", handleMentorDrop);
card.addEventListener("dragend", clearMentorDragState);
});
refreshIcons();
}
function toggleMentorSortMode() {
state.mentorSortMode = !state.mentorSortMode;
if (state.mentorSortMode) {
state.mentorQuery = "";
state.mentorGrade = "all";
document.querySelector("#mentorSearchInput").value = "";
document.querySelectorAll("[data-mentor-grade]").forEach((button) => {
button.classList.toggle("active", button.dataset.mentorGrade === "all");
});
}
renderMentorDirectory();
}
async function toggleMentorPin(mentorId) {
if (state.mentorSavingPreferences) return;
const mentors = state.mentorSetup?.mentors || [];
const index = mentors.findIndex((item) => item.id === mentorId);
if (index < 0) return;
const [mentor] = mentors.splice(index, 1);
mentor.pinned = !mentor.pinned;
if (mentor.pinned) {
mentors.unshift(mentor);
} else {
const firstUnpinned = mentors.findIndex((item) => !item.pinned);
mentors.splice(firstUnpinned < 0 ? mentors.length : firstUnpinned, 0, mentor);
}
normalizeMentorOrder();
renderMentorWorkspace();
await persistMentorPreferences();
}
async function moveMentor(mentorId, direction) {
if (state.mentorSavingPreferences) return;
const mentors = state.mentorSetup?.mentors || [];
const index = mentors.findIndex((item) => item.id === mentorId);
if (index < 0) return;
const step = direction === "up" ? -1 : 1;
const targetIndex = index + step;
if (targetIndex < 0 || targetIndex >= mentors.length) return;
if (Boolean(mentors[index].pinned) !== Boolean(mentors[targetIndex].pinned)) return;
[mentors[index], mentors[targetIndex]] = [mentors[targetIndex], mentors[index]];
normalizeMentorOrder();
renderMentorDirectory();
await persistMentorPreferences();
}
function handleMentorDragStart(event) {
if (!state.mentorSortMode || state.mentorSavingPreferences) {
event.preventDefault();
return;
}
state.mentorDragId = event.currentTarget.dataset.mentorCard || "";
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", state.mentorDragId);
event.currentTarget.classList.add("is-dragging");
}
function handleMentorDragOver(event) {
const source = state.mentorSetup?.mentors.find((item) => item.id === state.mentorDragId);
const target = state.mentorSetup?.mentors.find((item) => item.id === event.currentTarget.dataset.mentorCard);
if (!source || !target || Boolean(source.pinned) !== Boolean(target.pinned)) return;
event.preventDefault();
event.dataTransfer.dropEffect = "move";
event.currentTarget.classList.add("is-drag-over");
}
async function handleMentorDrop(event) {
event.preventDefault();
const sourceId = state.mentorDragId || event.dataTransfer.getData("text/plain");
const targetId = event.currentTarget.dataset.mentorCard || "";
clearMentorDragState();
if (!sourceId || !targetId || sourceId === targetId) return;
const mentors = state.mentorSetup?.mentors || [];
const sourceIndex = mentors.findIndex((item) => item.id === sourceId);
const targetIndex = mentors.findIndex((item) => item.id === targetId);
if (sourceIndex < 0 || targetIndex < 0) return;
if (Boolean(mentors[sourceIndex].pinned) !== Boolean(mentors[targetIndex].pinned)) return;
const [mentor] = mentors.splice(sourceIndex, 1);
const insertionIndex = mentors.findIndex((item) => item.id === targetId);
mentors.splice(insertionIndex, 0, mentor);
normalizeMentorOrder();
renderMentorDirectory();
await persistMentorPreferences();
}
function clearMentorDragState() {
state.mentorDragId = "";
document.querySelectorAll(".mentor-option.is-dragging, .mentor-option.is-drag-over").forEach((item) => {
item.classList.remove("is-dragging", "is-drag-over");
});
}
function normalizeMentorOrder() {
(state.mentorSetup?.mentors || []).forEach((mentor, index) => {
mentor.sort_order = index;
});
}
async function persistMentorPreferences() {
const mentors = state.mentorSetup?.mentors || [];
state.mentorSavingPreferences = true;
renderMentorDirectory();
try {
await apiRequest("/api/mentors/preferences", "POST", {
order: mentors.map((item) => item.id),
pinned: mentors.filter((item) => item.pinned).map((item) => item.id),
});
} catch (error) {
showToast(error.message || "问师顺序保存失败");
await loadMentorSetup(true);
} finally {
state.mentorSavingPreferences = false;
renderMentorDirectory();
}
}
function renderMentorBadges(mentor, expanded = false) {
const badges = [];
if (mentor.private) {
@@ -2166,13 +2330,14 @@ function renderMentorMessages() {
`;
} else {
container.innerHTML = state.mentorMessages.map((message) => `
<article class="mentor-message ${message.role}">
<article class="mentor-message ${message.role} ${message.error ? "is-error" : ""}">
<div class="mentor-message-label">${message.role === "user" ? "我" : escapeHtml(selected?.name || "问师")}</div>
<div class="mentor-message-content">${message.role === "assistant" ? formatMentorAnswer(message.content) : escapeHtml(message.content)}</div>
${message.meta ? `<small>${escapeHtml(message.meta)}</small>` : ""}
${message.streaming ? '<span class="assistant-stream-caret" aria-hidden="true"></span>' : ""}
${message.meta && !message.streaming ? `<small>${escapeHtml(message.meta)}</small>` : ""}
</article>
`).join("");
if (state.mentorLoading) {
if (state.mentorLoading && !state.mentorMessages.some((message) => message.streaming)) {
container.insertAdjacentHTML("beforeend", `
<article class="mentor-message assistant loading-message">
<div class="mentor-message-label">${escapeHtml(selected?.name || "问师")}</div>
@@ -2184,6 +2349,7 @@ function renderMentorMessages() {
document.querySelector("#clearMentorChatButton").disabled = !state.mentorMessages.length || state.mentorLoading;
document.querySelector("#mentorQuestion").disabled = state.mentorLoading || !state.selectedMentorId;
document.querySelector("#sendMentorQuestion").disabled = state.mentorLoading || !state.selectedMentorId;
document.querySelector("#mentorSortToggle").disabled = state.mentorLoading;
requestAnimationFrame(() => { container.scrollTop = container.scrollHeight; });
}
@@ -2198,36 +2364,99 @@ async function sendMentorQuestion(event) {
content: item.content.slice(0, 3500),
}));
state.mentorMessages.push({ role: "user", content: question });
const responseMessage = { role: "assistant", content: "", streaming: true, meta: "" };
state.mentorMessages.push(responseMessage);
input.value = "";
state.mentorLoading = true;
state.mentorController = new AbortController();
hideMentorNotice();
renderMentorMessages();
renderMentorDirectory();
setStatus("问师正在读取复盘数据");
try {
const payload = await apiRequest("/api/mentors/chat", "POST", {
mentor_id: state.selectedMentorId,
trade_date: elements.tradeDate.value,
question,
history,
});
state.mentorMessages.push({
role: "assistant",
content: payload.answer,
meta: `${displayCompactDate(payload.data_trade_date)} · 回答完成`,
});
if (payload.notice) showMentorNotice(payload.notice);
await streamMentorRequest(
{
mentor_id: state.selectedMentorId,
trade_date: elements.tradeDate.value,
question,
history,
},
state.mentorController.signal,
(chunk) => {
responseMessage.content += chunk;
scheduleMentorRender();
},
(meta) => {
responseMessage.meta = `${displayCompactDate(meta.data_trade_date || elements.tradeDate.value)} · 回答完成`;
if (meta.notice) showMentorNotice(meta.notice);
},
);
responseMessage.streaming = false;
setStatus("问师回答完成");
} catch (error) {
responseMessage.streaming = false;
responseMessage.error = true;
if (!responseMessage.content) {
state.mentorMessages = state.mentorMessages.filter((item) => item !== responseMessage);
}
showMentorNotice(error.message || "问师回答失败");
showToast(error.message || "问师回答失败");
setStatus("问师回答失败");
} finally {
state.mentorLoading = false;
state.mentorController = null;
renderMentorMessages();
renderMentorDirectory();
input.focus();
}
}
let mentorRenderFrame = 0;
function scheduleMentorRender() {
if (mentorRenderFrame) return;
mentorRenderFrame = requestAnimationFrame(() => {
mentorRenderFrame = 0;
renderMentorMessages();
});
}
async function streamMentorRequest(body, signal, onDelta, onMeta) {
const response = await fetch("/api/mentors/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
...(state.csrfToken ? { "X-CSRF-Token": state.csrfToken } : {}),
},
body: JSON.stringify(body),
signal,
});
if (!response.ok) {
const payload = await response.json().catch(() => ({}));
throw new Error(payload.error || "问师暂不可用");
}
if (!response.body) throw new Error("当前浏览器不支持流式回答");
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
const consume = (line) => {
if (!line.trim()) return;
const event = JSON.parse(line);
if (event.type === "delta") onDelta(String(event.content || ""));
if (event.type === "meta") onMeta(event);
if (event.type === "error") throw new Error(event.error || "问师回答失败");
};
while (true) {
const { value, done } = await reader.read();
buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
lines.forEach(consume);
if (done) break;
}
if (buffer.trim()) consume(buffer);
}
function useMentorQuickPrompt(prompt) {
const input = document.querySelector("#mentorQuestion");
input.value = prompt || "";
@@ -2278,13 +2507,45 @@ function hideMentorNotice() {
}
function formatMentorAnswer(content) {
return escapeHtml(content).split("\n").map((line) => {
const blocks = [];
let listType = "";
let listItems = [];
const flushList = () => {
if (!listItems.length) return;
blocks.push(`<${listType} class="mentor-answer-list">${listItems.map((item) => `<li>${item}</li>`).join("")}</${listType}>`);
listItems = [];
listType = "";
};
String(content || "").replace(/\r\n?/g, "\n").replace(/\n{3,}/g, "\n\n").split("\n").forEach((rawLine) => {
const line = rawLine.trim();
if (!line) {
flushList();
return;
}
const heading = line.match(/^#{1,3}\s+(.+)$/);
if (heading) return `<strong class="mentor-answer-heading">${formatMentorInline(heading[1])}</strong>`;
if (/^-{3,}$/.test(line.trim())) return '<span class="mentor-answer-rule"></span>';
if (line.startsWith("&gt; ")) return `<span class="mentor-answer-quote">${formatMentorInline(line.slice(5))}</span>`;
return formatMentorInline(line);
}).join("<br>");
const bullet = line.match(/^[-*]\s+(.+)$/);
const ordered = line.match(/^\d+[.、]\s*(.+)$/);
if (heading) {
flushList();
blocks.push(`<strong class="mentor-answer-heading">${formatMentorInline(escapeHtml(heading[1]))}</strong>`);
} else if (/^-{3,}$/.test(line)) {
flushList();
blocks.push('<span class="mentor-answer-rule"></span>');
} else if (line.startsWith("> ")) {
flushList();
blocks.push(`<span class="mentor-answer-quote">${formatMentorInline(escapeHtml(line.slice(2)))}</span>`);
} else if (bullet || ordered) {
const nextType = bullet ? "ul" : "ol";
if (listType && listType !== nextType) flushList();
listType = nextType;
listItems.push(formatMentorInline(escapeHtml((bullet || ordered)[1])));
} else {
flushList();
blocks.push(`<p class="mentor-answer-paragraph">${formatMentorInline(escapeHtml(line))}</p>`);
}
});
flushList();
return blocks.join("");
}
function formatMentorInline(content) {
+5 -1
View File
@@ -813,7 +813,10 @@
<div id="mentorDirectoryContent" class="mentor-directory-content">
<div class="workspace-heading mentor-directory-heading">
<div><h3>思维模型</h3><span id="mentorCount">0 位</span></div>
<button id="closeMentorDirectory" class="icon-button mentor-directory-close" type="button" aria-label="关闭思维模型目录" title="关闭"><i data-lucide="x"></i></button>
<div class="mentor-directory-actions">
<button id="mentorSortToggle" class="mentor-sort-toggle" type="button" aria-pressed="false" title="整理顺序"><i data-lucide="list-ordered"></i><span>整理</span></button>
<button id="closeMentorDirectory" class="icon-button mentor-directory-close" type="button" aria-label="关闭思维模型目录" title="关闭"><i data-lucide="x"></i></button>
</div>
</div>
<label class="mentor-search-field">
<span class="visually-hidden">搜索思维模型</span>
@@ -826,6 +829,7 @@
<button type="button" data-mentor-grade="B">B 多源</button>
<button type="button" data-mentor-grade="C">C 推演</button>
</div>
<p id="mentorSortHint" class="mentor-sort-hint" hidden>拖动卡片,或使用箭头调整顺序</p>
<div id="mentorList" class="mentor-list"></div>
<div id="mentorListEmpty" class="mentor-list-empty" hidden>没有符合条件的思维模型</div>
<p class="mentor-evidence-legend">素材等级反映蒸馏依据,不代表人物能力或收益水平。</p>
+214 -2
View File
@@ -11779,7 +11779,7 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
height: 100%;
min-height: 0;
display: grid;
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
grid-template-rows: auto auto auto auto minmax(0, 1fr) auto;
gap: 10px;
padding: 14px 12px 10px;
}
@@ -11805,6 +11805,39 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
font-size: 11px;
}
.mentor-directory-actions {
display: flex;
align-items: center;
gap: 4px;
}
.mentor-sort-toggle {
min-height: 30px;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 0 8px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--surface);
color: var(--text-muted);
cursor: pointer;
font: inherit;
font-size: 10px;
}
.mentor-sort-toggle:hover,
.mentor-sort-toggle.active {
border-color: color-mix(in srgb, var(--action) 38%, var(--border));
background: var(--action-soft);
color: var(--action);
}
.mentor-sort-toggle .lucide {
width: 13px;
height: 13px;
}
.mentor-search-field {
height: 40px;
display: grid;
@@ -11876,6 +11909,14 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
outline-offset: 1px;
}
.mentor-sort-hint {
margin: 0;
padding: 0 3px;
color: var(--text-muted);
font-size: 9px;
line-height: 1.4;
}
.mentor-list {
min-height: 0;
display: grid;
@@ -11893,7 +11934,7 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 8px;
padding: 8px 9px;
padding: 0 5px 0 0;
border: 1px solid transparent;
border-bottom-color: var(--border);
border-radius: 4px;
@@ -11901,6 +11942,96 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
transition: border-color var(--motion-fast) ease, background-color var(--motion-fast) ease;
}
.mentor-option-main {
min-width: 0;
min-height: 64px;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 8px;
padding: 8px 5px 8px 9px;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
}
.mentor-option-main:disabled {
cursor: default;
}
.mentor-option .mentor-option-tools {
display: flex;
align-items: center;
gap: 2px;
margin: 0;
overflow: visible;
color: inherit;
font-size: inherit;
line-height: normal;
white-space: normal;
}
.mentor-pin-button,
.mentor-order-button {
width: 28px;
min-width: 28px;
height: 30px;
display: grid;
place-items: center;
padding: 0;
border: 0;
border-radius: 4px;
background: transparent;
color: var(--text-muted);
cursor: pointer;
}
.mentor-pin-button:hover,
.mentor-order-button:hover:not(:disabled) {
background: var(--surface-muted);
color: var(--text);
}
.mentor-pin-button.active {
background: #fff7df;
color: #9a6815;
}
.mentor-pin-button.active .lucide {
fill: currentColor;
}
.mentor-pin-button .lucide,
.mentor-order-button .lucide {
width: 14px;
height: 14px;
}
.mentor-order-button:disabled {
color: color-mix(in srgb, var(--text-muted) 35%, transparent);
cursor: default;
}
.mentor-list.is-sorting .mentor-option {
cursor: grab;
}
.mentor-list.is-sorting .mentor-option-badges {
display: none;
}
.mentor-option.is-dragging {
opacity: 0.45;
}
.mentor-option.is-drag-over {
border-color: var(--action);
background: var(--action-soft);
}
.mentor-option:hover {
border-color: var(--border-strong);
background: var(--surface);
@@ -11923,6 +12054,16 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
gap: 4px;
}
.mentor-option .mentor-option-copy {
display: grid;
margin: 0;
overflow: visible;
color: inherit;
font-size: inherit;
line-height: normal;
white-space: normal;
}
.mentor-option-copy strong {
overflow: hidden;
font-size: 13px;
@@ -11949,6 +12090,16 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
gap: 4px;
}
.mentor-option .mentor-option-badges {
display: flex;
margin: 0;
overflow: visible;
color: inherit;
font-size: inherit;
line-height: normal;
white-space: normal;
}
.mentor-option-badges {
max-width: 76px;
}
@@ -11971,6 +12122,13 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
white-space: nowrap;
}
.mentor-option .mentor-badge {
display: inline-flex;
margin: 0;
overflow: visible;
line-height: 1;
}
.mentor-badge .lucide {
width: 11px;
height: 11px;
@@ -12057,6 +12215,47 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
max-height: none;
}
.mentor-message {
margin-bottom: 12px;
}
.mentor-message-content {
line-height: 1.62;
white-space: normal;
}
.mentor-message .mentor-answer-paragraph {
margin: 0 0 6px;
line-height: inherit;
}
.mentor-message .mentor-answer-paragraph:last-child {
margin-bottom: 0;
}
.mentor-answer-heading {
display: block;
margin: 9px 0 4px;
line-height: 1.45;
}
.mentor-message-content > .mentor-answer-heading:first-child {
margin-top: 0;
}
.mentor-answer-list {
margin: 3px 0 7px;
padding-left: 20px;
}
.mentor-answer-list li + li {
margin-top: 3px;
}
.mentor-message.is-error {
border-color: var(--danger);
}
@media (min-width: 721px) and (max-width: 1023px) {
.mentor-layout {
grid-template-columns: 280px minmax(0, 1fr);
@@ -12181,6 +12380,12 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
display: grid;
}
.mentor-sort-toggle {
min-height: 44px;
padding: 0 10px;
font-size: 11px;
}
.mentor-search-field {
height: 44px;
}
@@ -12199,6 +12404,13 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
min-height: 68px;
}
.mentor-pin-button,
.mentor-order-button {
width: 40px;
min-width: 40px;
height: 44px;
}
.mentor-chat-panel {
min-height: 580px;
}