feat(ui): add dynamic window title and sprite-based project/file icons (#529)
* feat(ui): add dynamic titles and sprite-based project/file icons * feat(files): add viewer syntax fallback and tab file icons * fix(files): restore file viewer highlighting and add diff file icons * feat(git): add file icons and async file-viewer syntax fallback * fix(files): force codemirror token colors in file viewer * feat(files): add shiki view mode for file viewer * fix(files): force codemirror parse after programmatic content updates * feat(files): support markdown frontmatter preview * feat(chat): use pierre diffs for tool previews * feat(chat): add configurable beautiful-mermaid rendering * feat(perf): virtualize chat rendering and add react-scan toggle * feat(build): enable React Compiler in Vite React apps * fix(chat): reduce rerenders from tooltips and streamed activity * fix(ui): make MessageList React Compiler safe * chore(ui): batch commit remaining pending ui updates * fix: polish chat and diff preview rendering - Keep Mermaid action buttons fixed while diagram content scrolls - Align Diff All Files headers and match Git-style path truncation - Default chat tool diffs to unified view with lightweight indicators disabled * fix: preserve file tree expansion and delay git action label collapse * fix: refine project icon controls in settings --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
d6b8f28e6f
commit
1d8ff97c95
@@ -115,6 +115,19 @@ const sanitizeSkillCatalogs = (value: unknown): DesktopSettings['skillCatalogs']
|
||||
return result;
|
||||
};
|
||||
|
||||
const HEX_COLOR_PATTERN = /^#(?:[\da-fA-F]{3}|[\da-fA-F]{6})$/;
|
||||
|
||||
const normalizeIconBackground = (value: unknown): string | null => {
|
||||
if (typeof value !== 'string') {
|
||||
return null;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
return HEX_COLOR_PATTERN.test(trimmed) ? trimmed.toLowerCase() : null;
|
||||
};
|
||||
|
||||
const sanitizeProjects = (value: unknown): DesktopSettings['projects'] | undefined => {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
@@ -150,9 +163,32 @@ const sanitizeProjects = (value: unknown): DesktopSettings['projects'] | undefin
|
||||
if (typeof candidate.icon === 'string' && candidate.icon.trim().length > 0) {
|
||||
project.icon = candidate.icon.trim();
|
||||
}
|
||||
if (candidate.iconImage === null) {
|
||||
(project as unknown as Record<string, unknown>).iconImage = null;
|
||||
} else if (candidate.iconImage && typeof candidate.iconImage === 'object') {
|
||||
const iconImage = candidate.iconImage as Record<string, unknown>;
|
||||
const mime = typeof iconImage.mime === 'string' ? iconImage.mime.trim() : '';
|
||||
const updatedAt = typeof iconImage.updatedAt === 'number' && Number.isFinite(iconImage.updatedAt)
|
||||
? Math.max(0, Math.round(iconImage.updatedAt))
|
||||
: 0;
|
||||
const source = iconImage.source === 'custom' || iconImage.source === 'auto'
|
||||
? iconImage.source
|
||||
: null;
|
||||
if (mime && updatedAt > 0 && source) {
|
||||
(project as unknown as Record<string, unknown>).iconImage = { mime, updatedAt, source };
|
||||
}
|
||||
}
|
||||
if (typeof candidate.color === 'string' && candidate.color.trim().length > 0) {
|
||||
project.color = candidate.color.trim();
|
||||
}
|
||||
if (candidate.iconBackground === null) {
|
||||
(project as unknown as Record<string, unknown>).iconBackground = null;
|
||||
} else {
|
||||
const iconBackground = normalizeIconBackground(candidate.iconBackground);
|
||||
if (iconBackground) {
|
||||
(project as unknown as Record<string, unknown>).iconBackground = iconBackground;
|
||||
}
|
||||
}
|
||||
if (typeof candidate.addedAt === 'number' && Number.isFinite(candidate.addedAt) && candidate.addedAt >= 0) {
|
||||
project.addedAt = candidate.addedAt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user