1d89cc2058
Правки по 5 находкам ревью #498. F1 (регрессия отступов списков в Reasoning-панели): добавлен `.reasoningText li p {margin:0}` (зеркало существующего `.markdown li p`). Reasoning рендерит через тот же renderChatMarkdown (теперь всегда <li><p>…</p></li>), но под .reasoningText, где `.reasoningText p{margin:0 0 4px}` давал 4px на пункт. Обе поверхности покрыты. F2 (глухой catch): `.catch` вставки был `()=>{}` → теперь `(err)=>console.error( "markdown paste conversion failed, inserting raw text", err)` — тихая деградация в raw-текст больше не невидима (покрывает и конвертер, и тело success-.then, напр. PMNode.fromJSON при дрейфе схемы). F3 (нет теста doc-changed guard): +3 теста в markdown-clipboard.paste.test.ts: success-ветка при mid-flight изменении дока → вставка в живую selection (маркер цел, без клоббера/throw); fail-open ветка при mid-flight + провале конверсии → raw-текст в живую selection без RangeError; две вставки в полёте → инвариант «ни один payload не потерян». F4 (устаревшие комменты): исправлены ссылки на удалённый md-слой в markdown- clipboard.ts, footnote-sync/util(+test), docmost-schema, foreign-markdown, footnote-canonicalize → на @docmost/prosemirror-markdown / локальные символы. F5 (внешние доки): AGENTS.md (apps/client как потребитель через browser-entry, jsdom только в Node, удалён marked/turndown-слой); prosemirror-markdown/README (секция Node vs browser entry, markdownToProseMirrorSync); CHANGELOG. Тесты: client paste+canonicalize+ai-chat 61; pmd 744; editor-ext 196; клиентская сборка успешна, grep бандла на JSDOM/parse5/happy-dom/turndown — 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
235 lines
6.4 KiB
CSS
235 lines
6.4 KiB
CSS
.panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
min-height: 0;
|
|
}
|
|
|
|
.messages {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
/* Smaller, denser chat text (cascades into user bubble + assistant markdown
|
|
+ tool cards; the explicit-size Mantine labels keep their own size). */
|
|
font-size: var(--mantine-font-size-xs);
|
|
}
|
|
|
|
.messageRow {
|
|
margin-bottom: var(--mantine-spacing-md);
|
|
}
|
|
|
|
.userBubble {
|
|
background: var(--mantine-color-gray-light);
|
|
border-radius: var(--mantine-radius-md);
|
|
padding: 8px 12px;
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* Rendered markdown for assistant messages. Keep block margins compact. */
|
|
.markdown {
|
|
overflow-wrap: break-word;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.markdown p {
|
|
margin-block-start: 0;
|
|
margin-block-end: 0.5em;
|
|
}
|
|
|
|
.markdown pre {
|
|
background: var(--mantine-color-gray-light);
|
|
border-radius: var(--mantine-radius-sm);
|
|
padding: 8px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.markdown code {
|
|
font-size: 0.85em;
|
|
}
|
|
|
|
.markdown ul,
|
|
.markdown ol {
|
|
margin-block-start: 0;
|
|
margin-block-end: 0.5em;
|
|
padding-inline-start: 1.4em;
|
|
}
|
|
|
|
/* The canonical converter renders list items through the editor schema, which
|
|
wraps each item's content in a <p> (listItem content is `paragraph+`). Drop
|
|
that paragraph's block margin so list items render TIGHT (no extra vertical
|
|
gap), matching the previous marked output — same rule already applied to
|
|
table cells above (issue #347). */
|
|
.markdown li p {
|
|
margin: 0;
|
|
}
|
|
|
|
/* GFM tables in assistant markdown. The chat lives in a NARROW side panel, so a
|
|
wide LLM table must scroll horizontally instead of collapsing its columns:
|
|
`.markdown` sets `word-break: break-word`, which (with the default table
|
|
layout) shrinks columns to a single glyph and wraps headers mid-word
|
|
("Секция" -> "Секци / я"). Make the table a horizontally scrollable block,
|
|
give cells a readable minimum width, and restore word-boundary wrapping. */
|
|
.markdown table {
|
|
display: block;
|
|
/* lets the table scroll horizontally on its own */
|
|
max-width: 100%;
|
|
overflow-x: auto;
|
|
border-collapse: collapse;
|
|
margin-block-end: 0.5em;
|
|
}
|
|
|
|
.markdown th,
|
|
.markdown td {
|
|
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
|
padding: 3px 8px;
|
|
/* readable floor; the block scrolls when the row exceeds the panel */
|
|
min-width: 6em;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
/* cancel the inherited break-word so words don't split mid-glyph */
|
|
word-break: normal;
|
|
/* still wrap genuinely long words / URLs at the cell edge */
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
.markdown th {
|
|
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* GFM wraps cell text in <p>; drop its default block margin inside cells. */
|
|
.markdown table p {
|
|
margin: 0;
|
|
}
|
|
|
|
/* Animated three-dot "typing" indicator shown while the agent is thinking but
|
|
has not yet produced any visible text/tool parts. */
|
|
.typingDots {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.typingDots span {
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background: var(--mantine-color-dimmed, var(--mantine-color-gray-5));
|
|
opacity: 0.4;
|
|
animation: aiTypingBounce 1.2s infinite ease-in-out;
|
|
}
|
|
|
|
.typingDots span:nth-child(2) {
|
|
animation-delay: 0.2s;
|
|
}
|
|
|
|
.typingDots span:nth-child(3) {
|
|
animation-delay: 0.4s;
|
|
}
|
|
|
|
@keyframes aiTypingBounce {
|
|
0%,
|
|
80%,
|
|
100% {
|
|
transform: translateY(0);
|
|
opacity: 0.4;
|
|
}
|
|
40% {
|
|
/* Bounce height is driven by --bounce so reduced-motion can dampen it
|
|
(below) without disabling the animation outright. */
|
|
transform: translateY(var(--bounce, -6px));
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Respect reduced-motion preferences: keep a smaller bounce instead of a full
|
|
stop, so the "thinking" indicator still reads as active rather than frozen. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.typingDots span {
|
|
--bounce: -3px;
|
|
}
|
|
}
|
|
|
|
.toolCard {
|
|
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
|
border-radius: var(--mantine-radius-sm);
|
|
padding: 6px 10px;
|
|
margin-bottom: 6px;
|
|
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
|
}
|
|
|
|
/* Collapsible "Thinking" (reasoning) block: a subtle left rule, dimmer than the
|
|
answer so it reads as secondary thinking context above the real answer. */
|
|
.reasoningBlock {
|
|
border-left: 2px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
|
padding-left: 8px;
|
|
}
|
|
|
|
.reasoningText {
|
|
margin-top: 4px;
|
|
font-size: var(--mantine-font-size-xs);
|
|
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
|
/* NOTE: `white-space: pre-wrap` is intentionally NOT set here. On the
|
|
rendered markdown <div> it would turn the newlines between block tags
|
|
(</li>\n<li>, </p>\n<ol>) into visible blank lines/indents on top of the
|
|
margins. The streaming plain-text path that needs pre-wrap sets it
|
|
per chunk instead, in PlainChunk (see streaming-plain-text.tsx). */
|
|
}
|
|
|
|
.reasoningText p {
|
|
margin: 0 0 4px;
|
|
}
|
|
|
|
/* Same as `.markdown li p` above: the canonical converter wraps every list
|
|
item's content in a <p>, so without this each reasoning-panel list item would
|
|
pick up `.reasoningText p`'s 4px bottom margin and render too loose. Drop it
|
|
so Reasoning-panel lists stay tight, mirroring the pre-#347 marked output. */
|
|
.reasoningText li p {
|
|
margin: 0;
|
|
}
|
|
|
|
.inputWrapper {
|
|
flex: 0 0 auto;
|
|
padding-top: var(--mantine-spacing-xs);
|
|
}
|
|
|
|
.conversationItem {
|
|
cursor: pointer;
|
|
border-radius: var(--mantine-radius-sm);
|
|
}
|
|
|
|
.conversationItem:hover {
|
|
background: var(--mantine-color-gray-light);
|
|
}
|
|
|
|
.conversationItemActive {
|
|
background: var(--mantine-color-gray-light);
|
|
}
|
|
|
|
/* Pending messages queued by the user while a turn is still streaming. They
|
|
are sent automatically, FIFO, once the current turn finishes. */
|
|
.queuedList {
|
|
padding-bottom: var(--mantine-spacing-xs);
|
|
}
|
|
|
|
.queuedItem {
|
|
background: var(--mantine-color-gray-light);
|
|
border-radius: var(--mantine-radius-sm);
|
|
padding: 4px 8px;
|
|
}
|
|
|
|
.queuedIcon {
|
|
flex: none;
|
|
color: var(--mantine-color-dimmed);
|
|
}
|
|
|
|
.queuedText {
|
|
flex: 1;
|
|
min-width: 0;
|
|
color: var(--mantine-color-dimmed);
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
word-break: break-word;
|
|
}
|