feat: add support for Kotlin language and themes, enhance language detection for various programming languages
This commit is contained in:
@@ -5,6 +5,10 @@
|
|||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🖼️</text></svg>" />
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🖼️</text></svg>" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Code Canvas - Create Beautiful Code Images</title>
|
<title>Code Canvas - Create Beautiful Code Images</title>
|
||||||
|
<!-- Google Fonts: JetBrains Mono, Fira Code, Source Code Pro for code -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Source+Code+Pro:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -235,9 +235,15 @@ export const createCodeElement = (x: number, y: number): CodeElement => ({
|
|||||||
locked: false,
|
locked: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
props: {
|
props: {
|
||||||
code: '// Your code here\nconsole.log("Hello, World!");',
|
code: `@Composable
|
||||||
language: 'javascript',
|
fun Greeting(name: String) {
|
||||||
theme: 'github-dark',
|
Text(
|
||||||
|
text = "Hello, $name!",
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
}`,
|
||||||
|
language: 'kotlin',
|
||||||
|
theme: 'dracula',
|
||||||
fontFamily: 'JetBrains Mono',
|
fontFamily: 'JetBrains Mono',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
|
|||||||
+30
-16
@@ -125,44 +125,58 @@ export const ASPECT_RATIOS: AspectRatio[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const CODE_THEMES = [
|
export const CODE_THEMES = [
|
||||||
{ id: 'github-dark', name: 'GitHub Dark', bg: '#0d1117' },
|
// IntelliJ / Android Studio themes (prioritized for Kotlin)
|
||||||
{ id: 'github-light', name: 'GitHub Light', bg: '#ffffff' },
|
{ id: 'andromeeda', name: 'Andromeeda', bg: '#23262e' },
|
||||||
|
{ id: 'aurora-x', name: 'Aurora X', bg: '#07090f' },
|
||||||
|
{ id: 'dark-plus', name: 'Dark+ (VS Code)', bg: '#1e1e1e' },
|
||||||
{ id: 'dracula', name: 'Dracula', bg: '#282a36' },
|
{ id: 'dracula', name: 'Dracula', bg: '#282a36' },
|
||||||
{ id: 'nord', name: 'Nord', bg: '#2e3440' },
|
{ id: 'dracula-soft', name: 'Dracula Soft', bg: '#282a36' },
|
||||||
{ id: 'one-dark-pro', name: 'One Dark Pro', bg: '#282c34' },
|
{ id: 'github-dark', name: 'GitHub Dark', bg: '#0d1117' },
|
||||||
{ id: 'monokai', name: 'Monokai', bg: '#272822' },
|
{ id: 'github-dark-dimmed', name: 'GitHub Dimmed', bg: '#22272e' },
|
||||||
{ id: 'tokyo-night', name: 'Tokyo Night', bg: '#1a1b26' },
|
{ id: 'github-light', name: 'GitHub Light', bg: '#ffffff' },
|
||||||
{ id: 'vitesse-dark', name: 'Vitesse Dark', bg: '#121212' },
|
{ id: 'light-plus', name: 'Light+ (VS Code)', bg: '#ffffff' },
|
||||||
{ id: 'vitesse-light', name: 'Vitesse Light', bg: '#ffffff' },
|
|
||||||
{ id: 'material-theme-darker', name: 'Material Darker', bg: '#212121' },
|
{ id: 'material-theme-darker', name: 'Material Darker', bg: '#212121' },
|
||||||
{ id: 'catppuccin-mocha', name: 'Catppuccin Mocha', bg: '#1e1e2e' },
|
{ id: 'material-theme-ocean', name: 'Material Ocean', bg: '#0f111a' },
|
||||||
{ id: 'catppuccin-latte', name: 'Catppuccin Latte', bg: '#eff1f5' },
|
{ id: 'material-theme-palenight', name: 'Material Palenight', bg: '#292d3e' },
|
||||||
{ id: 'slack-dark', name: 'Slack Dark', bg: '#222222' },
|
|
||||||
{ id: 'poimandres', name: 'Poimandres', bg: '#1b1e28' },
|
|
||||||
{ id: 'night-owl', name: 'Night Owl', bg: '#011627' },
|
|
||||||
{ id: 'min-dark', name: 'Min Dark', bg: '#1f1f1f' },
|
{ id: 'min-dark', name: 'Min Dark', bg: '#1f1f1f' },
|
||||||
{ id: 'min-light', name: 'Min Light', bg: '#ffffff' },
|
{ id: 'min-light', name: 'Min Light', bg: '#ffffff' },
|
||||||
{ id: 'ayu-dark', name: 'Ayu Dark', bg: '#0b0e14' },
|
{ id: 'monokai', name: 'Monokai', bg: '#272822' },
|
||||||
|
{ id: 'night-owl', name: 'Night Owl', bg: '#011627' },
|
||||||
|
{ id: 'nord', name: 'Nord', bg: '#2e3440' },
|
||||||
|
{ id: 'one-dark-pro', name: 'One Dark Pro', bg: '#282c34' },
|
||||||
|
{ id: 'poimandres', name: 'Poimandres', bg: '#1b1e28' },
|
||||||
|
{ id: 'rose-pine', name: 'Rosé Pine', bg: '#191724' },
|
||||||
|
{ id: 'rose-pine-moon', name: 'Rosé Pine Moon', bg: '#232136' },
|
||||||
|
{ id: 'slack-dark', name: 'Slack Dark', bg: '#222222' },
|
||||||
{ id: 'solarized-dark', name: 'Solarized Dark', bg: '#002b36' },
|
{ id: 'solarized-dark', name: 'Solarized Dark', bg: '#002b36' },
|
||||||
{ id: 'solarized-light', name: 'Solarized Light', bg: '#fdf6e3' },
|
{ id: 'solarized-light', name: 'Solarized Light', bg: '#fdf6e3' },
|
||||||
|
{ id: 'tokyo-night', name: 'Tokyo Night', bg: '#1a1b26' },
|
||||||
|
{ id: 'vesper', name: 'Vesper', bg: '#101010' },
|
||||||
|
{ id: 'vitesse-dark', name: 'Vitesse Dark', bg: '#121212' },
|
||||||
|
{ id: 'vitesse-light', name: 'Vitesse Light', bg: '#ffffff' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type CodeThemeId = typeof CODE_THEMES[number]['id'];
|
export type CodeThemeId = typeof CODE_THEMES[number]['id'];
|
||||||
|
|
||||||
export const LANGUAGES = [
|
export const LANGUAGES = [
|
||||||
'javascript',
|
|
||||||
'typescript',
|
|
||||||
'kotlin',
|
'kotlin',
|
||||||
'java',
|
'java',
|
||||||
|
'typescript',
|
||||||
|
'javascript',
|
||||||
'python',
|
'python',
|
||||||
'rust',
|
'rust',
|
||||||
'go',
|
'go',
|
||||||
|
'swift',
|
||||||
|
'dart',
|
||||||
'html',
|
'html',
|
||||||
'css',
|
'css',
|
||||||
'json',
|
'json',
|
||||||
|
'xml',
|
||||||
|
'yaml',
|
||||||
'markdown',
|
'markdown',
|
||||||
'bash',
|
'bash',
|
||||||
'sql',
|
'sql',
|
||||||
|
'groovy',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const FONT_FAMILIES = {
|
export const FONT_FAMILIES = {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function getThemeBackground(themeId: CodeThemeId): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isLightTheme(themeId: CodeThemeId): boolean {
|
export function isLightTheme(themeId: CodeThemeId): boolean {
|
||||||
const lightThemes = ['github-light', 'vitesse-light', 'catppuccin-latte', 'min-light', 'solarized-light'];
|
const lightThemes = ['github-light', 'vitesse-light', 'min-light', 'solarized-light', 'light-plus'];
|
||||||
return lightThemes.includes(themeId);
|
return lightThemes.includes(themeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +81,22 @@ export function isHighlighterReady(): boolean {
|
|||||||
|
|
||||||
// Language detection based on common patterns
|
// Language detection based on common patterns
|
||||||
export function detectLanguage(code: string): string {
|
export function detectLanguage(code: string): string {
|
||||||
|
// Kotlin / Jetpack Compose patterns (check first - primary language)
|
||||||
|
// Look for @Composable, fun, val, var, class, object, companion object, etc.
|
||||||
|
if (/@Composable|@Preview|@OptIn|@Suppress/.test(code)) {
|
||||||
|
return 'kotlin';
|
||||||
|
}
|
||||||
|
if (/^(fun|val|var|class|object|package|import|sealed|data\s+class|enum\s+class|interface)\s+/m.test(code)) {
|
||||||
|
// Check for Kotlin-specific syntax
|
||||||
|
if (/:\s*\w+(\s*[={()]|$|\s*,)/.test(code) ||
|
||||||
|
/\b(Unit|String|Int|Long|Boolean|Float|Double|List|Map|Set|suspend|inline|crossinline|noinline|reified)\b/.test(code) ||
|
||||||
|
/\bModifier\b|\bColumn\b|\bRow\b|\bBox\b|\bText\b|\bButton\b|\bScaffold\b/.test(code) ||
|
||||||
|
/\.copy\(|\.let\s*\{|\.apply\s*\{|\.also\s*\{|\.run\s*\{/.test(code) ||
|
||||||
|
/\blambda\b|->/.test(code)) {
|
||||||
|
return 'kotlin';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TypeScript/JavaScript patterns
|
// TypeScript/JavaScript patterns
|
||||||
if (/^import\s+.*from\s+['"]|^export\s+(default\s+)?|const\s+\w+:\s*\w+/m.test(code)) {
|
if (/^import\s+.*from\s+['"]|^export\s+(default\s+)?|const\s+\w+:\s*\w+/m.test(code)) {
|
||||||
if (/:\s*(string|number|boolean|any|void|Promise|Array)\b/.test(code)) {
|
if (/:\s*(string|number|boolean|any|void|Promise|Array)\b/.test(code)) {
|
||||||
@@ -89,15 +105,22 @@ export function detectLanguage(code: string): string {
|
|||||||
return 'javascript';
|
return 'javascript';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kotlin patterns
|
// Java patterns (after Kotlin to avoid false positives)
|
||||||
if (/^(fun|val|var|class|object|package|import)\s+/m.test(code) &&
|
if (/^(public|private|protected)\s+(static\s+)?(class|void|int|String)/m.test(code) &&
|
||||||
/:\s*\w+(\s*=|\s*\{|\s*\))/.test(code)) {
|
!/@Composable/.test(code)) {
|
||||||
return 'kotlin';
|
return 'java';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Java patterns
|
// Swift patterns
|
||||||
if (/^(public|private|protected)\s+(static\s+)?(class|void|int|String)/m.test(code)) {
|
if (/^(func|var|let|class|struct|enum|protocol|import\s+\w+)\s+/m.test(code) &&
|
||||||
return 'java';
|
/@State|@Binding|@Published|@ObservedObject|some\s+View/.test(code)) {
|
||||||
|
return 'swift';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dart/Flutter patterns
|
||||||
|
if (/^(class|void|final|const|import\s+')/m.test(code) &&
|
||||||
|
/Widget|StatelessWidget|StatefulWidget|BuildContext|setState/.test(code)) {
|
||||||
|
return 'dart';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Python patterns
|
// Python patterns
|
||||||
@@ -115,6 +138,17 @@ export function detectLanguage(code: string): string {
|
|||||||
return 'go';
|
return 'go';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Groovy/Gradle patterns
|
||||||
|
if (/^(plugins|dependencies|android|buildscript)\s*\{/m.test(code) ||
|
||||||
|
/implementation\s*\(|compile\s*\(/.test(code)) {
|
||||||
|
return 'groovy';
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML patterns (for Android layouts)
|
||||||
|
if (/^<\?xml|^<resources|^<layout|^<LinearLayout|^<RelativeLayout|^<ConstraintLayout|^<androidx\./m.test(code)) {
|
||||||
|
return 'xml';
|
||||||
|
}
|
||||||
|
|
||||||
// HTML patterns
|
// HTML patterns
|
||||||
if (/^<!DOCTYPE|<html|<head|<body|<div/m.test(code)) {
|
if (/^<!DOCTYPE|<html|<head|<body|<div/m.test(code)) {
|
||||||
return 'html';
|
return 'html';
|
||||||
@@ -135,6 +169,11 @@ export function detectLanguage(code: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YAML patterns
|
||||||
|
if (/^[\w-]+:\s*(\n|$)|^\s+-\s+/m.test(code) && !/[{};]/.test(code)) {
|
||||||
|
return 'yaml';
|
||||||
|
}
|
||||||
|
|
||||||
// SQL patterns
|
// SQL patterns
|
||||||
if (/^(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)\s+/im.test(code)) {
|
if (/^(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)\s+/im.test(code)) {
|
||||||
return 'sql';
|
return 'sql';
|
||||||
@@ -145,5 +184,5 @@ export function detectLanguage(code: string): string {
|
|||||||
return 'bash';
|
return 'bash';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'javascript'; // Default
|
return 'kotlin'; // Default to Kotlin as primary language
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user