- Introduced a comprehensive FAQ section in the README to address common user queries regarding app installation, code handling, agent communication, and project management. - Enhanced cross-platform keyboard shortcut handling in the Electron app for better user experience on macOS and Windows/Linux. - Updated signal handling in the standalone process to ensure proper shutdown behavior across platforms. - Improved WSL user resolution logic to support default user retrieval for better compatibility. - Enhanced notification handling to support cross-platform features and improve user feedback. - Refactored SSH connection management to include additional key file types and improve authentication handling. - Updated team management services to ensure consistent process termination across platforms. - Improved project path handling in team provisioning to accommodate different operating systems. - Enhanced editor components to utilize shared utility functions for path management, improving code maintainability.
99 lines
2.9 KiB
TypeScript
99 lines
2.9 KiB
TypeScript
/**
|
|
* Tab label disambiguation — adds suffix labels when multiple tabs share the same file name.
|
|
*
|
|
* Algorithm:
|
|
* 1. Group tabs by fileName
|
|
* 2. For groups with >1 tab, find the minimal unique path suffix
|
|
* 3. Format as "(parent/dir)" — e.g. "(main/utils)", "(renderer/hooks)"
|
|
* 4. Unique file names get no label (disambiguatedLabel = undefined)
|
|
*/
|
|
|
|
import { splitPath } from '@shared/utils/platformPath';
|
|
|
|
import type { EditorFileTab } from '@shared/types/editor';
|
|
|
|
/**
|
|
* Compute disambiguated labels for all tabs.
|
|
* Returns a new array with `disambiguatedLabel` set where needed.
|
|
*/
|
|
export function computeDisambiguatedTabs(tabs: EditorFileTab[]): EditorFileTab[] {
|
|
if (tabs.length === 0) return tabs;
|
|
|
|
// Single tab — just clear any stale label
|
|
if (tabs.length === 1) {
|
|
const tab = tabs[0];
|
|
if (tab.disambiguatedLabel === undefined) return tabs;
|
|
return [{ ...tab, disambiguatedLabel: undefined }];
|
|
}
|
|
|
|
// Group tabs by fileName
|
|
const groups = new Map<string, EditorFileTab[]>();
|
|
for (const tab of tabs) {
|
|
const existing = groups.get(tab.fileName);
|
|
if (existing) {
|
|
existing.push(tab);
|
|
} else {
|
|
groups.set(tab.fileName, [tab]);
|
|
}
|
|
}
|
|
|
|
// Build a map of tabId → disambiguatedLabel
|
|
const labels = new Map<string, string | undefined>();
|
|
|
|
for (const [, group] of groups) {
|
|
if (group.length <= 1) {
|
|
// Unique name — no label needed
|
|
for (const tab of group) {
|
|
labels.set(tab.id, undefined);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Split paths into segments for comparison
|
|
const pathSegments = group.map((tab) => {
|
|
const parts = splitPath(tab.filePath);
|
|
// Remove the file name (last segment)
|
|
parts.pop();
|
|
return parts;
|
|
});
|
|
|
|
// Find minimal unique suffix depth
|
|
// Start from depth=1 (immediate parent) and go deeper until all labels are unique
|
|
let depth = 1;
|
|
const maxDepth = Math.max(...pathSegments.map((s) => s.length));
|
|
|
|
while (depth <= maxDepth) {
|
|
const suffixes = pathSegments.map((parts) => {
|
|
const start = Math.max(0, parts.length - depth);
|
|
return parts.slice(start).join('/');
|
|
});
|
|
|
|
// Check if all suffixes are unique
|
|
const unique = new Set(suffixes);
|
|
if (unique.size === suffixes.length) {
|
|
// All unique — assign labels
|
|
for (let i = 0; i < group.length; i++) {
|
|
labels.set(group[i].id, `(${suffixes[i]})`);
|
|
}
|
|
break;
|
|
}
|
|
depth++;
|
|
}
|
|
|
|
// If we couldn't find unique suffixes (shouldn't happen with different file paths),
|
|
// use full parent path
|
|
if (depth > maxDepth) {
|
|
for (let i = 0; i < group.length; i++) {
|
|
const fullParent = pathSegments[i].join('/');
|
|
labels.set(group[i].id, `(${fullParent})`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply labels to tabs
|
|
return tabs.map((tab) => {
|
|
const label = labels.get(tab.id);
|
|
if (label === tab.disambiguatedLabel) return tab;
|
|
return { ...tab, disambiguatedLabel: label };
|
|
});
|
|
}
|