Skip to content

Commit 32d6813

Browse files
committed
simplify tab bar - clean rectangular tabs without broken styling
1 parent ac134ae commit 32d6813

1 file changed

Lines changed: 20 additions & 107 deletions

File tree

src/components/live-preview/terminal-window.component.ts

Lines changed: 20 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ import { FontPresetsService } from '../../services/font-presets.service';
99
template: `
1010
<div class="flex-1 overflow-hidden flex flex-col rounded-lg" [ngStyle]="windowStyles()">
1111
@if (!hideDecorations()) {
12-
<div class="flex items-center px-3 py-2 select-none border-b"
13-
[style.background]="titleBarBg()"
14-
[style.border-color]="borderColor()">
12+
<div class="flex items-center px-3 py-2 select-none border-b" [style.background]="titleBarBg()" [style.border-color]="borderColor()">
1513
<div class="flex items-center gap-1.5">
1614
<div class="w-3 h-3 rounded-full" style="background: #ff5f56;"></div>
1715
<div class="w-3 h-3 rounded-full" style="background: #ffbd2e;"></div>
1816
<div class="w-3 h-3 rounded-full" style="background: #27c93f;"></div>
1917
</div>
20-
<span class="flex-1 text-center text-xs select-none opacity-50"
21-
[style.font-family]="terminalFont()"
22-
[style.color]="colors().foreground">bash</span>
18+
<span class="flex-1 text-center text-xs select-none opacity-50" [style.font-family]="terminalFont()" [style.color]="colors().foreground">bash</span>
2319
</div>
2420
}
2521
2622
@if (tabBarVisible() && tabBarEdge() === 'top') {
27-
<ng-container *ngTemplateOutlet="tabBar"></ng-container>
23+
<div class="flex bg-opacity-90" [style.background]="tabBarBg()">
24+
@for (tab of tabs; track tab.name; let i = $index) {
25+
<div class="px-4 py-1.5 text-xs" [ngStyle]="tabStyles(i === 0)">{{ tab.name }}</div>
26+
}
27+
</div>
2828
}
2929
3030
<div class="flex-1 overflow-hidden relative p-3" [ngStyle]="terminalStyles()">
@@ -52,88 +52,32 @@ import { FontPresetsService } from '../../services/font-presets.service';
5252
<span [style.color]="c('color4')">arch</span>
5353
<span [style.color]="c('foreground')" class="opacity-60">:~$ </span>
5454
<span [style.color]="c('foreground')">cat </span>
55-
<span class="url-text" [style.color]="urlColor()" [style.text-decoration]="urlUnderline()">https://sw.kovidgoyal.net/kitty/</span>
55+
<span class="url-text" [style.color]="urlColor()">https://sw.kovidgoyal.net/kitty/</span>
5656
</div>
5757
<div class="whitespace-nowrap mt-2">
5858
<span [style.color]="c('color2')">user</span>
5959
<span [style.color]="c('foreground')" class="opacity-60">@</span>
6060
<span [style.color]="c('color4')">arch</span>
6161
<span [style.color]="c('foreground')" class="opacity-60">:~$ </span>
6262
@if (cursorShape() === 'block') {
63-
<span class="inline-block w-2 h-4 align-text-bottom"
64-
[class.animate-blink]="cursorBlinking()"
65-
[style.background]="cursorColor()"
66-
[style.color]="cursorTextColor()">&nbsp;</span>
63+
<span class="inline-block w-2 h-4 align-text-bottom" [class.animate-blink]="cursorBlinking()" [style.background]="cursorColor()">&nbsp;</span>
6764
} @else if (cursorShape() === 'beam') {
68-
<span class="inline-block w-0.5 h-4 align-text-bottom border-l-2"
69-
[class.animate-blink]="cursorBlinking()"
70-
[style.border-color]="cursorColor()"></span>
65+
<span class="inline-block w-0.5 h-4 align-text-bottom border-l-2" [class.animate-blink]="cursorBlinking()" [style.border-color]="cursorColor()"></span>
7166
} @else {
72-
<span class="inline-block w-2 h-4 align-text-bottom border-b-2"
73-
[class.animate-blink]="cursorBlinking()"
74-
[style.border-color]="cursorColor()">&nbsp;</span>
67+
<span class="inline-block w-2 h-4 align-text-bottom border-b-2" [class.animate-blink]="cursorBlinking()" [style.border-color]="cursorColor()">&nbsp;</span>
7568
}
7669
</div>
7770
</div>
7871
</div>
7972
8073
@if (tabBarVisible() && tabBarEdge() === 'bottom') {
81-
<ng-container *ngTemplateOutlet="tabBar"></ng-container>
74+
<div class="flex bg-opacity-90" [style.background]="tabBarBg()">
75+
@for (tab of tabs; track tab.name; let i = $index) {
76+
<div class="px-4 py-1.5 text-xs" [ngStyle]="tabStyles(i === 0)">{{ tab.name }}</div>
77+
}
78+
</div>
8279
}
8380
</div>
84-
85-
<ng-template #tabBar>
86-
<div class="flex items-stretch overflow-hidden select-none" [ngStyle]="tabBarContainerStyles()">
87-
@switch (tabBarStyle()) {
88-
@case ('fade') {
89-
@for (tab of tabs; track tab.name; let i = $index; let first = $first) {
90-
@if (!first) { <div class="w-px" [style.background]="tabBarBg()"></div> }
91-
<div class="px-3 py-1.5 text-xs whitespace-nowrap flex items-center" [ngStyle]="fadeTabStyles(i === 0, i)">{{ tab.name }}</div>
92-
}
93-
<div class="flex-1" [style.background]="tabBarBg()"></div>
94-
}
95-
@case ('separator') {
96-
@for (tab of tabs; track tab.name; let i = $index; let first = $first) {
97-
@if (!first) {
98-
<div class="px-1 py-1.5 text-xs flex items-center opacity-50" [style.color]="inactiveTabColor()">{{ tabSeparator() }}</div>
99-
}
100-
<div class="px-3 py-1.5 text-xs whitespace-nowrap flex items-center" [ngStyle]="simpleTabStyles(i === 0)">{{ tab.name }}</div>
101-
}
102-
<div class="flex-1" [style.background]="tabBarBg()"></div>
103-
}
104-
@case ('slant') {
105-
@for (tab of tabs; track tab.name; let i = $index) {
106-
<div class="px-3 py-1.5 text-xs whitespace-nowrap flex items-center relative" [ngStyle]="slantTabStyles(i === 0)">
107-
<span class="relative z-10">{{ tab.name }}</span>
108-
</div>
109-
}
110-
<div class="flex-1" [style.background]="tabBarBg()"></div>
111-
}
112-
@case ('powerline') {
113-
@for (tab of tabs; track tab.name; let i = $index) {
114-
<div class="flex items-center">
115-
<div class="px-3 py-1.5 text-xs whitespace-nowrap flex items-center" [ngStyle]="simpleTabStyles(i === 0)">
116-
{{ tab.name }}
117-
</div>
118-
@if (i < tabs.length - 1) {
119-
<svg width="14" height="24" viewBox="0 0 14 24" preserveAspectRatio="none" class="flex-shrink-0">
120-
<path d="M0,0 L14,12 L0,24 Z" [attr.fill]="tabBgColor(i === 0)"/>
121-
<path d="M0,0 L14,12 L0,24 L14,24 L14,0 Z" [attr.fill]="tabBgColor(false)"/>
122-
</svg>
123-
}
124-
</div>
125-
}
126-
<div class="flex-1" [style.background]="tabBarBg()"></div>
127-
}
128-
@default {
129-
@for (tab of tabs; track tab.name; let i = $index) {
130-
<div class="px-3 py-1.5 text-xs whitespace-nowrap flex items-center" [ngStyle]="simpleTabStyles(i === 0)">{{ tab.name }}</div>
131-
}
132-
<div class="flex-1" [style.background]="tabBarBg()"></div>
133-
}
134-
}
135-
</div>
136-
</ng-template>
13781
`,
13882
styles: [`
13983
.url-text { text-decoration: underline; cursor: pointer; }
@@ -162,7 +106,6 @@ export class TerminalWindowComponent {
162106
mouseCfg = computed(() => this.store.configState().mouse);
163107

164108
c(key: string): string { return (this.colors() as unknown as Record<string, string>)[key] || '#888888'; }
165-
166109
terminalFont = computed(() => { const f = this.fonts().font_family; return `"${f}", "JetBrains Mono", monospace`; });
167110

168111
windowStyles = computed(() => {
@@ -174,7 +117,6 @@ export class TerminalWindowComponent {
174117
titleBarBg = computed(() => this.darken(this.colors().background, 10));
175118
borderColor = computed(() => this.darken(this.colors().background, 20));
176119
tabBarBg = computed(() => { const cfg = this.tabBarCfg(); return (cfg.tab_bar_background && cfg.tab_bar_background !== 'none') ? cfg.tab_bar_background : this.darken(this.colors().background, 8); });
177-
178120
hideDecorations = computed(() => this.windowCfg().hide_window_decorations);
179121

180122
terminalStyles = computed(() => {
@@ -188,47 +130,18 @@ export class TerminalWindowComponent {
188130

189131
tabBarVisible = computed(() => { const cfg = this.tabBarCfg(); const minTabs = cfg.tab_bar_min_tabs ?? 2; return cfg.tab_bar_style !== 'hidden' && 3 >= minTabs; });
190132
tabBarEdge = computed(() => this.tabBarCfg().tab_bar_edge);
191-
tabBarStyle = computed(() => this.tabBarCfg().tab_bar_style);
192-
tabSeparator = computed(() => this.tabBarCfg().tab_separator || ' ┇');
193-
194-
tabBarContainerStyles = computed(() => ({ background: this.tabBarBg(), borderTop: this.tabBarEdge() === 'bottom' ? `1px solid ${this.borderColor()}` : 'none', borderBottom: this.tabBarEdge() === 'top' ? `1px solid ${this.borderColor()}` : 'none' }));
195-
inactiveTabColor = computed(() => this.tabBarCfg().inactive_tab_foreground);
196-
197-
simpleTabStyles(isActive: boolean): Record<string, string> {
198-
const cfg = this.tabBarCfg();
199-
return isActive ? { background: cfg.active_tab_background, color: cfg.active_tab_foreground, fontWeight: '600' } : { background: cfg.inactive_tab_background, color: cfg.inactive_tab_foreground };
200-
}
201-
202-
fadeTabStyles(isActive: boolean, index: number): Record<string, string> {
203-
const cfg = this.tabBarCfg();
204-
const fade = cfg.tab_fade || [0.25, 0.5, 0.75, 1];
205-
const fadeVal = isActive ? 1 : (fade[Math.min(index, fade.length - 1)] ?? 1);
206-
return isActive ? { background: cfg.active_tab_background, color: cfg.active_tab_foreground, fontWeight: '600' } : { background: cfg.inactive_tab_background, color: cfg.inactive_tab_foreground, opacity: String(fadeVal) };
207-
}
208-
209-
slantTabStyles(isActive: boolean): Record<string, string> {
210-
const cfg = this.tabBarCfg();
211-
return isActive ? { background: cfg.active_tab_background, color: cfg.active_tab_foreground, fontWeight: '600' } : { background: cfg.inactive_tab_background, color: cfg.inactive_tab_foreground };
212-
}
213133

214-
powerlineTabStyles(isActive: boolean): Record<string, string> {
134+
tabStyles(isActive: boolean): Record<string, string> {
215135
const cfg = this.tabBarCfg();
216-
const bg = isActive ? cfg.active_tab_background : cfg.inactive_tab_background;
217-
return { background: bg, color: isActive ? cfg.active_tab_foreground : cfg.inactive_tab_foreground, fontWeight: isActive ? '600' : '400', '--tab-bg': bg } as Record<string, string>;
218-
}
219-
220-
tabBgColor(isActive: boolean): string {
221-
const cfg = this.tabBarCfg();
222-
return isActive ? cfg.active_tab_background : cfg.inactive_tab_background;
136+
return isActive
137+
? { background: cfg.active_tab_background, color: cfg.active_tab_foreground, fontWeight: '600' }
138+
: { background: cfg.inactive_tab_background, color: cfg.inactive_tab_foreground };
223139
}
224140

225141
cursorShape = computed(() => this.cursorCfg().cursor_shape);
226142
cursorColor = computed(() => { const c = this.cursorCfg().cursor; return c === 'none' || !c?.startsWith('#') ? this.colors().foreground : c; });
227-
cursorTextColor = computed(() => { const c = this.cursorCfg().cursor_text_color; return c === 'background' ? this.colors().background : (c?.startsWith('#') ? c : this.colors().background); });
228143
cursorBlinking = computed(() => this.cursorCfg().cursor_blink_interval !== 0);
229-
230144
urlColor = computed(() => this.mouseCfg().url_color || this.colors().color4 || '#0087bd');
231-
urlUnderline = computed(() => { const style = this.mouseCfg().url_style; if (style === 'none') return 'none'; if (style === 'double') return 'underline double'; if (style === 'curly') return 'underline wavy'; if (style === 'dashed') return 'underline dashed'; if (style === 'dotted') return 'underline dotted'; return 'underline'; });
232145

233146
private parseBorderWidth(val: string): string { if (!val || val === '0' || val === '0pt' || val === '0px') return '0px'; if (val.endsWith('pt')) { const n = Number.parseFloat(val); return `${Math.max(0, Math.round(n * 1.333))}px`; } return val.endsWith('px') ? val : '1px'; }
234147
private darken(hex: string, amount: number): string { if (!hex?.startsWith('#')) return '#333'; const c = hex.slice(1); if (c.length !== 6) return hex; const r = Math.max(0, Math.min(255, Number.parseInt(c.slice(0, 2), 16) - amount)); const g = Math.max(0, Math.min(255, Number.parseInt(c.slice(2, 4), 16) - amount)); const b = Math.max(0, Math.min(255, Number.parseInt(c.slice(4, 6), 16) - amount)); return `#${r.toString(16).padStart(2,'0')}${g.toString(16).padStart(2,'0')}${b.toString(16).padStart(2,'0')}`; }

0 commit comments

Comments
 (0)