Skip to content

Commit

Permalink
refactor some components
Browse files Browse the repository at this point in the history
  • Loading branch information
RodrigoLuglio committed Oct 11, 2024
1 parent 01717cf commit c948fa8
Show file tree
Hide file tree
Showing 17 changed files with 950 additions and 751 deletions.
13 changes: 13 additions & 0 deletions src/app/(private)/generator/loading.tsx
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>
)
}
26 changes: 6 additions & 20 deletions src/app/(private)/generator/page.tsx
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>
)
}
1 change: 0 additions & 1 deletion src/app/(private)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Navigation from '@/components/Navigation'
import { auth } from '@clerk/nextjs/server'

export default async function PrivateLayout({
Expand Down
4 changes: 2 additions & 2 deletions src/app/(private)/saved-themes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default async function SavedThemesPage() {
const themes = userId ? await getThemesByUserId(userId) : []

return (
<div className="mx-auto py-8">
<main className="mx-auto px-10 py-8">
<h1 className="text-3xl font-bold mb-8">Your Saved Themes</h1>
<SavedThemesContent initialThemes={themes} />
</div>
</main>
)
}
8 changes: 2 additions & 6 deletions src/app/discover/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ThemeCardPublic from '@/components/ThemeCardPublic'
import Discover from '@/components/Discover'

import { getPublicThemes } from '@/lib/db/themes'

Expand All @@ -9,11 +9,7 @@ export default async function DiscoverPage() {
<div>
<main className="mx-auto mt-8 p-4">
<h1 className="text-3xl font-bold mb-4">Discover Themes</h1>
<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} />
))}
</div>
<Discover themes={themes} />
</main>
</div>
)
Expand Down
17 changes: 8 additions & 9 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ import './globals.css'
import { Inter } from 'next/font/google'

import { ThemeProvider } from 'next-themes'
import {
ClerkProvider,
SignInButton,
SignedIn,
SignedOut,
UserButton,
} from '@clerk/nextjs'
import { ClerkProvider } from '@clerk/nextjs'
import { auth } from '@clerk/nextjs/server'
import { dark } from '@clerk/themes'
import Navigation from '@/components/Navigation'
import { ThemeProvider as ThemeProviderContext } from '@/contexts/ThemeContext'

const inter = Inter({ subsets: ['latin'] })

Expand All @@ -26,6 +22,7 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode
}>) {
const { userId } = auth()
return (
<ClerkProvider
appearance={{
Expand All @@ -41,8 +38,10 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
<Navigation />
{children}
<ThemeProviderContext userId={userId ?? undefined}>
<Navigation />
{children}
</ThemeProviderContext>
</ThemeProvider>
</body>
</html>
Expand Down
25 changes: 25 additions & 0 deletions src/components/Discover.tsx
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>
)
}
213 changes: 213 additions & 0 deletions src/components/EditorSmall.tsx
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>
)
}
Loading

0 comments on commit c948fa8

Please sign in to comment.