ДОМЕНЫ - ВОПРОСЫ - СКЛАД - Починить selected-object follow-up по складу и включить разговорные/UI-варианты в обязательный domain-loop
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>NDC AI Normalizer Playground</title>
|
||||
<script type="module" crossorigin src="/assets/index-CEJTyo9A.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Cx2J_KsF.css">
|
||||
<script type="module" crossorigin src="/assets/index-B9mz4jx4.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dcuz1nX5.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { designConfig } from "../../../designconfig";
|
||||
import type {
|
||||
AssistantConversationItem,
|
||||
AssistantAnnotationRecord,
|
||||
AssistantSelectionChip,
|
||||
ConnectionState,
|
||||
HistoryItem,
|
||||
NormalizeResultState,
|
||||
@@ -60,6 +61,25 @@ function diffPrompts(current: PromptState, previous: PromptState | null): string
|
||||
return `Changed fields: ${changed.length}. ${changed.join(" | ")}`;
|
||||
}
|
||||
|
||||
function buildAssistantFollowupMessage(inputValue: string, selectedChip: AssistantSelectionChip | null): string {
|
||||
const trimmedInput = inputValue.trim();
|
||||
if (!trimmedInput) {
|
||||
return "";
|
||||
}
|
||||
if (!selectedChip) {
|
||||
return trimmedInput;
|
||||
}
|
||||
|
||||
const normalizedInput = trimmedInput.toLowerCase();
|
||||
const selectionAnchor = selectedChip.anchor_text.trim();
|
||||
const normalizedSelection = selectionAnchor.toLowerCase();
|
||||
if (normalizedSelection && normalizedInput.includes(normalizedSelection)) {
|
||||
return trimmedInput;
|
||||
}
|
||||
|
||||
return `По выбранному объекту "${selectionAnchor}": ${trimmedInput}`;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [connection, setConnection] = useState<ConnectionState>(DEFAULT_CONNECTION);
|
||||
const [prompts, setPrompts] = useState<PromptState>(DEFAULT_PROMPTS);
|
||||
@@ -116,6 +136,7 @@ export default function App() {
|
||||
const [assistantSessionId, setAssistantSessionId] = useState("");
|
||||
const [assistantConversation, setAssistantConversation] = useState<AssistantConversationItem[]>([]);
|
||||
const [assistantInput, setAssistantInput] = useState("");
|
||||
const [assistantSelectedChip, setAssistantSelectedChip] = useState<AssistantSelectionChip | null>(null);
|
||||
const [assistantBusy, setAssistantBusy] = useState(false);
|
||||
const [assistantStatus, setAssistantStatus] = useState("");
|
||||
const [assistantError, setAssistantError] = useState("");
|
||||
@@ -140,6 +161,10 @@ export default function App() {
|
||||
root.style.setProperty("--rgb-surface-main", colors.mainSurfaceRgb);
|
||||
root.style.setProperty("--rgb-surface-horizontal", colors.horizontalSurfaceRgb);
|
||||
root.style.setProperty("--rgb-surface-focus", colors.focusSurfaceRgb);
|
||||
root.style.setProperty("--rgb-assistant-chip", colors.assistantChipRgb);
|
||||
root.style.setProperty("--rgb-assistant-chip-hover", colors.assistantChipHoverRgb);
|
||||
root.style.setProperty("--rgb-assistant-chip-selected", colors.assistantChipSelectedRgb);
|
||||
root.style.setProperty("--rgb-assistant-chip-selected-text", colors.assistantChipSelectedTextRgb);
|
||||
root.style.setProperty("--rgb-active", colors.activeRgb);
|
||||
root.style.setProperty("--rgb-active-text", colors.activeTextRgb);
|
||||
root.style.setProperty("--rgb-text-main", colors.textMainRgb);
|
||||
@@ -785,6 +810,7 @@ export default function App() {
|
||||
setAssistantSessionId("");
|
||||
setAssistantConversation([]);
|
||||
setAssistantInput("");
|
||||
setAssistantSelectedChip(null);
|
||||
setAssistantStatus("");
|
||||
setAssistantError("");
|
||||
setAssistantAnnotations([]);
|
||||
@@ -793,7 +819,7 @@ export default function App() {
|
||||
}
|
||||
|
||||
async function sendAssistantMessage() {
|
||||
const userMessage = assistantInput.trim();
|
||||
const userMessage = buildAssistantFollowupMessage(assistantInput, assistantSelectedChip);
|
||||
if (!userMessage) {
|
||||
return;
|
||||
}
|
||||
@@ -1035,6 +1061,9 @@ export default function App() {
|
||||
conversation={assistantConversation}
|
||||
inputValue={assistantInput}
|
||||
onInputChange={setAssistantInput}
|
||||
selectedContextChip={assistantSelectedChip}
|
||||
onSelectContextChip={setAssistantSelectedChip}
|
||||
onClearContextChip={() => setAssistantSelectedChip(null)}
|
||||
useMock={useMock}
|
||||
onUseMockChange={setUseMock}
|
||||
onSend={sendAssistantMessage}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { AssistantConversationItem } from "../state/types";
|
||||
import type { AssistantConversationItem, AssistantSelectionChip } from "../state/types";
|
||||
import { buildConversationExportForCopy } from "../utils/conversationExport";
|
||||
import { JsonView } from "./JsonView";
|
||||
import { PanelFrame } from "./PanelFrame";
|
||||
@@ -9,6 +9,9 @@ interface AssistantPanelProps {
|
||||
conversation: AssistantConversationItem[];
|
||||
inputValue: string;
|
||||
onInputChange: (value: string) => void;
|
||||
selectedContextChip: AssistantSelectionChip | null;
|
||||
onSelectContextChip: (value: AssistantSelectionChip) => void;
|
||||
onClearContextChip: () => void;
|
||||
useMock: boolean;
|
||||
onUseMockChange: (value: boolean) => void;
|
||||
onSend: () => Promise<void> | void;
|
||||
@@ -147,17 +150,134 @@ function lineClassName(line: string): string {
|
||||
return "assistant-msg-line";
|
||||
}
|
||||
|
||||
function renderAssistantMessageBody(text: string): JSX.Element[] {
|
||||
const blocks = splitAssistantMessageBlocks(text);
|
||||
function buildSelectionPreview(label: string, maxChars = 40): string {
|
||||
const normalized = label.replace(/\s+/g, " ").trim();
|
||||
if (normalized.length <= maxChars) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
const firstWords = normalized.split(" ").slice(0, 3).join(" ").trim();
|
||||
if (firstWords.length >= 10 && firstWords.length <= maxChars) {
|
||||
return `${firstWords}…`;
|
||||
}
|
||||
|
||||
return `${normalized.slice(0, maxChars - 1).trimEnd()}…`;
|
||||
}
|
||||
|
||||
function stripBlockSelectionPrefix(line: string): string {
|
||||
return line.replace(/\*\*(.+?)\*\*/g, "$1").replace(/^\d+\.\s*/, "").trim();
|
||||
}
|
||||
|
||||
function extractSelectionAnchorText(block: string): string {
|
||||
const firstLine = block
|
||||
.replace(/\r\n?/g, "\n")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.find(Boolean);
|
||||
const normalizedFirstLine = stripBlockSelectionPrefix(firstLine ?? "");
|
||||
const primarySegment = normalizedFirstLine.split("|")[0]?.trim() ?? normalizedFirstLine;
|
||||
return primarySegment.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function isSelectableEntityBlock(block: string): boolean {
|
||||
const firstLine = block
|
||||
.replace(/\r\n?/g, "\n")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.find(Boolean);
|
||||
if (!firstLine || !/^\d+\.\s/.test(firstLine)) {
|
||||
return false;
|
||||
}
|
||||
const normalizedFirstLine = stripBlockSelectionPrefix(firstLine);
|
||||
return normalizedFirstLine.includes("|");
|
||||
}
|
||||
|
||||
function buildSelectionChipForBlock(item: AssistantConversationItem, block: string): AssistantSelectionChip | null {
|
||||
const normalizedBlock = block
|
||||
.replace(/\r\n?/g, "\n")
|
||||
.replace(/\*\*(.+?)\*\*/g, "$1")
|
||||
.split("\n")
|
||||
.map((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
if (index === 0) {
|
||||
return trimmed.replace(/^\d+\.\s*/, "");
|
||||
}
|
||||
return trimmed;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
|
||||
if (!normalizedBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const anchorText = extractSelectionAnchorText(block) || normalizedBlock;
|
||||
return {
|
||||
message_id: item.message_id,
|
||||
source_text: normalizedBlock,
|
||||
anchor_text: anchorText,
|
||||
preview_text: buildSelectionPreview(anchorText)
|
||||
};
|
||||
}
|
||||
|
||||
function renderAssistantMessageBody(
|
||||
item: AssistantConversationItem,
|
||||
selectedContextChip: AssistantSelectionChip | null,
|
||||
onSelectContextChip: (value: AssistantSelectionChip) => void,
|
||||
onClearContextChip: () => void
|
||||
): JSX.Element[] {
|
||||
const blocks = splitAssistantMessageBlocks(item.text);
|
||||
return blocks.map((block, blockIndex) => {
|
||||
const lines = block.split("\n");
|
||||
const isSelectable = item.role === "assistant" && isSelectableEntityBlock(block);
|
||||
const selectionChip = isSelectable ? buildSelectionChipForBlock(item, block) : null;
|
||||
const selected =
|
||||
Boolean(selectionChip) &&
|
||||
selectedContextChip?.message_id === selectionChip?.message_id &&
|
||||
selectedContextChip?.source_text === selectionChip?.source_text;
|
||||
|
||||
const body = lines.map((line, lineIndex) => (
|
||||
<p key={`line-${blockIndex}-${lineIndex}`} className={lineClassName(line)}>
|
||||
{renderInlineBold(line, `line-${blockIndex}-${lineIndex}`)}
|
||||
</p>
|
||||
));
|
||||
|
||||
if (!isSelectable || !selectionChip) {
|
||||
return (
|
||||
<div key={`block-${blockIndex}`} className="assistant-msg-block">
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={`block-${blockIndex}`} className="assistant-msg-block">
|
||||
{lines.map((line, lineIndex) => (
|
||||
<p key={`line-${blockIndex}-${lineIndex}`} className={lineClassName(line)}>
|
||||
{renderInlineBold(line, `line-${blockIndex}-${lineIndex}`)}
|
||||
</p>
|
||||
))}
|
||||
<div
|
||||
key={`block-${blockIndex}`}
|
||||
className={selected ? "assistant-msg-block selectable active" : "assistant-msg-block selectable"}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
if (selected) {
|
||||
onClearContextChip();
|
||||
return;
|
||||
}
|
||||
onSelectContextChip(selectionChip);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
if (selected) {
|
||||
onClearContextChip();
|
||||
return;
|
||||
}
|
||||
onSelectContextChip(selectionChip);
|
||||
}}
|
||||
>
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -168,6 +288,9 @@ export function AssistantPanel({
|
||||
conversation,
|
||||
inputValue,
|
||||
onInputChange,
|
||||
selectedContextChip,
|
||||
onSelectContextChip,
|
||||
onClearContextChip,
|
||||
useMock,
|
||||
onUseMockChange,
|
||||
onSend,
|
||||
@@ -316,7 +439,9 @@ export function AssistantPanel({
|
||||
</div>
|
||||
) : null}
|
||||
</header>
|
||||
<div className="assistant-msg-body">{renderAssistantMessageBody(item.text)}</div>
|
||||
<div className="assistant-msg-body">
|
||||
{renderAssistantMessageBody(item, selectedContextChip, onSelectContextChip, onClearContextChip)}
|
||||
</div>
|
||||
{item.role === "assistant" && item.debug ? (
|
||||
<details className="assistant-debug">
|
||||
<summary>Показать технический разбор</summary>
|
||||
@@ -329,6 +454,23 @@ export function AssistantPanel({
|
||||
</div>
|
||||
|
||||
<div className="assistant-compose">
|
||||
{selectedContextChip ? (
|
||||
<div className="assistant-compose-context">
|
||||
<span className="assistant-compose-context-label">Выбранный объект</span>
|
||||
<div className="assistant-compose-context-pill" title={selectedContextChip.source_text}>
|
||||
<span className="assistant-compose-context-pill-text">{selectedContextChip.preview_text}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="assistant-compose-context-clear"
|
||||
onClick={onClearContextChip}
|
||||
aria-label="Убрать выбранный объект"
|
||||
title="Убрать выбранный объект"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<label className="full-width">
|
||||
Сообщение
|
||||
<textarea
|
||||
@@ -336,7 +478,11 @@ export function AssistantPanel({
|
||||
value={inputValue}
|
||||
onChange={(event) => onInputChange(event.target.value)}
|
||||
rows={4}
|
||||
placeholder="Введите вопрос к данным компании..."
|
||||
placeholder={
|
||||
selectedContextChip
|
||||
? "Продолжите вопрос по выбранному объекту..."
|
||||
: "Введите вопрос к данным компании..."
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<div className="button-row assistant-send-row">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { apiClient } from "../api/client";
|
||||
import type {
|
||||
AssistantConversationItem,
|
||||
AssistantAnnotationRecord,
|
||||
AssistantSelectionChip,
|
||||
AsyncEvalRunJob,
|
||||
AutoGenHistoryRecord,
|
||||
AutoGenMode,
|
||||
@@ -106,6 +107,25 @@ const AUTORUNS_UI_CONFIG_KEY = "ndc_autoruns_ui_config_v1";
|
||||
const AUTORUNS_SAVE_EVENT = "ndc-autoruns-save";
|
||||
const ASSISTANT_STAGES = ["Анализ запроса", "Получение данных", "Подготовка ответа"];
|
||||
|
||||
function buildAssistantLiveFollowupMessage(inputValue: string, selectedChip: AssistantSelectionChip | null): string {
|
||||
const trimmedInput = inputValue.trim();
|
||||
if (!trimmedInput) {
|
||||
return "";
|
||||
}
|
||||
if (!selectedChip) {
|
||||
return trimmedInput;
|
||||
}
|
||||
|
||||
const normalizedInput = trimmedInput.toLowerCase();
|
||||
const selectionAnchor = selectedChip.anchor_text.trim();
|
||||
const normalizedSelection = selectionAnchor.toLowerCase();
|
||||
if (normalizedSelection && normalizedInput.includes(normalizedSelection)) {
|
||||
return trimmedInput;
|
||||
}
|
||||
|
||||
return `По выбранному объекту "${selectionAnchor}": ${trimmedInput}`;
|
||||
}
|
||||
|
||||
const AUTOGEN_PERSONALITIES: AutoGenPersonalityDefinition[] = [
|
||||
{
|
||||
id: "general",
|
||||
@@ -496,6 +516,7 @@ export function AutoRunsHistoryPanel({
|
||||
const [assistantLiveConversation, setAssistantLiveConversation] = useState<AssistantConversationItem[]>([]);
|
||||
const [assistantLiveAnnotations, setAssistantLiveAnnotations] = useState<AssistantAnnotationRecord[]>([]);
|
||||
const [assistantLiveInput, setAssistantLiveInput] = useState("");
|
||||
const [assistantLiveSelectedChip, setAssistantLiveSelectedChip] = useState<AssistantSelectionChip | null>(null);
|
||||
const [assistantLiveUseMock, setAssistantLiveUseMock] = useState(false);
|
||||
const [assistantLiveBusy, setAssistantLiveBusy] = useState(false);
|
||||
const [assistantLiveStatus, setAssistantLiveStatus] = useState("");
|
||||
@@ -704,6 +725,7 @@ export function AutoRunsHistoryPanel({
|
||||
setAssistantLiveConversation([]);
|
||||
setAssistantLiveAnnotations([]);
|
||||
setAssistantLiveInput("");
|
||||
setAssistantLiveSelectedChip(null);
|
||||
setAssistantLiveStatus("");
|
||||
setAssistantLiveError("");
|
||||
closeAssistantLiveCommentModal({ force: true });
|
||||
@@ -711,7 +733,7 @@ export function AutoRunsHistoryPanel({
|
||||
}, [closeAssistantLiveCommentModal, log]);
|
||||
|
||||
const sendAssistantLiveMessage = useCallback(async () => {
|
||||
const userMessage = assistantLiveInput.trim();
|
||||
const userMessage = buildAssistantLiveFollowupMessage(assistantLiveInput, assistantLiveSelectedChip);
|
||||
if (!userMessage) {
|
||||
return;
|
||||
}
|
||||
@@ -759,6 +781,7 @@ export function AutoRunsHistoryPanel({
|
||||
}
|
||||
}, [
|
||||
assistantLiveInput,
|
||||
assistantLiveSelectedChip,
|
||||
assistantLiveSessionId,
|
||||
assistantLiveUseMock,
|
||||
assistantPromptVersion,
|
||||
@@ -2302,6 +2325,9 @@ export function AutoRunsHistoryPanel({
|
||||
conversation={assistantLiveConversation}
|
||||
inputValue={assistantLiveInput}
|
||||
onInputChange={setAssistantLiveInput}
|
||||
selectedContextChip={assistantLiveSelectedChip}
|
||||
onSelectContextChip={setAssistantLiveSelectedChip}
|
||||
onClearContextChip={() => setAssistantLiveSelectedChip(null)}
|
||||
useMock={assistantLiveUseMock}
|
||||
onUseMockChange={setAssistantLiveUseMock}
|
||||
onSend={sendAssistantLiveMessage}
|
||||
|
||||
@@ -486,6 +486,13 @@ export interface AssistantConversationItem {
|
||||
debug: AssistantDebugState | null;
|
||||
}
|
||||
|
||||
export interface AssistantSelectionChip {
|
||||
message_id: string;
|
||||
source_text: string;
|
||||
anchor_text: string;
|
||||
preview_text: string;
|
||||
}
|
||||
|
||||
export interface AssistantMessageResultState {
|
||||
ok: boolean;
|
||||
session_id: string;
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
--rgb-surface-main: 26, 26, 31;
|
||||
--rgb-surface-horizontal: 32, 32, 38;
|
||||
--rgb-surface-focus: 40, 40, 47;
|
||||
--rgb-assistant-chip: 18, 18, 18;
|
||||
--rgb-assistant-chip-hover: 44, 44, 44;
|
||||
--rgb-assistant-chip-selected: 228, 142, 92;
|
||||
--rgb-assistant-chip-selected-text: 18, 18, 18;
|
||||
--rgb-active: 228, 142, 92;
|
||||
--rgb-active-text: 18, 18, 18;
|
||||
--rgb-text-main: 240, 240, 240;
|
||||
@@ -486,6 +490,7 @@ button:disabled {
|
||||
padding: 8px 10px;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.assistant-msg.user {
|
||||
@@ -552,17 +557,48 @@ button:disabled {
|
||||
gap: 10px;
|
||||
line-height: 1.35;
|
||||
font-size: 0.84rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.assistant-msg-block {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.assistant-msg-block.selectable {
|
||||
cursor: pointer;
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
|
||||
.assistant-msg-block.selectable:hover,
|
||||
.assistant-msg-block.selectable:focus-visible {
|
||||
background: rgba(var(--rgb-active), 0.18);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.assistant-msg-block.selectable.active {
|
||||
background: rgb(var(--rgb-active));
|
||||
color: rgb(var(--rgb-active-text));
|
||||
}
|
||||
|
||||
.assistant-msg-block.selectable.active:hover,
|
||||
.assistant-msg-block.selectable.active:focus-visible {
|
||||
background: rgb(var(--rgb-active));
|
||||
}
|
||||
|
||||
.assistant-msg-block.selectable.active .assistant-msg-line,
|
||||
.assistant-msg-block.selectable.active .assistant-msg-line strong {
|
||||
color: rgb(var(--rgb-active-text));
|
||||
}
|
||||
|
||||
.assistant-msg-line {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.assistant-msg-line.heading {
|
||||
@@ -601,6 +637,63 @@ button:disabled {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.assistant-compose-context {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--radius-md);
|
||||
background: rgb(var(--rgb-surface-horizontal));
|
||||
}
|
||||
|
||||
.assistant-compose-context-label {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.74rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.assistant-compose-context-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
max-width: 100%;
|
||||
width: fit-content;
|
||||
min-height: 38px;
|
||||
padding: 0 10px 0 12px;
|
||||
border-radius: 999px;
|
||||
background: rgb(var(--rgb-assistant-chip-selected));
|
||||
color: rgb(var(--rgb-assistant-chip-selected-text));
|
||||
}
|
||||
|
||||
.assistant-compose-context-pill-text {
|
||||
min-width: 0;
|
||||
max-width: min(100%, 460px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.assistant-compose-context-clear {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
padding: 0;
|
||||
border-radius: 999px;
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
color: inherit;
|
||||
font-size: 0.96rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.assistant-compose-context-clear:hover {
|
||||
background: rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
.assistant-send-row {
|
||||
align-items: center;
|
||||
margin-top: 2px;
|
||||
|
||||
Reference in New Issue
Block a user