fix(ui): show project names exactly as the folder is named
Auto-derived project labels were title-cased, turning .ssh into .Ssh and opencode-claude into Opencode Claude. Show the folder name verbatim in the sidebar, window title, settings selector and notification templates, and migrate persisted legacy labels back to the folder name (manual renames are preserved).
This commit is contained in:
@@ -12,9 +12,7 @@ import { isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
const formatProjectLabel = (label: string): string => {
|
||||
return label.replace(/[-_]/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
};
|
||||
const formatProjectLabel = (label: string): string => label.trim();
|
||||
|
||||
export const SettingsProjectSelector: React.FC<{ className?: string }> = ({ className }) => {
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -156,11 +156,8 @@ export const resolveArchivedFolderName = (session: Session, projectRoot: string
|
||||
return segments[segments.length - 1] ?? 'unassigned';
|
||||
};
|
||||
|
||||
export const formatProjectLabel = (label: string): string => {
|
||||
return label
|
||||
.replace(/[-_]/g, ' ')
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
};
|
||||
// Folder names are shown exactly as they are on disk — no title-casing.
|
||||
export const formatProjectLabel = (label: string): string => label.trim();
|
||||
|
||||
export const renderHighlightedText = (text: string, query: string): React.ReactNode => {
|
||||
if (!query) {
|
||||
|
||||
@@ -7,9 +7,7 @@ import { getRuntimeApiBaseUrl } from '@/lib/runtime-switch';
|
||||
|
||||
const APP_TITLE = 'OpenChamber';
|
||||
|
||||
const formatProjectLabel = (label: string): string => {
|
||||
return label.replace(/[-_]/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
};
|
||||
const formatProjectLabel = (label: string): string => label.trim();
|
||||
|
||||
const getProjectNameFromPath = (path: string): string => {
|
||||
const normalized = path.replace(/\\/g, '/').replace(/\/+$/, '');
|
||||
|
||||
@@ -166,14 +166,22 @@ const normalizeProjectPath = (value: string): string => {
|
||||
return normalized.length > 1 ? normalized.replace(/\/+$/, '') : normalized;
|
||||
};
|
||||
|
||||
// Folder names are shown verbatim: title-casing them turned `.ssh` into `.Ssh`
|
||||
// and made every project look like a name the user never chose.
|
||||
const deriveProjectLabel = (path: string): string => {
|
||||
const normalized = normalizeProjectPath(path);
|
||||
if (!normalized || normalized === '/') {
|
||||
return 'Root';
|
||||
}
|
||||
const segments = normalized.split('/').filter(Boolean);
|
||||
const raw = segments[segments.length - 1] || normalized;
|
||||
return raw.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
return segments[segments.length - 1] || normalized;
|
||||
};
|
||||
|
||||
// Labels auto-derived by older versions were title-cased and persisted. Drop
|
||||
// them back to the folder name; labels the user typed themselves are kept.
|
||||
const legacyAutoProjectLabel = (path: string): string => {
|
||||
const derived = deriveProjectLabel(path);
|
||||
return derived.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
};
|
||||
|
||||
const sanitizeProjectIconImage = (value: unknown): ProjectEntry['iconImage'] | undefined => {
|
||||
@@ -261,7 +269,10 @@ const sanitizeProjects = (value: unknown): ProjectEntry[] => {
|
||||
};
|
||||
|
||||
if (typeof candidate.label === 'string' && candidate.label.trim().length > 0) {
|
||||
project.label = candidate.label.trim();
|
||||
const storedLabel = candidate.label.trim();
|
||||
project.label = storedLabel === legacyAutoProjectLabel(normalizedPath)
|
||||
? deriveProjectLabel(normalizedPath)
|
||||
: storedLabel;
|
||||
}
|
||||
if (typeof candidate.icon === 'string' && candidate.icon.trim().length > 0) {
|
||||
project.icon = candidate.icon.trim();
|
||||
|
||||
@@ -27,9 +27,8 @@ export const createNotificationTemplateRuntime = (deps) => {
|
||||
|
||||
const formatProjectLabel = (label) => {
|
||||
if (!label || typeof label !== 'string') return '';
|
||||
return label
|
||||
.replace(/[-_]/g, ' ')
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
// Folder names are shown exactly as they are on disk — no title-casing.
|
||||
return label.trim();
|
||||
};
|
||||
|
||||
const resolveNotificationTemplate = (template, variables) => {
|
||||
|
||||
Reference in New Issue
Block a user