-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
01717cf
commit c948fa8
Showing
17 changed files
with
950 additions
and
751 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Loader2 } from 'lucide-react' | ||
|
||
// src/app/saved-themes/loading.tsx | ||
export default function Loading() { | ||
return ( | ||
<section className="flex justify-center items-center h-screen"> | ||
<div className="flex flex-col items-center justify-center gap-4"> | ||
<Loader2 className="h-4 w-4 animate-spin" /> | ||
<p>Loading theme generator...</p> | ||
</div> | ||
</section> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,12 @@ | ||
'use client' | ||
|
||
import { useUser } from '@clerk/nextjs' | ||
import { ThemeProvider } from '@/contexts/ThemeContext' | ||
import { ThemeGenerator } from '@/components/ThemeGenerator' | ||
import Navigation from '@/components/Navigation' | ||
|
||
export default function GeneratorPage() { | ||
const { isLoaded, isSignedIn, user } = useUser() | ||
|
||
if (!isLoaded) { | ||
return <div>Loading...</div> | ||
} | ||
import { auth } from '@clerk/nextjs/server' | ||
|
||
if (!isSignedIn) { | ||
return <div>Please sign in to access the theme generator.</div> | ||
} | ||
export default async function GeneratorPage() { | ||
const { userId } = auth() | ||
|
||
return ( | ||
<ThemeProvider userId={user.id}> | ||
<div> | ||
<ThemeGenerator /> | ||
</div> | ||
</ThemeProvider> | ||
<main> | ||
<ThemeGenerator /> | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
'use client' | ||
|
||
import { SavedTheme } from '@/lib/types/colors' | ||
import ThemeCardPublic from './ThemeCardPublic' | ||
import ThemePreviewSmall from './ThemePreviewSmall' | ||
import { useState } from 'react' | ||
|
||
export default function Discover({ themes }: { themes: SavedTheme[] }) { | ||
const [selectedTheme, setSelectedTheme] = useState(themes[0]) | ||
return ( | ||
<section> | ||
<ThemePreviewSmall theme={selectedTheme} /> | ||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | ||
{themes.map((theme) => ( | ||
<ThemeCardPublic | ||
key={theme.id} | ||
theme={theme} | ||
onClick={setSelectedTheme} | ||
/> | ||
))} | ||
</div> | ||
</section> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import { useCallback, useRef, useState, useEffect } from 'react' | ||
import dynamic from 'next/dynamic' | ||
|
||
import type { editor } from 'monaco-editor' | ||
import { loadWASM } from 'onigasm' | ||
import { IGrammarDefinition, Registry, RegistryOptions } from 'monaco-textmate' | ||
import { wireTmGrammars } from 'monaco-editor-textmate' | ||
import { generateSemanticThemeJSON } from '@/lib/generator/export' | ||
import { convertTheme } from '@/lib/utils/convertTheme' | ||
import { SavedTheme } from '@/lib/types/colors' | ||
import { useTheme } from '@/contexts/ThemeContext' | ||
import { | ||
type CodeSnippetKey, | ||
codeSnippets as snippets, | ||
} from '@/lib/utils/codeSnippets' | ||
|
||
const MonacoEditor = dynamic(() => import('@monaco-editor/react'), { | ||
ssr: false, | ||
loading: () => <p>Loading editor...</p>, | ||
}) | ||
|
||
export default function EditorSmall({ | ||
theme, | ||
selectedFile = 'typescript.tsx', | ||
}: { | ||
theme: SavedTheme | ||
selectedFile: CodeSnippetKey | ||
}) { | ||
const editorRef = useRef<{ | ||
editor: editor.IStandaloneCodeEditor | ||
monaco: typeof import('monaco-editor') | ||
} | null>(null) | ||
const { isOnigasmInitialized, setIsOnigasmInitialized } = useTheme() | ||
const [isEditorReady, setIsEditorReady] = useState(false) | ||
const getTheme = useCallback((): editor.IStandaloneThemeData => { | ||
const { themeObject } = generateSemanticThemeJSON( | ||
'Generated Color Theme', | ||
theme.uiColors, | ||
theme.syntaxColors, | ||
theme.ansiColors | ||
) | ||
|
||
return convertTheme(themeObject) | ||
}, [theme]) | ||
|
||
const updateTheme = useCallback(() => { | ||
if (editorRef.current) { | ||
const { monaco, editor } = editorRef.current | ||
const theme = getTheme() | ||
const model = editor.getModel() | ||
if (model) { | ||
model.updateOptions({ | ||
bracketColorizationOptions: { | ||
enabled: false, | ||
independentColorPoolPerBracketType: false, | ||
}, | ||
}) | ||
} | ||
monaco.editor.defineTheme('custom-theme', theme) | ||
monaco.editor.setTheme('custom-theme') | ||
} | ||
}, [getTheme]) | ||
|
||
const setupTextmate = useCallback(async () => { | ||
if (!editorRef.current) return | ||
|
||
if (!isOnigasmInitialized) { | ||
await loadWASM('onigasm.wasm') | ||
setIsOnigasmInitialized(true) | ||
} | ||
|
||
const registry = new Registry({ | ||
getGrammarDefinition: async ( | ||
scopeName: string | ||
): Promise<IGrammarDefinition> => { | ||
const grammarMap: { [key: string]: string } = { | ||
'source.tsx': 'TypeScriptReact.tmLanguage.json', | ||
'source.js.jsx': 'JavaScriptReact.tmLanguage.json', | ||
'source.ts': 'TypeScript.tmLanguage.json', | ||
'source.js': 'JavaScript.tmLanguage.json', | ||
'source.css': 'css.tmLanguage.json', | ||
'text.html.markdown': 'markdown.tmLanguage.json', | ||
'text.html.basic': 'html.tmLanguage.json', | ||
'source.python': 'MagicPython.tmLanguage.json', | ||
'source.yaml': 'yaml.tmLanguage.json', | ||
} | ||
|
||
if (scopeName in grammarMap) { | ||
try { | ||
const response = await fetch(grammarMap[scopeName]) | ||
if (!response.ok) { | ||
throw new Error(`HTTP error! status: ${response.status}`) | ||
} | ||
const content = await response.text() | ||
|
||
return { | ||
format: 'json' as const, | ||
content, | ||
} | ||
} catch (error) { | ||
console.error(`Failed to load grammar for ${scopeName}:`, error) | ||
} | ||
} | ||
|
||
return { | ||
format: 'json', | ||
content: JSON.stringify({ | ||
name: 'Default', | ||
scopeName: scopeName, | ||
patterns: [], | ||
}), | ||
} | ||
}, | ||
} as RegistryOptions) | ||
|
||
try { | ||
await wireTmGrammars( | ||
editorRef.current.monaco, | ||
registry, | ||
new Map([ | ||
['typescript', 'source.tsx'], | ||
['javascript', 'source.jsx'], | ||
['typescript', 'source.ts'], | ||
['javascript', 'source.js'], | ||
['css', 'source.css'], | ||
['markdown', 'text.html.markdown'], | ||
['html', 'text.html.basic'], | ||
['python', 'source.python'], | ||
['yaml', 'source.yaml'], | ||
]) | ||
) | ||
} catch (error) { | ||
console.error('Error setting up TextMate:', error) | ||
} | ||
}, [isOnigasmInitialized, setIsOnigasmInitialized]) | ||
|
||
const handleEditorDidMount = useCallback( | ||
( | ||
editor: editor.IStandaloneCodeEditor, | ||
monaco: typeof import('monaco-editor') | ||
) => { | ||
editorRef.current = { editor, monaco } | ||
const model = editor.getModel() | ||
if (model) { | ||
model.updateOptions({ | ||
bracketColorizationOptions: { | ||
enabled: false, | ||
independentColorPoolPerBracketType: false, | ||
}, | ||
}) | ||
} | ||
setIsEditorReady(true) | ||
setupTextmate() | ||
updateTheme() | ||
}, | ||
[setupTextmate, updateTheme] | ||
) | ||
|
||
useEffect(() => { | ||
if (isEditorReady && editorRef.current) { | ||
updateTheme() | ||
} | ||
}, [isEditorReady, updateTheme]) | ||
|
||
const getLanguage = (filename: string) => { | ||
const extension = filename.split('.').pop() | ||
switch (extension) { | ||
case 'js': | ||
case 'jsx': | ||
return 'javascript' | ||
case 'ts': | ||
case 'tsx': | ||
return 'typescript' | ||
case 'py': | ||
return 'python' | ||
case 'html': | ||
return 'html' | ||
case 'css': | ||
return 'css' | ||
case 'md': | ||
return 'markdown' | ||
case 'yaml': | ||
return 'yaml' | ||
default: | ||
return 'plaintext' | ||
} | ||
} | ||
|
||
return ( | ||
<section className="h-full w-full"> | ||
<div className="h-full w-full"> | ||
<MonacoEditor | ||
height="100%" | ||
language={getLanguage(selectedFile)} | ||
value={snippets[selectedFile]} | ||
theme="custom-theme" | ||
options={{ | ||
minimap: { enabled: true }, | ||
scrollBeyondLastLine: false, | ||
fontSize: 13, | ||
readOnly: true, | ||
bracketPairColorization: { | ||
enabled: false, | ||
independentColorPoolPerBracketType: false, | ||
}, | ||
'semanticHighlighting.enabled': true, | ||
}} | ||
onMount={handleEditorDidMount} | ||
/> | ||
</div> | ||
</section> | ||
) | ||
} |
Oops, something went wrong.