Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { ToastService } from './core/services/toast.service';
import { AppNetworkInitService, IBootstrapIssue } from './core/services/app-initNetwork.service';
import { SsoRedirectService } from './core/services/sso-redirect.service';
import { DashboardHistorySeriesSyncService } from './core/services/dashboard-history-series-sync.service';
import { Title } from '@angular/platform-browser';
import { resolveBrowserTabTitle } from './core/utils/browser-tab-title.util';

@Component({
selector: 'app-root',
Expand Down Expand Up @@ -64,6 +66,8 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
private readonly _uiEvent = inject(uiEventService);
private readonly _dialog = inject(DialogService);
public readonly settings = inject(SettingsService);
private readonly _titleService = inject(Title);
private readonly _browserTabTitle = toSignal(this.settings.getBrowserTabTitleAsO(), { initialValue: 'SKip' });
private readonly _responsive = inject(BreakpointObserver);
private readonly _destroyRef = inject(DestroyRef);
private readonly _notificationOverlay = inject(NotificationOverlayService);
Expand Down Expand Up @@ -92,6 +96,11 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
private readonly _hotkeyHandler = (key: string) => this.handleKeyDown(key);

constructor() {
// Keep the browser tab title (document.title) in sync with the user setting (#1055).
effect(() => {
this._titleService.setTitle(resolveBrowserTabTitle(this._browserTabTitle()));
});

effect(() => {
if (this.settings.configUpgrade()) {
const liveVersion = this.settings.getConfigVersion();
Expand Down
20 changes: 20 additions & 0 deletions src/app/core/components/options/display/display.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@
Enable light theme for brighter display settings.
</mat-slide-toggle>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Browser Tab
</mat-panel-title>
@if (!isPhonePortrait().matches) {
<mat-panel-description>
Set the browser tab title to tell multiple SKip instances apart.
</mat-panel-description>
}
</mat-expansion-panel-header>
<mat-form-field class="optional-field">
<mat-label>Browser tab title</mat-label>
<input matInput
(ngModelChange)="displayForm.form.markAsDirty()"
name="browserTabTitle"
placeholder="Ex. Mast-SKip"
[(ngModel)]="browserTabTitle">
</mat-form-field>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Expand Down
3 changes: 3 additions & 0 deletions src/app/core/components/options/display/display.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class SettingsDisplayComponent implements OnInit {
protected isLightTheme = model<boolean>(false);
protected isRemoteControl = model<boolean>(false);
protected instanceName = model<string>('');
protected browserTabTitle = model<string>('SKip');
protected splitShellEnabled = model<boolean>(false);
protected splitShellSide = model<'left' | 'right'>('left');
protected splitShellSwipeDisabled = model<boolean>(false);
Expand All @@ -78,6 +79,7 @@ export class SettingsDisplayComponent implements OnInit {
this.isRedNightMode.set(this.settings.getRedNightMode());
this.isRemoteControl.set(this.settings.getIsRemoteControl());
this.instanceName.set(this.settings.getInstanceName());
this.browserTabTitle.set(this.settings.getBrowserTabTitle());
this.splitShellEnabled.set(this.settings.getSplitShellEnabled());
this.splitShellSide.set(this.settings.getSplitShellSide());
this.splitShellSwipeDisabled.set(this.settings.getSplitShellSwipeDisabled());
Expand Down Expand Up @@ -127,6 +129,7 @@ export class SettingsDisplayComponent implements OnInit {
this.settings.setSplitShellSide(this.splitShellSide());
this.settings.setSplitShellSwipeDisabled(this.splitShellSwipeDisabled());
this.settings.setWidgetHistoryDisabled(this.widgetHistoryDisabled());
this.settings.setBrowserTabTitle(this.browserTabTitle());
this.settings.setDisablePathValidation(this.isPathValidationDisabled());
// Await the server write; setKipPluginConfig() returns a Promise, so the old
// `if (!this.setKipPluginConfig())` was always false (a Promise is truthy) —
Expand Down
1 change: 1 addition & 0 deletions src/app/core/interfaces/app-settings.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface IAppConfig {
splitShellSwipeDisabled?: boolean;
splitShellWidth?: number;
widgetHistoryDisabled?: boolean;
browserTabTitle?: string;
}

export interface IThemeConfig {
Expand Down
30 changes: 29 additions & 1 deletion src/app/core/services/settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class SettingsService {
private nightModeBrightness: BehaviorSubject<number> = new BehaviorSubject<number>(1);
private isRemoteControl: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private instanceName: BehaviorSubject<string> = new BehaviorSubject<string>('');
private browserTabTitle: BehaviorSubject<string> = new BehaviorSubject<string>('SKip');
private splitShellEnabled: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private splitShellSide: BehaviorSubject<'left' | 'right'> = new BehaviorSubject<'left' | 'right'>('left');
private splitShellSwipeDisabled: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
Expand Down Expand Up @@ -248,6 +249,12 @@ export class SettingsService {
this._dashboards = this.activeConfig.dashboards;
}

if (this.activeConfig.app.browserTabTitle === undefined) {
this.browserTabTitle.next('SKip');
} else {
this.browserTabTitle.next(this.activeConfig.app.browserTabTitle);
}

if (this.activeConfig.app.splitShellEnabled === undefined) {
this.setSplitShellEnabled(false);
} else {
Expand Down Expand Up @@ -457,6 +464,26 @@ export class SettingsService {
this.saveConnectionConfigToLocalStorage();
}

// Browser tab title (document.title)
public getBrowserTabTitleAsO() {
return this.browserTabTitle.asObservable();
}

public getBrowserTabTitle(): string {
return this.browserTabTitle.getValue();
}

public setBrowserTabTitle(title: string) {
this.browserTabTitle.next(title);
const appConf = this.buildAppStorageObject();

if (this.useSharedConfig) {
this.storage.patchConfig('IAppConfig', appConf);
} else {
this.saveAppConfigToLocalStorage();
}
}

public getDisablePathValidation(): boolean {
return this.disablePathValidation;
}
Expand Down Expand Up @@ -726,7 +753,8 @@ export class SettingsService {
splitShellSide: this.splitShellSide.getValue() ?? 'right',
splitShellWidth: this.splitShellWidth.getValue() ?? 0.5,
splitShellSwipeDisabled: this.splitShellSwipeDisabled.getValue(),
widgetHistoryDisabled: this.widgetHistoryDisabled.getValue()
widgetHistoryDisabled: this.widgetHistoryDisabled.getValue(),
browserTabTitle: this.browserTabTitle.getValue()
}
return storageObject;
}
Expand Down
16 changes: 16 additions & 0 deletions src/app/core/utils/browser-tab-title.util.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, it, expect } from 'vitest';
import { resolveBrowserTabTitle } from './browser-tab-title.util';

describe('resolveBrowserTabTitle (#1055)', () => {
it('defaults to SKip when the value is missing or blank', () => {
expect(resolveBrowserTabTitle(undefined)).toBe('SKip');
expect(resolveBrowserTabTitle(null)).toBe('SKip');
expect(resolveBrowserTabTitle('')).toBe('SKip');
expect(resolveBrowserTabTitle(' ')).toBe('SKip');
});

it('uses the trimmed configured value when set', () => {
expect(resolveBrowserTabTitle('Mast-SKip')).toBe('Mast-SKip');
expect(resolveBrowserTabTitle(' Port Engine ')).toBe('Port Engine');
});
});
8 changes: 8 additions & 0 deletions src/app/core/utils/browser-tab-title.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Resolves the browser tab title (document.title) from the user-configured value.
* Falls back to 'SKip' when the value is empty/whitespace so the tab is never blank (#1055).
*/
export function resolveBrowserTabTitle(value?: string | null): string {
const trimmed = (value ?? '').trim();
return trimmed.length ? trimmed : 'SKip';
}
3 changes: 2 additions & 1 deletion src/default-config/config.blank.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export const DefaultAppConfig: IAppConfig = {
"splitShellEnabled": true,
"splitShellSide": "left",
"splitShellSwipeDisabled": false,
"splitShellWidth": 0.5
"splitShellWidth": 0.5,
"browserTabTitle": "SKip"
}

export const DefaultThemeConfig: IThemeConfig = {
Expand Down
Loading