Files
codecraft/web/src/styles.css
T
Richard ZinkandClaude Opus 5.5 013695722a Add hooks, @file mentions, session search/fork/export, follow-scroll
- Hooks ([[hooks]]): pre_tool, post_tool, user_prompt, stop; sandboxed, exit 2 blocks
- @file mentions with autocomplete; mentioned files/folders are attached to the prompt
- Session full-text search in the sidebar, fork into a copy, export as Markdown/JSON
- Chat follows the newest message until the user scrolls up; jump-to-latest button
- SVG activity spark and thinking icon instead of Unicode/emoji glyphs
- File search ranks name matches first and returns relative paths

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:17:12 +01:00

2320 lines
40 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.
/* CodeCraft – layout and components. Colours come exclusively from the theme
variables defined in src/themes/*.css. */
* {
box-sizing: border-box;
}
html,
body,
#root {
height: 100%;
margin: 0;
}
body {
background: var(--bg0);
color: var(--fg1);
font: 14px/1.5 var(--font);
-webkit-font-smoothing: antialiased;
}
a {
color: var(--blue);
}
code,
pre,
.mono {
font-family: var(--mono);
font-size: 12.5px;
}
button,
input,
select,
textarea {
font: inherit;
color: inherit;
}
button {
cursor: pointer;
border: 1px solid var(--bg3);
background: var(--bg2);
border-radius: 6px;
padding: 5px 12px;
}
button:hover:not(:disabled) {
border-color: var(--fg3);
}
button:disabled {
opacity: 0.5;
cursor: default;
}
button.primary {
background: var(--accent);
border-color: var(--accent);
color: var(--on-accent);
font-weight: 600;
}
button.danger {
color: var(--red);
}
button.ghost {
background: transparent;
border-color: transparent;
}
button.ghost:hover:not(:disabled) {
background: var(--bg2);
}
button.small {
padding: 2px 8px;
font-size: 12px;
}
input,
select,
textarea {
background: var(--bg1);
border: 1px solid var(--bg3);
border-radius: 6px;
padding: 5px 8px;
outline: none;
}
input:focus,
select:focus,
textarea:focus {
border-color: var(--accent);
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
/* --- layout ---------------------------------------------------------------- */
.app {
display: flex;
height: 100%;
}
.sidebar {
width: 280px;
flex-shrink: 0;
background: var(--bg1);
border-right: 1px solid var(--bg2);
display: flex;
flex-direction: column;
min-height: 0;
}
.sidebar-head {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
padding-top: max(12px, env(safe-area-inset-top));
}
.sidebar-head .logo {
font-weight: 700;
font-size: 16px;
color: var(--fg0);
flex: 1;
display: flex;
align-items: center;
gap: 8px;
}
.sidebar-head .logo img {
width: 22px;
height: 22px;
}
.conn-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--red);
}
.conn-dot.on {
background: var(--green);
}
.session-list {
flex: 1;
overflow-y: auto;
padding: 4px 8px;
}
.session-group {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--muted);
padding: 10px 8px 4px;
}
.session-item {
display: block;
width: 100%;
text-align: left;
border: none;
background: transparent;
padding: 7px 8px;
border-radius: 6px;
margin-bottom: 1px;
}
.session-item:hover {
background: var(--bg2);
}
.session-item.active {
background: var(--bg3);
}
.session-item .title {
display: flex;
align-items: center;
gap: 6px;
color: var(--fg0);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.session-item .title span {
overflow: hidden;
text-overflow: ellipsis;
}
.session-item .sub {
font-size: 11.5px;
color: var(--muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.busy-dot {
flex-shrink: 0;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--accent);
animation: pulse 1.2s infinite;
}
.attn-dot {
flex-shrink: 0;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent2);
}
@keyframes pulse {
50% {
opacity: 0.3;
}
}
.sidebar-foot {
border-top: 1px solid var(--bg2);
padding: 8px;
padding-bottom: max(8px, env(safe-area-inset-bottom));
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
}
.sidebar-foot button {
flex: 1;
}
.sidebar-foot button.active {
background: var(--bg3);
}
.main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
min-height: 0;
}
.topbar {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 14px;
padding-top: max(8px, env(safe-area-inset-top));
border-bottom: 1px solid var(--bg2);
min-height: 48px;
flex-wrap: wrap;
}
.topbar .grow {
flex: 1;
min-width: 0;
}
.topbar h1 {
font-size: 15px;
margin: 0;
color: var(--fg0);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.topbar .path {
font-size: 11.5px;
color: var(--muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.menu-btn {
display: none;
}
.chip {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
padding: 2px 8px;
border-radius: 999px;
background: var(--bg2);
color: var(--fg2);
white-space: nowrap;
}
.chip.warn {
color: var(--accent2);
}
.page {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.page-inner {
max-width: 1100px;
margin: 0 auto;
}
/* --- chat ------------------------------------------------------------------ */
.chat-scroll {
flex: 1;
overflow-y: auto;
overscroll-behavior: contain;
}
.chat {
max-width: 900px;
margin: 0 auto;
padding: 16px 16px 24px;
}
.msg {
margin-bottom: 14px;
}
.msg-user {
background: var(--bg1);
border: 1px solid var(--bg2);
border-radius: var(--radius);
padding: 8px 12px;
white-space: pre-wrap;
word-break: break-word;
position: relative;
}
.msg-user .msg-actions {
position: absolute;
top: 4px;
right: 4px;
display: none;
}
.msg-user:hover .msg-actions {
display: block;
}
.msg-user img {
max-height: 160px;
border-radius: 6px;
margin: 6px 6px 0 0;
}
.msg-meta {
font-size: 11px;
color: var(--muted);
margin-top: 2px;
}
.markdown {
word-break: break-word;
}
.markdown > :first-child {
margin-top: 0;
}
.markdown > :last-child {
margin-bottom: 0;
}
.markdown pre {
background: var(--bg1);
border: 1px solid var(--bg2);
border-radius: 6px;
padding: 10px;
overflow-x: auto;
}
.markdown code {
background: var(--bg1);
padding: 1px 4px;
border-radius: 4px;
}
.markdown pre code {
background: none;
padding: 0;
}
.markdown table {
border-collapse: collapse;
}
.markdown th,
.markdown td {
border: 1px solid var(--bg3);
padding: 4px 8px;
}
.markdown blockquote {
border-left: 3px solid var(--bg3);
margin-left: 0;
padding-left: 12px;
color: var(--fg2);
}
.reasoning {
color: var(--muted);
font-size: 13px;
border-left: 2px solid var(--bg3);
padding-left: 10px;
margin: 6px 0;
}
.reasoning-head {
display: inline-flex;
align-items: center;
gap: 8px;
border: none;
background: transparent;
padding: 2px 0;
color: var(--muted);
font-size: 12.5px;
}
.reasoning-head:hover {
color: var(--fg2);
}
.reasoning .body {
white-space: pre-wrap;
margin-top: 4px;
font-style: italic;
line-height: 1.5;
}
.reasoning.live .body {
max-height: 260px;
overflow-y: auto;
}
.reasoning.live {
border-left-color: var(--accent);
}
.streaming-text::after {
content: "";
display: inline-block;
width: 0.5em;
height: 1em;
margin-left: 2px;
vertical-align: text-bottom;
background: var(--muted);
animation: blink 1s steps(1) infinite;
}
.tool {
border: 1px solid var(--bg2);
border-radius: var(--radius);
margin: 6px 0;
background: var(--bg1);
overflow: hidden;
}
.tool > summary {
list-style: none;
cursor: pointer;
padding: 6px 10px;
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.tool > summary::-webkit-details-marker {
display: none;
}
.tool .tool-name {
font-family: var(--mono);
font-size: 12px;
color: var(--accent);
}
.tool .tool-title {
color: var(--fg2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
min-width: 0;
}
.tool .status {
font-size: 12px;
}
.tool .status.ok {
color: var(--green);
}
.tool .status.err {
color: var(--red);
}
.tool .status.run {
color: var(--accent);
}
.tool-body {
border-top: 1px solid var(--bg2);
padding: 8px 10px;
}
.tool-body pre {
margin: 0;
white-space: pre-wrap;
word-break: break-word;
max-height: 420px;
overflow: auto;
}
.tool-body img {
max-width: 100%;
border-radius: 6px;
margin-top: 6px;
}
.tool-body .label {
font-size: 11px;
color: var(--muted);
text-transform: uppercase;
margin: 6px 0 2px;
}
.diff {
font-family: var(--mono);
font-size: 12px;
overflow-x: auto;
max-height: 480px;
overflow-y: auto;
border-radius: 6px;
background: var(--bg0);
}
.diff div {
white-space: pre;
padding: 0 8px;
}
.diff .add {
background: var(--diff-add);
}
.diff .del {
background: var(--diff-del);
}
.diff .hunk {
color: var(--blue);
}
.diff .meta {
color: var(--muted);
}
.summary-divider {
border: 1px dashed var(--bg3);
border-radius: var(--radius);
padding: 8px 12px;
color: var(--muted);
font-size: 13px;
margin: 14px 0;
}
.todos {
border: 1px solid var(--bg2);
border-radius: var(--radius);
padding: 8px 12px;
margin: 8px 0;
font-size: 13px;
}
.todos ul {
list-style: none;
padding: 0;
margin: 4px 0 0;
}
.todos li {
display: flex;
gap: 8px;
}
.todos li.completed {
color: var(--muted);
text-decoration: line-through;
}
.todos li.in_progress {
color: var(--accent);
font-weight: 600;
}
.card {
border: 1px solid var(--accent);
background: var(--bg1);
border-radius: var(--radius);
padding: 12px;
margin: 10px 0;
box-shadow: var(--shadow);
}
.card h3 {
margin: 0 0 6px;
font-size: 14px;
color: var(--fg0);
display: flex;
gap: 8px;
align-items: center;
}
.card .actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 10px;
}
.card .from {
font-size: 11.5px;
color: var(--muted);
}
.q-options {
display: grid;
gap: 6px;
margin: 8px 0;
}
.q-option {
display: flex;
gap: 8px;
align-items: flex-start;
text-align: left;
padding: 8px 10px;
border: 1px solid var(--bg3);
border-radius: 6px;
background: var(--bg0);
cursor: pointer;
}
.q-option.selected {
border-color: var(--accent);
background: var(--bg2);
}
.q-option .desc {
font-size: 12.5px;
color: var(--fg3);
}
.q-header {
display: inline-block;
font-size: 11px;
padding: 1px 6px;
border-radius: 4px;
background: var(--bg3);
color: var(--fg1);
margin-bottom: 4px;
}
.error-box {
border: 1px solid var(--red);
color: var(--red);
border-radius: var(--radius);
padding: 8px 12px;
margin: 8px 0;
display: flex;
gap: 8px;
align-items: flex-start;
word-break: break-word;
}
.error-box span {
flex: 1;
}
/* --- composer -------------------------------------------------------------- */
.composer-wrap {
border-top: 1px solid var(--bg2);
padding: 8px 12px;
padding-bottom: max(8px, env(safe-area-inset-bottom));
background: var(--bg0);
}
.composer {
max-width: 900px;
margin: 0 auto;
position: relative;
}
.composer textarea {
width: 100%;
resize: none;
min-height: 44px;
max-height: 40vh;
padding: 10px 12px;
border-radius: var(--radius);
background: var(--bg1);
display: block;
}
.composer-bar {
display: flex;
align-items: center;
gap: 6px;
margin-top: 6px;
flex-wrap: wrap;
}
.composer-bar select {
max-width: 220px;
font-size: 12.5px;
padding: 3px 6px;
}
.composer-options {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
flex: 1;
min-width: 0;
}
.composer-options > * {
flex-shrink: 0;
}
.composer-actions {
display: flex;
gap: 6px;
margin-left: auto;
}
.todo-line {
font-size: 12.5px;
color: var(--muted);
margin: 4px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mode-tabs {
display: inline-flex;
border: 1px solid var(--bg3);
border-radius: 6px;
overflow: hidden;
}
.mode-tabs button {
border: none;
border-radius: 0;
background: transparent;
padding: 3px 10px;
font-size: 12.5px;
}
.mode-tabs button.active {
background: var(--accent);
color: var(--on-accent);
font-weight: 600;
}
.attachments {
display: flex;
gap: 6px;
margin-bottom: 6px;
flex-wrap: wrap;
}
.attachments .att {
position: relative;
}
.attachments .att.file {
display: flex;
align-items: center;
gap: 6px;
height: 36px;
padding: 0 22px 0 10px;
border: 1px solid var(--bg3);
border-radius: 6px;
background: var(--bg1);
font-size: 12.5px;
max-width: 260px;
}
.attachments .att.file span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.attachments .att.file small {
color: var(--muted);
white-space: nowrap;
}
.attachments img {
height: 56px;
border-radius: 6px;
border: 1px solid var(--bg3);
}
.attachments button {
position: absolute;
top: -6px;
right: -6px;
padding: 0 5px;
font-size: 11px;
border-radius: 50%;
}
.suggest {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
background: var(--bg1);
border: 1px solid var(--bg3);
border-radius: var(--radius);
box-shadow: var(--shadow);
max-height: 260px;
overflow-y: auto;
margin-bottom: 4px;
z-index: 5;
}
.suggest div {
padding: 6px 10px;
cursor: pointer;
display: flex;
gap: 10px;
}
.suggest div.active,
.suggest div:hover {
background: var(--bg2);
}
.suggest .name {
font-family: var(--mono);
color: var(--accent);
}
.suggest .desc {
color: var(--fg3);
font-size: 12.5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- forms, settings ------------------------------------------------------- */
.tabs {
display: flex;
gap: 4px;
border-bottom: 1px solid var(--bg2);
margin-bottom: 16px;
flex-wrap: wrap;
}
.tabs button {
border: none;
border-bottom: 2px solid transparent;
border-radius: 0;
background: transparent;
padding: 8px 12px;
}
.tabs button.active {
border-bottom-color: var(--accent);
color: var(--fg0);
}
.form {
display: grid;
grid-template-columns: minmax(140px, 220px) 1fr;
gap: 10px 16px;
align-items: center;
max-width: 820px;
}
.form label {
color: var(--fg2);
}
.form .hint {
grid-column: 2;
font-size: 12px;
color: var(--muted);
margin-top: -6px;
}
.panel {
border: 1px solid var(--bg2);
border-radius: var(--radius);
padding: 14px;
margin-bottom: 14px;
background: var(--bg1);
}
.panel h3 {
margin: 0 0 10px;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
color: var(--fg0);
}
.row {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.spacer {
flex: 1;
}
table.grid {
border-collapse: collapse;
width: 100%;
font-size: 13px;
}
table.grid th {
text-align: left;
color: var(--muted);
font-weight: 500;
font-size: 12px;
border-bottom: 1px solid var(--bg3);
padding: 6px;
}
table.grid td {
border-bottom: 1px solid var(--bg2);
padding: 5px 6px;
}
table.grid td.num,
table.grid th.num {
text-align: right;
font-variant-numeric: tabular-nums;
}
table.grid input {
width: 100%;
min-width: 60px;
}
table.grid input[type="number"] {
min-width: 92px;
}
.raw-editor {
width: 100%;
min-height: 60vh;
font-family: var(--mono);
font-size: 12.5px;
line-height: 1.5;
tab-size: 2;
}
.notice {
padding: 8px 12px;
border-radius: var(--radius);
background: var(--bg2);
margin-bottom: 12px;
font-size: 13px;
}
.notice.ok {
color: var(--green);
}
.notice.err {
color: var(--red);
}
.empty {
color: var(--muted);
text-align: center;
padding: 40px 0;
}
.dir-list {
border: 1px solid var(--bg2);
border-radius: var(--radius);
max-height: 340px;
overflow-y: auto;
}
.dir-list button {
display: flex;
width: 100%;
gap: 8px;
border: none;
border-bottom: 1px solid var(--bg2);
border-radius: 0;
background: transparent;
text-align: left;
padding: 7px 10px;
}
.dir-list button:hover {
background: var(--bg2);
}
/* --- stats ----------------------------------------------------------------- */
.kpis {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 10px;
margin-bottom: 16px;
}
.kpi {
background: var(--bg1);
border: 1px solid var(--bg2);
border-radius: var(--radius);
padding: 12px;
}
.kpi .k-label {
font-size: 12px;
color: var(--fg3);
}
.kpi .k-value {
font-size: 22px;
font-weight: 650;
color: var(--fg0);
font-variant-numeric: tabular-nums;
}
.kpi .k-sub {
font-size: 11.5px;
color: var(--muted);
}
.chart {
position: relative;
}
.chart svg {
display: block;
width: 100%;
overflow: visible;
}
.chart .axis text {
fill: var(--muted);
font-size: 11px;
}
.chart .grid-line {
stroke: var(--bg2);
stroke-width: 1;
}
.legend {
display: flex;
gap: 12px;
flex-wrap: wrap;
font-size: 12px;
color: var(--fg2);
margin-bottom: 8px;
}
.legend span {
display: inline-flex;
align-items: center;
gap: 5px;
}
.legend i {
width: 10px;
height: 10px;
border-radius: 3px;
display: inline-block;
}
.tooltip {
position: absolute;
pointer-events: none;
background: var(--bg2);
border: 1px solid var(--bg3);
border-radius: 6px;
padding: 6px 9px;
font-size: 12px;
box-shadow: var(--shadow);
white-space: nowrap;
z-index: 10;
color: var(--fg1);
}
.tooltip b {
color: var(--fg0);
}
.tooltip .tt-row {
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
}
.tooltip i {
width: 8px;
height: 8px;
border-radius: 2px;
display: inline-block;
}
.two-col {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
/* --- login ----------------------------------------------------------------- */
.login {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.login form {
background: var(--bg1);
border: 1px solid var(--bg2);
border-radius: 12px;
padding: 28px;
width: min(360px, 92vw);
display: grid;
gap: 12px;
text-align: center;
}
.login img {
width: 56px;
margin: 0 auto;
}
/* --- responsive ------------------------------------------------------------ */
@media (max-width: 800px) {
.sidebar {
position: fixed;
inset: 0 auto 0 0;
z-index: 20;
transform: translateX(-100%);
transition: transform 0.18s ease;
box-shadow: var(--shadow);
width: min(300px, 86vw);
}
.sidebar.open {
transform: none;
}
.backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 15;
}
.menu-btn {
display: inline-block;
}
.form {
grid-template-columns: 1fr;
}
.form .hint {
grid-column: 1;
}
.two-col {
grid-template-columns: 1fr;
}
.page {
padding: 12px;
}
.chat {
padding: 12px 10px 20px;
}
.hide-mobile {
display: none !important;
}
.topbar {
flex-wrap: nowrap;
gap: 4px;
padding-left: 6px;
padding-right: 6px;
}
.topbar .path {
display: none;
}
.topbar button.small {
padding: 2px 5px;
}
/* One scrollable row of options, actions stay visible. */
.composer-bar {
flex-wrap: nowrap;
}
.composer-options {
flex-wrap: nowrap;
overflow-x: auto;
scrollbar-width: none;
}
.composer-options::-webkit-scrollbar {
display: none;
}
.composer-options select {
max-width: 150px;
}
/* Avoid iOS zoom on focus. */
.composer textarea,
input,
select,
textarea {
font-size: 16px;
}
}
/* --- syntax highlighting (highlight.js classes, Gruvbox colours) ----------- */
.hljs-keyword,
.hljs-selector-tag,
.hljs-built_in {
color: var(--syntax-keyword);
}
.hljs-string,
.hljs-attr,
.hljs-regexp {
color: var(--syntax-string);
}
.hljs-number,
.hljs-literal {
color: var(--syntax-number);
}
.hljs-comment,
.hljs-quote {
color: var(--syntax-comment);
font-style: italic;
}
.hljs-title,
.hljs-section,
.hljs-function {
color: var(--syntax-function);
}
.hljs-type,
.hljs-class,
.hljs-tag {
color: var(--syntax-type);
}
.hljs-variable,
.hljs-template-variable,
.hljs-params {
color: var(--syntax-variable);
}
.hljs-meta,
.hljs-symbol {
color: var(--syntax-meta);
}
.hljs-addition {
background: var(--diff-add);
}
.hljs-deletion {
background: var(--diff-del);
}
a.button {
display: inline-block;
text-align: center;
text-decoration: none;
color: var(--fg1);
border: 1px solid var(--bg3);
background: var(--bg2);
border-radius: 6px;
padding: 5px 12px;
}
a.button:hover {
border-color: var(--fg3);
}
.sidebar-user {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 6px 12px;
border-top: 1px solid var(--bg2);
font-size: 13px;
color: var(--fg2);
}
.sidebar-user > span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
z-index: 30;
padding: 16px;
}
.modal {
background: var(--bg1);
border: 1px solid var(--bg3);
border-radius: 12px;
padding: 18px;
width: min(440px, 100%);
max-height: 80vh;
overflow-y: auto;
box-shadow: var(--shadow);
}
.modal h3 {
margin: 0 0 10px;
}
.check-list label {
display: flex;
gap: 8px;
align-items: center;
padding: 6px 0;
border-bottom: 1px solid var(--bg2);
}
.readonly-bar {
border-top: 1px solid var(--bg2);
padding: 10px 14px;
text-align: center;
color: var(--muted);
padding-bottom: max(10px, env(safe-area-inset-bottom));
}
/* --- activity animations ---------------------------------------------------- */
.activity {
display: flex;
align-items: center;
gap: 10px;
margin: 10px 0;
font-size: 13px;
color: var(--fg3);
}
.activity.paused {
color: var(--accent2);
}
.dots {
display: inline-flex;
gap: 4px;
}
.dots i {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--accent);
animation: dot-bounce 1.2s infinite ease-in-out;
}
.dots i:nth-child(2) {
animation-delay: 0.15s;
}
.dots i:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes dot-bounce {
0%,
80%,
100% {
transform: translateY(0);
opacity: 0.35;
}
40% {
transform: translateY(-5px);
opacity: 1;
}
}
.pause-icon {
width: 10px;
height: 12px;
border-left: 3px solid var(--accent2);
border-right: 3px solid var(--accent2);
}
.spinner {
display: inline-block;
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid var(--bg3);
border-top-color: var(--accent);
animation: spin 0.8s linear infinite;
flex-shrink: 0;
}
.spinner.small {
width: 12px;
height: 12px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.tool.running {
border-color: var(--accent);
animation: glow 1.6s ease-in-out infinite;
}
@keyframes glow {
50% {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
}
}
.busy-ring {
flex-shrink: 0;
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid var(--bg3);
border-top-color: var(--accent);
animation: spin 0.8s linear infinite;
}
/* Blinking caret at the end of streaming text. */
.streaming .markdown > :last-child::after {
content: "";
display: inline-block;
width: 0.55em;
height: 1.05em;
margin-left: 2px;
vertical-align: text-bottom;
background: var(--accent);
animation: blink 1s steps(1) infinite;
}
@keyframes blink {
50% {
opacity: 0;
}
}
/* Indeterminate progress bar under the top bar. */
.workbar {
height: 2px;
flex-shrink: 0;
position: relative;
overflow: hidden;
}
.workbar.on::before {
content: "";
position: absolute;
inset: 0;
width: 35%;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
animation: slide 1.4s ease-in-out infinite;
}
@keyframes slide {
from {
transform: translateX(-100%);
}
to {
transform: translateX(300%);
}
}
@media (prefers-reduced-motion: reduce) {
.dots i,
.spinner,
.star line,
.star-spin,
.busy-ring,
.tool.running,
.streaming .markdown > :last-child::after,
.streaming-text::after,
.workbar.on::before {
animation: none;
}
.dots i {
opacity: 1;
}
.workbar.on::before {
width: 100%;
opacity: 0.6;
}
}
/* --- image gallery --------------------------------------------------------- */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
}
.gallery.small {
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
margin-top: 6px;
}
.gallery-item {
margin: 0;
background: var(--bg1);
border: 1px solid var(--bg2);
border-radius: var(--radius);
overflow: hidden;
display: flex;
flex-direction: column;
}
.gallery-item img {
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
display: block;
cursor: zoom-in;
background: var(--bg2);
margin: 0;
border-radius: 0;
}
.gallery-item.placeholder {
aspect-ratio: 4 / 3;
align-items: center;
justify-content: center;
}
.gallery-item figcaption {
padding: 8px;
display: grid;
gap: 4px;
font-size: 12.5px;
}
.gallery-item .prompt {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: var(--fg1);
}
.gallery-item .meta,
.viewer .meta {
color: var(--muted);
font-size: 11.5px;
}
a.button.small {
padding: 2px 8px;
font-size: 12px;
}
.viewer {
margin: 0;
max-width: min(1200px, 96vw);
max-height: 92vh;
display: flex;
flex-direction: column;
gap: 8px;
color: var(--fg0);
}
.viewer img {
max-width: 100%;
max-height: 80vh;
object-fit: contain;
border-radius: var(--radius);
}
.budget-bar {
height: 8px;
border-radius: 4px;
background: var(--bg2);
overflow: hidden;
}
.budget-bar > div {
height: 100%;
border-radius: 4px;
}
.muted {
color: var(--muted);
}
/* --- message queue --------------------------------------------------------- */
.queue {
max-width: 900px;
margin: 0 auto;
padding: 6px 12px 0;
width: 100%;
}
.queue-head {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
color: var(--muted);
margin-bottom: 4px;
}
.queue-item {
display: flex;
align-items: center;
gap: 8px;
border: 1px dashed var(--bg3);
border-radius: var(--radius);
padding: 5px 8px;
margin-bottom: 4px;
font-size: 13px;
background: var(--bg1);
}
.queue-text {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--fg2);
}
.queue-item img {
height: 28px;
border-radius: 4px;
}
/* --- file manager ------------------------------------------------------------ */
.files-layout {
flex: 1;
min-height: 0;
display: grid;
grid-template-columns: minmax(260px, 360px) 1fr;
}
.files-side {
border-right: 1px solid var(--bg2);
display: flex;
flex-direction: column;
min-height: 0;
overflow-y: auto;
}
.files-main {
min-width: 0;
min-height: 0;
display: flex;
}
.files-list {
display: flex;
flex-direction: column;
min-height: 100%;
}
.files-list.drag {
outline: 2px dashed var(--accent);
outline-offset: -4px;
}
.files-toolbar {
display: flex;
gap: 4px;
padding: 8px;
flex-wrap: wrap;
align-items: center;
}
.files-toolbar button.active {
border-color: var(--accent);
color: var(--accent);
}
.files-search {
flex: 1;
min-width: 100px;
}
.crumbs {
padding: 0 10px 6px;
font-size: 12.5px;
color: var(--muted);
word-break: break-all;
}
.entries {
flex: 1;
}
.entry {
display: flex;
align-items: center;
width: 100%;
border: none;
border-radius: 0;
background: transparent;
padding: 0;
text-align: left;
}
button.entry {
padding: 6px 10px;
gap: 8px;
}
.entry:hover,
.entry.active {
background: var(--bg2);
}
.entry-main {
flex: 1;
min-width: 0;
display: flex;
gap: 8px;
align-items: center;
border: none;
background: transparent;
border-radius: 0;
padding: 6px 4px 6px 10px;
text-align: left;
}
.entry .name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.entry .size {
font-size: 11px;
color: var(--muted);
}
.entry-menu {
position: relative;
}
.entry-menu summary {
list-style: none;
cursor: pointer;
padding: 4px 10px;
color: var(--muted);
}
.entry-menu summary::-webkit-details-marker {
display: none;
}
.entry-menu .menu {
position: absolute;
right: 4px;
top: 100%;
z-index: 5;
background: var(--bg1);
border: 1px solid var(--bg3);
border-radius: var(--radius);
box-shadow: var(--shadow);
display: grid;
min-width: 150px;
padding: 4px;
}
.entry-menu .menu > * {
text-align: left;
border: none;
background: transparent;
padding: 6px 10px;
}
.entry-menu .menu > *:hover {
background: var(--bg2);
}
.drop-hint {
font-size: 11.5px;
color: var(--muted);
padding: 8px 10px;
text-align: center;
}
.git-mark {
font-family: var(--mono);
font-size: 11px;
font-weight: 700;
min-width: 14px;
text-align: center;
color: var(--accent2);
}
.git-mark.m-A,
.git-mark.m-U {
color: var(--green);
}
.git-mark.m-D {
color: var(--red);
}
.git-mark.m-dot {
color: var(--muted);
}
.editor-pane {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.editor-pane.empty {
align-items: center;
justify-content: center;
}
.editor-bar {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-bottom: 1px solid var(--bg2);
}
.editor-bar .grow {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dirty {
color: var(--accent);
}
.code-editor {
flex: 1;
min-height: 0;
overflow: hidden;
}
.code-editor .cm-editor {
height: 100%;
}
.pane-scroll {
flex: 1;
min-height: 0;
overflow: auto;
}
.image-preview {
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
background: repeating-conic-gradient(var(--bg1) 0% 25%, var(--bg0) 0% 50%) 50% / 20px 20px;
}
.image-preview img {
max-width: 100%;
max-height: 100%;
}
.files-status {
position: fixed;
right: 12px;
bottom: 8px;
font-size: 11px;
color: var(--muted);
pointer-events: none;
}
.git-init {
display: flex;
gap: 6px;
padding: 8px 10px;
border-top: 1px solid var(--bg2);
}
.mobile-only {
display: none;
}
/* git panel */
.git-panel {
padding: 8px 10px;
}
.git-branch {
display: flex;
gap: 4px;
align-items: center;
flex-wrap: wrap;
}
.git-branch select {
max-width: 160px;
}
.git-output {
font-size: 12px;
background: var(--bg1);
border: 1px solid var(--bg2);
border-radius: 6px;
padding: 6px 8px;
white-space: pre-wrap;
max-height: 160px;
overflow: auto;
margin: 6px 0 0;
}
.git-output.err {
color: var(--red);
border-color: var(--red);
}
.git-section {
margin-bottom: 10px;
}
.git-head {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11.5px;
text-transform: uppercase;
color: var(--muted);
padding: 4px 0;
}
.git-file {
display: flex;
align-items: center;
gap: 4px;
}
.git-name {
flex: 1;
min-width: 0;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 3px 4px;
font-size: 13px;
}
.git-commit textarea {
width: 100%;
margin-bottom: 6px;
}
.git-log {
display: grid;
}
.git-commit-row {
display: grid;
text-align: left;
border: none;
border-bottom: 1px solid var(--bg2);
border-radius: 0;
background: transparent;
padding: 6px 2px;
}
.git-commit-row:hover {
background: var(--bg2);
}
.git-commit-row .subject {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.git-commit-row .meta {
font-size: 11.5px;
color: var(--muted);
}
@media (max-width: 800px) {
.files-layout {
grid-template-columns: 1fr;
}
.files-main {
display: none;
}
.files-layout.show-editor .files-side {
display: none;
}
.files-layout.show-editor .files-main {
display: flex;
}
.mobile-only {
display: inline-block;
}
}
/* --- images in chat ------------------------------------------------------------ */
.markdown img.inline-image,
.svg-block img,
.image-result img {
max-width: min(100%, 640px);
max-height: 480px;
border-radius: 6px;
display: block;
margin: 6px 0;
background: repeating-conic-gradient(var(--bg1) 0% 25%, var(--bg0) 0% 50%) 50% / 16px 16px;
}
.svg-block {
margin: 8px 0;
}
.image-refs {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin: 6px 0;
}
.image-refs img {
height: 140px;
max-width: 260px;
object-fit: contain;
border-radius: 6px;
border: 1px solid var(--bg2);
background: repeating-conic-gradient(var(--bg1) 0% 25%, var(--bg0) 0% 50%) 50% / 16px 16px;
}
.image-result {
padding: 0 10px 8px;
}
.tool-head {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0;
font-size: 13px;
}
/* --- usage overlay ------------------------------------------------------------- */
.modal.wide {
width: min(1100px, 100%);
max-height: 90vh;
}
.kv-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 6px 20px;
}
.kv-title {
font-size: 11px;
text-transform: uppercase;
color: var(--muted);
margin: 4px 0 2px;
}
.kv {
display: flex;
justify-content: space-between;
gap: 10px;
font-size: 13px;
padding: 2px 0;
border-bottom: 1px dashed var(--bg2);
}
.kv span {
color: var(--fg3);
}
.kv b {
font-weight: 500;
color: var(--fg0);
text-align: right;
font-variant-numeric: tabular-nums;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- file previews ------------------------------------------------------------- */
.pdf-preview {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.pdf-nav {
padding: 6px 10px;
border-bottom: 1px solid var(--bg2);
font-size: 13px;
}
.pdf-preview .image-preview img {
background: #fff;
box-shadow: var(--shadow);
}
.doc-text {
margin: 0;
padding: 16px;
white-space: pre-wrap;
font-size: 13px;
}
.sheet-tabs {
margin: 6px 10px;
flex-wrap: wrap;
align-self: flex-start;
}
table.grid.sheet td,
table.grid.sheet th {
white-space: nowrap;
border-right: 1px solid var(--bg2);
}
.audio-preview {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
padding: 24px;
}
.audio-preview audio {
width: min(520px, 100%);
}
.model-viewer {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.model-canvas {
flex: 1;
min-height: 300px;
touch-action: none;
}
.model-info {
font-size: 12px;
color: var(--muted);
padding: 4px 10px;
border-top: 1px solid var(--bg2);
}
.star {
width: 16px;
height: 16px;
flex-shrink: 0;
vertical-align: -3px;
overflow: visible;
}
.star line {
stroke: var(--accent2);
stroke-width: 2.6;
stroke-linecap: round;
opacity: 0.25;
animation: star-ray 0.9s linear infinite;
}
.star-spin {
transform-origin: 0 0;
animation: spin 6s linear infinite;
}
@keyframes star-ray {
0% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
.think-icon {
width: 15px;
height: 15px;
vertical-align: -2px;
fill: none;
stroke: var(--fg3);
stroke-width: 1.6;
stroke-linejoin: round;
}
.think-icon circle {
fill: var(--fg3);
stroke: none;
}
.activity-text {
color: var(--accent2);
}
.activity-meta {
color: var(--muted);
font-variant-numeric: tabular-nums;
}
tr.audit-warn td {
color: var(--accent2);
}
a.button.primary {
background: var(--accent);
border-color: var(--accent);
color: var(--on-accent);
font-weight: 600;
}
.mode-tabs button.active.deep {
background: var(--accent2);
}
.rewind-files {
max-height: 200px;
overflow-y: auto;
font-size: 12.5px;
padding-left: 18px;
}
/* --- in-app dialogs and toasts --------------------------------------------------- */
.modal.dialog {
width: min(420px, 100%);
z-index: 40;
}
.dialog-message {
white-space: pre-wrap;
font-size: 14px;
color: var(--fg1);
margin: 0 0 12px;
}
.dialog-actions {
justify-content: flex-end;
margin-top: 14px;
}
button.danger-solid {
background: var(--red);
border-color: var(--red);
color: var(--bg0);
font-weight: 600;
}
.toasts {
position: fixed;
right: 16px;
bottom: 16px;
display: grid;
gap: 8px;
z-index: 50;
max-width: min(420px, calc(100vw - 32px));
padding-bottom: env(safe-area-inset-bottom);
}
.toast {
background: var(--bg1);
border: 1px solid var(--bg3);
border-left: 4px solid var(--blue);
border-radius: var(--radius);
padding: 10px 14px;
box-shadow: var(--shadow);
font-size: 13px;
cursor: pointer;
animation: toast-in 0.18s ease-out;
word-break: break-word;
}
.toast.error {
border-left-color: var(--red);
}
.toast.success {
border-left-color: var(--green);
}
@keyframes toast-in {
from {
transform: translateY(8px);
opacity: 0;
}
}
.shell-output {
background: var(--bg0);
border: 1px solid var(--bg2);
border-radius: 6px;
padding: 8px 10px;
max-height: 55vh;
overflow: auto;
white-space: pre-wrap;
font-size: 12px;
margin: 0;
}
.segmented {
display: inline-flex;
gap: 2px;
background: var(--bg1);
border-radius: 8px;
padding: 2px;
width: fit-content;
}
.segmented button.active {
background: var(--accent);
color: var(--on-accent);
}
.sidebar-user .theme-toggle {
margin-left: auto;
font-size: 15px;
min-width: 32px;
}
.form .stack {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
}
/* --- files in chat (preview + type/size badges + name) ------------------------- */
.file-figure {
display: inline-flex;
flex-direction: column;
gap: 4px;
margin: 6px 8px 6px 0;
max-width: min(100%, 640px);
vertical-align: top;
}
.file-preview {
min-width: 128px;
position: relative;
display: inline-block;
max-width: 100%;
}
.file-preview img {
margin: 0 !important;
display: block;
}
.file-badge {
font-size: 10px;
font-weight: 600;
letter-spacing: 0.03em;
line-height: 1;
padding: 3px 6px;
border-radius: 4px;
background: color-mix(in srgb, var(--bg0) 82%, transparent);
color: var(--fg1);
border: 1px solid var(--bg3);
white-space: nowrap;
}
.file-preview .file-badge {
position: absolute;
top: 6px;
pointer-events: none;
backdrop-filter: blur(4px);
}
.file-preview .file-badge.left {
left: 6px;
}
.file-preview .file-badge.right {
right: 6px;
}
.file-figure figcaption {
font-size: 12px;
color: var(--fg3);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
.file-figure figcaption a {
color: inherit;
}
.file-figure.thumb img {
height: 140px;
max-width: 260px;
object-fit: contain;
}
.msg-user .file-figure img {
max-height: 160px;
border-radius: 6px;
}
.user-files {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 6px;
}
.file-chip {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 100%;
padding: 4px 8px;
border-radius: 6px;
border: 1px solid var(--bg3);
background: var(--bg1);
color: var(--fg1);
font-size: 12px;
text-decoration: none;
white-space: nowrap;
margin: 2px 0;
}
.file-chip:hover {
border-color: var(--accent);
}
.file-chip-name {
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
.file-chip-size {
color: var(--muted);
}
.file-chip .file-badge {
background: var(--bg2);
border: none;
}
.gallery .file-figure { margin: 0; max-width: none; display: flex; }
.nav-icon {
display: inline-block;
width: 1.3em;
margin-right: 6px;
text-align: center;
font-size: 0.95em;
}
.sidebar-foot button {
display: flex;
align-items: center;
justify-content: flex-start;
}
/* --- jump to latest message ---------------------------------------------------- */
.jump-latest {
position: sticky;
bottom: 14px;
height: 0;
display: flex;
justify-content: center;
pointer-events: none;
z-index: 4;
}
.jump-latest button {
pointer-events: auto;
transform: translateY(-100%);
width: 40px;
height: 40px;
border-radius: 50%;
font-size: 20px;
line-height: 1;
padding: 0;
box-shadow: var(--shadow);
}
/* --- session search -------------------------------------------------------------- */
.session-search {
padding: 0 12px 8px;
}
.session-search input {
width: 100%;
}
.session-item .snippet {
white-space: normal;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
color: var(--fg2);
}
/* --- small popup menu (export) ---------------------------------------------- */
.menu-pop {
position: relative;
}
.menu-pop > summary {
list-style: none;
cursor: pointer;
padding: 4px 8px;
border-radius: 6px;
}
.menu-pop > summary::-webkit-details-marker {
display: none;
}
.menu-pop > summary:hover {
background: var(--bg2);
}
.menu-pop-body {
position: absolute;
right: 0;
top: calc(100% + 4px);
z-index: 20;
background: var(--bg1);
border: 1px solid var(--bg3);
border-radius: var(--radius);
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
min-width: 160px;
padding: 4px;
}
.menu-pop-body a {
padding: 6px 10px;
border-radius: 6px;
color: var(--fg1);
text-decoration: none;
white-space: nowrap;
}
.menu-pop-body a:hover {
background: var(--bg2);
}