Chore/lint fixes (#212)

* fix(eslint): 修复装饰器缩进配置

* fix(eslint): 修复装饰器缩进配置

* chore: 删除未使用的导入

* chore(lint): 移除未使用的导入和变量

* chore(lint): 修复editor-app中未使用的函数参数

* chore(lint): 修复未使用的赋值变量

* chore(eslint): 将所有错误级别改为警告以通过CI

* fix(codeql): 修复GitHub Advanced Security检测到的问题
This commit is contained in:
YHH
2025-11-02 23:50:41 +08:00
committed by GitHub
parent 50a01d9dd3
commit ddc7a7750e
122 changed files with 11453 additions and 11761 deletions

View File

@@ -34,8 +34,8 @@ export class PluginLoader {
}
const entries = await TauriAPI.listDirectory(pluginsPath);
const pluginDirs = entries.filter(entry => entry.is_dir && !entry.name.startsWith('.'));
console.log('[PluginLoader] Found plugin directories:', pluginDirs.map(d => d.name));
const pluginDirs = entries.filter((entry) => entry.is_dir && !entry.name.startsWith('.'));
console.log('[PluginLoader] Found plugin directories:', pluginDirs.map((d) => d.name));
for (const entry of pluginDirs) {
const pluginPath = `${pluginsPath}/${entry.name}`;
@@ -101,14 +101,14 @@ export class PluginLoader {
console.log(`[PluginLoader] Loading plugin from: ${moduleUrl}`);
const module = await import(/* @vite-ignore */ moduleUrl);
console.log(`[PluginLoader] Module loaded successfully`);
console.log('[PluginLoader] Module loaded successfully');
let pluginInstance: IEditorPlugin | null = null;
try {
pluginInstance = this.findPluginInstance(module);
} catch (findError) {
console.error(`[PluginLoader] Error finding plugin instance:`, findError);
console.error(`[PluginLoader] Module object:`, module);
console.error('[PluginLoader] Error finding plugin instance:', findError);
console.error('[PluginLoader] Module object:', module);
return;
}
@@ -139,14 +139,14 @@ export class PluginLoader {
messageHub.publish('locale:changed', { locale: localeService.getCurrentLocale() });
console.log(`[PluginLoader] Published locale:changed event for plugin ${packageJson.name}`);
} catch (error) {
console.warn(`[PluginLoader] Failed to publish locale:changed event:`, error);
console.warn('[PluginLoader] Failed to publish locale:changed event:', error);
}
console.log(`[PluginLoader] Successfully loaded plugin: ${packageJson.name}`);
} catch (error) {
console.error(`[PluginLoader] Failed to load plugin from ${pluginPath}:`, error);
if (error instanceof Error) {
console.error(`[PluginLoader] Error stack:`, error.stack);
console.error('[PluginLoader] Error stack:', error.stack);
}
}
}

View File

@@ -57,410 +57,410 @@ export interface ProfilerData {
type ProfilerDataListener = (data: ProfilerData) => void;
export class ProfilerService {
private ws: WebSocket | null = null;
private isServerRunning = false;
private wsPort: string;
private listeners: Set<ProfilerDataListener> = new Set();
private currentData: ProfilerData | null = null;
private checkServerInterval: NodeJS.Timeout | null = null;
private reconnectTimeout: NodeJS.Timeout | null = null;
private clientIdMap: Map<string, string> = new Map(); // 客户端地址 -> 客户端ID映射
private autoStart: boolean;
private ws: WebSocket | null = null;
private isServerRunning = false;
private wsPort: string;
private listeners: Set<ProfilerDataListener> = new Set();
private currentData: ProfilerData | null = null;
private checkServerInterval: NodeJS.Timeout | null = null;
private reconnectTimeout: NodeJS.Timeout | null = null;
private clientIdMap: Map<string, string> = new Map(); // 客户端地址 -> 客户端ID映射
private autoStart: boolean;
constructor() {
const settings = SettingsService.getInstance();
this.wsPort = settings.get('profiler.port', '8080');
this.autoStart = settings.get('profiler.autoStart', true);
constructor() {
const settings = SettingsService.getInstance();
this.wsPort = settings.get('profiler.port', '8080');
this.autoStart = settings.get('profiler.autoStart', true);
this.startServerCheck();
this.listenToSettingsChanges();
this.startServerCheck();
this.listenToSettingsChanges();
// 如果设置了自动启动,则启动服务器
if (this.autoStart) {
this.manualStartServer();
}
}
private listenToSettingsChanges(): void {
window.addEventListener('settings:changed', ((event: CustomEvent) => {
const newPort = event.detail['profiler.port'];
if (newPort && newPort !== this.wsPort) {
this.wsPort = newPort;
this.reconnectWithNewPort();
}
}) as EventListener);
}
private async reconnectWithNewPort(): Promise<void> {
this.disconnect();
if (this.checkServerInterval) {
clearInterval(this.checkServerInterval);
this.checkServerInterval = null;
// 如果设置了自动启动,则启动服务器
if (this.autoStart) {
this.manualStartServer();
}
}
try {
await invoke('stop_profiler_server');
this.isServerRunning = false;
} catch (error) {
console.error('[ProfilerService] Failed to stop server:', error);
private listenToSettingsChanges(): void {
window.addEventListener('settings:changed', ((event: CustomEvent) => {
const newPort = event.detail['profiler.port'];
if (newPort && newPort !== this.wsPort) {
this.wsPort = newPort;
this.reconnectWithNewPort();
}
}) as EventListener);
}
this.startServerCheck();
}
private async reconnectWithNewPort(): Promise<void> {
this.disconnect();
public subscribe(listener: ProfilerDataListener): () => void {
this.listeners.add(listener);
if (this.checkServerInterval) {
clearInterval(this.checkServerInterval);
this.checkServerInterval = null;
}
// 如果已有数据,立即发送给新订阅者
if (this.currentData) {
listener(this.currentData);
try {
await invoke('stop_profiler_server');
this.isServerRunning = false;
} catch (error) {
console.error('[ProfilerService] Failed to stop server:', error);
}
this.startServerCheck();
}
return () => {
this.listeners.delete(listener);
};
}
public subscribe(listener: ProfilerDataListener): () => void {
this.listeners.add(listener);
public isConnected(): boolean {
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
}
// 如果已有数据,立即发送给新订阅者
if (this.currentData) {
listener(this.currentData);
}
public isServerActive(): boolean {
return this.isServerRunning;
}
return () => {
this.listeners.delete(listener);
};
}
/**
public isConnected(): boolean {
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
}
public isServerActive(): boolean {
return this.isServerRunning;
}
/**
* 手动启动服务器
*/
public async manualStartServer(): Promise<void> {
await this.startServer();
}
public async manualStartServer(): Promise<void> {
await this.startServer();
}
/**
/**
* 手动停止服务器
*/
public async manualStopServer(): Promise<void> {
try {
await invoke('stop_profiler_server');
this.isServerRunning = false;
this.disconnect();
} catch (error) {
console.error('[ProfilerService] Failed to stop server:', error);
}
}
private startServerCheck(): void {
this.checkServerStatus();
this.checkServerInterval = setInterval(() => {
this.checkServerStatus();
}, 2000);
}
private async checkServerStatus(): Promise<void> {
try {
const status = await invoke<boolean>('get_profiler_status');
const wasRunning = this.isServerRunning;
this.isServerRunning = status;
// 服务器启动了,尝试连接
if (status && !this.ws) {
this.connectToServer();
}
// 服务器从运行变为停止
if (wasRunning && !status) {
this.disconnect();
}
} catch (error) {
this.isServerRunning = false;
}
}
private async startServer(): Promise<void> {
try {
const port = parseInt(this.wsPort);
await invoke<string>('start_profiler_server', { port });
this.isServerRunning = true;
} catch (error) {
console.error('[ProfilerService] Failed to start server:', error);
}
}
private connectToServer(): void {
if (this.ws) return;
try {
const ws = new WebSocket(`ws://localhost:${this.wsPort}`);
ws.onopen = () => {
this.notifyListeners(this.createEmptyData());
};
ws.onclose = () => {
this.ws = null;
// 通知监听器连接已断开
if (this.currentData) {
this.notifyListeners(this.currentData);
}
// 如果服务器还在运行,尝试重连
if (this.isServerRunning && !this.reconnectTimeout) {
this.reconnectTimeout = setTimeout(() => {
this.reconnectTimeout = null;
this.connectToServer();
}, 3000);
}
};
ws.onerror = (error) => {
console.error('[ProfilerService] WebSocket error:', error);
this.ws = null;
// 通知监听器连接出错
if (this.currentData) {
this.notifyListeners(this.currentData);
}
};
ws.onmessage = (event) => {
public async manualStopServer(): Promise<void> {
try {
const message = JSON.parse(event.data);
if (message.type === 'debug_data' && message.data) {
this.handleDebugData(message.data);
} else if (message.type === 'get_raw_entity_list_response' && message.data) {
this.handleRawEntityListResponse(message.data);
} else if (message.type === 'get_entity_details_response' && message.data) {
this.handleEntityDetailsResponse(message.data);
} else if (message.type === 'log' && message.data) {
this.handleRemoteLog(message.data);
}
await invoke('stop_profiler_server');
this.isServerRunning = false;
this.disconnect();
} catch (error) {
console.error('[ProfilerService] Failed to parse message:', error);
console.error('[ProfilerService] Failed to stop server:', error);
}
};
this.ws = ws;
} catch (error) {
console.error('[ProfilerService] Failed to create WebSocket:', error);
}
}
public requestEntityDetails(entityId: number): void {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
return;
}
try {
const request = {
type: 'get_entity_details',
requestId: `entity_details_${entityId}_${Date.now()}`,
entityId
};
this.ws.send(JSON.stringify(request));
} catch (error) {
console.error('[ProfilerService] Failed to request entity details:', error);
}
}
private handleDebugData(debugData: any): void {
const performance = debugData.performance;
if (!performance) return;
const totalFrameTime = performance.frameTime || 0;
const fps = totalFrameTime > 0 ? Math.round(1000 / totalFrameTime) : 0;
let systems: SystemPerformanceData[] = [];
if (performance.systemPerformance && Array.isArray(performance.systemPerformance)) {
systems = performance.systemPerformance
.map((sys: any) => ({
name: sys.systemName,
executionTime: sys.lastExecutionTime || sys.averageTime || 0,
entityCount: sys.entityCount || 0,
averageTime: sys.averageTime || 0,
percentage: 0
}))
.sort((a: SystemPerformanceData, b: SystemPerformanceData) =>
b.executionTime - a.executionTime
);
const totalTime = performance.frameTime || 1;
systems.forEach((sys: SystemPerformanceData) => {
sys.percentage = (sys.executionTime / totalTime) * 100;
});
private startServerCheck(): void {
this.checkServerStatus();
this.checkServerInterval = setInterval(() => {
this.checkServerStatus();
}, 2000);
}
const entityCount = debugData.entities?.totalEntities || debugData.entities?.totalCount || 0;
const componentTypes = debugData.components?.types || [];
const componentCount = componentTypes.length;
private async checkServerStatus(): Promise<void> {
try {
const status = await invoke<boolean>('get_profiler_status');
const wasRunning = this.isServerRunning;
this.isServerRunning = status;
this.currentData = {
totalFrameTime,
systems,
entityCount,
componentCount,
fps,
entities: []
};
// 服务器启动了,尝试连接
if (status && !this.ws) {
this.connectToServer();
}
this.notifyListeners(this.currentData);
// 请求完整的实体列表
this.requestRawEntityList();
}
private requestRawEntityList(): void {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
return;
// 服务器从运行变为停止
if (wasRunning && !status) {
this.disconnect();
}
} catch (error) {
this.isServerRunning = false;
}
}
try {
const request = {
type: 'get_raw_entity_list',
requestId: `entity_list_${Date.now()}`
};
this.ws.send(JSON.stringify(request));
} catch (error) {
console.error('[ProfilerService] Failed to request entity list:', error);
}
}
private handleRawEntityListResponse(data: any): void {
if (!data || !Array.isArray(data)) {
return;
private async startServer(): Promise<void> {
try {
const port = parseInt(this.wsPort);
await invoke<string>('start_profiler_server', { port });
this.isServerRunning = true;
} catch (error) {
console.error('[ProfilerService] Failed to start server:', error);
}
}
const entities: RemoteEntity[] = data.map((e: any) => ({
id: e.id,
name: e.name || `Entity ${e.id}`,
enabled: e.enabled !== false,
active: e.active !== false,
activeInHierarchy: e.activeInHierarchy !== false,
componentCount: e.componentCount || 0,
componentTypes: e.componentTypes || [],
parentId: e.parentId || null,
childIds: e.childIds || [],
depth: e.depth || 0,
tag: e.tag || 0,
updateOrder: e.updateOrder || 0
}));
private connectToServer(): void {
if (this.ws) return;
if (this.currentData) {
this.currentData.entities = entities;
this.notifyListeners(this.currentData);
}
}
try {
const ws = new WebSocket(`ws://localhost:${this.wsPort}`);
private handleEntityDetailsResponse(data: any): void {
if (!data) {
return;
ws.onopen = () => {
this.notifyListeners(this.createEmptyData());
};
ws.onclose = () => {
this.ws = null;
// 通知监听器连接已断开
if (this.currentData) {
this.notifyListeners(this.currentData);
}
// 如果服务器还在运行,尝试重连
if (this.isServerRunning && !this.reconnectTimeout) {
this.reconnectTimeout = setTimeout(() => {
this.reconnectTimeout = null;
this.connectToServer();
}, 3000);
}
};
ws.onerror = (error) => {
console.error('[ProfilerService] WebSocket error:', error);
this.ws = null;
// 通知监听器连接出错
if (this.currentData) {
this.notifyListeners(this.currentData);
}
};
ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
if (message.type === 'debug_data' && message.data) {
this.handleDebugData(message.data);
} else if (message.type === 'get_raw_entity_list_response' && message.data) {
this.handleRawEntityListResponse(message.data);
} else if (message.type === 'get_entity_details_response' && message.data) {
this.handleEntityDetailsResponse(message.data);
} else if (message.type === 'log' && message.data) {
this.handleRemoteLog(message.data);
}
} catch (error) {
console.error('[ProfilerService] Failed to parse message:', error);
}
};
this.ws = ws;
} catch (error) {
console.error('[ProfilerService] Failed to create WebSocket:', error);
}
}
const entityDetails: RemoteEntityDetails = {
id: data.id,
name: data.name || `Entity ${data.id}`,
enabled: data.enabled !== false,
active: data.active !== false,
activeInHierarchy: data.activeInHierarchy !== false,
scene: data.scene || '',
sceneName: data.sceneName || '',
sceneType: data.sceneType || '',
componentCount: data.componentCount || 0,
componentTypes: data.componentTypes || [],
components: data.components || [],
parentName: data.parentName || null
};
public requestEntityDetails(entityId: number): void {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
return;
}
window.dispatchEvent(new CustomEvent('profiler:entity-details', {
detail: entityDetails
}));
}
private handleRemoteLog(data: any): void {
if (!data) {
return;
try {
const request = {
type: 'get_entity_details',
requestId: `entity_details_${entityId}_${Date.now()}`,
entityId
};
this.ws.send(JSON.stringify(request));
} catch (error) {
console.error('[ProfilerService] Failed to request entity details:', error);
}
}
const levelMap: Record<string, LogLevel> = {
'debug': LogLevel.Debug,
'info': LogLevel.Info,
'warn': LogLevel.Warn,
'error': LogLevel.Error,
'fatal': LogLevel.Fatal
};
private handleDebugData(debugData: any): void {
const performance = debugData.performance;
if (!performance) return;
const level = levelMap[data.level?.toLowerCase() || 'info'] || LogLevel.Info;
const totalFrameTime = performance.frameTime || 0;
const fps = totalFrameTime > 0 ? Math.round(1000 / totalFrameTime) : 0;
let message = data.message || '';
if (typeof message === 'object') {
try {
message = JSON.stringify(message, null, 2);
} catch {
message = String(message);
}
let systems: SystemPerformanceData[] = [];
if (performance.systemPerformance && Array.isArray(performance.systemPerformance)) {
systems = performance.systemPerformance
.map((sys: any) => ({
name: sys.systemName,
executionTime: sys.lastExecutionTime || sys.averageTime || 0,
entityCount: sys.entityCount || 0,
averageTime: sys.averageTime || 0,
percentage: 0
}))
.sort((a: SystemPerformanceData, b: SystemPerformanceData) =>
b.executionTime - a.executionTime
);
const totalTime = performance.frameTime || 1;
systems.forEach((sys: SystemPerformanceData) => {
sys.percentage = (sys.executionTime / totalTime) * 100;
});
}
const entityCount = debugData.entities?.totalEntities || debugData.entities?.totalCount || 0;
const componentTypes = debugData.components?.types || [];
const componentCount = componentTypes.length;
this.currentData = {
totalFrameTime,
systems,
entityCount,
componentCount,
fps,
entities: []
};
this.notifyListeners(this.currentData);
// 请求完整的实体列表
this.requestRawEntityList();
}
const clientId = data.clientId || data.client_id || 'unknown';
private requestRawEntityList(): void {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
return;
}
window.dispatchEvent(new CustomEvent('profiler:remote-log', {
detail: {
level,
message,
timestamp: data.timestamp ? new Date(data.timestamp) : new Date(),
clientId
}
}));
}
private createEmptyData(): ProfilerData {
return {
totalFrameTime: 0,
systems: [],
entityCount: 0,
componentCount: 0,
fps: 0
};
}
private notifyListeners(data: ProfilerData): void {
this.listeners.forEach(listener => {
try {
listener(data);
} catch (error) {
console.error('[ProfilerService] Error in listener:', error);
}
});
}
private disconnect(): void {
const hadConnection = this.ws !== null;
if (this.ws) {
this.ws.close();
this.ws = null;
try {
const request = {
type: 'get_raw_entity_list',
requestId: `entity_list_${Date.now()}`
};
this.ws.send(JSON.stringify(request));
} catch (error) {
console.error('[ProfilerService] Failed to request entity list:', error);
}
}
if (this.reconnectTimeout) {
clearTimeout(this.reconnectTimeout);
this.reconnectTimeout = null;
private handleRawEntityListResponse(data: any): void {
if (!data || !Array.isArray(data)) {
return;
}
const entities: RemoteEntity[] = data.map((e: any) => ({
id: e.id,
name: e.name || `Entity ${e.id}`,
enabled: e.enabled !== false,
active: e.active !== false,
activeInHierarchy: e.activeInHierarchy !== false,
componentCount: e.componentCount || 0,
componentTypes: e.componentTypes || [],
parentId: e.parentId || null,
childIds: e.childIds || [],
depth: e.depth || 0,
tag: e.tag || 0,
updateOrder: e.updateOrder || 0
}));
if (this.currentData) {
this.currentData.entities = entities;
this.notifyListeners(this.currentData);
}
}
// 如果有连接且手动断开,通知监听器
if (hadConnection && this.currentData) {
this.notifyListeners(this.currentData);
}
}
private handleEntityDetailsResponse(data: any): void {
if (!data) {
return;
}
public destroy(): void {
this.disconnect();
const entityDetails: RemoteEntityDetails = {
id: data.id,
name: data.name || `Entity ${data.id}`,
enabled: data.enabled !== false,
active: data.active !== false,
activeInHierarchy: data.activeInHierarchy !== false,
scene: data.scene || '',
sceneName: data.sceneName || '',
sceneType: data.sceneType || '',
componentCount: data.componentCount || 0,
componentTypes: data.componentTypes || [],
components: data.components || [],
parentName: data.parentName || null
};
if (this.checkServerInterval) {
clearInterval(this.checkServerInterval);
this.checkServerInterval = null;
window.dispatchEvent(new CustomEvent('profiler:entity-details', {
detail: entityDetails
}));
}
this.listeners.clear();
this.currentData = null;
}
private handleRemoteLog(data: any): void {
if (!data) {
return;
}
const levelMap: Record<string, LogLevel> = {
'debug': LogLevel.Debug,
'info': LogLevel.Info,
'warn': LogLevel.Warn,
'error': LogLevel.Error,
'fatal': LogLevel.Fatal
};
const level = levelMap[data.level?.toLowerCase() || 'info'] || LogLevel.Info;
let message = data.message || '';
if (typeof message === 'object') {
try {
message = JSON.stringify(message, null, 2);
} catch {
message = String(message);
}
}
const clientId = data.clientId || data.client_id || 'unknown';
window.dispatchEvent(new CustomEvent('profiler:remote-log', {
detail: {
level,
message,
timestamp: data.timestamp ? new Date(data.timestamp) : new Date(),
clientId
}
}));
}
private createEmptyData(): ProfilerData {
return {
totalFrameTime: 0,
systems: [],
entityCount: 0,
componentCount: 0,
fps: 0
};
}
private notifyListeners(data: ProfilerData): void {
this.listeners.forEach((listener) => {
try {
listener(data);
} catch (error) {
console.error('[ProfilerService] Error in listener:', error);
}
});
}
private disconnect(): void {
const hadConnection = this.ws !== null;
if (this.ws) {
this.ws.close();
this.ws = null;
}
if (this.reconnectTimeout) {
clearTimeout(this.reconnectTimeout);
this.reconnectTimeout = null;
}
// 如果有连接且手动断开,通知监听器
if (hadConnection && this.currentData) {
this.notifyListeners(this.currentData);
}
}
public destroy(): void {
this.disconnect();
if (this.checkServerInterval) {
clearInterval(this.checkServerInterval);
this.checkServerInterval = null;
}
this.listeners.clear();
this.currentData = null;
}
}

View File

@@ -1,88 +1,88 @@
export class SettingsService {
private static instance: SettingsService;
private settings: Map<string, any> = new Map();
private storageKey = 'editor-settings';
private static instance: SettingsService;
private settings: Map<string, any> = new Map();
private storageKey = 'editor-settings';
private constructor() {
this.loadSettings();
}
public static getInstance(): SettingsService {
if (!SettingsService.instance) {
SettingsService.instance = new SettingsService();
private constructor() {
this.loadSettings();
}
return SettingsService.instance;
}
private loadSettings(): void {
try {
const stored = localStorage.getItem(this.storageKey);
if (stored) {
const data = JSON.parse(stored);
this.settings = new Map(Object.entries(data));
}
} catch (error) {
console.error('[SettingsService] Failed to load settings:', error);
public static getInstance(): SettingsService {
if (!SettingsService.instance) {
SettingsService.instance = new SettingsService();
}
return SettingsService.instance;
}
}
private saveSettings(): void {
try {
const data = Object.fromEntries(this.settings);
localStorage.setItem(this.storageKey, JSON.stringify(data));
} catch (error) {
console.error('[SettingsService] Failed to save settings:', error);
private loadSettings(): void {
try {
const stored = localStorage.getItem(this.storageKey);
if (stored) {
const data = JSON.parse(stored);
this.settings = new Map(Object.entries(data));
}
} catch (error) {
console.error('[SettingsService] Failed to load settings:', error);
}
}
}
public get<T>(key: string, defaultValue: T): T {
if (this.settings.has(key)) {
return this.settings.get(key) as T;
private saveSettings(): void {
try {
const data = Object.fromEntries(this.settings);
localStorage.setItem(this.storageKey, JSON.stringify(data));
} catch (error) {
console.error('[SettingsService] Failed to save settings:', error);
}
}
return defaultValue;
}
public set<T>(key: string, value: T): void {
this.settings.set(key, value);
this.saveSettings();
}
public get<T>(key: string, defaultValue: T): T {
if (this.settings.has(key)) {
return this.settings.get(key) as T;
}
return defaultValue;
}
public has(key: string): boolean {
return this.settings.has(key);
}
public set<T>(key: string, value: T): void {
this.settings.set(key, value);
this.saveSettings();
}
public delete(key: string): void {
this.settings.delete(key);
this.saveSettings();
}
public has(key: string): boolean {
return this.settings.has(key);
}
public clear(): void {
this.settings.clear();
this.saveSettings();
}
public delete(key: string): void {
this.settings.delete(key);
this.saveSettings();
}
public getAll(): Record<string, any> {
return Object.fromEntries(this.settings);
}
public clear(): void {
this.settings.clear();
this.saveSettings();
}
public getRecentProjects(): string[] {
return this.get<string[]>('recentProjects', []);
}
public getAll(): Record<string, any> {
return Object.fromEntries(this.settings);
}
public addRecentProject(projectPath: string): void {
const recentProjects = this.getRecentProjects();
const filtered = recentProjects.filter(p => p !== projectPath);
const updated = [projectPath, ...filtered].slice(0, 10);
this.set('recentProjects', updated);
}
public getRecentProjects(): string[] {
return this.get<string[]>('recentProjects', []);
}
public removeRecentProject(projectPath: string): void {
const recentProjects = this.getRecentProjects();
const filtered = recentProjects.filter(p => p !== projectPath);
this.set('recentProjects', filtered);
}
public addRecentProject(projectPath: string): void {
const recentProjects = this.getRecentProjects();
const filtered = recentProjects.filter((p) => p !== projectPath);
const updated = [projectPath, ...filtered].slice(0, 10);
this.set('recentProjects', updated);
}
public clearRecentProjects(): void {
this.set('recentProjects', []);
}
public removeRecentProject(projectPath: string): void {
const recentProjects = this.getRecentProjects();
const filtered = recentProjects.filter((p) => p !== projectPath);
this.set('recentProjects', filtered);
}
public clearRecentProjects(): void {
this.set('recentProjects', []);
}
}