desktop: components/apps/text-editor — code editor with toolbar, cursor position, dark theme

This commit is contained in:
Butterfly Dev 2026-04-07 03:34:27 +00:00
parent 7496fbeced
commit 283e53d1dc
3 changed files with 153 additions and 0 deletions

View File

@ -0,0 +1,23 @@
<div class="text-editor">
<!-- Toolbar -->
<div class="editor-toolbar">
<button class="toolbar-btn" (click)="newFile()" title="New">
<svg width="14" height="14" viewBox="0 0 14 14"><path d="M1 1h12v12H1z" fill="none" stroke="currentColor" stroke-width="1.2"/><line x1="7" y1="4" x2="7" y2="10" stroke="currentColor" stroke-width="1.2"/><line x1="4" y1="7" x2="10" y2="7" stroke="currentColor" stroke-width="1.2"/></svg>
New
</button>
<div class="toolbar-separator"></div>
<span class="file-name">{{ fileName() }}</span>
<span class="spacer"></span>
<span class="cursor-info">Ln {{ cursorLine() }}, Col {{ cursorCol() }}</span>
</div>
<!-- Editor area -->
<textarea
class="editor-area"
[value]="content()"
placeholder="Start typing…"
spellcheck="false"
(input)="onInput($event)"
(keyup)="onKeyUp($event)"
></textarea>
</div>

View File

@ -0,0 +1,84 @@
:host {
display: block;
width: 100%;
height: 100%;
}
.text-editor {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: #1e1e1e;
}
.editor-toolbar {
display: flex;
align-items: center;
height: 32px;
padding: 0 8px;
background: #2d2d2d;
border-bottom: 1px solid #404040;
gap: 4px;
flex-shrink: 0;
}
.toolbar-btn {
display: flex;
align-items: center;
gap: 4px;
height: 26px;
padding: 0 8px;
border: none;
background: transparent;
color: #ccc;
font-size: 12px;
cursor: pointer;
border-radius: 4px;
&:hover {
background: rgba(255, 255, 255, 0.08);
}
}
.toolbar-separator {
width: 1px;
height: 20px;
background: #555;
}
.file-name {
font-size: 12px;
color: #999;
margin-left: 4px;
}
.spacer {
flex: 1;
}
.cursor-info {
font-size: 11px;
color: #888;
font-family: 'Consolas', monospace;
}
.editor-area {
flex: 1;
width: 100%;
resize: none;
border: none;
outline: none;
padding: 12px 16px;
background: #1e1e1e;
color: #d4d4d4;
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
font-size: 14px;
line-height: 1.6;
tab-size: 2;
box-sizing: border-box;
&::placeholder {
color: #555;
}
}

View File

@ -0,0 +1,46 @@
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-text-editor',
standalone: true,
imports: [CommonModule],
templateUrl: './text-editor.component.html',
styleUrl: './text-editor.component.scss',
})
export class TextEditorComponent {
readonly content = signal('');
readonly fileName = signal('untitled.txt');
readonly showSaveDialog = signal(false);
/** Cursor position. */
readonly cursorLine = signal(1);
readonly cursorCol = signal(1);
onInput(event: Event): void {
const textarea = event.target as HTMLTextAreaElement;
this.content.set(textarea.value);
this.updateCursorPosition(textarea);
}
onKeyUp(event: Event): void {
this.updateCursorPosition(event.target as HTMLTextAreaElement);
}
private updateCursorPosition(textarea: HTMLTextAreaElement): void {
const val = textarea.value.substring(0, textarea.selectionStart);
const lines = val.split('\n');
this.cursorLine.set(lines.length);
this.cursorCol.set(lines[lines.length - 1].length + 1);
}
newFile(): void {
this.content.set('');
this.fileName.set('untitled.txt');
}
saveFile(): void {
// In a real implementation this would save to the server's virtual filesystem.
this.showSaveDialog.set(false);
}
}