diff --git a/.gitignore b/.gitignore index 2aba7aa9..5ea76d9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ .nx/ .turbo/ +.million/ .ignore/ dist/ .next/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 064723dc..da70bce1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,7 @@ "vite.config.chrome.ts": "vite", "vite.config.firefox.ts": "vite" }, - "cSpell.words": ["posthog", "Resolvables"], + "cSpell.words": ["color", "posthog", "Resolvables"], "todo-tree.general.tags": ["=====", "NOTE", "IDEA", "TODO", "FIX", "HACK"], "todo-tree.highlights.customHighlight": { "=====": { diff --git a/apps/browser-extension/package.json b/apps/browser-extension/package.json index d57a96d7..cbbc5dcb 100644 --- a/apps/browser-extension/package.json +++ b/apps/browser-extension/package.json @@ -16,14 +16,14 @@ "@evaluate/engine": "workspace:^", "@evaluate/helpers": "workspace:^", "@evaluate/shapes": "workspace:^", - "@evaluate/style": "workspace:^", + "@evaluate/styles": "workspace:^", "@t3-oss/env-core": "^0.11.1", - "lucide-react": "^0.475.0", - "posthog-js": "^1.218.1", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "sonner": "^1.7.4", - "tailwind-merge": "^2.6.0", + "lucide-react": "^0.476.0", + "posthog-js": "^1.223.4", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "sonner": "^2.0.1", + "tailwind-merge": "^3.0.2", "webext-bridge": "^6.0.1", "webextension-polyfill": "^0.12.0", "zod": "3.22.4" @@ -34,13 +34,14 @@ "@babel/traverse": "^7.26.9", "@babel/types": "^7.26.9", "@crxjs/vite-plugin": "2.0.0-beta.30", + "@tailwindcss/vite": "^4.0.9", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.6", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/webextension-polyfill": "^0.12.1", "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "^10.4.20", + "tailwindcss": "^4.0.9", "vite": "3.2.11", "vite-plugin-zip-pack": "^1.2.4", "vite-tsconfig-paths": "^5.1.4" diff --git a/apps/browser-extension/postcss.config.cjs b/apps/browser-extension/postcss.config.cjs deleted file mode 100644 index 12a703d9..00000000 --- a/apps/browser-extension/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/apps/browser-extension/src/content-script/execution/dialog.tsx b/apps/browser-extension/src/content-script/execution/dialog.tsx index f3fe617e..b349b5b3 100644 --- a/apps/browser-extension/src/content-script/execution/dialog.tsx +++ b/apps/browser-extension/src/content-script/execution/dialog.tsx @@ -35,7 +35,7 @@ export function ExecutionDialog({ return ( - + diff --git a/apps/browser-extension/src/content-script/index.tsx b/apps/browser-extension/src/content-script/index.tsx index 970a1815..a9574fe7 100644 --- a/apps/browser-extension/src/content-script/index.tsx +++ b/apps/browser-extension/src/content-script/index.tsx @@ -1,10 +1,10 @@ import { Toaster } from '@evaluate/components/toast'; -import tailwindCss from '@evaluate/style/.css?inline'; import { createRoot } from 'react-dom/client'; import sonnerCss from 'sonner/dist/styles.css?inline'; import { onMessage, sendMessage } from 'webext-bridge/content-script'; import { extractRuntimeResolvables } from '~/helpers/runtime-resolvables'; import posthog, { sessionLog } from '~/services/posthog'; +import tailwindCss from '../styles.css?inline'; import { Execution } from './execution'; import { createIsolatedElement } from './shadow-root'; @@ -31,7 +31,8 @@ const { parentElement, portalElement, isolatedElement } = createIsolatedElement( inter: 'https://rsms.me/inter/inter.css', }, styles: { - tailwind: tailwindCss.replaceAll(':root', ':host'), + // TODO: Figure out why this is necessary + tailwind: tailwindCss.replaceAll('border-style:', '__ignore__:'), sonner: sonnerCss, }, isolatedEvents: true, diff --git a/apps/browser-extension/src/styles.css b/apps/browser-extension/src/styles.css new file mode 100644 index 00000000..80fb7b0b --- /dev/null +++ b/apps/browser-extension/src/styles.css @@ -0,0 +1,3 @@ +@import "@evaluate/styles"; +@import "@evaluate/components"; +@source "."; diff --git a/apps/browser-extension/tailwind.config.cjs b/apps/browser-extension/tailwind.config.cjs deleted file mode 100644 index 3285e92d..00000000 --- a/apps/browser-extension/tailwind.config.cjs +++ /dev/null @@ -1,12 +0,0 @@ -const twComponentsPreset = require('@evaluate/components/tailwind-preset'); -const twStylesPreset = require('@evaluate/style/tailwind-preset'); - -/** @type {import('tailwindcss').Config} */ -const tailwindConfig = { - presets: [twStylesPreset, twComponentsPreset], - content: ['./src/**/**/*'] - .concat(twStylesPreset.content) - .concat(twComponentsPreset.content), -}; - -module.exports = tailwindConfig; diff --git a/apps/browser-extension/vite.config.chrome.ts b/apps/browser-extension/vite.config.chrome.ts index 25430670..0215f0c1 100644 --- a/apps/browser-extension/vite.config.chrome.ts +++ b/apps/browser-extension/vite.config.chrome.ts @@ -1,4 +1,5 @@ import { chromeExtension } from '@crxjs/vite-plugin'; +import tailwindCss from '@tailwindcss/vite'; import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; import zipPack from 'vite-plugin-zip-pack'; @@ -9,6 +10,7 @@ import { removeExternalScriptLoading } from './vite-plugins'; export default defineConfig({ plugins: [ removeExternalScriptLoading(), + tailwindCss(), tsconfigPaths(), react(), chromeExtension({ diff --git a/apps/browser-extension/vite.config.firefox.ts b/apps/browser-extension/vite.config.firefox.ts index 4121ee60..488f4cb2 100644 --- a/apps/browser-extension/vite.config.firefox.ts +++ b/apps/browser-extension/vite.config.firefox.ts @@ -1,4 +1,5 @@ import { chromeExtension } from '@crxjs/vite-plugin'; +import tailwindCss from '@tailwindcss/vite'; import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; import zipPack from 'vite-plugin-zip-pack'; @@ -9,6 +10,7 @@ import { removeExternalScriptLoading } from './vite-plugins'; export default defineConfig({ plugins: [ removeExternalScriptLoading(), + tailwindCss(), tsconfigPaths(), react(), chromeExtension({ diff --git a/apps/discord-bot/package.json b/apps/discord-bot/package.json index a6ddecdb..6f8f59a0 100644 --- a/apps/discord-bot/package.json +++ b/apps/discord-bot/package.json @@ -18,14 +18,14 @@ "dev": "pnpm build --watch" }, "dependencies": { - "@buape/carbon": "0.0.0-beta-20250204223027", + "@buape/carbon": "0.7.0", "@evaluate/engine": "workspace:^", "@evaluate/helpers": "workspace:^", "@evaluate/shapes": "workspace:^", "@t3-oss/env-core": "^0.11.1", "es-toolkit": "^1.32.0", "date-fns": "^4.1.0", - "posthog-node": "^4.6.0", + "posthog-node": "^4.7.0", "zod": "3.22.4" } } diff --git a/apps/website/next-env.d.ts b/apps/website/next-env.d.ts index 40c3d680..1b3be084 100644 --- a/apps/website/next-env.d.ts +++ b/apps/website/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/website/next.config.js b/apps/website/next.config.js index b0372547..f76d16bb 100644 --- a/apps/website/next.config.js +++ b/apps/website/next.config.js @@ -1,17 +1,26 @@ -import withBundleAnalyser from '@next/bundle-analyzer'; +import { fetchRuntimes } from '@evaluate/engine/runtimes'; /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, typescript: { ignoreBuildErrors: true }, - redirects: async () => [ - { - source: '/', - destination: '/playgrounds', - permanent: false, - }, - ], + redirects: async () => { + return [ + { + source: '/', + destination: '/playgrounds', + permanent: false, + }, + { + source: `/:slug(${(await fetchRuntimes()) + .map((r) => r.id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) + .join('|')})`, + destination: '/playgrounds/:slug', + permanent: true, + }, + ]; + }, rewrites: async () => [ { source: '/api/ingest/static/:path*', @@ -35,7 +44,12 @@ const nextConfig = { }, }; -const isTruthy = (v) => ['true', 't', '1'].includes(v); -export default withBundleAnalyser({ - enabled: isTruthy(String(process.env.ANALYSE)), -})(nextConfig); +const truthy = (v) => ['true', 't', '1'].includes(v); +export default [ + truthy(process.env.ANALYSE) && + (await import('@next/bundle-analyzer')).default({ enabled: true }), + !truthy(process.env.TURBOPACK) && + (await import('@million/lint')).next({ rsc: true }), +] + .filter(Boolean) + .reduce((acc, curr) => curr(acc), nextConfig); diff --git a/apps/website/package.json b/apps/website/package.json index 6fa8aa85..dc0d39c6 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -6,24 +6,25 @@ "check": "tsc --noEmit", "build": "use-env -p NEXT -P -- next build --no-lint", "start": "use-env -p NEXT -P -- next start", - "dev": "use-env -p NEXT -- next dev --turbo" + "dev": "use-env -p NEXT -- next dev --turbo", + "dev:lint": "use-env -p NEXT -- next dev" }, "dependencies": { - "@codemirror/commands": "^6.7.1", - "@codemirror/view": "^6.35.0", + "@codemirror/commands": "^6.8.0", + "@codemirror/view": "^6.36.3", "@evaluate/components": "workspace:^", "@evaluate/engine": "workspace:^", "@evaluate/helpers": "workspace:^", "@evaluate/hooks": "workspace:^", "@evaluate/shapes": "workspace:^", - "@evaluate/style": "workspace:^", - "@hookform/resolvers": "^3.10.0", + "@evaluate/styles": "workspace:^", + "@hookform/resolvers": "^4.1.2", "@t3-oss/env-nextjs": "^0.11.1", - "@tanstack/react-query": "^5.66.0", - "@tanstack/react-query-devtools": "^5.66.0", - "@uiw/codemirror-extensions-langs": "^4.23.6", - "@uiw/codemirror-theme-vscode": "^4.23.6", - "@uiw/react-codemirror": "^4.23.6", + "@tanstack/react-query": "^5.66.9", + "@tanstack/react-query-devtools": "^5.66.9", + "@uiw/codemirror-extensions-langs": "^4.23.8", + "@uiw/codemirror-theme-vscode": "^4.23.8", + "@uiw/react-codemirror": "^4.23.8", "@vercel/speed-insights": "^1.2.0", "date-fns": "^4.1.0", "discord-bot": "workspace:^", @@ -34,29 +35,31 @@ "fuse.js": "^7.1.0", "is-mobile": "^5.0.0", "jszip": "^3.10.1", - "lucide-react": "^0.475.0", + "lucide-react": "^0.476.0", "material-icon-theme": "^5.19.0", - "next": "^14.2.24", + "next": "^15.1.7", "next-themes": "npm:@wits/next-themes@^0.2.16", - "posthog-js": "^1.218.1", - "react": "^18.3.1", + "posthog-js": "^1.223.4", + "react": "^19.0.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^18.3.1", + "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", "react-hotkeys-hook": "^4.6.1", "sharp": "^0.33.5", - "tailwind-merge": "^2.6.0", + "tailwind-merge": "^3.0.2", + "type-fest": "^4.35.0", "virtual-file-explorer-backend": "^0.0.4", "vscode-icons-js": "^11.6.1", "zod": "3.22.4" }, "devDependencies": { - "@next/bundle-analyzer": "^14.2.24", - "@types/dompurify": "^3.2.0", + "@million/lint": "^1.0.14", + "@next/bundle-analyzer": "^15.1.7", + "@tailwindcss/postcss": "^4.0.9", "@types/file-saver": "^2.0.7", - "@types/react": "^18.3.18", + "@types/react": "^19.0.10", "autoprefixer": "^10.4.20", - "tailwindcss": "^3.4.17" + "tailwindcss": "^4.0.9" } } diff --git a/apps/website/postcss.config.cjs b/apps/website/postcss.config.cjs index 12a703d9..b4bee663 100644 --- a/apps/website/postcss.config.cjs +++ b/apps/website/postcss.config.cjs @@ -1,6 +1,6 @@ module.exports = { plugins: { - tailwindcss: {}, + '@tailwindcss/postcss': {}, autoprefixer: {}, }, }; diff --git a/apps/website/src/app/(editor)/playgrounds/[playground]/page.tsx b/apps/website/src/app/(editor)/playgrounds/[playground]/page.tsx index e9379d0d..505bbac6 100644 --- a/apps/website/src/app/(editor)/playgrounds/[playground]/page.tsx +++ b/apps/website/src/app/(editor)/playgrounds/[playground]/page.tsx @@ -14,9 +14,8 @@ export async function generateStaticParams() { return runtimes.map((r) => ({ playground: r.id })); } -export async function generateMetadata({ - params: { playground }, -}: PageProps<['playground']>) { +export async function generateMetadata(props: PageProps<['[playground]']>) { + const playground = (await props.params).playground; const runtime = await getRuntime(decodeURIComponent(playground)); if (!runtime) notFound(); @@ -28,9 +27,8 @@ export async function generateMetadata({ }); } -export default async function EditorPage({ - params: { playground }, -}: PageProps<['playground']>) { +export default async function EditorPage(props: PageProps<['[playground]']>) { + const playground = (await props.params).playground; const runtime = await getRuntime(decodeURIComponent(playground)); if (!runtime) notFound(); diff --git a/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/desktop.tsx b/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/desktop.tsx index 3339750e..a5fdbc2b 100644 --- a/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/desktop.tsx +++ b/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/desktop.tsx @@ -14,7 +14,7 @@ export function DesktopWrapper({ children }: React.PropsWithChildren) { defaultSize={15} minSize={10} collapsible={false} - className="m-1.5 rounded-xl border-2 bg-card" + className="m-1.5 rounded-lg border-2 bg-card" > {explorer} @@ -25,7 +25,7 @@ export function DesktopWrapper({ children }: React.PropsWithChildren) { defaultSize={55} minSize={35} collapsible={false} - className="m-1.5 rounded-xl border-2 bg-card" + className="m-1.5 rounded-lg border-2 bg-card" > {editor} @@ -36,7 +36,7 @@ export function DesktopWrapper({ children }: React.PropsWithChildren) { defaultSize={30} minSize={10} collapsible={false} - className="m-1.5 rounded-xl border-2 bg-card" + className="m-1.5 rounded-lg border-2 bg-card" > {terminal} diff --git a/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/mobile.tsx b/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/mobile.tsx index 19b551be..16b095ef 100644 --- a/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/mobile.tsx +++ b/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/mobile.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Sheet, SheetContent } from '@evaluate/components/sheet'; +import { Sheet, SheetBody } from '@evaluate/components/sheet'; import { useEventListener } from '@evaluate/hooks/event-listener'; import { Children, useState } from 'react'; @@ -15,7 +15,7 @@ export function MobileWrapper({ children }: React.PropsWithChildren) { return ( <> - setExplorerOpen(false)} @@ -27,13 +27,13 @@ export function MobileWrapper({ children }: React.PropsWithChildren) { > {explorer} - + -
{editor}
+
{editor}
- setExplorerOpen(false)} @@ -45,7 +45,7 @@ export function MobileWrapper({ children }: React.PropsWithChildren) { > {terminal} - + ); diff --git a/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/skeleton.tsx b/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/skeleton.tsx index 04956837..dc64db97 100644 --- a/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/skeleton.tsx +++ b/apps/website/src/app/(editor)/playgrounds/[playground]/wrapper/skeleton.tsx @@ -10,7 +10,7 @@ export function EditorWrapperSkeleton({ className }: { className?: string }) { )} > - + ); diff --git a/apps/website/src/app/(playgrounds)/playgrounds/playground-card-list.tsx b/apps/website/src/app/(playgrounds)/playgrounds/playground-card-list.tsx index e4891a98..88ba84cf 100644 --- a/apps/website/src/app/(playgrounds)/playgrounds/playground-card-list.tsx +++ b/apps/website/src/app/(playgrounds)/playgrounds/playground-card-list.tsx @@ -21,26 +21,28 @@ import { XIcon, } from 'lucide-react'; import { useDeferredValue, useEffect, useMemo } from 'react'; -import { useHashFragment } from '~/hooks/use-hash-fragment'; -import { useLocalStorage } from '~/hooks/use-local-storage'; -import { useQueryParameter } from '~/hooks/use-query-parameter'; +import { useHashFragment } from '~/hooks/hash-fragment'; +import { useLocalStorage } from '~/hooks/local-storage'; +import { useQueryParameter } from '~/hooks/query-parameter'; import { PlaygroundCard } from './playground-card'; export function PlaygroundCardList({ initialRuntimes, -}: { initialRuntimes: PartialRuntime[] }) { +}: { + initialRuntimes: PartialRuntime[]; +}) { const [search, setSearch] = useQueryParameter('search'); const deferredSearch = useDeferredValue(search); - const searchedRuntimes = useMemo(() => { - if (!deferredSearch) return initialRuntimes; - const engine = new Fuse(initialRuntimes, { + const searchEngine = useMemo(() => { + return new Fuse(initialRuntimes, { keys: ['name', 'aliases', 'tags'], threshold: 0.3, }); - return engine - .search(deferredSearch) // - .map((result) => result.item); - }, [initialRuntimes, deferredSearch]); + }, [initialRuntimes]); + const searchedRuntimes = useMemo(() => { + if (!deferredSearch) return initialRuntimes; + return searchEngine.search(deferredSearch).map((result) => result.item); + }, [initialRuntimes, deferredSearch, searchEngine]); type SortBy = 'popularity' | 'name'; const [sortBy, setSortBy] = useQueryParameter('sort', 'popularity'); @@ -51,6 +53,13 @@ export function PlaygroundCardList({ }); }, [searchedRuntimes, sortBy]); + const [pinnedRuntimeIds] = useLocalStorage('evaluate.pinned', []); + const pinnedRuntimes = useMemo(() => { + return pinnedRuntimeIds + .map((id) => initialRuntimes.find((r) => r.id === id)!) + .filter(Boolean); + }, [pinnedRuntimeIds, initialRuntimes]); + const [hash] = useHashFragment(); useEffect(() => { if (hash) @@ -59,11 +68,6 @@ export function PlaygroundCardList({ }); }, [hash]); - const [pinnedRuntimeIds] = useLocalStorage('evaluate.pinned', []); - const pinnedRuntimes = pinnedRuntimeIds - .map((id) => initialRuntimes.find((r) => r.id === id)!) - .filter(Boolean); - return (
diff --git a/apps/website/src/app/(playgrounds)/playgrounds/playground-card.tsx b/apps/website/src/app/(playgrounds)/playgrounds/playground-card.tsx index 34c97cd8..4a899211 100644 --- a/apps/website/src/app/(playgrounds)/playgrounds/playground-card.tsx +++ b/apps/website/src/app/(playgrounds)/playgrounds/playground-card.tsx @@ -12,7 +12,7 @@ import { CodeIcon, PinIcon } from 'lucide-react'; import Link from 'next/link'; import { useCallback, useMemo, useRef, useState } from 'react'; import { ImageWithFallback } from '~/components/image-fallback'; -import { useLocalStorage } from '~/hooks/use-local-storage'; +import { useLocalStorage } from '~/hooks/local-storage'; import { type RGB, getDominantColour } from './get-colour'; declare module 'react' { @@ -46,8 +46,8 @@ export function PlaygroundCard({ return ( diff --git a/apps/website/src/app/layout.css b/apps/website/src/app/layout.css deleted file mode 100644 index 6f2e7984..00000000 --- a/apps/website/src/app/layout.css +++ /dev/null @@ -1,16 +0,0 @@ -::-webkit-scrollbar { - width: 17px; -} - -::-webkit-scrollbar-track { - background: hsl(var(--background)); -} - -::-webkit-scrollbar-thumb { - background: hsl(var(--muted)); - border-radius: var(--radius); -} - -::-webkit-scrollbar-thumb:hover { - background: hsl(var(--muted) / 0.8); -} diff --git a/apps/website/src/app/layout.tsx b/apps/website/src/app/layout.tsx index 8b35d041..712840b9 100644 --- a/apps/website/src/app/layout.tsx +++ b/apps/website/src/app/layout.tsx @@ -1,20 +1,19 @@ +import { Toaster } from '@evaluate/components/toast'; +import { Inter } from 'next/font/google'; +import { twMerge as cn } from 'tailwind-merge'; +import { Footer } from '~/components/footer'; import { Header } from '~/components/header'; import { BodyProviders, HtmlProviders } from '~/components/providers'; import type { LayoutProps } from '../types'; import { generateBaseMetadata } from './metadata'; +import '../styles.css'; + +const inter = Inter({ subsets: ['latin'] }); export function generateMetadata() { return generateBaseMetadata('/'); } -import { Inter } from 'next/font/google'; -const inter = Inter({ subsets: ['latin'] }); -import { Toaster } from '@evaluate/components/toast'; -import '@evaluate/style/css'; -import { twMerge as cn } from 'tailwind-merge'; -import { Footer } from '~/components/footer'; -import './layout.css'; - export default function RootLayout(p: LayoutProps) { return ( diff --git a/apps/website/src/components/context-menu-wrapper.tsx b/apps/website/src/components/context-menu-wrapper.tsx deleted file mode 100644 index e8743f85..00000000 --- a/apps/website/src/components/context-menu-wrapper.tsx +++ /dev/null @@ -1,73 +0,0 @@ -'use client'; - -import { Button } from '@evaluate/components/button'; -import { - ContextMenu, - ContextMenuContent, - ContextMenuItem, - ContextMenuSeparator, - ContextMenuShortcut, - ContextMenuTrigger, -} from '@evaluate/components/context-menu'; -import { useHotkeys } from 'react-hotkeys-hook'; - -namespace ContextMenuWrapper { - export type Item = - | { - label: string; - action: React.MouseEventHandler; - } - | { - label: string; - action: () => void; - shortcut: string; - }; - - export interface Props { - items?: (Item | null)[]; - children: React.ReactNode; - } -} - -// NOTE: No longer used, but kept for archival purposes - -export function ContextMenuWrapper(props: ContextMenuWrapper.Props) { - const { items = [], children } = props; - - for (const item of items) { - if (item && 'shortcut' in item) { - useHotkeys(item.shortcut, item.action, { enableOnContentEditable: true }); - } - } - - return ( - - {children} - - - {items.map((item, i) => - item ? ( - - - - ) : ( - - ), - )} - - {items.length === 0 && ( -
No actions available
- )} -
-
- ); -} diff --git a/apps/website/src/components/editor/execute-bar/index.tsx b/apps/website/src/components/editor/execute-bar/index.tsx index c01a2470..643ad523 100644 --- a/apps/website/src/components/editor/execute-bar/index.tsx +++ b/apps/website/src/components/editor/execute-bar/index.tsx @@ -12,14 +12,12 @@ import { import { toast } from '@evaluate/components/toast'; import { executeCode } from '@evaluate/engine/execute'; import { useEventListener } from '@evaluate/hooks/event-listener'; -import { useMediaQuery } from '@evaluate/hooks/media-query'; import { ExecuteOptions, type PartialRuntime } from '@evaluate/shapes'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation } from '@tanstack/react-query'; import { Loader2Icon, PlayIcon } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; -import { twMerge as cn } from 'tailwind-merge'; import type { File } from 'virtual-file-explorer-backend'; import { useExplorer, useWatch } from '~/components/explorer/use'; import { useTerminal } from '~/components/terminal/use'; @@ -153,7 +151,7 @@ export function ExecuteBar({ runtime }: { runtime: PartialRuntime }) { diff --git a/apps/website/src/components/editor/index.tsx b/apps/website/src/components/editor/index.tsx index d10b2557..b5b24544 100644 --- a/apps/website/src/components/editor/index.tsx +++ b/apps/website/src/components/editor/index.tsx @@ -32,7 +32,7 @@ export function Editor({ runtime }: { runtime: PartialRuntime }) {
diff --git a/apps/website/src/components/explorer/file/item.tsx b/apps/website/src/components/explorer/file/item.tsx index a4257dd2..b79e82b6 100644 --- a/apps/website/src/components/explorer/file/item.tsx +++ b/apps/website/src/components/explorer/file/item.tsx @@ -5,7 +5,7 @@ import { TextCursorInputIcon, Trash2Icon } from 'lucide-react'; import { twMerge as cn } from 'tailwind-merge'; import type { File } from 'virtual-file-explorer-backend'; import { MaterialIcon } from '~/components/material-icon'; -import { useIsMobile } from '~/hooks/use-is-mobile'; +import { useIsMobile } from '~/hooks/is-mobile'; import { ExplorerItemName } from '../name'; import { useWatch } from '../use'; import { @@ -55,9 +55,12 @@ export function ExplorerFileItem({ file, meta }: ExplorerFileItem.Props) { variant={null} className="h-auto w-auto p-0" onClick={handleRenameClick} + asChild > - - Rename File +
+ + Rename File +
)} diff --git a/apps/website/src/components/explorer/folder/item.tsx b/apps/website/src/components/explorer/folder/item.tsx index edbd9752..c1df3e87 100644 --- a/apps/website/src/components/explorer/folder/item.tsx +++ b/apps/website/src/components/explorer/folder/item.tsx @@ -11,7 +11,7 @@ import { TextCursorInputIcon, Trash2Icon } from 'lucide-react'; import { twMerge as cn } from 'tailwind-merge'; import type { Folder } from 'virtual-file-explorer-backend'; import { MaterialIcon } from '~/components/material-icon'; -import { useIsMobile } from '~/hooks/use-is-mobile'; +import { useIsMobile } from '~/hooks/is-mobile'; import { ExplorerItemName } from '../name'; import { useWatch } from '../use'; import { ExplorerFolderChildren } from './children'; @@ -82,9 +82,12 @@ export function ExplorerFolderItem({ folder }: ExplorerFolderItem.Props) { variant={null} className="h-auto w-auto p-0" onClick={handleRenameClick} + asChild > - - Rename File +
+ + Rename File +
diff --git a/apps/website/src/components/explorer/index.tsx b/apps/website/src/components/explorer/index.tsx index a695cb5c..28b79bbf 100644 --- a/apps/website/src/components/explorer/index.tsx +++ b/apps/website/src/components/explorer/index.tsx @@ -35,7 +35,7 @@ export function Explorer() { return (
-
+
Explorer {/* TODO: Upload button? Would likely need a confirm dialog as this will overwrite the current content */} diff --git a/apps/website/src/components/explorer/use.tsx b/apps/website/src/components/explorer/use.tsx index fd3a4a2e..362e2ce9 100644 --- a/apps/website/src/components/explorer/use.tsx +++ b/apps/website/src/components/explorer/use.tsx @@ -15,7 +15,7 @@ import { import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { File, Folder } from 'virtual-file-explorer-backend'; -import { useHashFragment } from '~/hooks/use-hash-fragment'; +import { useHashFragment } from '~/hooks/hash-fragment'; export const ExplorerContext = // createContext>(null!); diff --git a/apps/website/src/components/header/index.tsx b/apps/website/src/components/header/index.tsx index 87662bb9..b97bbef9 100644 --- a/apps/website/src/components/header/index.tsx +++ b/apps/website/src/components/header/index.tsx @@ -1,7 +1,7 @@ 'use client'; import { Button } from '@evaluate/components/button'; -import { Sheet, SheetContent, SheetTrigger } from '@evaluate/components/sheet'; +import { Sheet, SheetBody, SheetTrigger } from '@evaluate/components/sheet'; import { useMediaQuery } from '@evaluate/hooks/media-query'; import { MenuIcon } from 'lucide-react'; import Image from 'next/image'; @@ -76,8 +76,8 @@ function DesktopNavigationWrapper(p: React.PropsWithChildren) {
- +
); diff --git a/apps/website/src/components/image-fallback.tsx b/apps/website/src/components/image-fallback.tsx index 471a4be7..92cd8e5c 100644 --- a/apps/website/src/components/image-fallback.tsx +++ b/apps/website/src/components/image-fallback.tsx @@ -1,20 +1,19 @@ 'use client'; import Image from 'next/image'; -import { forwardRef, isValidElement, useState } from 'react'; +import { isValidElement, useState } from 'react'; -export const ImageWithFallback = forwardRef< - React.ElementRef, - Omit, 'src'> & { - src?: string | undefined; - fallback: string | React.ReactElement; - } ->((p, ref) => { - const [errored, setErrored] = useState(!p.src); - if (errored && typeof p.fallback === 'string') - return ; - if (errored && isValidElement(p.fallback)) return p.fallback; - return ( - setErrored(true)} /> - ); -}); +export function ImageWithFallback({ + src, + fallback, + ...props +}: Omit, 'src'> & { + src?: string | undefined; + fallback: string | React.ReactElement; +}) { + const [errored, setErrored] = useState(!src); + if (errored && typeof fallback === 'string') + return ; + if (errored && isValidElement(fallback)) return fallback; + return setErrored(true)} {...props} />; +} diff --git a/apps/website/src/components/providers.tsx b/apps/website/src/components/providers.tsx index d9dcf9d1..d99d9be3 100644 --- a/apps/website/src/components/providers.tsx +++ b/apps/website/src/components/providers.tsx @@ -3,6 +3,7 @@ import { ServerThemeProvider } from 'next-themes'; export function HtmlProviders(p: React.PropsWithChildren) { + // TODO: This causes hydration errors, can this be replaced with latest? return ( - + {( [ [ diff --git a/apps/website/src/hooks/use-hash-fragment.ts b/apps/website/src/hooks/hash-fragment.ts similarity index 100% rename from apps/website/src/hooks/use-hash-fragment.ts rename to apps/website/src/hooks/hash-fragment.ts diff --git a/apps/website/src/hooks/use-is-mobile.ts b/apps/website/src/hooks/is-mobile.ts similarity index 100% rename from apps/website/src/hooks/use-is-mobile.ts rename to apps/website/src/hooks/is-mobile.ts diff --git a/apps/website/src/hooks/use-local-storage.ts b/apps/website/src/hooks/local-storage.ts similarity index 100% rename from apps/website/src/hooks/use-local-storage.ts rename to apps/website/src/hooks/local-storage.ts diff --git a/apps/website/src/hooks/use-query-parameter.ts b/apps/website/src/hooks/query-parameter.ts similarity index 100% rename from apps/website/src/hooks/use-query-parameter.ts rename to apps/website/src/hooks/query-parameter.ts diff --git a/apps/website/src/styles.css b/apps/website/src/styles.css new file mode 100644 index 00000000..1795026a --- /dev/null +++ b/apps/website/src/styles.css @@ -0,0 +1,17 @@ +@import "@evaluate/styles"; +@import "@evaluate/components"; +@source "."; + +::-webkit-scrollbar { + width: 17px; +} +::-webkit-scrollbar-track { + background: hsl(var(--color-background)); +} +::-webkit-scrollbar-thumb { + background: hsl(var(--color-muted)); + border-radius: var(--radius-xl); +} +::-webkit-scrollbar-thumb:hover { + background: hsl(var(--color-muted) / 0.8); +} diff --git a/apps/website/src/types.ts b/apps/website/src/types.ts index 65b400c7..259d5452 100644 --- a/apps/website/src/types.ts +++ b/apps/website/src/types.ts @@ -1,5 +1,6 @@ // =============== Next.js Specific =============== +/* export type ParamType = TParam extends `${infer TName}[]` ? { [key in TName]: string[] } : { [key in TParam]: string }; @@ -22,3 +23,50 @@ export type LayoutProps = React.PropsWithChildren<{ params: TParams extends string[] ? Params : object; }>; +*/ + +import type { Simplify } from 'type-fest'; + +type Infer = TT extends '[]' + ? T extends `[...${infer TName}]` + ? [TName, string[]] + : T extends `[${infer TName}]` + ? [TName, string] + : [never, never] + : TT extends '@' + ? T extends `@${infer TName}` + ? [TName, React.ReactNode] + : [never, never] + : [never, never]; + +type ParseDynamic = Simplify< + { + [key: string]: string | string[] | undefined; + } & { + [K in T[number] as Infer[0]]: Infer[1]; + } +>; +type ParseParallel = Simplify< + { + [key: string]: React.ReactNode | undefined; + } & { + [K in T[number] as Infer[0]]: Infer[1]; + } +>; + +// + +type LayoutEntry = `[${string}]` | `[...${string}]` | `@${string}`; +export type LayoutProps = Simplify< + { + children?: React.ReactNode; + } & { + params: Promise>; + } & ParseParallel +>; + +type PageEntry = `[${string}]` | `[...${string}]`; +export type PageProps = Simplify<{ + params: Promise>; + searchParams: Promise<{ [key: string]: string | string[] }>; +}>; diff --git a/apps/website/tailwind.config.cjs b/apps/website/tailwind.config.cjs deleted file mode 100644 index 3285e92d..00000000 --- a/apps/website/tailwind.config.cjs +++ /dev/null @@ -1,12 +0,0 @@ -const twComponentsPreset = require('@evaluate/components/tailwind-preset'); -const twStylesPreset = require('@evaluate/style/tailwind-preset'); - -/** @type {import('tailwindcss').Config} */ -const tailwindConfig = { - presets: [twStylesPreset, twComponentsPreset], - content: ['./src/**/**/*'] - .concat(twStylesPreset.content) - .concat(twComponentsPreset.content), -}; - -module.exports = tailwindConfig; diff --git a/packages/components/package.json b/packages/components/package.json index 64a16198..2d2e9dec 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,19 +1,13 @@ { "name": "@evaluate/components", - "version": "1.0.0", "type": "module", "exports": { - "./tailwind-preset": { - "types": "./dist/tailwind.preset.d.ts", - "default": "./dist/tailwind.preset.js" + ".": { + "style": "./src/styles.css" }, "./*": { "import": "./dist/*.js", "types": "./dist/*.d.ts" - }, - "./dist/*": { - "import": "./dist/*.js", - "types": "./dist/*.d.ts" } }, "scripts": { @@ -22,7 +16,6 @@ }, "dependencies": { "@radix-ui/react-accordion": "^1.2.3", - "@radix-ui/react-context-menu": "^2.2.6", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-popover": "^1.1.6", @@ -30,21 +23,19 @@ "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", - "@radix-ui/react-switch": "^1.1.3", "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-tooltip": "^1.1.8", "class-variance-authority": "^0.7.1", - "cmdk": "^0.2.1", "embla-carousel-react": "^8.5.2", - "lucide-react": "^0.475.0", - "react": "^18.3.1", + "lucide-react": "^0.476.0", + "react": "^19.0.0", "react-hook-form": "^7.54.2", "react-resizable-panels": "^2.1.7", - "sonner": "^1.7.4", - "tailwind-merge": "^2.6.0" + "sonner": "^2.0.1", + "tailwind-merge": "^3.0.2" }, "devDependencies": { - "@types/react": "^18.3.18", - "tailwindcss": "^3.4.17" + "@types/react": "^19.0.10", + "tailwindcss": "^4.0.9" } } diff --git a/packages/components/src/accordion.tsx b/packages/components/src/accordion.tsx index 3181dc76..3ab51975 100644 --- a/packages/components/src/accordion.tsx +++ b/packages/components/src/accordion.tsx @@ -2,75 +2,63 @@ import * as AccordionPrimitive from '@radix-ui/react-accordion'; import { ChevronDownIcon } from 'lucide-react'; -import * as React from 'react'; import { twMerge as cn } from 'tailwind-merge'; -const Accordion = AccordionPrimitive.Root; +function Accordion({ + ...props +}: React.ComponentProps) { + return ; +} -const AccordionItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AccordionItem.displayName = 'AccordionItem'; +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} -const AccordionHeader = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AccordionHeader.displayName = AccordionPrimitive.Header.displayName; +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180', + className, + )} + {...props} + > + {children} + + + + ); +} -const AccordionTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - withChevron?: boolean; - } ->(({ className, children, withChevron, ...props }, ref) => ( - svg]:rotate-180', - className, - )} - {...props} - > - {children} - {withChevron && ( - - )} - -)); -AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ); +} -const AccordionContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - -
{children}
-
-)); -AccordionContent.displayName = AccordionPrimitive.Content.displayName; - -export { - Accordion, - AccordionItem, - AccordionHeader, - AccordionTrigger, - AccordionContent, -}; +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/packages/components/src/breadcrumb.tsx b/packages/components/src/breadcrumb.tsx deleted file mode 100644 index c1f8659c..00000000 --- a/packages/components/src/breadcrumb.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { Slot } from '@radix-ui/react-slot'; -import { ChevronRightIcon, CircleEllipsisIcon } from 'lucide-react'; -import * as React from 'react'; -import { twMerge as cn } from 'tailwind-merge'; - -const Breadcrumb = React.forwardRef< - HTMLElement, - React.ComponentPropsWithoutRef<'nav'> & { - separator?: React.ReactNode; - } ->(({ ...props }, ref) =>