Skip to content

Commit 523a1f3

Browse files
refactor(language,ui): better language support and ui
1 parent 208b960 commit 523a1f3

11 files changed

Lines changed: 884 additions & 298 deletions

File tree

apps/web/package.json

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@radix-ui/react-tooltip": "^1.2.8",
1616
"@tanstack/react-form": "^1.12.3",
1717
"@tanstack/react-query": "^5.85.5",
18+
"@uiw/codemirror-extensions-langs": "^4.25.4",
1819
"@uiw/codemirror-themes": "^4.25.3",
1920
"@uiw/react-codemirror": "^4.25.3",
2021
"babel-plugin-react-compiler": "^1.0.0",
@@ -41,23 +42,8 @@
4142
},
4243
"devDependencies": {
4344
"@codemirror/commands": "^6.10.0",
44-
"@codemirror/lang-cpp": "^6.0.3",
45-
"@codemirror/lang-go": "^6.0.1",
46-
"@codemirror/lang-html": "^6.4.11",
47-
"@codemirror/lang-java": "^6.0.2",
48-
"@codemirror/lang-javascript": "^6.2.4",
49-
"@codemirror/lang-json": "^6.0.2",
50-
"@codemirror/lang-less": "^6.0.2",
5145
"@codemirror/lang-lezer": "^6.0.2",
52-
"@codemirror/lang-markdown": "^6.5.0",
53-
"@codemirror/lang-php": "^6.0.2",
54-
"@codemirror/lang-python": "^6.2.1",
5546
"@codemirror/lang-rust": "^6.0.2",
56-
"@codemirror/lang-sass": "^6.0.2",
57-
"@codemirror/lang-sql": "^6.10.0",
58-
"@codemirror/lang-xml": "^6.1.0",
59-
"@lezer/highlight": "^1.2.3",
60-
"@nextjournal/lang-clojure": "^1.0.0",
6147
"@paircode/config": "workspace:*",
6248
"@paircode/db": "workspace:*",
6349
"@replit/codemirror-lang-csharp": "^6.2.0",

apps/web/src/app/page.tsx

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import NavBar from "@/components/navbar";
66
import HeroSection from "@/components/hero-section";
77
import HeroSectionMobile from "@/components/hero-section-mobile";
88
import PostLoginRedirectHandler from "@/components/post-login-redirect-handler";
9+
import Image from "next/image";
10+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
911

1012
export const metadata: Metadata = {
1113
title: 'PairCode - Real-Time Code Collaboration with Video',
@@ -132,7 +134,7 @@ export default function Home() {
132134
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
133135
/>
134136
<div
135-
className="absolute inset-0 bg-grid-pattern opacity-[0.02] dark:opacity-[0.1] text-foreground pointer-events-none"
137+
className="absolute inset-0 bg-grid-pattern opacity-[0.02] dark:opacity-[0.2] text-foreground pointer-events-none"
136138
style={{
137139
backgroundImage: `linear-gradient(to right, currentColor 1px, transparent 1px),
138140
linear-gradient(to bottom, currentColor 1px, transparent 1px)`,
@@ -153,7 +155,7 @@ export default function Home() {
153155

154156
<ActionButtons showGithub />
155157

156-
<section className="my-20 md:my-30 -mx-4 sm:mx-0">
158+
<section className="my-20 md:my-40 -mx-4 sm:mx-0">
157159
<div className="w-full flex justify-center items-center flex-col">
158160
<div className="relative overflow-hidden rounded-lg sm:rounded-2xl border border-[#DFDFDF] shadow-2xl p-2 dark:border-[#2E2E2E]">
159161
<div className="flex gap-3 ml-3 my-2">
@@ -188,6 +190,45 @@ export default function Home() {
188190
</div>
189191
</section>
190192

193+
<section className="mb-20 md:mb-40 mx-4 sm:mx-10 md:mx-40">
194+
<div className="flex flex-col xl:flex-row justify-between gap-8 xl:gap-12 items-center">
195+
<div className='text-center xl:text-left text-lg sm:text-xl md:text-2xl text-balance xl:max-w-md xl:shrink-0 tracking-tight'>
196+
<div className="text-[#898989]">Use PairCode With</div>
197+
70+ languages
198+
</div>
199+
200+
<div className="flex justify-center items-center w-full">
201+
<div className="flex flex-wrap justify-center gap-2 sm:gap-3 xl:flex-nowrap items-center">
202+
<Image width={40} alt="JavaScript" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/javascript/javascript.png" height={40} className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(247,223,30,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
203+
<Image width={40} alt="TypeScript" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/typescript/typescript.png" height={40} className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(49,120,198,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
204+
<Image width={40} height={40} alt="C" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/c/c.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(93,108,191,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
205+
<Image width={40} height={40} alt="C++" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/cpp/cpp.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(0,149,221,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
206+
<Image width={40} height={40} alt="C#" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/csharp/csharp.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(149,96,199,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
207+
<Image width={40} height={40} alt="CSS" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/css/css.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(21,114,182,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
208+
<Image width={40} height={40} alt="Go" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/go/go.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(0,173,216,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
209+
<Image width={40} height={40} alt="HTML" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/html/html.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(227,79,38,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
210+
<Image width={40} height={40} alt="Java" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/java/java.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(237,76,60,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
211+
<Image width={40} height={40} alt="Lua" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/lua/lua.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(0,0,128,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
212+
<Image width={40} height={40} alt="PHP" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/php/php.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(119,123,180,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
213+
<Image width={40} height={40} alt="Python" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/python/python.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(55,118,171,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
214+
<Image width={40} height={40} alt="R" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/r/r.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(25,140,219,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
215+
<Image width={40} height={40} alt="Ruby" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/ruby/ruby.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(204,52,45,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
216+
<Image width={40} height={40} alt="Swift" src="https://cdn.jsdelivr.net/npm/programming-languages-logos/src/swift/swift.png" className="grayscale hover:grayscale-0 hover:drop-shadow-[0_0_12px_rgba(240,81,56,0.8)] transition-all duration-300 w-8 h-8 sm:w-10 sm:h-10" />
217+
<Tooltip>
218+
<TooltipTrigger>
219+
<div className="flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full border-2 border-dashed border-muted-foreground/30 text-muted-foreground text-xs sm:text-sm font-medium hover:border-foreground/50 hover:text-foreground hover:drop-shadow-[0_0_8px_rgba(255,255,255,0.3)] transition-all duration-300">
220+
+58
221+
</div>
222+
</TooltipTrigger>
223+
<TooltipContent>
224+
<p className="text-sm">And 58+ more</p>
225+
</TooltipContent>
226+
</Tooltip>
227+
</div>
228+
</div>
229+
</div>
230+
</section>
231+
191232
<section className="mt-10">
192233
<h2 className='text-3xl sm:text-4xl md:text-5xl tacking-wide text-balance text-center'>Join the community</h2>
193234
<p className='text-lg sm:text-xl text-muted-foreground text-center mt-5'>Discover what our community has to say about their experience.</p>

apps/web/src/app/vscode-extension/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export default function ExtensionPage() {
8080
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
8181
/>
8282
<div
83-
className="absolute inset-0 bg-grid-pattern opacity-[0.02] dark:opacity-[0.1] text-foreground pointer-events-none"
83+
className="absolute inset-0 bg-grid-pattern opacity-[0.02] dark:opacity-[0.2] text-foreground pointer-events-none"
8484
style={{
8585
backgroundImage: `linear-gradient(to right, currentColor 1px, transparent 1px),
8686
linear-gradient(to bottom, currentColor 1px, transparent 1px)`,

apps/web/src/components/code-share.tsx

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,73 +13,92 @@ interface CodeShareProps {
1313
isOutSourcedScreen?: boolean
1414
}
1515

16+
// Customize themes: https://uiwjs.github.io/react-codemirror/#/editor/theme/single
1617
const myDarkTheme = createTheme({
17-
1818
theme: "dark",
1919
settings: {
20-
background: "#282C34",
20+
backgroundImage: '',
21+
selection: 'rgba(137, 180, 250, 0.3)',
22+
gutterBorder: '#313244',
23+
gutterActiveForeground: '#89b4fa',
24+
background: "#1e1e2e",
2125
foreground: "#cdd6f4",
2226
caret: "#f5e0dc",
23-
selection: "#585b70aa",
24-
selectionMatch: "#89b4fa",
25-
lineHighlight: "#313244",
26-
gutterBackground: "#1e1e2e",
27+
selectionMatch: 'rgba(137, 180, 250, 0.2)',
28+
lineHighlight: "rgba(137, 180, 250, 0.05)",
29+
gutterBackground: "#181825",
2730
gutterForeground: "#6c7086"
2831
},
2932
styles: [
30-
{ tag: t.comment, color: "#6c7086" },
33+
{ tag: t.comment, color: "#6c7086", fontStyle: "italic" },
3134
{ tag: t.variableName, color: "#cba6f7" },
35+
{ tag: t.special(t.variableName), color: "#f5c2e7" },
3236
{ tag: t.quote, color: "#cdd6f4" },
3337
{ tag: t.moduleKeyword, color: "#f38ba8" },
3438
{ tag: [t.string, t.special(t.brace)], color: "#a6e3a1" },
3539
{ tag: t.number, color: "#fab387" },
3640
{ tag: t.bool, color: "#fab387" },
3741
{ tag: t.null, color: "#fab387" },
38-
{ tag: t.keyword, color: "#f38ba8" },
42+
{ tag: t.keyword, color: "#cba6f7" },
3943
{ tag: t.operator, color: "#89dceb" },
44+
{ tag: t.function(t.variableName), color: "#89b4fa" },
4045
{ tag: t.className, color: "#f9e2af" },
46+
{ tag: t.definition(t.className), color: "#f9e2af" },
4147
{ tag: t.definition(t.typeName), color: "#f9e2af" },
4248
{ tag: t.typeName, color: "#f9e2af" },
4349
{ tag: t.angleBracket, color: "#cdd6f4" },
4450
{ tag: t.tagName, color: "#89b4fa" },
45-
{ tag: t.attributeName, color: "#f9e2af" },
46-
{ tag: t.propertyName, color: "#a6e3a1" },
47-
{ tag: t.annotation, color: "#f38ba8" }
51+
{ tag: t.attributeName, color: "#fab387" },
52+
{ tag: t.propertyName, color: "#94e2d5" },
53+
{ tag: t.annotation, color: "#f38ba8" },
54+
{ tag: t.regexp, color: "#f38ba8" },
55+
{ tag: t.labelName, color: "#89dceb" },
56+
{ tag: t.processingInstruction, color: "#f38ba8" }
4857
]
4958
});
5059

60+
5161
const myLightTheme = createTheme({
52-
theme: "light",
62+
theme: 'light',
5363
settings: {
54-
background: "#ffffff",
55-
foreground: "#4c4f69",
56-
caret: "#dc8a78",
57-
selection: "#dce0e8aa",
58-
selectionMatch: "#7287fd",
59-
lineHighlight: "#eff1f5",
60-
gutterBackground: "#ffffff",
61-
gutterForeground: "#9ca0b0"
64+
background: '#eff1f5',
65+
backgroundImage: '',
66+
foreground: '#4c4f69',
67+
caret: '#dc8a78',
68+
selection: 'rgba(220, 138, 120, 0.3)',
69+
selectionMatch: 'rgba(220, 138, 120, 0.2)',
70+
gutterBackground: '#e6e9ef',
71+
gutterForeground: '#6c6f85',
72+
gutterBorder: '#ccd0da',
73+
gutterActiveForeground: '#1e66f5',
74+
lineHighlight: 'rgba(220, 138, 120, 0.08)',
6275
},
6376
styles: [
64-
{ tag: t.comment, color: "#9ca0b0" },
65-
{ tag: t.variableName, color: "#8839ef" },
66-
{ tag: t.quote, color: "#4c4f69" },
67-
{ tag: t.moduleKeyword, color: "#d20f39" },
68-
{ tag: [t.string, t.special(t.brace)], color: "#40a02b" },
69-
{ tag: t.number, color: "#df8e1d" },
70-
{ tag: t.bool, color: "#df8e1d" },
71-
{ tag: t.null, color: "#df8e1d" },
72-
{ tag: t.keyword, color: "#d20f39" },
73-
{ tag: t.operator, color: "#04a5e5" },
74-
{ tag: t.className, color: "#e5c890" },
75-
{ tag: t.definition(t.typeName), color: "#e5c890" },
76-
{ tag: t.typeName, color: "#e5c890" },
77-
{ tag: t.angleBracket, color: "#4c4f69" },
78-
{ tag: t.tagName, color: "#1e66f5" },
79-
{ tag: t.attributeName, color: "#e5c890" },
80-
{ tag: t.propertyName, color: "#40a02b" },
81-
{ tag: t.annotation, color: "#d20f39" }
82-
]
77+
{ tag: t.comment, color: '#9ca0b0', fontStyle: "italic" },
78+
{ tag: t.variableName, color: '#8839ef' },
79+
{ tag: t.special(t.variableName), color: '#ea76cb' },
80+
{ tag: t.quote, color: '#4c4f69' },
81+
{ tag: t.moduleKeyword, color: '#d20f39' },
82+
{ tag: [t.string, t.special(t.brace)], color: '#40a02b' },
83+
{ tag: t.number, color: '#fe640b' },
84+
{ tag: t.bool, color: '#fe640b' },
85+
{ tag: t.null, color: '#fe640b' },
86+
{ tag: t.keyword, color: '#8839ef' },
87+
{ tag: t.operator, color: '#179299' },
88+
{ tag: t.function(t.variableName), color: '#1e66f5' },
89+
{ tag: t.className, color: '#df8e1d' },
90+
{ tag: t.definition(t.className), color: '#df8e1d' },
91+
{ tag: t.definition(t.typeName), color: '#df8e1d' },
92+
{ tag: t.typeName, color: '#df8e1d' },
93+
{ tag: t.angleBracket, color: '#4c4f69' },
94+
{ tag: t.tagName, color: '#1e66f5' },
95+
{ tag: t.attributeName, color: '#fe640b' },
96+
{ tag: t.propertyName, color: '#04a5e5' },
97+
{ tag: t.annotation, color: '#d20f39' },
98+
{ tag: t.regexp, color: '#d20f39' },
99+
{ tag: t.labelName, color: '#179299' },
100+
{ tag: t.processingInstruction, color: '#d20f39' }
101+
],
83102
});
84103

85104

apps/web/src/components/lang-dropdown.tsx

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import {
77
DropdownMenuContent,
88
DropdownMenuTrigger,
99
} from "@/components/ui/dropdown-menu"
10+
import { Input } from "@/components/ui/input"
11+
import { supportedLanguages } from "@/lib/languageExtensions"
12+
import { useState } from "react"
13+
import { X } from "lucide-react"
1014

1115
interface DropDownProps {
1216
onLanguageChange: (language: string) => void;
@@ -17,44 +21,60 @@ export function DropdownMenuLanguageCheckboxes({
1721
selectedLanguage,
1822
onLanguageChange
1923
}: DropDownProps) {
20-
const languages = [
21-
"cpp",
22-
"html",
23-
"java",
24-
"go",
25-
"javascript",
26-
"typescript",
27-
"json",
28-
"markdown",
29-
"php",
30-
"python",
31-
"rust",
32-
"sql",
33-
"xml",
34-
"less",
35-
"sass",
36-
"clojure",
37-
"csharp",
38-
"lezer"
39-
]
24+
const [searchQuery, setSearchQuery] = useState("")
25+
26+
const filteredLanguages = supportedLanguages.filter((language) =>
27+
language.toLowerCase().includes(searchQuery.toLowerCase())
28+
)
4029

4130
return (
4231
<DropdownMenu>
4332
<DropdownMenuTrigger asChild>
44-
<Button variant="outline" className="w-27 bg-[#D1D5DC] dark:bg-[#292929] text-black dark:text-[#D1D5DC] hover:bg-[#D1D5DC] dark:hover:bg-[#292929] dark:hover:text-white">{selectedLanguage || 'default'}</Button>
33+
<Button variant="outline" className="w-27 bg-[#D1D5DC] dark:bg-[#292929] text-black dark:text-[#D1D5DC] hover:bg-[#D1D5DC] dark:hover:bg-[#292929] dark:hover:text-white">
34+
{selectedLanguage || 'py'}
35+
</Button>
4536
</DropdownMenuTrigger>
4637
<DropdownMenuContent className="w-56 text-black dark:bg-[#292929] dark:text-[#D1D5DC]">
47-
{languages.map((language) => (
48-
<DropdownMenuCheckboxItem
49-
key={language}
50-
checked={selectedLanguage === language}
51-
onCheckedChange={(checked: boolean) => {
52-
if (checked) onLanguageChange(language)
53-
}}
54-
>
55-
{language}
56-
</DropdownMenuCheckboxItem>
57-
))}
38+
<div className="px-2 py-1.5 relative">
39+
<Input
40+
placeholder="Search languages..."
41+
value={searchQuery}
42+
onChange={(e) => setSearchQuery(e.target.value)}
43+
className="h-8 bg-white dark:bg-[#1a1a1a] border-gray-300 dark:border-gray-600 pr-8"
44+
onKeyDown={(e) => e.stopPropagation()}
45+
onClick={(e) => e.stopPropagation()}
46+
/>
47+
{searchQuery && (
48+
<button
49+
onClick={() => setSearchQuery("")}
50+
className="absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
51+
>
52+
<X className="h-4 w-4" />
53+
</button>
54+
)}
55+
</div>
56+
<div className="max-h-[300px] overflow-y-auto">
57+
{filteredLanguages.length > 0 ? (
58+
filteredLanguages.map((language) => (
59+
<DropdownMenuCheckboxItem
60+
key={language}
61+
checked={selectedLanguage === language}
62+
onCheckedChange={(checked: boolean) => {
63+
if (checked) {
64+
onLanguageChange(language)
65+
setSearchQuery("")
66+
}
67+
}}
68+
>
69+
{language}
70+
</DropdownMenuCheckboxItem>
71+
))
72+
) : (
73+
<div className="px-2 py-6 text-center text-sm text-gray-500 dark:text-gray-400">
74+
No languages found
75+
</div>
76+
)}
77+
</div>
5878
</DropdownMenuContent>
5979
</DropdownMenu>
6080
)

apps/web/src/components/outsource-client.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function OutsourceClient({ session }: OutsourceClientProps) {
4343

4444
setCode(sessionData.code || '');
4545
// BUG: if the language does not exist or so
46-
setSelectedLanguage(sessionData.language || 'python');
46+
setSelectedLanguage(sessionData.language || 'py');
4747

4848
toast.success('Code imported from VS Code!');
4949
} catch (error) {
@@ -68,18 +68,18 @@ export default function OutsourceClient({ session }: OutsourceClientProps) {
6868
setSelectedLanguage(outSourcedCodeLang);
6969
toast.success('Code imported from VS Code!');
7070
} else {
71-
setSelectedLanguage('python');
72-
toast.warning('Language extension is not supported. Falling back to default');
71+
setSelectedLanguage('text');
72+
toast.warning('Language extension is not supported. Falling back to text');
7373
}
7474
} else {
7575
// No code provided
76-
setCode(defaultCodeSnippets['python'] || '');
77-
setSelectedLanguage('python');
76+
setCode(defaultCodeSnippets['py'] || '');
77+
setSelectedLanguage('py');
7878
}
7979
} catch (error) {
8080
toast.error('Failed to load code from URL');
81-
setCode(defaultCodeSnippets['python'] || '');
82-
setSelectedLanguage('python');
81+
setCode(defaultCodeSnippets['py'] || '');
82+
setSelectedLanguage('py');
8383
}
8484
}
8585

0 commit comments

Comments
 (0)