eharris128_Obsidian-LLM-Plugin/styles.css
jsmorabito 013f14bb4b feat: add built-in MCP server for external vault access
Adds a Streamable HTTP MCP server (127.0.0.1-bound, bearer-token
authenticated) so Claude Desktop or any other MCP client can list,
read, search, create, edit, move, and delete vault notes directly,
without a separate companion plugin. Writes go through a new
permission modal (create/edit/move/delete); reads run immediately.
Every path is resolved and validated against the vault root to
reject traversal attempts, including encoded/double-encoded variants.

The MCP SDK is loaded lazily (dynamic import, gated on
Platform.isDesktop && settings.mcpServerSettings.enabled) so its
node:http dependency chain never executes at plugin load on mobile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 12:49:36 -04:00

3105 lines
76 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*VARS*/
:root {
--size-ratio: 1;
--fab-bottom: 1.6em;
--fab-left: -0.2em;
--fab-bottom-no-bar: -0.2em;
--widget-view-max-height: var(--dialog-max-height);
--modal-view-max-height: 50vh;
}
/*Generics*/
.llm-flex {
display: flex;
}
.llm-flex-column {
display: flex;
flex-direction: column;
}
.llm-flex-reverse {
display: flex;
flex-direction: row-reverse;
}
.assistants-create-button-div {
border-top: unset;
}
.llm-assistants-button {
width: fit-content;
}
.llm-update-settings {
padding-top: 0.75em;
border-top: var(--border-width) solid var(--background-modifier-border);
}
.mt-auto {
margin-top: auto;
}
/* TITLE */
.llm-title {
flex-direction: column;
font-weight: bold;
align-items: center;
justify-content: center;
flex: 1;
min-width: 0; /* allow text to shrink inside flex */
}
.llm-title-div {
align-items: stretch;
background-color: var(--background-primary);
display: flex;
justify-content: space-between;
overflow: auto;
padding: var(--size-4-1) var(--size-4-3);
position: relative;
box-sizing: border-box;
width: 100%;
}
/* Widget sidebar: clip horizontal overflow caused by the bleed technique below */
.workspace-leaf-content[data-type="tab-view"] > .view-content {
overflow-x: hidden;
}
/* Widget sidebar/tab: bleed out of .view-content's 16 px padding so the toolbar
spans edge-to-edge, matching the pdf-toolbar appearance */
.workspace-leaf-content[data-type="tab-view"] .llm-title-div {
background-color: transparent;
margin-left: calc(-1 * var(--size-4-4));
margin-right: calc(-1 * var(--size-4-4));
margin-top: calc(-1 * var(--size-4-4));
padding-left: var(--size-4-4);
padding-right: var(--size-4-4);
width: calc(100% + 2 * var(--size-4-4));
}
/* Chat in tab: keep the border-bottom to match .pdf-toolbar; hide the redundant lineBreak div */
.mod-root .workspace-leaf-content[data-type="tab-view"] .llm-title-div {
border-bottom: var(--border-width) solid var(--background-modifier-border);
}
.workspace-leaf-content[data-type="tab-view"] .llm-widget-title-border,
.modal-content .llm-modal-title-border {
display: none;
}
/* Sidebar widget: match Obsidian nav-header vertical spacing (8 px top + bottom) */
.mod-left-split .workspace-leaf-content[data-type="tab-view"] .llm-title-div,
.mod-right-split .workspace-leaf-content[data-type="tab-view"] .llm-title-div {
padding-top: var(--size-4-2);
padding-bottom: var(--size-4-2);
}
/* Modal (Ask AI): same bleed as widget — contentEl has var(--size-4-4) padding on all sides */
.modal-content .llm-title-div {
margin-left: calc(-1 * var(--size-4-4));
margin-right: calc(-1 * var(--size-4-4));
margin-top: calc(-1 * var(--size-4-4));
padding-left: var(--size-4-4);
padding-right: var(--size-4-4);
width: calc(100% + 2 * var(--size-4-4));
}
/* FAB popover: bleed out of fab-view-area's 16 px padding.
Rounded top corners match the card so the background stays seamless.
Background matches the card body so the bar blends in. */
.fab-content-area .llm-title-div {
background-color: var(--background-secondary);
border-radius: var(--radius-m) var(--radius-m) 0 0;
margin-top: calc(-1 * var(--size-4-4));
margin-left: calc(-1 * var(--size-4-4));
margin-right: calc(-1 * var(--size-4-4));
padding-left: var(--size-4-4);
padding-right: var(--size-4-4);
width: calc(100% + 2 * var(--size-4-4));
}
.llm-modal-title-border {
border-bottom: var(--border-width) solid var(--background-modifier-border);
margin-left: calc(-1 * var(--size-4-4));
width: calc(100% + 2 * var(--size-4-4));
}
.llm-model-name {
font-size: var(--font-ui-small);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.llm-left-buttons-div {
justify-self: flex-start;
align-items: center;
min-width: 0; /* allow text-overflow to work */
}
.llm-right-buttons-div {
justify-content: flex-end;
align-items: center;
flex-shrink: 0;
}
.llm-right-buttons-div,
.llm-left-buttons-div {
flex: 1;
}
/* Default header (Widget/Tab/Modal): left side shrinks to content width */
.llm-left-buttons-div--shrink {
flex: 0 1 auto;
min-width: 0;
max-width: 60%;
}
.llm-left-buttons-div--shrink .llm-chat-title {
flex: 0 1 auto;
}
/* FAB popover: chat title in the header */
.llm-chat-title {
font-size: var(--font-ui-small);
font-weight: var(--font-semibold);
color: var(--text-normal);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
padding-right: var(--size-2-1);
}
/*MODAL VIEW*/
.llm-modal-settings-container {
padding-top: var(--size-4-4);
}
.llm-modal-chat-container,
.llm-modal-chat-history-container,
.llm-modal-settings-container,
.llm-modal-assistants-container,
.llm-modal-skills-container {
min-height: 50vh;
max-height: var(--modal-view-max-height);
}
.llm-modal-chat-container,
.llm-modal-chat-history-container,
.llm-modal-settings-container,
.llm-modal-assistants-container,
.llm-modal-skills-container,
.llm-widget-chat-container,
.llm-widget-settings-container,
.llm-widget-chat-history-container,
.llm-widget-assistant-container,
.llm-widget-skills-container,
.fab-chat-container,
.fab-settings-container,
.fab-chat-history-container,
.fab-assistants-container,
.fab-skills-container {
flex-direction: column;
width: calc(100% - var(--size-4-1));
padding: 0 var(--size-2-1);
overflow: auto;
}
/* Modal container overrides — must come after the shared padding rule */
.llm-modal-chat-history-container {
padding-top: var(--size-4-4);
padding-bottom: var(--size-4-2);
gap: var(--size-4-2);
}
.llm-modal-assistants-container {
padding-top: var(--size-4-2);
}
.llm-modal-settings-container.llm-flex {
padding-top: var(--size-4-4);
}
.modal:not(.mod-settings) .llm-modal-chat-history-container .setting-item.llm-history-item {
background-color: var(--setting-items-background);
border: none;
border-radius: var(--radius-s);
padding: var(--size-4-2);
}
.llm-modal-chat-container,
.llm-widget-chat-container,
.fab-chat-container {
justify-content: space-between;
padding: 0 var(--size-2-1) var(--size-2-1);
width: 100%;
position: relative; /* anchor for absolutely-positioned empty state */
}
.llm-modal-messages-div {
min-height: 10rem;
max-height: 40vh;
overflow-y: auto;
padding-top: var(--size-4-4);
margin-bottom: var(--size-4-4);
}
.llm-flex-end {
flex-direction: row;
justify-content: flex-end;
flex: 1;
}
.llm-flex-start {
flex-direction: row;
justify-content: flex-start;
}
.llm-modal-prompt-container {
width: 100%;
display: flex;
flex-direction: column;
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-m);
overflow: hidden;
}
#chat-prompt-text-area {
padding: var(--size-4-2) var(--size-4-2) var(--size-2-1) var(--size-4-2);
font-size: var(--font-ui-medium);
resize: none;
border: none;
box-shadow: none;
background: transparent;
border-radius: 0;
}
.llm-modal-chat-prompt-text-area,
.llm-widget-chat-prompt-text-area {
height: 1px;
width: 100%;
min-height: 3rem;
}
button.llm-send-button {
border-radius: var(--button-radius);
box-shadow: none;
height: auto;
line-height: 1;
font-size: inherit;
align-items: center;
justify-content: center;
cursor: var(--cursor);
padding: var(--size-4-2);
color: var(--text-on-accent);
background-color: var(--interactive-accent);
--icon-size: var(--icon-s);
--icon-stroke: var(--icon-s-stroke-width);
flex-shrink: 0;
transition: opacity 0.15s ease, background-color 0.15s ease;
}
button.llm-send-button:not(.llm-send-button-disabled):hover {
background-color: var(--interactive-accent-hover);
color: var(--text-on-accent);
}
button.llm-send-button.llm-send-button-disabled,
button.llm-send-button:disabled {
opacity: 0.35;
cursor: not-allowed;
pointer-events: none;
}
/* Stop mode — shown while the model is generating */
button.llm-send-button.llm-stop-mode {
background-color: var(--background-primary);
color: var(--text-muted);
border: var(--border-width) solid var(--background-modifier-border);
}
button.llm-send-button.llm-stop-mode:hover {
background-color: var(--background-modifier-hover);
color: var(--text-normal);
border-color: var(--background-modifier-border-hover);
opacity: 1;
}
.llm-modal-send-button,
.fab-send-button,
.llm-widget-send-button {
/* Previously absolutely positioned; now flows in the toolbar row */
position: static;
transform: none;
top: auto;
right: auto;
bottom: auto;
}
/* Chat input two-section layout */
.llm-input-section {
display: flex;
}
/* Wrapper that stacks the mirror div and textarea on top of each other */
.llm-prompt-wrapper {
position: relative;
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.llm-input-section textarea {
/* No flex: the wrapper (.llm-prompt-wrapper) already fills the row width via
its own flex:1. Using flex:1 here (flex-basis:0%) on the textarea inside a
column flex container causes the flex algorithm to own the height and ignore
the explicit height: Xpx set by auto_height, breaking textarea expansion. */
width: 100%;
min-width: 0;
box-sizing: border-box;
}
/* Mirror overlay — rendered on top of the textarea (pointer-events:none).
Copies the textarea's text, but the skill prefix span is accent-colored. */
.llm-input-mirror {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
white-space: pre-wrap;
word-wrap: break-word;
overflow-y: auto;
overflow-x: hidden;
box-sizing: border-box;
color: var(--text-normal);
background: transparent;
border: 1px solid transparent; /* match textarea border thickness */
z-index: 1;
}
/* Accent color for the leading "/skill-id " portion */
.llm-skill-prefix {
color: var(--interactive-accent);
}
/* The rest of the message text in the mirror (normal color) */
.llm-mirror-rest {
color: var(--text-normal);
}
/* When the mirror is active, hide the textarea's text (keep caret visible).
Specificity-boosted (class repeated) instead of !important — a strong
margin against themes that style textarea color directly, traded for a
hard guarantee. A losing theme would double the visible text on top of
the mirror. */
textarea.llm-input-with-mirror.llm-input-with-mirror {
color: transparent;
caret-color: var(--text-normal);
}
.llm-input-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--size-2-1) var(--size-4-2) var(--size-4-2) var(--size-4-2);
gap: var(--size-4-2);
}
/* Compact model selector in the chat toolbar */
.llm-model-select {
background-color: transparent;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right var(--size-4-1) center;
background-size: 12px;
border: none;
box-shadow: none;
color: var(--text-muted);
font-size: var(--font-ui-small);
cursor: pointer;
padding: var(--size-4-1) calc(var(--size-4-1) + 18px) var(--size-4-1) var(--size-4-1);
border-radius: var(--radius-s);
width: fit-content;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-appearance: none;
appearance: none;
}
.llm-model-select:hover {
background-color: var(--background-modifier-hover);
color: var(--text-normal);
}
.llm-model-select:focus {
outline: none;
box-shadow: none;
}
/* IM LIKE MESSAGE*/
/*
* CLASS NAMING NOTE — llm-flex-start vs llm-flex-end
* These class names describe flex alignment direction, NOT message role.
* Counterintuitively, the mapping is:
* llm-flex-start → USER messages (even indices in the messages array: 0, 2, 4…)
* llm-flex-end → LLM responses (odd indices in the messages array: 1, 3, 5…)
* If you're refactoring, consider renaming to llm-message-user / llm-message-assistant.
*/
/* Outer alignment wrapper — one per message row, stacks bubble + actions vertically */
.llm-message-wrapper {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-bottom: var(--size-4-1);
}
/* Override the row-direction that llm-flex-start/llm-flex-end normally apply */
.llm-message-wrapper.llm-flex-start,
.llm-message-wrapper.llm-flex-end {
flex-direction: column;
justify-content: flex-start;
flex: unset;
}
/* User messages (llm-flex-start): right-align bubble and actions */
.llm-message-wrapper.llm-flex-start {
align-items: flex-end;
}
.llm-modal-messages-div > .llm-message-wrapper,
.fab-messages-div > .llm-message-wrapper,
.llm-widget-messages-div > .llm-message-wrapper {
min-width: 100%;
}
/* The bubble itself */
.im-like-message-container {
width: fit-content;
border-radius: var(--radius-s);
}
/* LLM responses fill the full row width */
.llm-message-wrapper.llm-flex-end .im-like-message-container {
width: 100%;
}
/* Strip background/shadow from every element inside a completed LLM response.
Specificity-boosted (.llm-message-wrapper repeated) instead of !important:
the targets are Obsidian's own markdown-preview elements, which core/theme
CSS restyles at varying specificity, so this trades a hard guarantee for a
strong margin instead. */
.llm-message-wrapper.llm-message-wrapper.llm-flex-end,
.llm-message-wrapper.llm-message-wrapper.llm-flex-end .im-like-message-container,
.llm-message-wrapper.llm-message-wrapper.llm-flex-end .im-like-message,
.llm-message-wrapper.llm-message-wrapper.llm-flex-end .markdown-rendered,
.llm-message-wrapper.llm-message-wrapper.llm-flex-end .markdown-preview-view,
.llm-message-wrapper.llm-message-wrapper.llm-flex-end .markdown-preview-section,
.llm-message-wrapper.llm-message-wrapper.llm-flex-end .markdown-preview-sizer {
background-color: transparent;
box-shadow: none;
border: none;
}
/* User messages get a visible bubble */
.llm-message-wrapper.llm-flex-start .im-like-message-container {
padding: var(--size-4-2) var(--size-4-3);
border-radius: var(--radius-m);
background-color: var(--background-modifier-hover);
width: fit-content;
}
.llm-message-wrapper.llm-flex-start .im-like-message {
text-align: left;
}
.llm-assistant-logo + .im-like-message-container {
display: flex;
flex-flow: column;
}
/* Actions bar — sits below the bubble */
.llm-message-actions {
display: flex;
align-items: center;
gap: var(--size-2-1);
padding: var(--size-2-1) var(--size-4-1);
visibility: hidden;
opacity: 0;
transition: opacity 0.1s ease;
}
.llm-message-actions .clickable-icon {
--icon-size: var(--icon-xs);
}
/* Reveal on hover of message wrapper (history messages) */
.llm-message-wrapper:hover .llm-message-actions {
visibility: visible;
opacity: 1;
}
/* Always visible for the active streaming response */
.llm-message-actions.llm-actions-visible {
visibility: visible;
opacity: 1;
}
/* LLM streaming responses are plain — no background, no border (on container
and all children). Specificity-boosted (.im-like-message-container
repeated) instead of !important — a strong margin against Obsidian/theme
styling of arbitrary rendered-markdown elements, traded for a hard
guarantee. */
.llm-assistant-logo + .im-like-message-container.im-like-message-container,
.llm-assistant-logo + .im-like-message-container.im-like-message-container * {
background-color: transparent;
box-shadow: none;
border: none;
}
.llm-assistant-logo {
margin-top: var(--size-4-1);
margin-right: var(--size-4-3);
}
.mod-sidedock .llm-assistant-logo,
.fab-messages-div .llm-assistant-logo {
margin-right: var(--size-4-2);
}
.im-like-message {
padding: var(--size-4-1) var(--size-4-2);
width: 100%;
p {
margin: 0;
}
}
div.im-like-message.llm-widget-chat-message > p {
margin: 0;
}
.center-llmgal {
margin-top: auto;
margin-bottom: auto;
}
.llm-hide {
visibility: hidden;
}
/* HISTORY CONTAINER */
.llm-history-item {
justify-content: space-between;
padding-left: var(--size-4-2);
border-radius: var(--radius-s);
}
.llm-history-item:first-child {
padding-top: var(--size-4-2);
}
.llm-history-item > p {
margin: 0;
min-width: 1px;
}
.llm-history-item:hover {
box-shadow: var(--input-shadow-hover);
}
.history-buttons-div {
align-items: center;
}
.llm-no-pointer {
pointer-events: none;
}
.llm-delete-history-button {
margin: 0 var(--size-4-2);
}
.edit-prompt-button,
.save-prompt-button,
.llm-add-text,
.llm-refresh-output,
#llm-delete-history-button {
padding: var(--size-4-1);
}
/* WIDGET*/
/* Make the Obsidian view-content div a flex column so llm-widget-body's
flex:1 takes effect and fills the remaining height below the header. */
.llm-widget-root {
display: flex;
flex-direction: column;
height: 100%;
}
.llm-widget-chat-container {
padding-bottom: var(--size-4-4);
}
.llm-widget-messages-div {
flex: 1;
min-height: 0;
overflow: auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
padding-top: var(--size-4-4);
}
.llm-widget-chat-prompt-text-area {
/* margin-top removed — textarea lives inside llm-input-section now */
}
.llm-widget-prompt-container {
display: flex;
flex-direction: column;
margin-top: auto;
margin-bottom: var(--size-4-2);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-m);
overflow: hidden;
}
.llm-widget-title-border {
border-bottom: var(--border-width) solid var(--background-modifier-border);
width: 100%;
}
.llm-widget-chat-container,
.llm-widget-chat-history-container,
.llm-widget-settings-container {
max-height: var(--widget-view-max-height);
height: calc(100% - 70px);
}
.llm-widget-chat-history-container,
.llm-widget-settings-container {
padding-top: var(--size-4-4);
}
/* ── Widget inline layout (PDF-reader style body + sidebars) ────────────────
The widget's content area splits into a flex row:
llm-widget-body
llm-widget-chats-sidebar (fixed width, left, shown via .is-open)
llm-widget-main (flex:1 — chat/history/settings panels)
llm-widget-details-sidebar (fixed width, right, shown via .is-open)
─────────────────────────────────────────────────────────────────────────── */
.llm-widget-body {
display: flex;
flex-direction: row;
flex: 1;
min-height: 0;
overflow: hidden;
gap: var(--size-4-3);
}
.llm-widget-main {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
overflow: hidden;
}
/* Inside the flex layout the containers fill the column — override the
absolute-height rule that was needed before the flex wrapper existed. */
.llm-widget-main > .llm-widget-chat-container,
.llm-widget-main > .llm-widget-chat-history-container,
.llm-widget-main > .llm-widget-settings-container {
flex: 1;
height: 100%;
max-height: none;
}
/* Inline Chats sidebar (left side) */
.llm-widget-chats-sidebar {
width: 260px;
flex-shrink: 0;
border-right: var(--border-width) solid var(--background-modifier-border);
overflow: hidden;
display: none;
flex-direction: column;
}
.llm-widget-chats-sidebar.is-open {
display: flex;
}
/* The file list inside the chats sidebar should scroll, not the whole sidebar */
.llm-widget-chats-sidebar .nav-files-container {
flex: 1;
overflow-y: auto;
min-height: 0;
}
/* Inline Chat Details sidebar (right side) */
.llm-widget-details-sidebar {
width: 260px;
flex-shrink: 0;
border-left: var(--border-width) solid var(--background-modifier-border);
overflow-y: auto;
padding: var(--size-4-2) 0;
display: none;
}
.llm-widget-details-sidebar.is-open {
display: block;
}
/* FAB VIEW CSS */
.fab-view-area {
padding: var(--size-4-4);
width: 400px;
min-width: 400px;
max-width: calc(100vw - var(--size-4-3));
max-height: calc(-36px + 100vh);
min-height: 360px;
background-color: var(--background-secondary);
border: var(--border-width) solid var(--background-modifier-border-focus);
border-radius: var(--radius-m);
margin-bottom: var(--size-4-2);
margin-right: var(--size-2-1);
box-shadow: var(--shadow-s);
display: flex;
flex-direction: column;
/* overflow intentionally omitted — overflow:hidden lives on .fab-content-area
so the resize handle can extend above the top border with a negative offset. */
box-sizing: border-box;
position: relative;
}
#_floating-action-button {
height: auto;
width: auto;
padding: var(--size-2-1);
}
#_floating-action-button > .buttonItem > svg.svg-icon {
height: var(--icon-s);
width: var(--icon-s);
}
#_floating-action-button .buttonItem {
height: 34px;
width: 34px;
float: right;
margin: var(--size-2-1);
cursor: pointer;
padding: var(--size-4-1);
margin-left: var(--size-2-1);
border-radius: var(--canvas-controls-radius);
font-size: initial;
background-color: var(--interactive-normal);
border: 1px solid var(--background-modifier-border);
box-shadow: none;
}
#_floating-action-button button.buttonItem:hover {
background-color: var(--interactive-hover);
}
.fab-chat-prompt-text-area {
width: 100%;
min-height: 50px;
resize: none;
font-size: var(--font-ui-medium);
}
.fab-prompt-container {
display: flex;
flex-direction: column;
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-m);
/* overflow: visible so the slash-command menu can float above the container */
background-color: var(--background-primary);
}
.fab-prompt-container > input[type="text"] {
height: unset;
}
.fab-settings-container,
.fab-assistants-container,
.fab-chat-history-container {
overflow-y: auto;
flex: 1 1 0;
min-height: 0;
padding-top: var(--size-4-4);
}
.fab-chat-container {
display: flex;
flex-direction: column;
flex: 1 1 0;
min-height: 0;
overflow: hidden;
}
.fab-messages-div {
overflow-y: auto;
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
padding-top: var(--size-4-4);
}
/* Empty-state: center the avatar SVG both axes within the available space.
The llm-justify-content-center class is toggled by displayNoChatView(). */
.fab-messages-div.llm-justify-content-center,
.llm-widget-messages-div.llm-justify-content-center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* Absolutely positioned over the top padding zone — out of flex flow so
it works regardless of whether the parent is display:flex or display:block.
Uses the same border-highlight + --interactive-accent pattern as
Obsidian's workspace-leaf-resize-handle. */
/* Inner content wrapper carries overflow:hidden so the resize handle (a sibling,
not a child) can extend above the card's top border without being clipped. */
.fab-content-area {
display: flex;
flex-direction: column;
flex: 1 1 0;
min-height: 0;
/* overflow:hidden removed — each inner container handles its own overflow,
and removing it allows the title bar to bleed to the card edges */
}
/* Straddling the top border: top:-8px centres the 14px handle on the 1px border
line, giving the user a comfortable grab target right where they expect it. */
.fab-resize-handle {
position: absolute;
top: -8px;
left: 0;
right: 0;
height: 14px;
cursor: ns-resize;
z-index: 10;
}
/* Hover affordance is a JS class toggle on the view area (mouseenter/leave on
the handle in FAB.ts) — the community review flags CSS :has(). */
.fab-view-area.llm-handle-hover,
.fab-view-area.is-resizing {
border-top-color: var(--interactive-accent);
}
.fab-settings-container,
.fab-assistants-container,
.llm-widget-settings-container {
.setting-item {
flex-direction: column;
.setting-item-info {
margin-right: auto;
}
.setting-item-control {
justify-content: center;
}
}
.setting-item-description {
padding-bottom: var(--size-4-1);
}
}
.llm-widget-sidebar-assistants {
.setting-item {
display: flex;
flex-direction: column;
.setting-item-info {
margin-right: auto;
}
.setting-item-control {
justify-content: center;
}
}
}
.llm-widget-tab-assistants {
.setting-item {
display: flex;
flex-direction: row;
}
}
.llm-font-size-medium {
font-size: var(--font-ui-small);
}
.llm-justify-content-center {
justify-content: center;
}
.empty-history-cta {
text-align: center;
max-width: 25ch;
display: flex;
flex-direction: column;
}
.llm-empty-history-button {
margin-top: var(--size-4-4);
}
.fab-chat-message {
font-size: var(--font-ui-medium);
}
.floating-action-button {
position: absolute;
right: calc(var(--fab-left) * var(--size-ratio) + var(--size-4-2));
bottom: calc(var(--fab-bottom) * var(--size-ratio) + var(--size-4-2));
overflow: hidden;
z-index: 16;
}
/* LOADING ANIMATION TO USE BEFORE WE GET STREAMING UP */
.streaming-dot {
color: var(--interactive-accent);
font-size: var(--font-ui-large);
display: inline-block;
animation-name: bouncing;
animation-duration: 700ms;
animation-iteration-count: infinite;
animation-timing-function: ease-out;
&:nth-child(2) {
animation-delay: 125ms;
}
&:nth-child(3) {
animation-delay: 250ms;
}
}
@keyframes bouncing {
0% {
transform: none;
}
33% {
transform: translateY(-0.5em);
}
66% {
transform: none;
}
}
/* THINKING ANIMATION */
.llm-thinking-animation {
display: flex;
align-items: center;
gap: 0.3em;
padding: 0.5em 0;
color: var(--text-muted);
}
.llm-thinking-text {
font-size: var(--font-ui-small);
font-style: italic;
opacity: 0.8;
animation: pulse 1.5s ease-in-out infinite;
}
.llm-thinking-dots {
display: inline-flex;
align-items: baseline;
}
@keyframes pulse {
0%, 100% {
opacity: 0.6;
}
50% {
opacity: 1;
}
}
.llm-egg-passphrase {
width: 100%;
}
.llm-special-standing {
display: flex;
align-items: center;
}
.llm-special-standing svg {
image-rendering: pixelated;
}
.llm-special-animation {
display: flex;
margin-top: 0.4em;
}
.llm-special-animation svg {
image-rendering: pixelated;
}
.llm-icon-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
> svg {
color: var(--color-accent-1);
width: 180px;
height: 90px;
> path {
fill: var(--color-accent-1);
}
}
}
.llm-icon-new-history > svg > path {
fill: var(--text-muted);
}
.llm-icon-new-chat > svg > path {
fill: var(--text-faint);
}
/* Empty-state avatar: taken out of flex flow and anchored to the stable
chat container so the prompt area's height never affects its position.
--llm-prompt-height approximates the default prompt container height
(textarea 50px + toolbar ~40px). The icon is shifted up by half that
value so it visually lands in the centre of the messages area above
the prompt, without moving when a context chip expands the prompt. */
.fab-chat-container .llm-icon-new-chat,
.llm-modal-chat-container .llm-icon-new-chat,
.llm-widget-chat-container .llm-icon-new-chat {
--llm-prompt-height: 90px;
position: absolute;
top: calc(50% - var(--llm-prompt-height) / 2);
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
/* Setup nudge shown in the empty state when no model provider is configured. */
.llm-setup-hint {
position: absolute;
top: calc(50% + 12px);
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--size-4-2);
padding: var(--size-4-3) var(--size-4-4);
background: var(--background-secondary);
border-radius: var(--radius-m);
white-space: nowrap;
}
.llm-setup-hint-text {
margin: 0;
color: var(--text-muted);
font-size: var(--font-ui-small);
line-height: var(--line-height-tight);
}
.llm-setup-hint-button {
font-size: var(--font-ui-small);
}
#llm-settings-credits {
text-align: center;
}
.default-llm-header {
font-weight: bold;
}
#llm-hero-credits {
font-weight: bold;
font-size: var(--font-ui-medium);
margin: var(--size-4-3) 0 0 0;
}
.llm-hero-names {
font-size: var(--font-ui-small);
margin: 0;
}
.llm-text-muted,
.version {
color: var(--llm-text-muted);
font-size: var(--font-ui-smaller);
}
#llm-settings-credits > p {
font-size: var(--font-ui-small);
margin: 0;
}
.llm-vector-files {
display: flex;
flex-direction: column;
align-items: flex-start;
width: fit-content;
background: var(--background-modifier-form-field);
border-radius: var(--radius-m);
max-width: 213px;
min-width: 213px;
gap: unset;
}
.llm-vector-file {
border-radius: var(--radius-m);
padding: var(--size-4-1);
width: 100%;
text-align: start;
border-bottom: 2px solid var(--background-modifier-border);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.llm-vector-file:hover {
box-shadow: var(--input-shadow-hover);
}
.llm-vector-dropdown {
gap: 50%;
}
.llm-file-added {
background-color: var(--icon-color-active);
}
/* ACTIVE FILE CONTEXT — chip + scan button */
/* Container inside the prompt card, above the textarea — scrolls horizontally */
.llm-context-chip-container {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
gap: var(--size-2-1);
padding: var(--size-4-2) var(--size-4-2) 0;
/* Hide scrollbar while keeping scroll behaviour */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.llm-context-chip-container::-webkit-scrollbar {
display: none; /* Chrome/Safari/WebKit */
}
/* Individual chip pill */
.llm-context-chip {
display: inline-flex;
align-items: center;
gap: var(--size-2-1);
padding: var(--size-2-1) var(--size-4-2);
background-color: var(--background-modifier-hover);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-l);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
max-width: 220px;
flex-shrink: 0; /* prevent flex from squishing chips — container scrolls instead */
}
.llm-context-chip-icon {
display: inline-flex;
align-items: center;
flex-shrink: 0;
--icon-size: var(--icon-xs);
--icon-stroke: 1.5;
color: var(--text-faint);
}
.llm-context-chip-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.llm-context-chip-remove {
flex-shrink: 0;
cursor: pointer;
color: var(--text-faint);
font-size: var(--font-ui-small);
line-height: 1;
user-select: none;
padding: 0 var(--size-2-1);
}
.llm-context-chip-remove:hover {
color: var(--text-muted);
}
/* Right-side toolbar group (scan + send) */
.llm-input-toolbar-right {
display: flex;
align-items: center;
gap: var(--size-4-1);
flex-shrink: 0;
}
/* Scan button — inactive state (button prefix beats app.css specificity) */
button.llm-scan-button {
border-radius: var(--button-radius);
box-shadow: none;
border: none;
height: auto;
line-height: 1;
padding: var(--size-4-2);
color: var(--text-muted);
background: transparent;
--icon-size: var(--icon-s);
--icon-stroke: var(--icon-s-stroke-width);
flex-shrink: 0;
cursor: var(--cursor);
transition: background-color 100ms ease, color 100ms ease;
}
button.llm-scan-button:hover {
background-color: var(--background-modifier-hover);
color: var(--text-normal);
box-shadow: none;
}
/* Active state */
button.llm-scan-button.is-active {
color: var(--icon-color);
background-color: var(--background-modifier-hover);
box-shadow: none;
}
/* ── SKILLS TAB ─────────────────────────────────────────────────── */
/* Top-level padding (mirrors llm-modal-settings-container) */
.llm-modal-skills-container.llm-flex,
.llm-widget-skills-container.llm-flex,
.fab-skills-container.llm-flex {
padding-top: var(--size-4-4);
}
/* Empty-state message when no skills are found */
.llm-skills-empty {
color: var(--text-muted);
font-size: var(--font-ui-small);
text-align: center;
padding: var(--size-4-4) var(--size-4-2);
}
/* Header row above the skills list */
.llm-skills-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--size-2-1) var(--size-4-2);
border-bottom: var(--border-width) solid var(--background-modifier-border);
margin-bottom: var(--size-4-2);
}
.llm-skills-header-title {
font-size: var(--font-ui-small);
font-weight: var(--font-semibold);
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.llm-skills-folder-hint {
font-size: var(--font-ui-smaller);
color: var(--text-faint);
}
/* Individual skill row */
.llm-skill-item {
display: flex;
align-items: flex-start;
gap: var(--size-4-2);
padding: var(--size-4-2);
border-radius: var(--radius-s);
transition: background-color 80ms ease;
}
.llm-skill-item:hover {
background-color: var(--background-modifier-hover);
}
/* Left column: icon */
.llm-skill-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: var(--size-4-6);
height: var(--size-4-6);
border-radius: var(--radius-s);
background-color: var(--background-modifier-form-field);
color: var(--text-muted);
--icon-size: var(--icon-s);
--icon-stroke: var(--icon-s-stroke-width);
}
/* Centre column: name + description + metadata */
.llm-skill-info {
flex: 1;
min-width: 0;
}
.llm-skill-name {
font-size: var(--font-ui-small);
font-weight: var(--font-semibold);
color: var(--text-normal);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.llm-skill-description {
font-size: var(--font-ui-smaller);
color: var(--text-muted);
margin-top: var(--size-2-1);
line-height: 1.4;
}
/* Badge row: slash command + allowed tools */
.llm-skill-badges {
display: flex;
flex-wrap: wrap;
gap: var(--size-2-1);
margin-top: var(--size-4-1);
}
.llm-skill-badge {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 1px var(--size-4-1);
border-radius: var(--radius-s);
font-size: var(--font-ui-smaller);
color: var(--text-faint);
background-color: var(--background-modifier-form-field);
border: var(--border-width) solid var(--background-modifier-border);
font-family: var(--font-monospace);
}
/* Right column: toggle */
.llm-skill-toggle {
flex-shrink: 0;
display: flex;
align-items: center;
}
button.llm-scan-button.is-active:hover {
background-color: var(--background-modifier-hover);
color: var(--icon-color);
box-shadow: none;
}
/* Generic visibility utility — toggle with el.toggleClass("llm-hidden", …).
Specificity-boosted (class repeated 3×) instead of !important: both sides
of this override live in this file, and no other selector here combines
more than 2 classes on a display rule, so this reliably wins without
!important. If a future rule ever needs 3+ combined classes on `display`,
bump this further. */
.llm-hidden.llm-hidden.llm-hidden {
display: none;
}
/* STATUS BAR BUTTON */
.llm-status-bar-button {
display: flex;
align-items: center;
gap: var(--size-2-1);
cursor: pointer;
padding: 0 var(--size-2-2);
border-radius: var(--radius-s);
color: var(--status-bar-text-color);
transition: background-color 100ms ease;
}
/* AGENT PERMISSION CARD */
.llm-permission-card {
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-m);
background-color: var(--background-secondary);
padding: var(--size-4-3);
margin: var(--size-4-2) 0;
display: flex;
flex-direction: column;
gap: var(--size-4-2);
}
.llm-permission-header {
display: flex;
align-items: center;
gap: var(--size-4-2);
font-weight: var(--font-semibold);
--icon-size: var(--icon-s);
--icon-stroke: var(--icon-s-stroke-width);
color: var(--color-accent);
}
.llm-permission-title {
color: var(--text-normal);
font-size: var(--font-ui-small);
font-weight: var(--font-semibold);
}
.llm-permission-body {
display: flex;
flex-direction: column;
gap: var(--size-4-1);
}
.llm-permission-description {
font-size: var(--font-ui-small);
color: var(--text-muted);
}
.llm-permission-input {
background-color: var(--background-modifier-form-field);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
padding: var(--size-4-2);
font-size: var(--font-ui-smaller);
font-family: var(--font-monospace);
color: var(--text-normal);
white-space: pre-wrap;
word-break: break-all;
margin: 0;
max-height: 120px;
overflow-y: auto;
}
.llm-permission-buttons {
display: flex;
gap: var(--size-4-2);
justify-content: flex-end;
}
button.llm-permission-deny {
background-color: transparent;
border: var(--border-width) solid var(--background-modifier-border);
color: var(--text-muted);
}
button.llm-permission-deny:hover {
background-color: var(--background-modifier-hover);
color: var(--text-normal);
box-shadow: none;
}
button.llm-permission-allow {
/* mod-cta provides accent background; just ensure sizing */
padding: var(--size-4-1) var(--size-4-3);
}
/* PERMISSION DENIED NOTICE (Claude Code auto-block) */
.llm-permission-denied-notice {
display: flex;
align-items: center;
gap: var(--size-4-2);
padding: var(--size-4-2) var(--size-4-3);
border-radius: var(--radius-s);
background-color: var(--background-modifier-error-hover);
border: var(--border-width) solid var(--background-modifier-error);
color: var(--text-error);
font-size: var(--font-ui-small);
margin: var(--size-4-2) 0;
}
.llm-permission-denied-icon {
display: flex;
align-items: center;
flex-shrink: 0;
}
.llm-permission-denied-icon svg {
width: var(--icon-s);
height: var(--icon-s);
}
/* MEMORY SCOPE PICKER (extends permission card) */
.llm-memory-content-preview {
font-size: var(--font-ui-small);
color: var(--text-muted);
font-style: italic;
background-color: var(--background-modifier-form-field);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
padding: var(--size-4-1) var(--size-4-2);
margin-bottom: var(--size-4-1);
}
.llm-memory-scope-label {
font-size: var(--font-ui-small);
color: var(--text-muted);
margin-bottom: var(--size-4-1);
}
.llm-memory-scope-options {
display: flex;
flex-direction: column;
gap: var(--size-4-1);
}
.llm-memory-scope-row {
display: flex;
align-items: center;
gap: var(--size-4-2);
cursor: pointer;
}
.llm-memory-scope-row input[type="checkbox"] {
cursor: pointer;
accent-color: var(--interactive-accent);
}
.llm-memory-scope-row label {
cursor: pointer;
font-size: var(--font-ui-small);
color: var(--text-normal);
}
.llm-memory-scope-row label span.llm-memory-scope-tag {
font-size: var(--font-ui-smaller);
color: var(--text-faint);
background-color: var(--background-modifier-hover);
border-radius: var(--radius-s);
padding: 1px var(--size-4-1);
margin-left: var(--size-4-1);
}
.llm-status-bar-button:hover {
background-color: var(--background-modifier-hover);
}
.llm-status-bar-icon {
display: flex;
align-items: center;
}
.llm-status-bar-icon svg {
width: 12px;
height: 12px;
}
.llm-status-bar-label {
font-size: var(--font-ui-smaller);
}
/* Popover — floats above the status bar, fixed to viewport */
.llm-status-bar-popover {
position: fixed;
z-index: var(--layer-popover);
width: 400px;
min-width: 400px;
padding: var(--size-4-4);
background-color: var(--background-secondary);
border-radius: var(--radius-m);
border: var(--border-width) solid var(--background-modifier-border);
box-shadow: var(--shadow-l);
display: flex;
flex-direction: column;
box-sizing: border-box;
}
/* RECENT CHATS POPOVER */
.llm-recent-chats-popover {
position: fixed;
z-index: var(--layer-popover);
width: 340px;
/* max-height caps upward growth; the list scrolls internally */
max-height: min(480px, 80vh);
background-color: var(--background-primary);
border-radius: var(--radius-m);
border: var(--border-width) solid var(--background-modifier-border);
box-shadow: var(--shadow-l);
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
}
.llm-recent-chats-search-wrapper {
padding: var(--size-4-2) var(--size-4-3);
border-bottom: var(--border-width) solid var(--background-modifier-border);
flex-shrink: 0;
}
.llm-recent-chats-search {
width: 100%;
box-sizing: border-box;
background-color: var(--background-modifier-form-field);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
color: var(--text-normal);
font-size: var(--font-ui-small);
padding: var(--size-2-2) var(--size-4-2);
outline: none;
}
.llm-recent-chats-search:focus {
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
}
.llm-recent-chats-list {
overflow-y: auto;
flex: 1;
padding: var(--size-2-1) 0;
}
.llm-recent-chats-item {
display: flex;
flex-direction: column;
gap: var(--size-2-1);
padding: var(--size-4-2) var(--size-4-3);
cursor: pointer;
border-radius: 0;
transition: background-color 80ms ease;
}
.llm-recent-chats-item:hover {
background-color: var(--background-modifier-hover);
}
.llm-recent-chats-item-text {
font-size: var(--font-ui-small);
color: var(--text-normal);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.llm-recent-chats-item-model {
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.llm-recent-chats-empty {
padding: var(--size-4-4) var(--size-4-3);
font-size: var(--font-ui-small);
color: var(--text-muted);
text-align: center;
}
/* ── File Selector Modal ──────────────────────────────────────────────────── */
.llm-file-selector-list {
max-height: 400px;
overflow-y: auto;
margin-top: var(--size-4-3);
margin-bottom: var(--size-4-3);
}
.llm-file-selector-buttons {
display: flex;
justify-content: flex-end;
gap: var(--size-4-2);
margin-top: var(--size-4-3);
}
.llm-file-selector-item {
display: flex;
align-items: center;
padding: var(--size-4-2);
border-bottom: var(--border-width) solid var(--background-modifier-border);
}
.llm-file-selector-item:last-child {
border-bottom: none;
}
.llm-file-selector-checkbox {
margin-right: var(--size-4-2);
flex-shrink: 0;
}
.llm-file-selector-info {
flex: 1;
min-width: 0;
}
.llm-file-selector-name {
font-weight: var(--font-medium);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.llm-file-selector-path {
font-size: var(--font-ui-smaller);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.llm-file-selector-count {
margin-top: var(--size-4-3);
text-align: center;
font-weight: var(--font-medium);
color: var(--text-muted);
font-size: var(--font-ui-small);
}
/* ── Dedicated Settings Modal ─────────────────────────────────────────────── */
/* Fallback size when opened outside the settings panel. */
.llm-dedicated-settings-modal {
width: min(960px, 94vw);
height: min(740px, 88vh);
}
/* Needed so the guidance editor overlay (position: absolute) is clipped to
the modal box rather than escaping to the viewport. */
.llm-dedicated-settings-modal {
position: relative;
}
/* Matches .modal.mod-settings .vertical-tab-header in app.css exactly,
since our modal uses .llm-dedicated-settings-modal instead of .mod-settings. */
.llm-dedicated-settings-modal .vertical-tab-header {
flex: 0 0 25%;
min-width: 180px;
max-width: 250px;
overflow: auto;
border-inline-end: var(--border-width) solid var(--divider-color);
}
/* The modal is pinned via fixed top/bottom, but .modal-content and the flex
container must explicitly fill that height — otherwise flex children can
grow unbounded and overflow-y never activates. */
.llm-dedicated-settings-modal .modal-content {
height: 100%;
overflow: hidden;
}
.llm-dedicated-settings-modal .vertical-tabs-container {
height: 100%;
overflow: hidden;
}
/* Allow the content pane to scroll independently when tab content is taller
than the modal. min-height: 0 is required so a flex child can shrink below
its content size and let overflow-y kick in. */
.llm-dedicated-settings-modal .vertical-tab-content-container {
flex: 1;
min-height: 0;
overflow-y: auto;
}
/* Tab section header — sits above the first setting-group card. */
.llm-dedicated-settings-tab-header {
margin-bottom: var(--size-4-4);
}
/* Re-use Obsidian's own h2 sizing / weight from the settings panel. */
.llm-dedicated-settings-tab-title {
font-size: var(--h2-size);
font-weight: var(--h2-weight);
color: var(--h2-color);
margin: 0 0 var(--size-4-1);
}
.llm-dedicated-settings-tab-desc {
margin: 0;
}
/* Ollama discovered-models helper text. */
.llm-settings-ollama-models {
margin: 0 0 var(--size-4-3);
}
/* Read-only path display in provider settings (e.g. GPT4All model path, port). */
.llm-settings-readonly-path {
color: var(--text-muted);
cursor: default;
user-select: all;
}
/* About tab credits block. */
.llm-dedicated-settings-about-credits {
margin-top: var(--size-4-6);
display: flex;
flex-direction: column;
gap: var(--size-4-1);
}
.llm-dedicated-settings-about-title {
font-size: var(--h3-size);
font-weight: var(--h3-weight);
color: var(--text-normal);
margin: 0;
}
/* RAG cited sources panel */
.llm-rag-sources {
margin-top: var(--size-4-2);
border-top: 1px solid var(--background-modifier-border);
padding-top: var(--size-4-2);
}
.llm-rag-sources-summary {
font-size: var(--font-ui-smaller);
color: var(--text-muted);
cursor: pointer;
user-select: none;
list-style: none;
display: flex;
align-items: center;
gap: var(--size-4-1);
}
.llm-rag-sources-summary::before {
content: "";
display: inline-block;
transition: transform 100ms ease;
}
details[open] .llm-rag-sources-summary::before {
transform: rotate(90deg);
}
.llm-rag-sources-list {
margin: var(--size-4-2) 0 0 var(--size-4-4);
padding: 0;
list-style: disc;
}
.llm-rag-sources-list li {
margin-bottom: var(--size-4-1);
}
.llm-rag-source-link {
font-size: var(--font-ui-smaller);
color: var(--text-accent);
cursor: pointer;
text-decoration: none;
}
.llm-rag-source-link:hover {
text-decoration: underline;
}
/* ── Web sources collapsible panel ──────────────────────────────────────── */
.llm-web-sources {
margin-top: var(--size-4-2);
border-top: 1px solid var(--background-modifier-border);
padding-top: var(--size-4-2);
}
.llm-web-sources-summary {
font-size: var(--font-ui-smaller);
color: var(--text-muted);
cursor: pointer;
user-select: none;
list-style: none;
display: flex;
align-items: center;
gap: var(--size-4-1);
}
.llm-web-sources-summary::before {
content: "";
display: inline-block;
transition: transform 100ms ease;
}
details[open] .llm-web-sources-summary::before {
transform: rotate(90deg);
}
.llm-web-sources-icon {
display: flex;
align-items: center;
color: var(--text-muted);
}
.llm-web-sources-icon svg {
width: var(--icon-xs);
height: var(--icon-xs);
}
.llm-web-sources-list {
margin: var(--size-4-2) 0 0 var(--size-4-4);
padding: 0;
list-style: disc;
}
.llm-web-sources-list li {
margin-bottom: var(--size-4-1);
}
.llm-web-source-link {
font-size: var(--font-ui-smaller);
color: var(--text-accent);
text-decoration: none;
}
.llm-web-source-link:hover {
text-decoration: underline;
}
/* ── Tool call collapsible panel ─────────────────────────────────────────── */
.llm-tool-calls {
margin-bottom: var(--size-4-2);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
overflow: hidden;
}
.llm-tool-calls-summary {
display: flex;
align-items: center;
gap: var(--size-4-1);
padding: var(--size-4-2) var(--size-4-3);
background: var(--background-secondary);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
cursor: pointer;
user-select: none;
list-style: none;
}
.llm-tool-calls-summary::-webkit-details-marker {
display: none;
}
.llm-tool-calls-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
}
.llm-tool-calls-label {
flex: 1;
}
.llm-tool-calls-chevron {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
transition: transform 120ms ease;
}
details[open] .llm-tool-calls-chevron {
transform: rotate(180deg);
}
.llm-tool-calls-body {
display: flex;
flex-direction: column;
gap: var(--size-4-2);
padding: var(--size-4-2) var(--size-4-3);
border-top: var(--border-width) solid var(--background-modifier-border);
}
.llm-tool-call-item {
display: flex;
flex-direction: column;
gap: var(--size-4-1);
}
.llm-tool-call-name {
font-size: var(--font-ui-smaller);
font-weight: var(--font-semibold);
color: var(--text-normal);
}
.llm-tool-call-input {
display: block;
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
background: var(--background-modifier-form-field);
padding: var(--size-4-1) var(--size-4-2);
border-radius: var(--radius-s);
word-break: break-all;
white-space: pre-wrap;
}
/* ── Settings → Tools: risk tier badges and tool name code ───────────────── */
.llm-tool-badge {
display: inline-block;
padding: 1px 6px;
border-radius: var(--radius-s);
font-size: var(--font-ui-smaller);
font-weight: var(--font-semibold);
text-transform: uppercase;
letter-spacing: 0.04em;
vertical-align: middle;
line-height: 1.6;
}
.llm-tool-badge-safe {
background: hsla(var(--color-green-hsl), 0.15);
color: var(--color-green);
}
.llm-tool-badge-write {
background: hsla(var(--color-yellow-hsl), 0.15);
color: var(--color-yellow);
}
.llm-tool-badge-danger {
background: hsla(var(--color-red-hsl), 0.15);
color: var(--color-red);
}
.llm-tool-name-code {
margin-top: var(--size-4-1);
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
color: var(--text-faint);
}
/* ── Shell command warning modal ─────────────────────────────────────────── */
.llm-shell-warning-note {
font-size: var(--font-ui-small);
color: var(--text-muted);
padding: var(--size-4-2) var(--size-4-3);
border-left: 3px solid var(--color-orange);
margin-top: var(--size-4-2);
}
.llm-shell-warning-buttons {
display: flex;
justify-content: flex-end;
gap: var(--size-4-2);
margin-top: var(--size-4-4);
}
/* ── API key test status ──────────────────────────────────────────────────── */
.llm-api-test-status {
margin-top: var(--size-4-1);
font-size: var(--font-ui-smaller);
font-weight: var(--font-medium);
}
.llm-api-test-running { color: var(--text-muted); }
.llm-api-test-ok { color: var(--color-green); }
.llm-api-test-fail { color: var(--color-red); }
/* ── Active skill indicator (shown above an assistant message) ────────────── */
.llm-skill-panel {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
margin-bottom: var(--size-4-2);
padding: var(--size-4-1) var(--size-4-2);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
background: var(--background-secondary);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.llm-skill-panel-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
}
.llm-skill-panel-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
/* ── Model / assistant attribution badge (shown below each assistant message) */
.llm-model-panel {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
margin-top: var(--size-4-2);
padding: var(--size-4-1) var(--size-4-2);
border-radius: var(--radius-s);
font-size: var(--font-ui-smaller);
color: var(--text-faint);
}
.llm-model-panel-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
opacity: 0.6;
}
.llm-model-panel-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
/* ── Slash command skill token (shown inside the input area) ─────────────── */
/* ── Slash command picker menu ────────────────────────────────────────────── */
/* Mounted on document.body; position: fixed is set via JS so it clears any
overflow:hidden ancestor container. Width/bottom are also set dynamically. */
.llm-slash-menu {
position: fixed;
background: var(--background-primary);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-m);
box-shadow: var(--shadow-l);
display: flex;
flex-direction: column;
overflow: hidden;
z-index: 9999;
max-height: 320px;
min-width: 260px;
max-width: 420px;
overflow-y: auto;
}
.llm-slash-menu-header {
padding: var(--size-4-2) var(--size-4-3) var(--size-2-1);
font-size: var(--font-ui-smaller);
font-weight: var(--font-semibold);
color: var(--text-faint);
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: var(--border-width) solid var(--background-modifier-border);
}
.llm-slash-menu-item {
display: flex;
align-items: center;
gap: var(--size-4-2);
padding: var(--size-4-2) var(--size-4-3);
cursor: pointer;
flex-shrink: 0;
}
.llm-slash-menu-item:hover,
.llm-slash-menu-item-selected {
background: var(--background-modifier-hover);
}
.llm-slash-menu-item-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 28px;
height: 28px;
border-radius: var(--radius-s);
background: var(--background-secondary);
color: var(--text-normal);
--icon-size: var(--icon-s);
}
.llm-slash-menu-item-text {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
}
/* Row containing skill name + argument hint */
.llm-slash-menu-item-name-row {
display: flex;
align-items: baseline;
min-width: 0;
overflow: hidden;
}
.llm-slash-menu-item-name {
font-size: var(--font-ui-small);
font-weight: var(--font-semibold);
color: var(--text-normal);
white-space: nowrap;
flex-shrink: 0;
}
/* Argument hint — grayed out, shown next to the name */
.llm-slash-menu-item-hint {
font-size: var(--font-ui-small);
font-weight: var(--font-normal);
color: var(--text-faint);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.llm-slash-menu-item-desc {
font-size: var(--font-ui-smaller);
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Edit (pencil) icon on each slash menu item — hidden until hover */
.llm-slash-menu-item-edit {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 24px;
height: 24px;
border-radius: var(--radius-s);
color: var(--text-faint);
cursor: pointer;
opacity: 0;
transition: opacity 0.1s ease, color 0.1s ease, background 0.1s ease;
--icon-size: var(--icon-xs);
}
.llm-slash-menu-item:hover .llm-slash-menu-item-edit,
.llm-slash-menu-item-selected .llm-slash-menu-item-edit {
opacity: 1;
}
.llm-slash-menu-item-edit:hover {
color: var(--text-normal);
background: var(--background-modifier-hover);
}
/* ── Memory panel ──────────────────────────────────────────────────────────── */
.llm-memory-panel {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
margin-bottom: var(--size-4-2);
padding: var(--size-4-1) var(--size-4-2);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
background: var(--background-secondary);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.llm-memory-panel-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
}
.llm-memory-panel-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
/* ── Memory chip (chip strip) ──────────────────────────────────────────────── */
/* Shows just the brain icon at rest; "Memory" label + remove revealed on hover. */
.llm-memory-chip {
border: 1px solid var(--color-purple);
background-color: color-mix(in srgb, var(--color-purple) 10%, transparent);
min-width: 32px;
max-width: 32px;
overflow: hidden;
transition: max-width 0.2s ease;
cursor: default;
}
.llm-memory-chip:hover {
max-width: 180px;
background-color: color-mix(in srgb, var(--color-purple) 16%, transparent);
}
.llm-memory-chip .llm-context-chip-icon {
color: var(--color-purple);
flex-shrink: 0;
}
.llm-memory-chip-name {
white-space: nowrap;
color: var(--text-normal);
}
/* ── Project chip (chip strip) ─────────────────────────────────────────────── */
/* Shows just the box icon at rest; name + remove button revealed on hover. */
.llm-project-chip {
border: 1px solid var(--interactive-accent);
background-color: color-mix(in srgb, var(--interactive-accent) 10%, transparent);
/* Clamp to icon-only width at rest: horizontal padding (2×8px) + icon (12px) */
min-width: 32px;
max-width: 32px;
overflow: hidden;
transition: max-width 0.2s ease;
cursor: default;
}
.llm-project-chip:hover {
max-width: 220px;
background-color: color-mix(in srgb, var(--interactive-accent) 16%, transparent);
}
.llm-project-chip .llm-context-chip-icon {
color: var(--interactive-accent);
flex-shrink: 0;
}
.llm-project-chip-name {
white-space: nowrap;
color: var(--text-normal);
}
/* ── Assistant switcher (header pill) ────────────────────────────────────── */
.llm-assistant-switcher {
display: inline-flex;
align-items: center;
gap: var(--size-2-1);
padding: var(--size-2-1) var(--size-4-2);
background: transparent;
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
cursor: pointer;
font-size: var(--font-ui-smaller);
color: var(--text-muted);
max-width: 160px;
white-space: nowrap;
overflow: hidden;
transition: background 100ms ease, border-color 100ms ease;
box-shadow: none;
margin-left: var(--size-2-2);
}
.llm-assistant-switcher:hover {
background: var(--background-modifier-hover);
border-color: var(--background-modifier-border-hover);
color: var(--text-normal);
}
.llm-assistant-switcher--active {
border-color: var(--color-purple);
color: var(--text-normal);
}
.llm-assistant-switcher--hidden {
display: none;
}
.llm-assistant-switcher-icon {
display: inline-flex;
align-items: center;
flex-shrink: 0;
--icon-size: var(--icon-xs);
--icon-stroke: 1.5;
color: var(--text-faint);
}
.llm-assistant-switcher--active .llm-assistant-switcher-icon {
color: var(--color-purple);
}
.llm-assistant-switcher-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
flex: 1;
}
.llm-assistant-switcher-chevron {
display: inline-flex;
align-items: center;
flex-shrink: 0;
--icon-size: var(--icon-xs);
--icon-stroke: 1.5;
color: var(--text-faint);
}
/* ── Assistant panel indicator (below assistant message) ─────────────────── */
.llm-assistant-panel {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
margin-bottom: var(--size-4-2);
padding: var(--size-4-1) var(--size-4-2);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
background: var(--background-secondary);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.llm-assistant-panel-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
color: var(--color-purple);
}
.llm-assistant-panel-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
/* ── Agent routing panel indicator (below Obsidian Agent message) ─────────── */
.llm-agent-routing-panel {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
margin-bottom: var(--size-4-2);
padding: var(--size-4-1) var(--size-4-2);
border: var(--border-width) solid var(--background-modifier-border);
border-radius: var(--radius-s);
background: var(--background-secondary);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.llm-agent-routing-panel-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
color: var(--interactive-accent);
}
.llm-agent-routing-panel-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 220px;
}
/* ── Token usage indicator ──────────────────────────────────────────────────── */
.llm-token-usage {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
margin-top: var(--size-4-1);
margin-bottom: var(--size-4-2);
padding: 2px var(--size-4-2);
border-radius: var(--radius-s);
background: transparent;
font-size: var(--font-ui-smaller);
color: var(--text-faint);
font-variant-numeric: tabular-nums;
user-select: none;
}
.llm-token-usage-icon {
display: flex;
align-items: center;
flex-shrink: 0;
width: var(--icon-xs);
height: var(--icon-xs);
color: var(--text-faint);
}
/* ── Obsidian Agent brand icon (end of last agent message) ─────────────────── */
.llm-obsidian-agent-brand {
display: flex;
align-items: center;
padding: var(--size-4-1) var(--size-4-2) var(--size-4-2);
}
.llm-obsidian-agent-brand-icon {
display: flex;
align-items: center;
justify-content: center;
width: var(--icon-xl);
height: var(--icon-xl);
color: var(--interactive-accent);
opacity: 0.9;
}
.llm-obsidian-agent-brand-icon svg {
width: var(--icon-xl);
height: var(--icon-xl);
}
/* ── Guidance file path input in settings ──────────────────────────────────── */
.llm-guidance-file-input {
width: 240px;
}
/* ── Guidance file inline editor overlay ───────────────────────────────────── */
/* Full-cover backdrop rendered inside the existing settings modal container */
.llm-guidance-editor-overlay {
position: absolute;
inset: 0;
background-color: var(--background-modifier-cover);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
border-radius: var(--modal-radius, var(--radius-m));
}
/* Inner panel — mirrors Obsidian's .modal sizing and chrome */
.llm-guidance-editor-panel {
width: min(640px, 90%);
background-color: var(--background-primary);
border-radius: var(--modal-radius, var(--radius-m));
box-shadow: var(--shadow-l);
padding: var(--size-4-6) var(--size-4-6) var(--size-4-4);
display: flex;
flex-direction: column;
gap: var(--size-4-2);
}
.llm-guidance-editor-title {
margin: 0;
font-size: var(--font-ui-larger);
font-weight: var(--font-semibold);
}
.llm-guidance-editor-path {
margin: 0;
font-size: var(--font-ui-smaller);
color: var(--text-muted);
font-family: var(--font-monospace);
}
.llm-guidance-editor-textarea {
width: 100%;
min-height: 360px;
resize: vertical;
font-family: var(--font-monospace);
font-size: var(--font-ui-small);
background-color: var(--background-primary);
color: var(--text-normal);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
padding: var(--size-4-2);
box-sizing: border-box;
line-height: var(--line-height-normal);
}
.llm-guidance-editor-textarea:focus {
outline: none;
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
}
/* ── Pinned note chip (project pinned notes — non-removable) ──────────────── */
.llm-context-chip--pinned {
border: 1px dashed var(--interactive-accent);
background-color: color-mix(in srgb, var(--interactive-accent) 8%, transparent);
}
.llm-context-chip--pinned .llm-context-chip-icon {
color: var(--interactive-accent);
}
.llm-context-chip--clickable {
cursor: pointer;
}
.llm-context-chip--clickable:hover {
background-color: color-mix(in srgb, var(--interactive-accent) 16%, transparent);
border-color: var(--interactive-accent-hover);
}
/* ── Whisper: mic button states ────────────────────────────────────────────── */
/* Recording — button pulses red */
button.llm-mic-button.llm-mic-recording {
color: var(--color-red);
animation: llm-mic-pulse 1.2s ease-in-out infinite;
}
button.llm-mic-button.llm-mic-recording:hover {
color: var(--color-red);
}
/* Transcribing — button is muted and disabled */
button.llm-mic-button.llm-mic-transcribing {
color: var(--text-faint);
cursor: not-allowed;
opacity: 0.6;
}
@keyframes llm-mic-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* ── Whisper: settings tab UI ──────────────────────────────────────────────── */
/* Sidecar setup instructions panel */
.llm-whisper-setup-panel {
margin-top: var(--size-4-2);
}
.llm-whisper-setup-code {
user-select: text;
-webkit-user-select: text;
cursor: text;
background: var(--background-secondary);
border-radius: var(--radius-s);
padding: var(--size-4-2) var(--size-4-3);
margin: var(--size-4-2) 0;
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
color: var(--text-normal);
white-space: pre;
overflow-x: auto;
}
/* Test connection status line */
.llm-whisper-status {
margin-top: var(--size-4-1);
font-size: var(--font-ui-smaller);
}
.llm-whisper-status-ok {
color: var(--color-green);
}
.llm-whisper-status-err {
color: var(--color-red);
}
/* Warning text when OpenAI key is missing */
.llm-whisper-warning {
color: var(--color-orange);
}
/* ── Whisper: sidecar setup wizard ─────────────────────────────────────────── */
.llm-whisper-env-row {
display: flex;
align-items: center;
gap: var(--size-4-2);
padding: var(--size-4-1) 0;
font-size: var(--font-ui-small);
}
.llm-whisper-env-icon {
font-size: var(--font-ui-small);
flex-shrink: 0;
width: 1.2em;
text-align: center;
}
.llm-whisper-env-ok { color: var(--color-green); }
.llm-whisper-env-err { color: var(--color-red); }
.llm-whisper-env-warn { color: var(--color-orange); }
.llm-whisper-env-checking { color: var(--text-faint); }
.llm-whisper-env-label {
color: var(--text-normal);
}
.llm-whisper-env-link {
color: var(--link-color);
text-decoration: underline;
cursor: pointer;
font-size: var(--font-ui-smaller);
}
.llm-whisper-btn-row {
display: flex;
flex-wrap: wrap;
gap: var(--size-4-2);
margin-top: var(--size-4-3);
}
.llm-whisper-setup-log {
margin-top: var(--size-4-2);
background: var(--background-secondary);
border-radius: var(--radius-s);
padding: var(--size-4-2) var(--size-4-3);
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
color: var(--text-muted);
white-space: pre-wrap;
max-height: 180px;
overflow-y: auto;
}
.llm-context-section-header {
margin-top: 1.5em;
margin-bottom: 0.5em;
}
/* ── Chats Panel ──────────────────────────────────────────────────────────── */
/*
* Most styling comes from Obsidian's native classes:
* nav-header / nav-buttons-container / nav-action-button — toolbar
* search-input-container — search box (via SearchComponent)
* nav-files-container — scrollable list
* tree-item / tree-item-self / tree-item-inner / tree-item-flair — rows
* tag — project / agent badges
* pane-empty — empty state
*
* Only the bits Obsidian doesn't already cover are defined here.
*/
/* Left icon column — size + colour to match file-explorer icons */
.llm-chats-row-icon {
color: var(--nav-item-color);
}
.llm-chats-row-icon svg {
width: var(--icon-s);
height: var(--icon-s);
}
/* Subtitle row that sits below the title inside tree-item-inner */
.llm-chats-meta {
display: flex;
align-items: center;
gap: var(--size-4-1);
margin-top: 2px;
flex-wrap: wrap;
}
/* Project badge — accent colour tint (overrides .tag defaults) */
.llm-chats-tag-project {
color: var(--text-accent);
background-color: color-mix(in srgb, var(--interactive-accent) 12%, transparent);
border-color: transparent;
cursor: default;
}
/* Agent badge — purple tint */
.llm-chats-tag-agent {
color: var(--color-purple);
background-color: color-mix(in srgb, var(--color-purple) 12%, transparent);
border-color: transparent;
cursor: default;
}
/* ── Three-dot context-menu button ──────────────────────────────────────────
The button carries Obsidian's native `clickable-icon` class which handles
all sizing, padding, hover/focus colours, and border-radius automatically.
We only add the overlay positioning and fade animation on top.
─────────────────────────────────────────────────────────────────────────── */
.llm-chats-row-date {
transition: opacity 120ms ease;
}
/* Give the flair outer a stacking context so the button can overlay the date.
The class is added by attachChatRowMenu() when it inserts the button —
avoids :has(), which the community review flags. */
.tree-item-flair-outer.llm-has-row-menu {
position: relative;
}
.llm-chats-row-menu-btn {
/* Overlay the date stamp — centred vertically, flush to the right edge */
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
/* Hidden until row hover — clickable-icon handles everything else */
opacity: 0;
pointer-events: none;
transition: opacity 120ms ease;
}
/* On row hover: reveal the button, hide the date */
.tree-item-self:hover .llm-chats-row-menu-btn,
.tree-item-self:focus-within .llm-chats-row-menu-btn {
opacity: 1;
pointer-events: auto;
}
.tree-item-self:hover .llm-chats-row-date,
.tree-item-self:focus-within .llm-chats-row-date {
opacity: 0;
}
/* ── Rename modal input ───────────────────────────────────────────────────── */
.llm-rename-input {
width: 100%;
margin-bottom: var(--size-4-3);
}
/* ═══════════════════════════════════════════════════════════════════════════
Chat Details Panel (.llm-chat-details-*)
Right-sidebar panel that shows live state for the active chat:
model/assistant, recalled memories, and context files.
═══════════════════════════════════════════════════════════════════════════ */
.llm-chat-details-root {
height: 100%;
display: flex;
flex-direction: column;
}
.llm-chat-details-content {
flex: 1;
overflow-y: auto;
padding: var(--size-4-2) 0;
}
/* ── Section ─────────────────────────────────────────────────────────────── */
.llm-chat-details-section {
margin-bottom: var(--size-4-3);
}
.llm-chat-details-section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--size-4-1) var(--size-4-4);
margin-bottom: var(--size-4-1);
}
.llm-chat-details-section-title {
font-size: var(--font-ui-smaller);
font-weight: var(--font-semibold);
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.llm-chat-details-section-count {
font-size: var(--font-ui-smaller);
color: var(--text-faint);
background: var(--background-modifier-hover);
border-radius: var(--radius-s);
padding: 0 var(--size-4-1);
min-width: 18px;
text-align: center;
}
.llm-chat-details-section-body {
/* rows are placed directly inside the body */
}
/* ── Row ─────────────────────────────────────────────────────────────────── */
/* Matches Obsidian's tree-item-self pattern */
.llm-chat-details-row {
display: flex;
align-items: flex-start;
gap: var(--size-4-2);
padding: var(--size-4-1) var(--size-4-4);
min-height: 32px;
}
.llm-chat-details-row--clickable {
cursor: pointer;
border-radius: var(--radius-s);
}
.llm-chat-details-row--clickable:hover {
background: var(--background-modifier-hover);
}
.llm-chat-details-row-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: var(--icon-s);
height: var(--icon-s);
margin-top: 2px;
color: var(--text-muted);
}
.llm-chat-details-row-icon svg {
width: var(--icon-s);
height: var(--icon-s);
}
/* Assistant icon gets accent colour */
.llm-chat-details-row-icon--assistant {
color: var(--interactive-accent);
}
/* Guidance icon uses muted colour — guidance files are ambient context, not primary */
.llm-chat-details-row-icon--guidance {
color: var(--text-muted);
}
.llm-chat-details-row-body {
flex: 1;
min-width: 0;
}
/* Memory bodies use muted normal-size text (memories can be a sentence long) */
.llm-chat-details-row-body--memory {
font-size: var(--font-ui-small);
color: var(--text-normal);
line-height: var(--line-height-tight);
padding-top: 2px;
white-space: normal;
word-break: break-word;
}
.llm-chat-details-row-title {
font-size: var(--font-ui-small);
color: var(--text-normal);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Assistant name in accent colour */
.llm-chat-details-row-title--assistant {
color: var(--interactive-accent);
}
.llm-chat-details-row-subtitle {
font-size: var(--font-ui-smaller);
color: var(--text-faint);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-top: 1px;
}
/* ── Empty state ──────────────────────────────────────────────────────────── */
.llm-chat-details-empty {
padding: var(--size-4-1) var(--size-4-4);
font-size: var(--font-ui-small);
color: var(--text-faint);
font-style: italic;
}
/* ── Badge row (project pill under model section) ───────────────────────── */
.llm-chat-details-badge-row {
padding: 0 var(--size-4-4) var(--size-4-2);
display: flex;
gap: var(--size-4-1);
flex-wrap: wrap;
}
.llm-chat-details-badge {
display: inline-flex;
align-items: center;
gap: var(--size-4-1);
cursor: default;
}
.llm-chat-details-badge svg {
width: var(--icon-xs);
height: var(--icon-xs);
}
/* Project badge — accent tint (matches .llm-chats-tag-project) */
.llm-chat-details-badge--project {
color: var(--text-accent);
background-color: color-mix(in srgb, var(--interactive-accent) 12%, transparent);
border-color: transparent;
}
/* ── U8 compliance additions ─────────────────────────────────────────────── */
/* Off-screen measurement span for the model-dropdown width ruler.
The font is applied at runtime (must match the select's computed font). */
.llm-measure-ruler {
visibility: hidden;
position: absolute;
white-space: nowrap;
pointer-events: none;
}
/* Wide text inputs in the settings modal (e.g. SearXNG host). */
.llm-settings-wide-input {
width: 260px;
}
/* ── MCP Server settings ────────────────────────────────────────────────── */
.llm-mcp-token-field {
width: 100%;
font-family: var(--font-monospace);
}
.llm-mcp-config-snippet {
user-select: text;
-webkit-user-select: text;
cursor: text;
background: var(--background-secondary);
border-radius: var(--radius-s);
padding: var(--size-4-2) var(--size-4-3);
margin: var(--size-4-2) 0;
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
color: var(--text-normal);
white-space: pre;
overflow-x: auto;
}
/* MCP write-tool permission modal */
.llm-mcp-permission-input {
user-select: text;
-webkit-user-select: text;
cursor: text;
background: var(--background-secondary);
border-radius: var(--radius-s);
padding: var(--size-4-2) var(--size-4-3);
margin: var(--size-4-2) 0;
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
white-space: pre-wrap;
word-break: break-word;
max-height: 240px;
overflow-y: auto;
}