feat(editor): Shiki highlighting for code files in PlanView and SkillsPage

Reuse the file-editor Shiki extension for the PlanView and SkillsPage editors,
gated to non-markdown files. Markdown sources keep the lezer highlighter (its
markdown-aware styling is better for editing and there's no Shiki view to match).

- PlanView: code files opened through it get Shiki colors; plan .md stays lezer.
- SkillsPage: code supporting files get Shiki colors; SKILL.md stays lezer.
This commit is contained in:
Bohdan Triapitsyn
2026-06-16 01:08:54 +03:00
parent 782982cdd3
commit e904abda04
2 changed files with 28 additions and 2 deletions
@@ -35,6 +35,9 @@ import {
import { useI18n } from '@/lib/i18n';
import { languageByExtension } from '@/lib/codemirror/languageByExtension';
import { createFlexokiCodeMirrorTheme } from '@/lib/codemirror/flexokiTheme';
import { shikiHighlightExtension } from '@/lib/codemirror/shikiHighlight';
import { getResolvedShikiTheme } from '@/lib/shiki/appThemeRegistry';
import { getLanguageFromExtension } from '@/lib/toolHelpers';
import { useThemeSystem } from '@/contexts/useThemeSystem';
import { cn } from '@/lib/utils';
import { EditorView } from '@codemirror/view';
@@ -253,11 +256,21 @@ const SkillsInstalledPage: React.FC = () => {
const supportingFileEditorExtensions = React.useMemo<Extension[]>(() => {
const filePath = newFileName.trim() || 'supporting-file.md';
const extensions: Extension[] = [createFlexokiCodeMirrorTheme(currentTheme)];
// Shiki token colors for code supporting files; markdown stays on lezer.
const shikiLanguage = getLanguageFromExtension(filePath);
const useShiki = Boolean(shikiLanguage) && shikiLanguage !== 'markdown';
const extensions: Extension[] = [createFlexokiCodeMirrorTheme(currentTheme, useShiki ? { syntaxColors: false } : undefined)];
const languageExtension = languageByExtension(filePath);
if (languageExtension) {
extensions.push(languageExtension);
}
if (useShiki && shikiLanguage) {
extensions.push(shikiHighlightExtension({
language: shikiLanguage,
themeName: currentTheme.metadata.id,
theme: getResolvedShikiTheme(currentTheme),
}));
}
extensions.push(EditorView.lineWrapping);
return extensions;
}, [currentTheme, newFileName]);
+14 -1
View File
@@ -19,6 +19,8 @@ import { getLanguageFromExtension } from '@/lib/toolHelpers';
import { useDeviceInfo } from '@/lib/device';
import { useThemeSystem } from '@/contexts/useThemeSystem';
import { createFlexokiCodeMirrorTheme } from '@/lib/codemirror/flexokiTheme';
import { shikiHighlightExtension } from '@/lib/codemirror/shikiHighlight';
import { getResolvedShikiTheme } from '@/lib/shiki/appThemeRegistry';
import { languageByExtension } from '@/lib/codemirror/languageByExtension';
import { useSessionUIStore } from '@/sync/session-ui-store';
import { useSessions } from '@/sync/sync-context';
@@ -344,11 +346,22 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
}, [cancel, commentText, editingDraftId, isMobile, lineSelection]);
const editorExtensions = React.useMemo(() => {
const extensions = [createFlexokiCodeMirrorTheme(currentTheme)];
// Shiki token colors only for code files; markdown keeps the lezer
// highlighter (markdown-aware bold headings etc., and no Shiki view to match).
const shikiLanguage = resolvedPath ? getLanguageFromExtension(resolvedPath) : null;
const useShiki = Boolean(shikiLanguage) && shikiLanguage !== 'markdown';
const extensions = [createFlexokiCodeMirrorTheme(currentTheme, useShiki ? { syntaxColors: false } : undefined)];
const language = languageByExtension(resolvedPath || 'plan.md');
if (language) {
extensions.push(language);
}
if (useShiki && shikiLanguage) {
extensions.push(shikiHighlightExtension({
language: shikiLanguage,
themeName: currentTheme.metadata.id,
theme: getResolvedShikiTheme(currentTheme),
}));
}
extensions.push(EditorView.lineWrapping);
return extensions;
}, [currentTheme, resolvedPath]);