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
@@ -225,10 +225,47 @@ export function createFlexokiCodeMirrorTheme(theme: Theme): Extension {
|
||||
{ tag: [t.heading, t.heading1, t.heading2, t.heading3, t.heading4, t.heading5, t.heading6], class: 'cm-keyword' },
|
||||
]);
|
||||
|
||||
const directSyntax = HighlightStyle.define([
|
||||
{ tag: [t.comment, t.docComment, t.meta, t.documentMeta], color: theme.colors.syntax.base.comment },
|
||||
{ tag: [t.keyword, t.controlKeyword, t.moduleKeyword, t.definitionKeyword, t.modifier], color: theme.colors.syntax.base.keyword },
|
||||
{
|
||||
tag: [
|
||||
t.operatorKeyword,
|
||||
t.operator,
|
||||
t.derefOperator,
|
||||
t.updateOperator,
|
||||
t.definitionOperator,
|
||||
t.typeOperator,
|
||||
t.controlOperator,
|
||||
t.logicOperator,
|
||||
t.bitwiseOperator,
|
||||
t.arithmeticOperator,
|
||||
t.compareOperator,
|
||||
],
|
||||
color: theme.colors.syntax.base.operator,
|
||||
},
|
||||
{ tag: [t.string, t.regexp, t.attributeValue, t.special(t.string), t.monospace], color: theme.colors.syntax.base.string },
|
||||
{ tag: t.escape, color: tokens.stringEscape || theme.colors.syntax.base.string },
|
||||
{ tag: [t.number, t.bool, t.atom, t.null, t.self], color: theme.colors.syntax.base.number },
|
||||
{ tag: [t.function(t.variableName), t.function(t.definition(t.variableName)), t.function(t.propertyName), t.standard(t.variableName), t.special(t.variableName)], color: theme.colors.syntax.base.function },
|
||||
{ tag: t.definition(t.variableName), color: tokens.variableGlobal || theme.colors.syntax.base.variable },
|
||||
{ tag: [t.variableName, t.local(t.variableName), t.constant(t.variableName), t.literal], color: theme.colors.syntax.base.variable },
|
||||
{ tag: t.propertyName, color: tokens.variableProperty || theme.colors.syntax.base.variable },
|
||||
{ tag: t.attributeName, color: tokens.variableOther || theme.colors.syntax.base.variable },
|
||||
{ tag: [t.className, t.typeName, t.namespace], color: theme.colors.syntax.base.type },
|
||||
{ tag: [t.tagName, t.labelName, t.annotation, t.macroName], color: tokens.tag || theme.colors.syntax.base.keyword },
|
||||
{ tag: t.link, color: tokens.url || theme.colors.syntax.base.function, textDecoration: 'underline' },
|
||||
{
|
||||
tag: [t.punctuation, t.separator, t.bracket, t.paren, t.brace, t.squareBracket, t.angleBracket],
|
||||
color: tokens.punctuation || theme.colors.syntax.base.comment,
|
||||
},
|
||||
]);
|
||||
|
||||
return [
|
||||
ui,
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
syntaxHighlighting(classHighlighter),
|
||||
syntaxHighlighting(syntax),
|
||||
syntaxHighlighting(directSyntax),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -109,6 +109,14 @@ function codeBlockLanguageResolver(info: string): Language | LanguageDescription
|
||||
|
||||
const normalizeFileName = (filePath: string) => filePath.split('/').pop()?.toLowerCase() ?? '';
|
||||
|
||||
const matchLanguageDescriptionForFile = (filePath: string): LanguageDescription | null => {
|
||||
const filename = normalizeFileName(filePath);
|
||||
if (!filename) {
|
||||
return null;
|
||||
}
|
||||
return LanguageDescription.matchFilename(languages, filename);
|
||||
};
|
||||
|
||||
const markdownHighlight = () => syntaxHighlighting(HighlightStyle.define([
|
||||
{ tag: [t.heading1, t.heading2, t.heading3, t.heading4, t.heading5, t.heading6], fontWeight: '600' },
|
||||
{ tag: t.strong, fontWeight: '600' },
|
||||
@@ -250,3 +258,20 @@ export function languageByExtension(filePath: string): Extension | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadLanguageByExtension(filePath: string): Promise<Extension | null> {
|
||||
const description = matchLanguageDescriptionForFile(filePath);
|
||||
if (!description) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (description.support) {
|
||||
return description.support;
|
||||
}
|
||||
|
||||
try {
|
||||
return await description.load();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user