Users, file manager, image generation, budgets and agent workflow
- User management: local accounts (Argon2) and OpenID Connect, admin/user roles, per-user folders plus admin-shared folders, read-only session sharing, event filtering per user - File manager with CodeMirror editor and git client (status, diff, stage, commit, branches, fetch/pull/push, log, clone/init) - Image generation via fal.ai: generate_image tool and image page - Budgets: monthly limits per provider, provider balances/costs (OpenRouter, Anthropic admin cost report, fal.ai), price discovery - Subagents on other models, background tasks, concurrency limits per host/model/subagent - MCP tools loaded on demand (mcp_search/mcp_call), read-only hints - Permission mode "auto", safe read-only commands always allowed - Persistent message queue with attachments, deletable - Live reasoning incl. <think> tags, activity animations, CSS themes, mobile layout, statistics per user, upload size handling - Docker: named volume for state, localhost-bound port Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f3a2fe3816
commit
49262c612c
70 files changed
+8495
-714
No files matched your search
+772
-137
@@ -1,126 +1,6 @@
|
||||
/* CodeCraft – Gruvbox theme (dark default, light via data-theme or OS). */
|
||||
/* CodeCraft – layout and components. Colours come exclusively from the theme
|
||||
variables defined in src/themes/*.css. */
|
||||
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--bg0: #282828;
|
||||
--bg1: #32302f;
|
||||
--bg2: #3c3836;
|
||||
--bg3: #504945;
|
||||
--fg0: #fbf1c7;
|
||||
--fg1: #ebdbb2;
|
||||
--fg2: #d5c4a1;
|
||||
--fg3: #a89984;
|
||||
--muted: #928374;
|
||||
--accent: #fabd2f;
|
||||
--accent2: #fe8019;
|
||||
--red: #fb4934;
|
||||
--green: #b8bb26;
|
||||
--aqua: #8ec07c;
|
||||
--blue: #83a598;
|
||||
--purple: #d3869b;
|
||||
--diff-add: rgba(184, 187, 38, 0.16);
|
||||
--diff-del: rgba(251, 73, 52, 0.16);
|
||||
--shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
||||
|
||||
/* Data viz: validated categorical palette (dark steps on #282828). */
|
||||
--series-1: #3987e5;
|
||||
--series-2: #d95926;
|
||||
--series-3: #199e70;
|
||||
--series-4: #c98500;
|
||||
--series-5: #d55181;
|
||||
--series-6: #008300;
|
||||
--series-7: #9085e9;
|
||||
--series-8: #e66767;
|
||||
/* Sequential (heatmap), blue ramp: low → high on dark. */
|
||||
--seq-0: #32302f;
|
||||
--seq-1: #104281;
|
||||
--seq-2: #184f95;
|
||||
--seq-3: #256abf;
|
||||
--seq-4: #3987e5;
|
||||
--seq-5: #6da7ec;
|
||||
--seq-6: #9ec5f4;
|
||||
--radius: 8px;
|
||||
--font: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
--mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root:where(:not([data-theme="dark"])) {
|
||||
color-scheme: light;
|
||||
--bg0: #fbf1c7;
|
||||
--bg1: #f2e5bc;
|
||||
--bg2: #ebdbb2;
|
||||
--bg3: #d5c4a1;
|
||||
--fg0: #282828;
|
||||
--fg1: #3c3836;
|
||||
--fg2: #504945;
|
||||
--fg3: #665c54;
|
||||
--muted: #7c6f64;
|
||||
--accent: #b57614;
|
||||
--accent2: #af3a03;
|
||||
--red: #9d0006;
|
||||
--green: #79740e;
|
||||
--aqua: #427b58;
|
||||
--blue: #076678;
|
||||
--purple: #8f3f71;
|
||||
--diff-add: rgba(121, 116, 14, 0.16);
|
||||
--diff-del: rgba(157, 0, 6, 0.12);
|
||||
--shadow: 0 6px 24px rgba(60, 56, 54, 0.18);
|
||||
--series-1: #2a78d6;
|
||||
--series-2: #eb6834;
|
||||
--series-3: #1baf7a;
|
||||
--series-4: #eda100;
|
||||
--series-5: #e87ba4;
|
||||
--series-6: #008300;
|
||||
--series-7: #6250d6;
|
||||
--series-8: #e34948;
|
||||
--seq-0: #f2e5bc;
|
||||
--seq-1: #cde2fb;
|
||||
--seq-2: #9ec5f4;
|
||||
--seq-3: #6da7ec;
|
||||
--seq-4: #3987e5;
|
||||
--seq-5: #256abf;
|
||||
--seq-6: #184f95;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
--bg0: #fbf1c7;
|
||||
--bg1: #f2e5bc;
|
||||
--bg2: #ebdbb2;
|
||||
--bg3: #d5c4a1;
|
||||
--fg0: #282828;
|
||||
--fg1: #3c3836;
|
||||
--fg2: #504945;
|
||||
--fg3: #665c54;
|
||||
--muted: #7c6f64;
|
||||
--accent: #b57614;
|
||||
--accent2: #af3a03;
|
||||
--red: #9d0006;
|
||||
--green: #79740e;
|
||||
--aqua: #427b58;
|
||||
--blue: #076678;
|
||||
--purple: #8f3f71;
|
||||
--diff-add: rgba(121, 116, 14, 0.16);
|
||||
--diff-del: rgba(157, 0, 6, 0.12);
|
||||
--shadow: 0 6px 24px rgba(60, 56, 54, 0.18);
|
||||
--series-1: #2a78d6;
|
||||
--series-2: #eb6834;
|
||||
--series-3: #1baf7a;
|
||||
--series-4: #eda100;
|
||||
--series-5: #e87ba4;
|
||||
--series-6: #008300;
|
||||
--series-7: #6250d6;
|
||||
--series-8: #e34948;
|
||||
--seq-0: #f2e5bc;
|
||||
--seq-1: #cde2fb;
|
||||
--seq-2: #9ec5f4;
|
||||
--seq-3: #6da7ec;
|
||||
--seq-4: #3987e5;
|
||||
--seq-5: #256abf;
|
||||
--seq-6: #184f95;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
@@ -176,7 +56,7 @@ button:disabled {
|
||||
button.primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #282828;
|
||||
color: var(--on-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
button.danger {
|
||||
@@ -335,7 +215,8 @@ textarea:focus {
|
||||
border-top: 1px solid var(--bg2);
|
||||
padding: 8px;
|
||||
padding-bottom: max(8px, env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px;
|
||||
}
|
||||
.sidebar-foot button {
|
||||
@@ -503,13 +384,41 @@ textarea:focus {
|
||||
padding-left: 10px;
|
||||
margin: 6px 0;
|
||||
}
|
||||
.reasoning summary {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
.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 {
|
||||
@@ -750,8 +659,29 @@ textarea:focus {
|
||||
font-size: 12.5px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
.composer-bar .grow {
|
||||
.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;
|
||||
@@ -768,7 +698,7 @@ textarea:focus {
|
||||
}
|
||||
.mode-tabs button.active {
|
||||
background: var(--accent);
|
||||
color: #282828;
|
||||
color: var(--on-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
.attachments {
|
||||
@@ -916,6 +846,9 @@ table.grid input {
|
||||
width: 100%;
|
||||
min-width: 60px;
|
||||
}
|
||||
table.grid input[type="number"] {
|
||||
min-width: 92px;
|
||||
}
|
||||
.raw-editor {
|
||||
width: 100%;
|
||||
min-height: 60vh;
|
||||
@@ -1123,46 +1056,83 @@ table.grid input {
|
||||
.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(--red);
|
||||
color: var(--syntax-keyword);
|
||||
}
|
||||
.hljs-string,
|
||||
.hljs-attr,
|
||||
.hljs-regexp {
|
||||
color: var(--green);
|
||||
color: var(--syntax-string);
|
||||
}
|
||||
.hljs-number,
|
||||
.hljs-literal {
|
||||
color: var(--purple);
|
||||
color: var(--syntax-number);
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: var(--muted);
|
||||
color: var(--syntax-comment);
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-function {
|
||||
color: var(--aqua);
|
||||
color: var(--syntax-function);
|
||||
}
|
||||
.hljs-type,
|
||||
.hljs-class,
|
||||
.hljs-tag {
|
||||
color: var(--accent);
|
||||
color: var(--syntax-type);
|
||||
}
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-params {
|
||||
color: var(--blue);
|
||||
color: var(--syntax-variable);
|
||||
}
|
||||
.hljs-meta,
|
||||
.hljs-symbol {
|
||||
color: var(--accent2);
|
||||
color: var(--syntax-meta);
|
||||
}
|
||||
.hljs-addition {
|
||||
background: var(--diff-add);
|
||||
@@ -1170,3 +1140,668 @@ table.grid input {
|
||||
.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,
|
||||
.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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user