velxio/frontend/src/components/editor/FileExplorer.css

361 lines
6.5 KiB
CSS
Raw Normal View History

.file-explorer {
width: 100%;
background: #252526;
border-right: 1px solid #333;
display: flex;
flex-direction: column;
overflow: hidden;
font-size: 12px;
font-family: 'Segoe UI', sans-serif;
}
.file-explorer-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
height: 46px;
color: #9d9d9d;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.5px;
border-bottom: 1px solid #333;
flex-shrink: 0;
}
.file-explorer-header-actions {
display: flex;
align-items: center;
gap: 2px;
}
.file-explorer-new-btn {
background: none;
border: none;
color: #9d9d9d;
cursor: pointer;
padding: 2px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
}
.file-explorer-new-btn:hover {
background: rgba(255, 255, 255, 0.08);
color: #fff;
}
.file-explorer-save-btn {
background: none;
border: none;
color: #9d9d9d;
cursor: pointer;
padding: 2px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
}
.file-explorer-save-btn:hover {
background: rgba(255, 255, 255, 0.08);
color: var(--color-action-primary);
}
/* The Save button doubles as the auto-save indicator once a project is
loaded (replaces the "Unsaved changes" text pill in the header). */
.file-explorer-save-btn.is-saved {
color: #3fb950;
}
.file-explorer-save-btn.is-dirty {
color: #f0883e;
}
.file-explorer-save-btn.is-saving {
color: #3fb950;
animation: velxio-pulse 1s ease-in-out infinite;
}
.file-explorer-save-btn.is-error {
color: #f85149;
}
.file-explorer-save-btn.is-saved:hover,
.file-explorer-save-btn.is-dirty:hover,
.file-explorer-save-btn.is-error:hover {
filter: brightness(1.25);
}
.file-explorer-list {
flex: 1;
overflow-y: auto;
padding: 4px 0;
scrollbar-width: thin;
scrollbar-color: #3c3c3c transparent;
}
.file-explorer-item {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 8px;
cursor: pointer;
color: #ccc;
border-radius: 3px;
margin: 1px 4px;
white-space: nowrap;
overflow: hidden;
user-select: none;
}
.file-explorer-item:hover {
background: rgba(255, 255, 255, 0.06);
}
.file-explorer-item-active {
background: rgba(0, 113, 227, 0.12);
color: #e2e2e2;
}
.file-explorer-item-new {
background: rgba(255, 255, 255, 0.04);
}
.file-explorer-icon {
font-size: 12px;
flex-shrink: 0;
opacity: 0.8;
}
.file-explorer-name {
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.file-explorer-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #e8a87c;
flex-shrink: 0;
}
.file-explorer-rename-input {
flex: 1;
background: #3c3c3c;
border: 1px solid var(--color-action-primary);
color: #fff;
font-size: 12px;
padding: 1px 4px;
border-radius: 2px;
outline: none;
min-width: 0;
font-family: inherit;
}
/* Context menu */
.file-explorer-context-menu {
position: fixed;
background: #2d2d2d;
border: 1px solid #3c3c3c;
border-radius: 4px;
z-index: 2000;
display: flex;
flex-direction: column;
min-width: 120px;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.file-explorer-context-menu button {
background: none;
border: none;
color: #ccc;
padding: 7px 14px;
text-align: left;
cursor: pointer;
font-size: 12px;
font-family: inherit;
}
.file-explorer-context-menu button:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.08);
color: #fff;
}
.file-explorer-context-menu button:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.ctx-delete:hover:not(:disabled) {
color: #ef5350 !important;
}
/* ─── Board-grouped FileExplorer ─────────────────── */
.fe-board-section {
margin-bottom: 2px;
}
.fe-board-header {
display: flex;
align-items: center;
gap: 5px;
padding: 5px 6px 5px 4px;
cursor: pointer;
color: #aaa;
border-radius: 3px;
margin: 0 4px;
user-select: none;
position: relative;
}
.fe-board-header:hover {
background: rgba(255, 255, 255, 0.06);
color: #ddd;
}
.fe-board-header-active {
background: rgba(255, 255, 255, 0.05);
color: #e2e2e2;
}
.fe-collapse-btn {
background: none;
border: none;
color: inherit;
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
flex-shrink: 0;
opacity: 0.6;
}
.fe-collapse-btn:hover {
opacity: 1;
}
.fe-board-icon {
font-size: 10px;
flex-shrink: 0;
line-height: 1;
}
.fe-board-label {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.3px;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
feat(editor): rename boards & custom chips; show which target owns each file Phase 2 of the run-system/UX work. - BoardInstance gains an optional user ; boardDisplayName(board) resolver (name || kind label) routes every INSTANCE-label surface: file-explorer section header, compile console (EditorToolbar), canvas selector/tooltip/ context-menu, Serial Monitor tabs, Oscilloscope board picker, Board Options subtitle. Board/component pickers keep the KIND label (they pick new boards). - Inline rename on board AND chip section headers (double-click the name, or a hover pencil button). Board -> updateBoard(id,{name}); chip -> chipName in properties. Enter commits, Escape cancels (cancel-flag ref guards the unmount-fires-onBlur footgun), empty clears to the kind / 'Custom Chip'. - FileTabs shows an owner badge naming the board/chip whose files are shown (resolved as a selector so it doesn't re-render on every sim pin toggle). - CustomChipDialog no longer clobbers a user-given chipName: chip.json's name only seeds the blank defaults (My Chip / Custom Chip); loading an example relabels explicitly. - Persistence: board name round-trips via projectPayload (+ dirty hash), vlxFile, ProjectByIdPage load + loadProjectState; chipName rides components_json. - Drive-by: fixed a pre-existing rules-of-hooks violation in BoardOptionsModal (early return before a useCallback). Reviewed by a 3-agent adversarial pass (completeness / persistence / correctness); all major findings folded in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 11:22:25 +07:00
/* Inline rename input for a board/chip section header — flex into the label
slot and match the header's compact type. */
.fe-section-rename-input {
flex: 1;
min-width: 0;
font-size: 11px;
font-weight: 600;
}
.fe-status-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
.fe-board-new-btn {
background: none;
border: none;
color: #9d9d9d;
cursor: pointer;
padding: 1px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
flex-shrink: 0;
opacity: 0;
transition: opacity 0.1s;
}
.fe-board-header:hover .fe-board-new-btn,
.fe-board-header-active .fe-board-new-btn {
opacity: 1;
}
.fe-board-new-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}
.fe-board-files {
/* files are indented to show hierarchy */
}
.fe-file-item {
/* --fe-depth: nesting level inside the board's folder tree (0 = root). */
padding-left: calc(26px + var(--fe-depth, 0) * 14px) !important;
}
/* ── Folder rows ────────────────────────────────────────────────────────── */
.fe-folder-item {
user-select: none;
}
.fe-folder-chevron {
display: inline-flex;
align-items: center;
margin-right: 2px;
color: #9d9d9d;
}
.fe-folder-icon {
color: #d8a12a;
}
.fe-folder-actions {
display: none;
margin-left: auto;
align-items: center;
gap: 2px;
}
.fe-folder-item:hover .fe-folder-actions {
display: inline-flex;
}
.fe-folder-actions button {
display: inline-flex;
align-items: center;
background: none;
border: none;
padding: 1px;
cursor: pointer;
color: #9d9d9d;
}
.fe-folder-actions button:hover {
color: #fff;
}
.fe-folder-actions button svg {
width: 14px;
height: 14px;
}
.fe-folder-actions .fe-folder-delete:hover {
color: #f87171;
}