diff --git a/package-lock.json b/package-lock.json index 54f0dc9..6483959 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11946,9 +11946,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -11964,12 +11964,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -28681,9 +28682,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -28698,10 +28699,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -32099,6 +32101,10 @@ "resolved": "test-apps/remix-vite", "link": true }, + "node_modules/remix-vite-embedded": { + "resolved": "test-apps/remix-vite-embedded", + "link": true + }, "node_modules/remix-website": { "resolved": "test-apps/remix-website", "link": true @@ -32771,9 +32777,10 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -33545,6 +33552,7 @@ "version": "3.4.13", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -37728,6 +37736,33 @@ "node": ">=18.0.0" } }, + "test-apps/remix-vite-embedded": { + "dependencies": { + "@remix-run/node": "^2.8.0", + "@remix-run/react": "^2.8.0", + "@remix-run/serve": "^2.8.0", + "isbot": "^3.6.8", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@remix-run/dev": "^2.8.0", + "@remix-run/eslint-config": "^2.8.0", + "@types/react": "^18.2.20", + "@types/react-dom": "^18.2.7", + "autoprefixer": "^10.4.20", + "eslint": "^8.38.0", + "postcss": "^8.4.47", + "remix-development-tools": "*", + "tailwindcss": "^3.4.13", + "typescript": "^5.1.6", + "vite": "^5.1.0", + "vite-tsconfig-paths": "^4.2.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "test-apps/remix-website": { "version": "30.1.0", "dependencies": { diff --git a/package.json b/package.json index d6cbe15..26b583d 100644 --- a/package.json +++ b/package.json @@ -68,9 +68,11 @@ "docs": "npm run dev -w docs", "prepublishOnly": "npm run build", "remix-vite": "npm run dev -w test-apps/remix-vite", + "remix-vite-embedded": "npm run dev -w test-apps/remix-vite-embedded", "remix-website": "npm run dev -w test-apps/remix-website", "runner": "npm-run-all -s build -p watch-all", "dev": "npm run runner remix-vite", + "dev:embedded": "npm run runner remix-vite-embedded", "dev:website": "npm run runner remix-website ", "build": "run-s tsup:* -- --clean", "watch-all": "npm-run-all -p tsup:index:watch tsup:client:watch tsup:server:watch -- --watch", diff --git a/src/client/EmbeddedDevTools.tsx b/src/client/EmbeddedDevTools.tsx index 07bc915..338e468 100644 --- a/src/client/EmbeddedDevTools.tsx +++ b/src/client/EmbeddedDevTools.tsx @@ -2,7 +2,7 @@ import { useLocation } from "@remix-run/react" import clsx from "clsx" import { useEffect, useState } from "react" import type { RemixDevToolsProps } from "./RemixDevTools.js" -import { RDTContextProvider } from "./context/RDTContext.js" +import { RDTContextProvider, RDTEmbeddedContextProvider as RemixDevToolsEmbeddedMode } from "./context/RDTContext.js" import { useSettingsContext } from "./context/useRDTContext.js" import { useBorderedRoutes } from "./hooks/useBorderedRoutes.js" import { useSetRouteBoundaries } from "./hooks/useSetRouteBoundaries.js" diff --git a/src/client/context/RDTContext.tsx b/src/client/context/RDTContext.tsx index 6b5975f..fb51fd0 100644 --- a/src/client/context/RDTContext.tsx +++ b/src/client/context/RDTContext.tsx @@ -1,6 +1,6 @@ import type { Dispatch } from "react" import type React from "react" -import { createContext, useEffect, useMemo, useReducer } from "react" +import { createContext, useContext, useEffect, useMemo, useReducer } from "react" import { useRemoveBody } from "../hooks/detached/useRemoveBody.js" import { checkIsDetached, checkIsDetachedOwner, checkIsDetachedWindow } from "../utils/detached.js" import { tryParseJson } from "../utils/sanitize.js" @@ -20,6 +20,16 @@ export const RDTContext = createContext<{ dispatch: Dispatch }>({ state: initialState, dispatch: () => null }) +const RDTEmbeddedContext = createContext(false) + +export function RDTEmbeddedContextProvider({ children }: React.PropsWithChildren) { + return {children} +} + +export function useIsEmbeddedRDT() { + return useContext(RDTEmbeddedContext) +} + RDTContext.displayName = "RDTContext" interface ContextProps { diff --git a/src/client/init/root.tsx b/src/client/init/root.tsx index f71dc17..34c375c 100644 --- a/src/client/init/root.tsx +++ b/src/client/init/root.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react" import { createPortal } from "react-dom" import { RemixDevTools, type RemixDevToolsProps } from "../RemixDevTools.js" -import type { RdtClientConfig } from "../context/RDTContext.js" +import { RDTEmbeddedContextProvider, type RdtClientConfig, useIsEmbeddedRDT } from "../context/RDTContext.js" import { hydrationDetector } from "./hydration.js" let hydrating = true @@ -19,34 +19,53 @@ function useHydrated() { export const defineClientConfig = (config: RdtClientConfig) => config -export const withDevTools = (Component: any, config?: RemixDevToolsProps) => () => { - hydrationDetector() - const hydrated = useHydrated() - if (!hydrated) return - - return ( - <> - - {createPortal(, document.body)} - - ) -} - -/** - * - * @description Injects the dev tools into the Vite App, ONLY meant to be used by the package plugin, do not use this yourself! - */ -export const withViteDevTools = (Component: any, config?: RemixDevToolsProps) => () => { - hydrationDetector() - function AppWithDevTools(props: any) { +export const withDevTools = + (Component: any, config?: RemixDevToolsProps & { panelMode?: "auto" | "embedded" }) => () => { + hydrationDetector() const hydrated = useHydrated() + const isEmbedded = useIsEmbeddedRDT() if (!hydrated) return + + if ((config?.panelMode ?? "auto") === "embedded") { + return ( + + + + ) + } + return ( <> - - {createPortal(, document.body)} + + {!isEmbedded && createPortal(, document.body)} ) } - return AppWithDevTools -} + +/** + * + * @description Injects the dev tools into the Vite App, ONLY meant to be used by the package plugin, do not use this yourself! + */ +export const withViteDevTools = + (Component: any, config?: RemixDevToolsProps & { panelMode?: "auto" | "embedded" }) => () => { + hydrationDetector() + function AppWithDevTools(props: any) { + const hydrated = useHydrated() + if (!hydrated) return + if ((config?.panelMode ?? "auto") === "embedded") { + return ( + + + + ) + } + + return ( + <> + + {createPortal(, document.body)} + + ) + } + return AppWithDevTools + } diff --git a/src/client/layout/MainPanel.tsx b/src/client/layout/MainPanel.tsx index 852a0f8..fad1b16 100644 --- a/src/client/layout/MainPanel.tsx +++ b/src/client/layout/MainPanel.tsx @@ -30,19 +30,28 @@ const MainPanel = ({ children, isOpen, isEmbedded = false, className }: MainPane return (
- {panelLocation === "bottom" && ( + {!isEmbedded && panelLocation === "bottom" && (
)} {children} - {panelLocation === "top" && ( + {!isEmbedded && panelLocation === "top" && (
{ const [maxHeight, setMaxHeight] = useState(settings.maxHeight.toString()) const [expansionLevel, setExpansionLevel] = useState(settings.expansionLevel.toString()) const [openHotkey, setOpenHotkey] = useState(settings.openHotkey.toString()) + const isEmbedded = useIsEmbeddedRDT() return ( @@ -20,40 +22,44 @@ export const SettingsTab = () => { Settings
- setSettings({ defaultOpen: !settings.defaultOpen })} - value={settings.defaultOpen} - > - Open dev tools by default - - setSettings({ requireUrlFlag: !settings.requireUrlFlag })} - value={settings.requireUrlFlag} - > - Show dev tools only when URL flag is set ?{settings.urlFlag}=true - - setSettings({ hideUntilHover: !settings.hideUntilHover })} - value={settings.hideUntilHover} - > - Hide the trigger until hovered - - setSettings({ showBreakpointIndicator: !settings.showBreakpointIndicator })} - value={settings.showBreakpointIndicator} - > - Show breakpoint indicator - + {!isEmbedded && ( + <> + setSettings({ defaultOpen: !settings.defaultOpen })} + value={settings.defaultOpen} + > + Open dev tools by default + + setSettings({ requireUrlFlag: !settings.requireUrlFlag })} + value={settings.requireUrlFlag} + > + Show dev tools only when URL flag is set ?{settings.urlFlag}=true + + setSettings({ hideUntilHover: !settings.hideUntilHover })} + value={settings.hideUntilHover} + > + Hide the trigger until hovered + + setSettings({ showBreakpointIndicator: !settings.showBreakpointIndicator })} + value={settings.showBreakpointIndicator} + > + Show breakpoint indicator + -
+
+ + )} {settings.requireUrlFlag && ( { } }} /> - setOpenHotkey(e.target.value ?? "")} - onBlur={(e) => { - const value = e.target.value - if (value) { - setSettings({ openHotkey: value }) - } - }} - /> -
- setMinHeight(e.target.value ?? "")} - onBlur={(e) => { - const value = Number.parseInt(e.target.value) - if (value && !Number.isNaN(value) && value < settings.maxHeight && value > 100) { - setSettings({ minHeight: value }) - } - }} - /> - setMaxHeight(e.target.value ?? "")} - onBlur={(e) => { - const value = Number.parseInt(e.target.value) - if (value && !Number.isNaN(value) && value > settings.minHeight) { - setSettings({ maxHeight: value }) - } - }} - /> -
- -
- setSettings({ position: value })} - value={settings.position} - className="w-full" - options={[ - { label: "Bottom Right", value: "bottom-right" }, - { label: "Bottom Left", value: "bottom-left" }, - { label: "Top Right", value: "top-right" }, - { label: "Top Left", value: "top-left" }, - { label: "Middle Right", value: "middle-right" }, - { label: "Middle Left", value: "middle-left" }, - ]} - hint="This will determine where your trigger position on the screen is when the tools are collapsed." - /> - setSettings({ liveUrlsPosition: value as any })} - value={settings.liveUrlsPosition} - className="w-full" - options={[ - { label: "Bottom Right", value: "bottom-right" }, - { label: "Bottom Left", value: "bottom-left" }, - { label: "Top Right", value: "top-right" }, - { label: "Top Left", value: "top-left" }, - ]} - hint="This will determine where your environments position on the screen is." - /> - setSettings({ panelLocation: value })} - value={settings.panelLocation} - className="w-full" - options={[ - { label: "Top", value: "top" }, - { label: "Bottom", value: "bottom" }, - ]} - hint="This will determine where your panel shows up once opened" - /> -
+ {!isEmbedded && ( + <> + setOpenHotkey(e.target.value ?? "")} + onBlur={(e) => { + const value = e.target.value + if (value) { + setSettings({ openHotkey: value }) + } + }} + /> +
+ setMinHeight(e.target.value ?? "")} + onBlur={(e) => { + const value = Number.parseInt(e.target.value) + if (value && !Number.isNaN(value) && value < settings.maxHeight && value > 100) { + setSettings({ minHeight: value }) + } + }} + /> + setMaxHeight(e.target.value ?? "")} + onBlur={(e) => { + const value = Number.parseInt(e.target.value) + if (value && !Number.isNaN(value) && value > settings.minHeight) { + setSettings({ maxHeight: value }) + } + }} + /> +
+
+ setSettings({ position: value })} + value={settings.position} + className="w-full" + options={[ + { label: "Bottom Right", value: "bottom-right" }, + { label: "Bottom Left", value: "bottom-left" }, + { label: "Top Right", value: "top-right" }, + { label: "Top Left", value: "top-left" }, + { label: "Middle Right", value: "middle-right" }, + { label: "Middle Left", value: "middle-left" }, + ]} + hint="This will determine where your trigger position on the screen is when the tools are collapsed." + /> + setSettings({ liveUrlsPosition: value as any })} + value={settings.liveUrlsPosition} + className="w-full" + options={[ + { label: "Bottom Right", value: "bottom-right" }, + { label: "Bottom Left", value: "bottom-left" }, + { label: "Top Right", value: "top-right" }, + { label: "Top Left", value: "top-left" }, + ]} + hint="This will determine where your environments position on the screen is." + /> + setSettings({ panelLocation: value })} + value={settings.panelLocation} + className="w-full" + options={[ + { label: "Top", value: "top" }, + { label: "Bottom", value: "bottom" }, + ]} + hint="This will determine where your panel shows up once opened" + /> +
+ + )}
Plugin[] = (args) => { const shouldInject = (mode: string | undefined) => mode === "development" || include + const panelMode = args?.panelMode ?? "auto" + // Set the server config on the process object so that it can be accessed by the plugin if (typeof process !== "undefined") { process.rdt_config = serverConfig @@ -223,7 +226,7 @@ export const remixDevTools: (args?: RemixViteConfig) => Plugin[] = (args) => { const augmentedDefaultExport = `export default withViteDevTools(AppExport, { config: ${JSON.stringify(clientConfig)}, plugins: [${pluginNames.join( "," - )}] })();` + )}], panelMode: "${panelMode}" })();` const updatedCode = lines.map((line) => { // Handles default export augmentation diff --git a/test-apps/remix-vite-embedded/.gitignore b/test-apps/remix-vite-embedded/.gitignore new file mode 100644 index 0000000..3f7bf98 --- /dev/null +++ b/test-apps/remix-vite-embedded/.gitignore @@ -0,0 +1,6 @@ +node_modules + +/.cache +/build +/public/build +.env diff --git a/test-apps/remix-vite-embedded/README.md b/test-apps/remix-vite-embedded/README.md new file mode 100644 index 0000000..08ffe67 --- /dev/null +++ b/test-apps/remix-vite-embedded/README.md @@ -0,0 +1,28 @@ +# templates/unstable-vite + +⚠️ Remix support for Vite is unstable and not recommended for production. + +📖 See the [Remix Vite docs][remix-vite-docs] for details on supported features. + +## Setup + +```shellscript +npx create-remix@latest --template remix-run/remix/templates/unstable-vite +``` + +## Run + +Spin up the Vite dev server: + +```shellscript +npm run dev +``` + +Or build your app for production and run it: + +```shellscript +npm run build +npm run start +``` + +[remix-vite-docs]: https://remix.run/docs/en/main/future/vite diff --git a/test-apps/remix-vite-embedded/app/components/Button.tsx b/test-apps/remix-vite-embedded/app/components/Button.tsx new file mode 100644 index 0000000..ce77853 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/components/Button.tsx @@ -0,0 +1,15 @@ +import { ReactNode } from "react"; + +interface ButtonProps extends React.ButtonHTMLAttributes { + children: ReactNode +} + +const Button = ({ children, ...props }: ButtonProps) => { + return ( + + ); +} + +export { Button }; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/modules/user.server.ts b/test-apps/remix-vite-embedded/app/modules/user.server.ts new file mode 100644 index 0000000..1c80077 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/modules/user.server.ts @@ -0,0 +1,4 @@ +export const userSomething = () => { + console.log("userSomething"); + return "userSomething"; +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/root.tsx b/test-apps/remix-vite-embedded/app/root.tsx new file mode 100644 index 0000000..7fe0658 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/root.tsx @@ -0,0 +1,55 @@ +import { + Form, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from '@remix-run/react'; +import { json } from '@remix-run/server-runtime'; +import { EmbeddedDevTools } from 'remix-development-tools/client'; +import { userSomething } from './modules/user.server'; +import './tailwind.css'; + +export function links() { + return []; +} + +export const loader = () => { + console.log('loader?'); + userSomething(); + return { message: 'Hello World' }; +}; + +export const action = () => { + return json({ message: 'Hello World' }); +}; + +export default function App() { + console.log('App?'); + return ( + + + + + + + + +
+ + +
+ + +
+
+ +
+ + + + + + ); +} diff --git a/test-apps/remix-vite/app/routes/embedded.tsx b/test-apps/remix-vite-embedded/app/routes/_index.tsx similarity index 56% rename from test-apps/remix-vite/app/routes/embedded.tsx rename to test-apps/remix-vite-embedded/app/routes/_index.tsx index f67d5f4..cb98d51 100644 --- a/test-apps/remix-vite/app/routes/embedded.tsx +++ b/test-apps/remix-vite-embedded/app/routes/_index.tsx @@ -1,8 +1,9 @@ import type { ActionFunctionArgs } from "@remix-run/node"; -import { redirect, type LoaderFunctionArgs, defer } from "@remix-run/node"; +import { json, redirect, type LoaderFunctionArgs, defer } from "@remix-run/node"; import type { MetaFunction } from "@remix-run/node"; -import { Link, useFetcher, useSubmit } from "@remix-run/react"; -import { EmbeddedDevTools } from "remix-development-tools/client"; +import { Link, useFetcher, useSubmit } from "@remix-run/react"; +import { Button } from "../components/Button"; + export const meta: MetaFunction = () => { return [ @@ -11,33 +12,50 @@ export const meta: MetaFunction = () => { ]; }; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const test = new Promise((resolve) => { + +export const loader = async ({ request, response }: LoaderFunctionArgs) => { + const test = new Promise((resolve, reject) => { setTimeout(() => { resolve("test"); - }, 1000); - }) - return defer({ message: "Hello World!", test }); + }, 2000); + }); + const test1 = new Promise((resolve, reject) => { + setTimeout(() => { + resolve("test1"); + }, 3500); + }); + return defer({ message: "Hello World!", test, test1, }); }; export const action = async ({ request }: ActionFunctionArgs) => { return redirect("/login"); }; -export default function Index() { - const lFetcher = useFetcher(); - const pFetcher = useFetcher(); +export default function Index() { + const lFetcher = useFetcher({ key: "lfetcher"}); + const pFetcher = useFetcher({ key: "test"}); const submit = useSubmit(); const data = new FormData(); data.append("test", "test"); + data.append("array", "test"); + data.append("array", "test1"); + data.append("person.name", "test1"); + data.append("person.surname", "test1"); + data.append("array2.0", "test1"); + data.append("array2.1", "test1"); + data.append("array2.2", "test1"); + data.append("obj", JSON.stringify({ test: "test" })); return (

Welcome to Remix

- + -
- -
- Login + +

Test Links

+ +
    +
  • + Login +
  • +
  • + File Route (file.tsx) +
  • +
  • + Folder Route (folder/route.tsx) +
  • +
+ +

Resources

+
  • + ); +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.final_test.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.final_test.tsx new file mode 100644 index 0000000..c1f6998 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.final_test.tsx @@ -0,0 +1,51 @@ +import type { V2_MetaFunction } from "@remix-run/react/dist/routeModules"; +import type { HeadersFunction, LinksFunction, LoaderArgs, ActionArgs } from "@remix-run/node"; +import { useLoaderData, isRouteErrorResponse, useRouteError } from "@remix-run/react"; +import type { ShouldRevalidateFunction } from "@remix-run/react"; + +export const links: LinksFunction = () => ( + [ + // your links here + ] +); + +export const meta: V2_MetaFunction = () => [ + // your meta here +]; + +export const handle = () => ({ + // your handler here +}); + +export const headers: HeadersFunction = () => ( + { + // your headers here + } +); + +export const loader = async ({ request }: LoaderArgs) => { + return null; +}; + +export const action = async ({ request }: ActionArgs) => { + return null; +}; + +export default function RouteComponent(){ + const data = useLoaderData() + return ( +
    + ); +} + +export function ErrorBoundary(){ + const error = useRouteError(); + if (isRouteErrorResponse(error)) { + return
    + } + return
    +} + +export const shouldRevalidate: ShouldRevalidateFunction = () => { + return true; +}; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.new_route.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.new_route.tsx new file mode 100644 index 0000000..1597684 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.new_route.tsx @@ -0,0 +1,5 @@ +export default function RouteComponent(){ + return ( +
    + ); +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.one_more_time.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.one_more_time.tsx new file mode 100644 index 0000000..c1f6998 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.one_more_time.tsx @@ -0,0 +1,51 @@ +import type { V2_MetaFunction } from "@remix-run/react/dist/routeModules"; +import type { HeadersFunction, LinksFunction, LoaderArgs, ActionArgs } from "@remix-run/node"; +import { useLoaderData, isRouteErrorResponse, useRouteError } from "@remix-run/react"; +import type { ShouldRevalidateFunction } from "@remix-run/react"; + +export const links: LinksFunction = () => ( + [ + // your links here + ] +); + +export const meta: V2_MetaFunction = () => [ + // your meta here +]; + +export const handle = () => ({ + // your handler here +}); + +export const headers: HeadersFunction = () => ( + { + // your headers here + } +); + +export const loader = async ({ request }: LoaderArgs) => { + return null; +}; + +export const action = async ({ request }: ActionArgs) => { + return null; +}; + +export default function RouteComponent(){ + const data = useLoaderData() + return ( +
    + ); +} + +export function ErrorBoundary(){ + const error = useRouteError(); + if (isRouteErrorResponse(error)) { + return
    + } + return
    +} + +export const shouldRevalidate: ShouldRevalidateFunction = () => { + return true; +}; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.$test.$wildcard.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.$test.$wildcard.tsx new file mode 100644 index 0000000..7b5b9c8 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.$test.$wildcard.tsx @@ -0,0 +1,133 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; +import { Link, useFetcher, useLoaderData, useSubmit } from "@remix-run/react"; + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + await new Promise((resolve) => setTimeout(resolve, 2000)); + return json({ + should: "work", + with: { + nested: { + objects: { + really: { + deep: "inside", + array: [ + "this", + "is", + "a", + "really", + "long", + "array", + "that", + "should", + "be", + "truncated", + ], + }, + }, + }, + }, + }, { headers: { "Set-Cookie": "test=1; Path=/; HttpOnly; Secure", "Cache-Control": "max-age=20"}}); +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return new Response(JSON.stringify({ test: "died" })); +}; + +export default function IndexRoute() { + const string = useLoaderData(); + const lFetcher = useFetcher(); + const lFetcher2 = useFetcher(); + const pFetcher = useFetcher(); + const submit = useSubmit(); + const data = new FormData(); + data.append("test", "test"); + return ( +
    +

    Welcome to Remix 5

    + {" "} + + + + + + + Login +
    +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.$test.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.$test.tsx new file mode 100644 index 0000000..e75cdb2 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.$test.tsx @@ -0,0 +1,45 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; +import { Link, Outlet, useFetcher, useLoaderData, useSubmit } from "@remix-run/react"; + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + return json({ + should: "work", + with: { + nested: { + objects: { + + }, + }, + }, + }, { headers: { "Cache-Control": "max-age=3600, private" } }); +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return new Response(JSON.stringify({ test: "died" })); +}; + +export default function IndexRoute() { + const string = useLoaderData(); + const lFetcher = useFetcher(); + const lFetcher2 = useFetcher(); + const pFetcher = useFetcher(); + const submit = useSubmit(); + const data = new FormData(); + data.append("test", "test"); + return ( +
    +

    Welcome to Remix 4

    +
    + +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.tsx new file mode 100644 index 0000000..2a5c91c --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.new.tsx @@ -0,0 +1,94 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; +import { Link, Outlet, useFetcher, useLoaderData, useSubmit } from "@remix-run/react"; + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + return json({ + should: "work", + with: { + nested: { + objects: { + really: { + deep: "inside", + array: [ + "this", + "is", + "a", + "really", + "long", + "array", + "that", + "should", + "be", + "truncated", + ], + }, + }, + }, + }, + }, { headers: { "Cache-Control": "max-age=60, s-maxage=6000" } }); +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return new Response(JSON.stringify({ test: "died" })); +}; + +export default function IndexRoute() { + const string = useLoaderData(); + const lFetcher = useFetcher(); + const lFetcher2 = useFetcher(); + const pFetcher = useFetcher(); + const submit = useSubmit(); + const data = new FormData(); + data.append("test", "test"); + return ( +
    +

    Welcome to Remix 3

    + + Login + + +
    + ); +} + +export const ErrorBoundary = ({ error }: { error: Error }) => { + return null; +}; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.tsx new file mode 100644 index 0000000..bb67ab9 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.edit.tsx @@ -0,0 +1,42 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, redirect, type LoaderFunctionArgs } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; +import { + Link, + Outlet, + useFetcher, + useLoaderData, + useSubmit, +} from "@remix-run/react"; + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + +export const loader = ({ request }: LoaderFunctionArgs) => { + return json({ test: "died" }, ) +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return json({ + test: "died", + }); +}; + +export default function Index() { + const data = new FormData(); + data.append("test", "test"); + return ( +
    +

    +

    + +

    +

    + +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.tsx new file mode 100644 index 0000000..d805036 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tests.$id.tsx @@ -0,0 +1,48 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, redirect, type LoaderFunctionArgs } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; +import { + Link, + Outlet, + useFetcher, + useLoaderData, + useSubmit, +} from "@remix-run/react"; + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + return new Response( + JSON.stringify({ + test: "JSON stringify with new response and a long ass string to test something", + }) + ); +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return json({ + test: "died", + }); +}; + +export default function Index() { + const { message } = useLoaderData(); + const lFetcher = useFetcher(); + const pFetcher = useFetcher(); + const submit = useSubmit(); + const data = new FormData(); + data.append("test", "test"); + return ( +
    +

    Welcome to Remix 2

    + + + +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tests.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tests.tsx new file mode 100644 index 0000000..7438d2a --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tests.tsx @@ -0,0 +1,42 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, redirect, type LoaderFunctionArgs } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; +import { + Link, + Outlet, + useFetcher, + useLoaderData, + useSubmit, +} from "@remix-run/react"; + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + return new Response(new URLSearchParams("test=1&test=2&test=3")); +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return json({ + test: "died", + }); +}; + +export default function Index() { + const { message } = useLoaderData(); + const lFetcher = useFetcher(); + const pFetcher = useFetcher(); + const submit = useSubmit(); + const data = new FormData(); + data.append("test", "test"); + return ( +
    +

    Welcome to Remix

    + +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/_layout.tsx b/test-apps/remix-vite-embedded/app/routes/_layout.tsx new file mode 100644 index 0000000..7c2a111 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout.tsx @@ -0,0 +1,15 @@ +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { Outlet } from "@remix-run/react"; +import {EmbeddedDevTools} from 'remix-development-tools/client' + +export const loader = async ({ request }: LoaderFunctionArgs) => { + return { test: "returning raw object" }; +}; + +export default function App() { + return ( +
    + +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/_layout/test.tsx b/test-apps/remix-vite-embedded/app/routes/_layout/test.tsx new file mode 100644 index 0000000..054f645 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/_layout/test.tsx @@ -0,0 +1,13 @@ +import type { LoaderArgs } from "@remix-run/node"; +import { useLoaderData } from "@remix-run/react"; + +export const loader = async ({ request }: LoaderArgs) => { + return null; +}; + +export default function RouteComponent(){ + const data = useLoaderData() + return ( +
    + ); +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/correct.tsx b/test-apps/remix-vite-embedded/app/routes/correct.tsx new file mode 100644 index 0000000..c1f6998 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/correct.tsx @@ -0,0 +1,51 @@ +import type { V2_MetaFunction } from "@remix-run/react/dist/routeModules"; +import type { HeadersFunction, LinksFunction, LoaderArgs, ActionArgs } from "@remix-run/node"; +import { useLoaderData, isRouteErrorResponse, useRouteError } from "@remix-run/react"; +import type { ShouldRevalidateFunction } from "@remix-run/react"; + +export const links: LinksFunction = () => ( + [ + // your links here + ] +); + +export const meta: V2_MetaFunction = () => [ + // your meta here +]; + +export const handle = () => ({ + // your handler here +}); + +export const headers: HeadersFunction = () => ( + { + // your headers here + } +); + +export const loader = async ({ request }: LoaderArgs) => { + return null; +}; + +export const action = async ({ request }: ActionArgs) => { + return null; +}; + +export default function RouteComponent(){ + const data = useLoaderData() + return ( +
    + ); +} + +export function ErrorBoundary(){ + const error = useRouteError(); + if (isRouteErrorResponse(error)) { + return
    + } + return
    +} + +export const shouldRevalidate: ShouldRevalidateFunction = () => { + return true; +}; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/dashboard.tsx b/test-apps/remix-vite-embedded/app/routes/dashboard.tsx new file mode 100644 index 0000000..64cde7a --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/dashboard.tsx @@ -0,0 +1,19 @@ +import type { LoaderFunctionArgs } from '@remix-run/node'; +import { useLoaderData, Form } from '@remix-run/react'; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + + return { message: 'You are logged in!' }; +}; + +export default function DashboardRoute() { + const { message } = useLoaderData(); + return ( +
    +

    {message}

    +
    + +
    +
    + ); +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/file.tsx b/test-apps/remix-vite-embedded/app/routes/file.tsx new file mode 100644 index 0000000..cd60bc6 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/file.tsx @@ -0,0 +1,10 @@ +import { Outlet } from "@remix-run/react"; + +export default function File() { + return ( +
    +

    File Route

    + +
    + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/folder/route.tsx b/test-apps/remix-vite-embedded/app/routes/folder/route.tsx new file mode 100644 index 0000000..b66d2c0 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/folder/route.tsx @@ -0,0 +1,8 @@ +import { Outlet } from "@remix-run/react"; + +export default function Folder() { + return
    +

    Folder Route

    + +
    +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/login.tsx b/test-apps/remix-vite-embedded/app/routes/login.tsx new file mode 100644 index 0000000..7a946f6 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/login.tsx @@ -0,0 +1,15 @@ +import { Form, Link } from "@remix-run/react"; + +interface SocialButtonProps { + label: string; +} + + +export default function LoginRoute() { + return ( + <> + Login + + + ); +} diff --git a/test-apps/remix-vite-embedded/app/routes/logout.tsx b/test-apps/remix-vite-embedded/app/routes/logout.tsx new file mode 100644 index 0000000..8ac305c --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/logout.tsx @@ -0,0 +1,5 @@ +import type { LoaderFunctionArgs } from "@remix-run/node" + +export const action = async ({ request }: LoaderFunctionArgs) => { + return null; +}; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/other._index.tsx b/test-apps/remix-vite-embedded/app/routes/other._index.tsx new file mode 100644 index 0000000..617bbf0 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/other._index.tsx @@ -0,0 +1,3 @@ +export default function OtherIndexRoute() { + return
    Other Home
    +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/other.page.tsx b/test-apps/remix-vite-embedded/app/routes/other.page.tsx new file mode 100644 index 0000000..1aa5ca6 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/other.page.tsx @@ -0,0 +1,3 @@ +export default function OtherPageRoute() { + return
    Other Page
    +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/other.tsx b/test-apps/remix-vite-embedded/app/routes/other.tsx new file mode 100644 index 0000000..1b1a7ea --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/other.tsx @@ -0,0 +1,8 @@ +import { Outlet } from "@remix-run/react"; + +export default function OtherLayout() { + return
    +

    Other Layout

    + +
    +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/routes/server-timings.tsx b/test-apps/remix-vite-embedded/app/routes/server-timings.tsx new file mode 100644 index 0000000..6e02b13 --- /dev/null +++ b/test-apps/remix-vite-embedded/app/routes/server-timings.tsx @@ -0,0 +1,46 @@ +import type { ActionFunctionArgs } from "@remix-run/node"; +import { json, redirect, type LoaderFunctionArgs, defer } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; + +import { getServerTiming } from "~/timing.server"; + + +export const meta: MetaFunction = () => { + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; +}; + + +export const loader = async ({ request, response }: LoaderFunctionArgs) => { + const { time, getServerTimingHeader } = getServerTiming(); + await time("test", () => { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve("test"); + }, 300); + }); + }) + await time("test1", () =>new Promise((resolve, reject) => { + setTimeout(() => { + resolve("test"); + }, 200); + })) + return json({ message: "Hello World!", }, { + headers: getServerTimingHeader(), + }); +}; + +export const action = async ({ request }: ActionFunctionArgs) => { + return redirect("/login"); +}; + +export default function Index() { + + return ( +

    + Server timings test route +

    + ); +} diff --git a/test-apps/remix-vite-embedded/app/tailwind.css b/test-apps/remix-vite-embedded/app/tailwind.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/test-apps/remix-vite-embedded/app/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/app/timing.server.ts b/test-apps/remix-vite-embedded/app/timing.server.ts new file mode 100644 index 0000000..5a6927a --- /dev/null +++ b/test-apps/remix-vite-embedded/app/timing.server.ts @@ -0,0 +1,129 @@ +export type PerformanceServerTimings = Record< + string, + Array +> + +/** + * Run this on the server to get a `time` function that can be used to time + * server-side operations and add them to the `Server-Timing` header. + */ +export function getServerTiming() { + const serverTimings: PerformanceServerTimings = {} + + return { + time( + serverTiming: + | string + | { + name: string + description: string + }, + fn: Promise | (() => T | Promise), + ) { + return time(serverTimings, serverTiming, fn) + }, + getHeaderField() { + return getServerTimeHeaderField(serverTimings) + }, + getServerTimingHeader() { + return { + "Server-Timing": getServerTimeHeaderField(serverTimings), + } + }, + } +} + +export async function time( + serverTimings: PerformanceServerTimings, + serverTimingParam: + | string + | { + name: string + description: string + }, + fn: Promise | (() => T | Promise), +) { + const start = performance.now() + + const name = + typeof serverTimingParam === "string" + ? serverTimingParam + : serverTimingParam.name + const description = + typeof serverTimingParam === "string" ? "" : serverTimingParam.description + + if (!serverTimings[name]) { + serverTimings[name] = [] + } + + let result: T + try { + result = typeof fn === "function" ? await fn() : await fn + } catch (error) { + void recordServerTiming(serverTimings, { + name, + description: "Error", + }) + + // Re-throw the error so that the caller can handle it + throw error + } + + void recordServerTiming(serverTimings, { + name, + description, + duration: performance.now() - start, + }) + + return result +} + +function recordServerTiming( + serverTimings: PerformanceServerTimings, + timing: { + name: string + description?: string + duration?: number + }, +) { + const serverTiming: PerformanceServerTiming = { + name: timing.name, + description: timing.description ?? "", + duration: timing.duration ?? 0, + toJSON() { + // https://w3c.github.io/server-timing/#dom-performanceservertiming-tojson + // we don't need this, but the PerformanceServerTiming spec asks for it + return JSON.parse(JSON.stringify(serverTiming)) + }, + } + + serverTimings[timing.name].push(serverTiming) +} + +export function getServerTimeHeaderField( + serverTimings: PerformanceServerTimings, +) { + // https://w3c.github.io/server-timing/#the-server-timing-header-field + return Object.entries(serverTimings) + .map(([name, timingInfos]) => { + // duration is in milliseconds with microseconds precision + const dur = timingInfos + .reduce((totalDuration, { duration }) => totalDuration + duration, 0) + .toFixed(3) + + const desc = timingInfos + .map(({ description }) => description) + .filter(Boolean) + .join(" & ") + + return [ + name.replaceAll(/(:| |@|=|;|,)/g, "_"), + // desc and dur are both optional + desc ? `desc=${JSON.stringify(desc)}` : null, + dur ? `dur=${dur}` : null, + ] + .filter(Boolean) + .join(";") + }) + .join(",") +} \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/env.d.ts b/test-apps/remix-vite-embedded/env.d.ts new file mode 100644 index 0000000..78ed234 --- /dev/null +++ b/test-apps/remix-vite-embedded/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/test-apps/remix-vite-embedded/package.json b/test-apps/remix-vite-embedded/package.json new file mode 100644 index 0000000..9325daa --- /dev/null +++ b/test-apps/remix-vite-embedded/package.json @@ -0,0 +1,37 @@ +{ + "name": "remix-vite-embedded", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "remix vite:build", + "dev": "remix vite:dev", + "start": "remix-serve ./build/server/index.js", + "typecheck": "tsc" + }, + "dependencies": { + "@remix-run/node": "^2.8.0", + "@remix-run/react": "^2.8.0", + "@remix-run/serve": "^2.8.0", + "isbot": "^3.6.8", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@remix-run/dev": "^2.8.0", + "@remix-run/eslint-config": "^2.8.0", + "@types/react": "^18.2.20", + "@types/react-dom": "^18.2.7", + "autoprefixer": "^10.4.20", + "eslint": "^8.38.0", + "postcss": "^8.4.47", + "remix-development-tools": "*", + "tailwindcss": "^3.4.13", + "typescript": "^5.1.6", + "vite": "^5.1.0", + "vite-tsconfig-paths": "^4.2.1" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/test-apps/remix-vite-embedded/plugins/tailwind-palette.tsx b/test-apps/remix-vite-embedded/plugins/tailwind-palette.tsx new file mode 100644 index 0000000..72d172b --- /dev/null +++ b/test-apps/remix-vite-embedded/plugins/tailwind-palette.tsx @@ -0,0 +1,266 @@ +import React from "react"; +// All tailwind color variants, need to be defined like this so your project picks them up +const colorVariants = { + red: [ + "bg-red-50", + "bg-red-100", + "bg-red-200", + "bg-red-300", + "bg-red-400", + "bg-red-500", + "bg-red-600", + "bg-red-700", + "bg-red-800", + "bg-red-900", + ], + orange: [ + "bg-orange-50", + "bg-orange-100", + "bg-orange-200", + "bg-orange-300", + "bg-orange-400", + "bg-orange-500", + "bg-orange-600", + "bg-orange-700", + "bg-orange-800", + "bg-orange-900", + ], + amber: [ + "bg-amber-50", + "bg-amber-100", + "bg-amber-200", + "bg-amber-300", + "bg-amber-400", + "bg-amber-500", + "bg-amber-600", + "bg-amber-700", + "bg-amber-800", + "bg-amber-900", + ], + yellow: [ + "bg-yellow-50", + "bg-yellow-100", + "bg-yellow-200", + "bg-yellow-300", + "bg-yellow-400", + "bg-yellow-500", + "bg-yellow-600", + "bg-yellow-700", + "bg-yellow-800", + "bg-yellow-900", + ], + lime: [ + "bg-lime-50", + "bg-lime-100", + "bg-lime-200", + "bg-lime-300", + "bg-lime-400", + "bg-lime-500", + "bg-lime-600", + "bg-lime-700", + "bg-lime-800", + "bg-lime-900", + ], + green: [ + "bg-green-50", + "bg-green-100", + "bg-green-200", + "bg-green-300", + "bg-green-400", + "bg-green-500", + "bg-green-600", + "bg-green-700", + "bg-green-800", + "bg-green-900", + ], + emerald: [ + "bg-emerald-50", + "bg-emerald-100", + "bg-emerald-200", + "bg-emerald-300", + "bg-emerald-400", + "bg-emerald-500", + "bg-emerald-600", + "bg-emerald-700", + "bg-emerald-800", + "bg-emerald-900", + ], + teal: [ + "bg-teal-50", + "bg-teal-100", + "bg-teal-200", + "bg-teal-300", + "bg-teal-400", + "bg-teal-500", + "bg-teal-600", + "bg-teal-700", + "bg-teal-800", + "bg-teal-900", + ], + cyan: [ + "bg-cyan-50", + "bg-cyan-100", + "bg-cyan-200", + "bg-cyan-300", + "bg-cyan-400", + "bg-cyan-500", + "bg-cyan-600", + "bg-cyan-700", + "bg-cyan-800", + "bg-cyan-900", + ], + blue: [ + "bg-blue-50", + "bg-blue-100", + "bg-blue-200", + "bg-blue-300", + "bg-blue-400", + "bg-blue-500", + "bg-blue-600", + "bg-blue-700", + "bg-blue-800", + "bg-blue-900", + ], + indigo: [ + "bg-indigo-50", + "bg-indigo-100", + "bg-indigo-200", + "bg-indigo-300", + "bg-indigo-400", + "bg-indigo-500", + "bg-indigo-600", + "bg-indigo-700", + "bg-indigo-800", + "bg-indigo-900", + ], + violet: [ + "bg-violet-50", + "bg-violet-100", + "bg-violet-200", + "bg-violet-300", + "bg-violet-400", + "bg-violet-500", + "bg-violet-600", + "bg-violet-700", + "bg-violet-800", + "bg-violet-900", + ], + purple: [ + "bg-purple-50", + "bg-purple-100", + "bg-purple-200", + "bg-purple-300", + "bg-purple-400", + "bg-purple-500", + "bg-purple-600", + "bg-purple-700", + "bg-purple-800", + "bg-purple-900", + ], + fuchsia: [ + "bg-fuchsia-50", + "bg-fuchsia-100", + "bg-fuchsia-200", + "bg-fuchsia-300", + "bg-fuchsia-400", + "bg-fuchsia-500", + "bg-fuchsia-600", + "bg-fuchsia-700", + "bg-fuchsia-800", + "bg-fuchsia-900", + ], + pink: [ + "bg-pink-50", + "bg-pink-100", + "bg-pink-200", + "bg-pink-300", + "bg-pink-400", + "bg-pink-500", + "bg-pink-600", + "bg-pink-700", + "bg-pink-800", + "bg-pink-900", + ], + rose: [ + "bg-rose-50", + "bg-rose-100", + "bg-rose-200", + "bg-rose-300", + "bg-rose-400", + "bg-rose-500", + "bg-rose-600", + "bg-rose-700", + "bg-rose-800", + "bg-rose-900", + ], + gray: [ + "bg-gray-50", + "bg-gray-100", + "bg-gray-200", + "bg-gray-300", + "bg-gray-400", + "bg-gray-500", + "bg-gray-600", + "bg-gray-700", + "bg-gray-800", + "bg-gray-900", + ], +} as const; + +// Color groups +const colors = Object.keys(colorVariants) as (keyof typeof colorVariants)[]; + +const TailwindColorPalette = () => { + return ( +
    +
    + {colors.map((color) => { + return ( +
    +
    {color}
    +
    + {colorVariants[color].map((variant: string) => ( +
    navigator.clipboard.writeText(variant)} + className={`h-12 w-12 cursor-pointer ${variant}`} + >
    + ))} +
    +
    + ); + })} +
    +
    + ); +}; + +export const tailwindPalettePlugin = () => ({ + // Adds palette icon, you can change + icon: ( + + + + + + + + ), + // Name of the tab + name: "Color Palette", + // id of the tab, must be unique + id: "palette", + requiresForge: false, + hideTimeline: false, + component: , +}); \ No newline at end of file diff --git a/test-apps/remix-vite-embedded/postcss.config.js b/test-apps/remix-vite-embedded/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/test-apps/remix-vite-embedded/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/test-apps/remix-vite-embedded/public/favicon.ico b/test-apps/remix-vite-embedded/public/favicon.ico new file mode 100644 index 0000000..8830cf6 Binary files /dev/null and b/test-apps/remix-vite-embedded/public/favicon.ico differ diff --git a/test-apps/remix-vite-embedded/tailwind.config.js b/test-apps/remix-vite-embedded/tailwind.config.js new file mode 100644 index 0000000..cf56a6d --- /dev/null +++ b/test-apps/remix-vite-embedded/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["../src/**/*.{tsx,ts}", "app/**/*.{tsx,ts}"], + theme: { + extend: {}, + }, + plugins: [], + corePlugins: { + preflight: false + } +}; diff --git a/test-apps/remix-vite-embedded/tsconfig.json b/test-apps/remix-vite-embedded/tsconfig.json new file mode 100644 index 0000000..269c0cc --- /dev/null +++ b/test-apps/remix-vite-embedded/tsconfig.json @@ -0,0 +1,23 @@ +{ + "include": ["env.d.ts", "**/*.ts", "**/*.tsx"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "target": "ES2022", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + + // Remix takes care of building everything in `remix build`. + "noEmit": true + } +} diff --git a/test-apps/remix-vite-embedded/vite.config.ts b/test-apps/remix-vite-embedded/vite.config.ts new file mode 100644 index 0000000..0f0eb18 --- /dev/null +++ b/test-apps/remix-vite-embedded/vite.config.ts @@ -0,0 +1,41 @@ +import { vitePlugin as remix } from "@remix-run/dev"; +import { defineConfig } from "vite"; +import tsconfigPaths from "vite-tsconfig-paths"; +import { remixDevTools, defineRdtConfig } from "remix-development-tools" + +const config = defineRdtConfig({ + client: { + defaultOpen: false, + panelLocation: "top", + position: "top-right", + requireUrlFlag: false, + liveUrls: [ + { url: "https://forge42.dev", name: "Production" }, + { + url: "https://forge42.dev/staging", + name: "Staging", + }], + }, + pluginDir: "./plugins", + includeInProd: true, + server: { + serverTimingThreshold: 250 + }, + panelMode: 'embedded', +}); + +export default defineConfig({ + plugins: [ + remixDevTools( config), + remix({ + future: { + unstable_singleFetch: true + } + }), + tsconfigPaths() + ], + server: { + open: true, + port: 3005 , + }, +});