+
@@ -53,7 +53,7 @@ export const ThemeGenerator = () => {
-
+
@@ -62,10 +62,10 @@ export const ThemeGenerator = () => {
-
*/}
)
diff --git a/src/components/ThemeLoader.tsx b/src/components/ThemeLoader.tsx
index c86f305..727f863 100644
--- a/src/components/ThemeLoader.tsx
+++ b/src/components/ThemeLoader.tsx
@@ -9,7 +9,13 @@ import {
} from '@/components/ui/select'
import { Loader2 } from 'lucide-react'
-const ThemeLoader: React.FC = () => {
+interface ThemeLoaderProps {
+ setThemeName: React.Dispatch
>
+}
+
+export const ThemeLoader: React.FC = ({
+ setThemeName,
+}: ThemeLoaderProps) => {
const { savedThemes, loadTheme, currentThemeId } = useTheme()
const [selectedThemeId, setSelectedThemeId] = useState(
currentThemeId?.toString() || ''
@@ -30,6 +36,7 @@ const ThemeLoader: React.FC = () => {
)
if (themeToLoad) {
loadTheme(themeToLoad)
+ setThemeName(themeToLoad.name)
}
})
}
@@ -66,5 +73,3 @@ const ThemeLoader: React.FC = () => {
)
}
-
-export default ThemeLoader
diff --git a/src/components/ThemePreview.tsx b/src/components/ThemePreview.tsx
index 3760267..670776b 100644
--- a/src/components/ThemePreview.tsx
+++ b/src/components/ThemePreview.tsx
@@ -20,14 +20,12 @@ const ThemePreview: React.FC = () => {
useState
('typescript.tsx')
return (
-
+
{/* Mock VS Code title bar */}
{
display: 'flex',
justifyContent: 'space-between',
}}
+ className="rounded-t-md"
>
VS Code Theme Preview
@@ -143,7 +142,7 @@ const ThemePreview: React.FC = () => {
display: 'flex',
justifyContent: 'space-between',
}}
- className="text-xs"
+ className="text-xs rounded-b-md"
>
{selectedFile.split('.').pop()?.toUpperCase()}
diff --git a/src/components/ThemePreviewSmall.tsx b/src/components/ThemePreviewSmall.tsx
index aa7e199..7d2f4d9 100644
--- a/src/components/ThemePreviewSmall.tsx
+++ b/src/components/ThemePreviewSmall.tsx
@@ -50,7 +50,7 @@ export default function ThemePreviewSmall({ theme }: { theme: SavedTheme }) {
style={{ backgroundColor: theme.uiColors.BG1 }}
>
{theme.name}
+
+ {theme.userName}
+
-
+
{theme.updatedAt.toDateString()}
diff --git a/src/components/ThemeSaver.tsx b/src/components/ThemeSaver.tsx
index 1920f2e..de5ec15 100644
--- a/src/components/ThemeSaver.tsx
+++ b/src/components/ThemeSaver.tsx
@@ -2,13 +2,22 @@ import React, { useState, useTransition, useEffect } from 'react'
import { useTheme } from '@/contexts/ThemeContext'
import { Input } from '@/components/ui/input'
import { ActionButton } from '@/components/ActionButton'
-import { useUser } from '@clerk/nextjs'
import { Switch } from '@/components/ui/switch'
+import { useUser } from '@clerk/nextjs'
-const ThemeSaver: React.FC = () => {
+interface ThemeSaverProps {
+ themeName: string
+ setThemeName: React.Dispatch>
+}
+
+const ThemeSaver: React.FC = ({
+ themeName,
+ setThemeName,
+}: ThemeSaverProps) => {
const {
saveCurrentTheme,
updateCurrentTheme,
+ updateSelectedThemeType,
currentThemeId,
savedThemes,
isPublic,
@@ -22,25 +31,18 @@ const ThemeSaver: React.FC = () => {
syntaxSaturation,
scheme,
} = useTheme()
- const [themeName, setThemeName] = useState('')
- const { user } = useUser()
+ const { user } = useUser()
const [isPending, startTransition] = useTransition()
- useEffect(() => {
- if (currentThemeId) {
- const currentTheme = savedThemes.find(
- (theme) => theme.id === currentThemeId
- )
- if (currentTheme) {
- setThemeName(currentTheme.name)
- setIsPublic(currentTheme.public)
+ const handlePublicToggle = (checked: boolean) => {
+ startTransition(async () => {
+ if (currentThemeId) {
+ await updateSelectedThemeType(currentThemeId, checked)
}
- } else {
- setThemeName('')
- setIsPublic(false)
- }
- }, [currentThemeId, savedThemes, setIsPublic])
+ setIsPublic(checked)
+ })
+ }
const handleSave = () => {
if (!user) return
@@ -58,6 +60,7 @@ const ThemeSaver: React.FC = () => {
uiSaturation: uiSaturation,
syntaxSaturation: syntaxSaturation,
scheme: scheme,
+ userName: user.firstName + ' ' + user.lastName,
})
} else {
await saveCurrentTheme({
@@ -72,6 +75,7 @@ const ThemeSaver: React.FC = () => {
uiSaturation: uiSaturation,
syntaxSaturation: syntaxSaturation,
scheme: scheme,
+ userName: user.firstName + ' ' + user.lastName,
})
}
})
@@ -97,7 +101,7 @@ const ThemeSaver: React.FC = () => {
handlePublicToggle(checked)}
id="public-switch"
/>
diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx
index 994f3fd..3a0b6a4 100644
--- a/src/contexts/ThemeContext.tsx
+++ b/src/contexts/ThemeContext.tsx
@@ -27,7 +27,7 @@ import {
getThemesByUserId,
deleteTheme,
updateTheme,
- updateThemePublicity,
+ updateThemeType,
} from '@/lib/db/themes'
import type { SavedTheme } from '@/lib/types/colors'
import { useTheme as useNextTheme } from 'next-themes'
@@ -41,19 +41,22 @@ import type {
interface ThemeContextType {
isDark: boolean
+ setIsDark: (value: boolean) => void
+ isPublic: boolean
+ setIsPublic: (value: boolean) => void
baseHue: number
+ setBaseHue: (value: number) => void
uiSaturation: number
+ setUiSaturation: (value: number) => void
syntaxSaturation: number
+ setSyntaxSaturation: (value: number) => void
scheme: ColorScheme
+ setScheme: (value: ColorScheme) => void
colors: UIColors
syntaxColors: SyntaxColors
- lockedColors: Set
- activeColor: string | null
- setIsDark: (value: boolean) => void
- setBaseHue: (value: number) => void
- setUiSaturation: (value: number) => void
- setSyntaxSaturation: (value: number) => void
- setScheme: (value: ColorScheme) => void
+ ansiColors: AnsiColors
+ regenerateAnsiColors: () => void
+ schemeHues: number[]
generateColors: (
options: Partial & {
lockedColors?: string[]
@@ -65,31 +68,33 @@ interface ThemeContextType {
newUiSaturation: number,
newSyntaxSaturation: number
) => void
+ lockedColors: Set
toggleColorLock: (colorKey: string) => void
+ activeColor: string | null
setActiveColor: (colorKey: string | null) => void
handleColorChange: (colorKey: string, newColor: string) => void
- ansiColors: AnsiColors
- regenerateAnsiColors: () => void
- schemeHues: number[]
+
savedThemes: SavedTheme[]
setSavedThemes: (themes: SavedTheme[]) => void
+ loadSavedThemes: (userId: string) => Promise
+
saveCurrentTheme: (
theme: Omit
) => Promise
loadTheme: (theme: SavedTheme) => void
- deleteTheme: (themeId: number) => Promise
- loadSavedThemes: (userId: string) => Promise
- updateThemePublicity: (themeId: number, isPublic: boolean) => Promise
+ deleteSavedTheme: (themeId: number) => Promise
+
currentThemeId: number | null
setCurrentThemeId: (id: number | null) => void
updateCurrentTheme: (
id: number,
theme: Partial>
) => Promise
+
+ updateSelectedThemeType: (themeId: number, isPublic: boolean) => Promise
+
isOnigasmInitialized: boolean
setIsOnigasmInitialized: (value: boolean) => void
- isPublic: boolean
- setIsPublic: (value: boolean) => void
}
const ThemeContext = createContext(undefined)
@@ -100,6 +105,7 @@ export const ThemeProvider: React.FC<{
}> = ({ children, userId }) => {
const { setTheme, theme } = useNextTheme()
const [isDark, setIsDarkState] = useState(theme === 'dark' ? true : false)
+ const [isPublic, setIsPublic] = useState(false)
const [baseHue, setBaseHueState] = useState(Math.floor(Math.random() * 360))
const [uiSaturation, setUiSaturationState] = useState(30)
const [syntaxSaturation, setSyntaxSaturationState] = useState(70)
@@ -107,17 +113,16 @@ export const ThemeProvider: React.FC<{
const [colors, setColors] = useState(initialColors)
const [syntaxColors, setSyntaxColors] =
useState(initialSyntaxColors)
- const [lockedColors, setLockedColors] = useState>(new Set())
- const [activeColor, setActiveColor] = useState(null)
const [ansiColors, setAnsiColors] = useState(() =>
generateAnsiColors(initialColors.BG1)
)
+ const [lockedColors, setLockedColors] = useState>(new Set())
+ const [activeColor, setActiveColor] = useState(null)
const [schemeHues, setSchemeHues] = useState([])
const generateColorsTimeoutRef = useRef(null)
const [savedThemes, setSavedThemes] = useState([])
const [currentThemeId, setCurrentThemeId] = useState(null)
const [isOnigasmInitialized, setIsOnigasmInitialized] = useState(false)
- const [isPublic, setIsPublic] = useState(false)
const loadSavedThemes = useCallback(async (userId: string) => {
if (userId) {
@@ -130,10 +135,10 @@ export const ThemeProvider: React.FC<{
async (theme: Omit) => {
const savedTheme = await saveTheme(theme)
setSavedThemes((prev) => [...prev, savedTheme])
+ setCurrentThemeId(savedTheme.id)
},
[]
)
-
const updateCurrentTheme = useCallback(
async (
id: number,
@@ -183,15 +188,15 @@ export const ThemeProvider: React.FC<{
[setTheme]
)
- const deleteThemeFromContext = useCallback(async (themeId: number) => {
+ const deleteSavedTheme = useCallback(async (themeId: number) => {
await deleteTheme(themeId)
setSavedThemes((prev) => prev.filter((theme) => theme.id !== themeId))
}, [])
- const updateThemePublicityInContext = useCallback(
+ const updateSelectedThemeType = useCallback(
async (themeId: number, isPublic: boolean) => {
try {
- await updateThemePublicity(themeId, isPublic)
+ await updateThemeType(themeId, isPublic)
setSavedThemes((prev) =>
prev.map((theme) =>
theme.id === themeId ? { ...theme, public: isPublic } : theme
@@ -407,7 +412,7 @@ export const ThemeProvider: React.FC<{
saveCurrentTheme,
loadTheme,
loadSavedThemes,
- deleteTheme: deleteThemeFromContext,
+ deleteSavedTheme,
setIsDark,
setBaseHue,
setUiSaturation,
@@ -425,7 +430,7 @@ export const ThemeProvider: React.FC<{
updateCurrentTheme,
isOnigasmInitialized,
setIsOnigasmInitialized,
- updateThemePublicity: updateThemePublicityInContext,
+ updateSelectedThemeType,
isPublic,
setIsPublic,
}
diff --git a/src/lib/db/themes.ts b/src/lib/db/themes.ts
index f880b4b..c6c8ec6 100644
--- a/src/lib/db/themes.ts
+++ b/src/lib/db/themes.ts
@@ -3,28 +3,34 @@
import { revalidatePath } from 'next/cache'
import { ColorScheme, type SavedTheme, SavedThemeSchema } from '../types/colors'
import { db } from '../drizzle/db'
-import { ThemesTable } from '../drizzle/schema'
+import { ThemesTable, UsersTable } from '../drizzle/schema'
import { eq } from 'drizzle-orm'
import { generateVSIX } from '@/lib/generator/exportVSIX'
-export async function downloadThemeVSIX(
- themeId: number
-): Promise {
- const theme = await getThemeById(themeId)
- if (!theme) return null
+export async function getPublicThemes(): Promise {
+ const results = await db
+ .select()
+ .from(ThemesTable)
+ .where(eq(ThemesTable.public, true))
- const vsixBuffer = await generateVSIX(theme)
- return vsixBuffer
+ return results.map(parseSavedTheme)
}
-export async function updateThemePublicity(themeId: number, isPublic: boolean) {
- await updateThemePublicityInDb(themeId, isPublic)
- revalidatePath('/saved-themes')
+export async function getThemesByUserId(userId: string): Promise {
+ const results = await db
+ .select()
+ .from(ThemesTable)
+ .where(eq(ThemesTable.userId, userId))
+ return results.map(parseSavedTheme)
}
-export async function deleteTheme(themeId: number) {
- await db.delete(ThemesTable).where(eq(ThemesTable.id, themeId))
- revalidatePath('/saved-themes')
+export async function getThemeById(id: number): Promise {
+ const result = await db
+ .select()
+ .from(ThemesTable)
+ .where(eq(ThemesTable.id, id))
+ .limit(1)
+ return result[0] ? parseSavedTheme(result[0]) : null
}
export async function saveTheme(
@@ -42,7 +48,7 @@ export async function saveTheme(
return savedTheme
}
-async function updateThemePublicityInDb(
+export async function updateThemeType(
themeId: number,
isPublic: boolean
): Promise {
@@ -51,7 +57,7 @@ async function updateThemePublicityInDb(
.set({ public: isPublic })
.where(eq(ThemesTable.id, themeId))
.returning()
-
+ revalidatePath('/saved-themes')
return parseSavedTheme(updatedTheme)
}
@@ -75,41 +81,19 @@ export async function updateTheme(
return updatedTheme
}
-export async function getThemesByUserId(userId: string): Promise {
- const results = await db
- .select()
- .from(ThemesTable)
- .where(eq(ThemesTable.userId, userId))
- return results.map(parseSavedTheme)
-}
-
-export async function getThemeById(id: number): Promise {
- const result = await db
- .select()
- .from(ThemesTable)
- .where(eq(ThemesTable.id, id))
- .limit(1)
- return result[0] ? parseSavedTheme(result[0]) : null
+export async function deleteTheme(themeId: number) {
+ await db.delete(ThemesTable).where(eq(ThemesTable.id, themeId))
+ revalidatePath('/saved-themes')
}
-export async function getPublicThemes(): Promise {
- const results = await db
- .select()
- .from(ThemesTable)
- .where(eq(ThemesTable.public, true))
- return results.map(parseSavedTheme)
-}
+export async function downloadThemeVSIX(
+ themeId: number
+): Promise {
+ const theme = await getThemeById(themeId)
+ if (!theme) return null
-function safeJsonParse(value: any) {
- if (typeof value === 'string') {
- try {
- return JSON.parse(value)
- } catch (error) {
- console.error('Error parsing JSON:', error)
- return value
- }
- }
- return value
+ const vsixBuffer = await generateVSIX(theme)
+ return vsixBuffer
}
function parseSavedTheme(rawTheme: any): SavedTheme {
@@ -134,3 +118,15 @@ function parseSavedTheme(rawTheme: any): SavedTheme {
throw error
}
}
+
+function safeJsonParse(value: any) {
+ if (typeof value === 'string') {
+ try {
+ return JSON.parse(value)
+ } catch (error) {
+ console.error('Error parsing JSON:', error)
+ return value
+ }
+ }
+ return value
+}
diff --git a/src/lib/drizzle/schema.ts b/src/lib/drizzle/schema.ts
index 65a254e..f9d7c07 100644
--- a/src/lib/drizzle/schema.ts
+++ b/src/lib/drizzle/schema.ts
@@ -34,15 +34,16 @@ export const ThemesTable = pgTable(
id: serial('id').primaryKey(),
name: text('name').notNull(),
userId: text('user_id').notNull(),
+ userName: text('user_name').notNull(),
public: boolean('public').notNull().default(false),
- uiColors: json('ui_colors').notNull(),
- syntaxColors: json('syntax_colors').notNull(),
- ansiColors: json('ansi_colors').notNull(),
+ isDark: boolean('is_dark').notNull(),
baseHue: integer('base_hue').notNull(),
uiSaturation: integer('ui_saturation').notNull(),
syntaxSaturation: integer('syntax_saturation').notNull(),
scheme: text('scheme').notNull(),
- isDark: boolean('is_dark').notNull(),
+ uiColors: json('ui_colors').notNull(),
+ syntaxColors: json('syntax_colors').notNull(),
+ ansiColors: json('ansi_colors').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
},
diff --git a/src/lib/types/colors.ts b/src/lib/types/colors.ts
index 701b964..8b8cf8b 100644
--- a/src/lib/types/colors.ts
+++ b/src/lib/types/colors.ts
@@ -297,6 +297,7 @@ export const SavedThemeSchema = z.object({
name: z.string(),
public: z.boolean(),
userId: z.string(),
+ userName: z.optional(z.string()),
uiColors: UIColorsSchema,
syntaxColors: SyntaxColorsSchema,
ansiColors: AnsiColorsSchema,