diff --git a/docs/posts/4.7.0/guides/plugins.mdx b/docs/posts/4.7.0/guides/plugins.mdx index d5edbf3..8b97748 100644 --- a/docs/posts/4.7.0/guides/plugins.mdx +++ b/docs/posts/4.7.0/guides/plugins.mdx @@ -2,8 +2,8 @@ title: "Plugins" description: "Remix Development Tools plugins" --- - -## Plugins in Vite + +## Plugins in Vite Plugins work in a different way in Vite. You create a directory for plugins and just provide the path to the directory to the plugin. The plugin will automatically import all the plugins from the directory and add them to the dev tools. You only need to make sure your exports are named exports and not default exports and that they are uniquely named. You can create a directory called plugins in your project and add your plugins there. Then you can add the following to your vite.config.js file: @@ -13,7 +13,7 @@ export default defineConfig({ plugins: [ remixDevTools({ pluginsDir: "./plugins" - })], + })], }); ``` @@ -39,8 +39,6 @@ Export a function with the following contract: icon: , // The component to be rendered when the tab is active component: , - // Whether the tab requires the Remix Forge VS Code extension to be connected to be shown - requiresForge: false, // Whether the timeline should be shown on the tab hideTimeline: false, } @@ -56,44 +54,5 @@ import { remixDevToolsPlugin } from "./remix-dev-tools-plugin"; - withDevTools(App); + withDevTools(App, { plugins: [remixDevToolsPlugin()] }) ``` - -You should now see your plugin in the Remix Development Tools as a new tab. - -## Using Remix Forge with your plugin -If you want to use Remix Forge with your plugin you can do so by setting the requiresForge property to true in your plugin. This will make sure that the plugin is only shown when the Remix Forge VS Code extension is connected. -Follow the above guide to create a plugin. -Import the following hook from remix-development-tools: -```tsx -import { useRemixForgeSocket } from "remix-development-tools"; - - const MyComponent = () => { - const socket = useRemixForgeSocket(); - // Implement your logic here - return
My Component
- } -``` - -You can now use the socket to send messages to the Remix Forge VS Code extension. For now it accepts reading/deleting/opening files in VS Code -```tsx - const MyComponent = () => { - const socket = useRemixForgeSocket(); - const runCommand = () => { - socket.sendJsonMessage({ subtype: "read_file", path: "package.json" }) - } - // Implement your logic here - return
My Component
- } - ``` -The following contract is returned from the extension: - ```tsx - interface RemixForgeResponse { - type: "plugin"; - subtype: "read_file" | "open_file" | "delete_file" | "write_file"; - error: boolean; - data: string | null; - } - ``` -Make sure you check if the type and subtype match your needs before using the data. -Refer to react-use-websocket for more information on how to use the socket and what options it accepts because that is what is used under the hood. -After you're done share your plugin with the community by opening a discussion! \ No newline at end of file +You should now see your plugin in the Remix Development Tools as a new tab. diff --git a/docs/posts/main/guides/plugins.mdx b/docs/posts/main/guides/plugins.mdx index d5edbf3..8b97748 100644 --- a/docs/posts/main/guides/plugins.mdx +++ b/docs/posts/main/guides/plugins.mdx @@ -2,8 +2,8 @@ title: "Plugins" description: "Remix Development Tools plugins" --- - -## Plugins in Vite + +## Plugins in Vite Plugins work in a different way in Vite. You create a directory for plugins and just provide the path to the directory to the plugin. The plugin will automatically import all the plugins from the directory and add them to the dev tools. You only need to make sure your exports are named exports and not default exports and that they are uniquely named. You can create a directory called plugins in your project and add your plugins there. Then you can add the following to your vite.config.js file: @@ -13,7 +13,7 @@ export default defineConfig({ plugins: [ remixDevTools({ pluginsDir: "./plugins" - })], + })], }); ``` @@ -39,8 +39,6 @@ Export a function with the following contract: icon: , // The component to be rendered when the tab is active component: , - // Whether the tab requires the Remix Forge VS Code extension to be connected to be shown - requiresForge: false, // Whether the timeline should be shown on the tab hideTimeline: false, } @@ -56,44 +54,5 @@ import { remixDevToolsPlugin } from "./remix-dev-tools-plugin"; - withDevTools(App); + withDevTools(App, { plugins: [remixDevToolsPlugin()] }) ``` - -You should now see your plugin in the Remix Development Tools as a new tab. - -## Using Remix Forge with your plugin -If you want to use Remix Forge with your plugin you can do so by setting the requiresForge property to true in your plugin. This will make sure that the plugin is only shown when the Remix Forge VS Code extension is connected. -Follow the above guide to create a plugin. -Import the following hook from remix-development-tools: -```tsx -import { useRemixForgeSocket } from "remix-development-tools"; - - const MyComponent = () => { - const socket = useRemixForgeSocket(); - // Implement your logic here - return
My Component
- } -``` - -You can now use the socket to send messages to the Remix Forge VS Code extension. For now it accepts reading/deleting/opening files in VS Code -```tsx - const MyComponent = () => { - const socket = useRemixForgeSocket(); - const runCommand = () => { - socket.sendJsonMessage({ subtype: "read_file", path: "package.json" }) - } - // Implement your logic here - return
My Component
- } - ``` -The following contract is returned from the extension: - ```tsx - interface RemixForgeResponse { - type: "plugin"; - subtype: "read_file" | "open_file" | "delete_file" | "write_file"; - error: boolean; - data: string | null; - } - ``` -Make sure you check if the type and subtype match your needs before using the data. -Refer to react-use-websocket for more information on how to use the socket and what options it accepts because that is what is used under the hood. -After you're done share your plugin with the community by opening a discussion! \ No newline at end of file +You should now see your plugin in the Remix Development Tools as a new tab. diff --git a/package.json b/package.json index 18de269..12a4d9a 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,9 @@ "default": "./dist/client.css" } }, - "files": ["dist"], + "files": [ + "dist" + ], "repository": { "type": "git", "url": "git+https://github.com/Code-Forge-Net/Remix-Dev-Tools.git" @@ -91,7 +93,11 @@ "icons": "npm run run:scripts scripts/icons.ts", "check:unused": "knip " }, - "workspaces": [".", "test-apps/*", "docs"], + "workspaces": [ + ".", + "test-apps/*", + "docs" + ], "peerDependencies": { "@remix-run/react": ">=1.15", "react": ">=17", @@ -139,7 +145,6 @@ "react-d3-tree": "^3.6.2", "react-diff-viewer-continued": "^3.3.1", "react-hotkeys-hook": "^4.5.0", - "react-use-websocket": "^4.8.1", "tailwind-merge": "^1.14.0" } -} +} \ No newline at end of file diff --git a/src/client.ts b/src/client.ts index 99834a6..36961c2 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,5 @@ // Named exports -export { useRemixForgeSocketExternal as useRemixForgeSocket } from "./client/hooks/useRemixForgeSocket.js" +//export { useRemixForgeSocketExternal as useRemixForgeSocket } from "./client/hooks/useRemixForgeSocket.js" export { EmbeddedDevTools } from "./client/EmbeddedDevTools.js" export { withViteDevTools, withDevTools } from "./client/init/root.js" diff --git a/src/client/components/NewRouteForm.tsx b/src/client/components/NewRouteForm.tsx index 76e156b..d7858e1 100644 --- a/src/client/components/NewRouteForm.tsx +++ b/src/client/components/NewRouteForm.tsx @@ -1,157 +1,151 @@ -import clsx from "clsx" -import { useState } from "react" -import { useRemixForgeSocket } from "../hooks/useRemixForgeSocket.js" -import { Checkbox } from "./Checkbox.js" -import { Input } from "./Input.js" - -interface NewRouteOptions { - path: string - loader: boolean - action: boolean - headers: boolean - errorBoundary: boolean - revalidate: boolean - handler: boolean - meta: boolean - links: boolean -} - -const DEFAULT_VALUES = { - path: "", - loader: false, - action: false, - headers: false, - errorBoundary: false, - revalidate: false, - handler: false, - meta: false, - links: false, -} +//import clsx from "clsx" +//import { useState } from "react" +//import { Checkbox } from "./Checkbox.js" +//import { Input } from "./Input.js" +// +//interface NewRouteOptions { +// path: string +// loader: boolean +// action: boolean +// headers: boolean +// errorBoundary: boolean +// revalidate: boolean +// handler: boolean +// meta: boolean +// links: boolean +//} +// +//const DEFAULT_VALUES = { +// path: "", +// loader: false, +// action: false, +// headers: false, +// errorBoundary: false, +// revalidate: false, +// handler: false, +// meta: false, +// links: false, +//} const NewRouteForm = () => { - const { sendJsonMessage } = useRemixForgeSocket({ - onMessage: (e) => { - const messageData = e.data - if (messageData.type === "route_added") { - setNewRouteInfo(DEFAULT_VALUES) - } - }, - }) - const [newRouteInfo, setNewRouteInfo] = useState(DEFAULT_VALUES) - - const handleSubmit = () => { - const { path, ...options } = newRouteInfo - sendJsonMessage({ - type: "add_route", - path, - options, - }) - } - - const setNewInfo = (info: Partial) => { - setNewRouteInfo({ ...newRouteInfo, ...info }) - } - return ( -
-
Route path:
- - setNewInfo({ - path: newRouteInfo.path.trim(), - }) - } - onChange={(e) => setNewInfo({ path: e.target.value })} - className="mb-1" - /> - - This will be added to your routes folder under your entered name, exclude the extension - -
Additional options:
- - setNewInfo({ - loader: !newRouteInfo.loader, - }) - } - id="loader" - > - Add a loader - - - setNewInfo({ - action: !newRouteInfo.action, - }) - } - id="action" - > - Add an action - - - setNewInfo({ - errorBoundary: !newRouteInfo.errorBoundary, - }) - } - id="error-boundary" - > - Add an error boundary - - - setNewInfo({ - handler: !newRouteInfo.handler, - }) - } - id="handle" - > - Add a handle - - setNewInfo({ meta: !newRouteInfo.meta })} id="meta"> - Add a meta export - - setNewInfo({ links: !newRouteInfo.links })} id="links"> - Add a links export - - - setNewInfo({ - headers: !newRouteInfo.headers, - }) - } - id="headers" - > - Add a headers export - - - setNewInfo({ - revalidate: !newRouteInfo.revalidate, - }) - } - id="shouldRevalidate" - > - Add a shouldRevalidate export - - -
- ) + return
+ // const sendJsonMessage = (params: any) => {} + // const [newRouteInfo, setNewRouteInfo] = useState(DEFAULT_VALUES) + // + // const handleSubmit = () => { + // const { path, ...options } = newRouteInfo + // sendJsonMessage({ + // type: "add_route", + // path, + // options, + // }) + // } + // + // const setNewInfo = (info: Partial) => { + // setNewRouteInfo({ ...newRouteInfo, ...info }) + // } + // return ( + //
+ //
Route path:
+ // + // setNewInfo({ + // path: newRouteInfo.path.trim(), + // }) + // } + // onChange={(e) => setNewInfo({ path: e.target.value })} + // className="mb-1" + // /> + // + // This will be added to your routes folder under your entered name, exclude the extension + // + //
Additional options:
+ // + // setNewInfo({ + // loader: !newRouteInfo.loader, + // }) + // } + // id="loader" + // > + // Add a loader + // + // + // setNewInfo({ + // action: !newRouteInfo.action, + // }) + // } + // id="action" + // > + // Add an action + // + // + // setNewInfo({ + // errorBoundary: !newRouteInfo.errorBoundary, + // }) + // } + // id="error-boundary" + // > + // Add an error boundary + // + // + // setNewInfo({ + // handler: !newRouteInfo.handler, + // }) + // } + // id="handle" + // > + // Add a handle + // + // setNewInfo({ meta: !newRouteInfo.meta })} id="meta"> + // Add a meta export + // + // setNewInfo({ links: !newRouteInfo.links })} id="links"> + // Add a links export + // + // + // setNewInfo({ + // headers: !newRouteInfo.headers, + // }) + // } + // id="headers" + // > + // Add a headers export + // + // + // setNewInfo({ + // revalidate: !newRouteInfo.revalidate, + // }) + // } + // id="shouldRevalidate" + // > + // Add a shouldRevalidate export + // + // + //
+ // ) } - +// export { NewRouteForm } +// diff --git a/src/client/hooks/useRemixForgeSocket.ts b/src/client/hooks/useRemixForgeSocket.ts deleted file mode 100644 index 3f7a940..0000000 --- a/src/client/hooks/useRemixForgeSocket.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { useState } from "react" -import { type Options, ReadyState } from "react-use-websocket" -import useWebSocket from "react-use-websocket" -import { useSettingsContext, useTerminalContext } from "../context/useRDTContext.js" - -const RETRY_COUNT = 2 - -export const useRemixForgeSocket = (options?: Options) => { - const { settings, setSettings } = useSettingsContext() - const { terminals, toggleTerminalLock, setProcessId } = useTerminalContext() - const { shouldConnectWithForge, port } = settings - const [retryCount, setRetryCount] = useState(0) - const opts: Options = { - ...options, - share: true, - shouldReconnect: () => true, - reconnectAttempts: RETRY_COUNT, - reconnectInterval: 0, - onClose: (e) => { - // connection closed by remix forge - if (e.code === 1005) { - setSettings({ shouldConnectWithForge: false }) - setRetryCount(0) - for (const terminal of terminals) { - toggleTerminalLock(terminal.id, false) - setProcessId(terminal.id, undefined) - } - - return - } - if (retryCount < RETRY_COUNT) { - return setRetryCount(retryCount + 1) - } - setSettings({ shouldConnectWithForge: false }) - }, - } - - const properties = useWebSocket(`ws://localhost:${port}`, opts, shouldConnectWithForge) - - const connectionStatus = { - [ReadyState.CONNECTING]: "Connecting", - [ReadyState.OPEN]: "Open", - [ReadyState.CLOSING]: "Closing", - [ReadyState.CLOSED]: "Closed", - [ReadyState.UNINSTANTIATED]: "Uninstantiated", - }[properties.readyState] - const isConnected = properties.readyState === ReadyState.OPEN - const isConnecting = properties.readyState === ReadyState.CONNECTING - - return { ...properties, connectionStatus, isConnected, isConnecting } -} - -interface RemixForgeMessage extends Record { - subtype: "read_file" | "open_file" | "delete_file" | "write_file" - path: string - data?: string -} - -export const useRemixForgeSocketExternal = (options?: Options) => { - const { sendJsonMessage, ...rest } = useRemixForgeSocket(options) - const sendJsonMessageExternal = (message: RemixForgeMessage) => { - sendJsonMessage({ ...message, type: "plugin" }) - } - return { sendJsonMessage: sendJsonMessageExternal, ...rest } -} diff --git a/src/client/layout/ContentPanel.tsx b/src/client/layout/ContentPanel.tsx index 23dcfa0..4c63f27 100644 --- a/src/client/layout/ContentPanel.tsx +++ b/src/client/layout/ContentPanel.tsx @@ -1,6 +1,5 @@ import clsx from "clsx" import { Fragment } from "react" -import { useRemixForgeSocket } from "../hooks/useRemixForgeSocket.js" import { useTabs } from "../hooks/useTabs.js" import { TimelineTab } from "../tabs/TimelineTab.js" import type { Tab } from "../tabs/index.js" @@ -11,8 +10,8 @@ interface ContentPanelProps { } const ContentPanel = ({ plugins }: ContentPanelProps) => { - const { isConnected, isConnecting } = useRemixForgeSocket() - const { Component, hideTimeline, isPluginTab, activeTab } = useTabs(isConnected, isConnecting, plugins) + //const { isConnected, isConnecting } = useRemixForgeSocket() + const { Component, hideTimeline, isPluginTab, activeTab } = useTabs(false, false, plugins) return (
diff --git a/src/client/layout/Tabs.tsx b/src/client/layout/Tabs.tsx index fb7053d..c765abd 100644 --- a/src/client/layout/Tabs.tsx +++ b/src/client/layout/Tabs.tsx @@ -9,7 +9,6 @@ import { useSettingsContext, } from "../context/useRDTContext.js" import { useHorizontalScroll } from "../hooks/useHorizontalScroll.js" -import { useRemixForgeSocket } from "../hooks/useRemixForgeSocket.js" import { useTabs } from "../hooks/useTabs.js" import type { Tab as TabType, Tabs as TabsType } from "../tabs/index.js" import { @@ -71,9 +70,10 @@ const Tabs = ({ plugins, setIsOpen }: TabsProps) => { const { htmlErrors } = useHtmlErrors() const { setPersistOpen } = usePersistOpen() const { activeTab } = settings - const { isConnected, isConnecting } = useRemixForgeSocket() - const { visibleTabs } = useTabs(isConnected, isConnecting, plugins) - const shouldShowConnectToForge = !isConnected || isConnecting + //const { isConnected, isConnecting } = useRemixForgeSocket() + const isConnecting = false + const { visibleTabs } = useTabs(false, false, plugins) + const shouldShowConnectToForge = false const scrollRef = useHorizontalScroll() const { setDetachedWindowOwner, detachedWindowOwner, detachedWindow } = useDetachedWindowControls() const handleDetachment = () => { diff --git a/src/client/tabs/RoutesTab.tsx b/src/client/tabs/RoutesTab.tsx index b7f0b06..cb62b8a 100644 --- a/src/client/tabs/RoutesTab.tsx +++ b/src/client/tabs/RoutesTab.tsx @@ -4,7 +4,6 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from ".. import { NewRouteForm } from "../components/NewRouteForm.js" import { useDetachedWindowControls, useSettingsContext } from "../context/useRDTContext.js" import { setRouteInLocalStorage } from "../hooks/detached/useListenToRouteChange.js" -import { useRemixForgeSocket } from "../hooks/useRemixForgeSocket.js" import { type ExtendedRoute, constructRoutePath, createExtendedRoutes } from "../utils/routing.js" import { createRouteTree } from "../utils/sanitize.js" @@ -20,7 +19,7 @@ const RoutesTab = () => { const activeRoutes = matches.map((match) => match.id) const { settings } = useSettingsContext() const { routeWildcards, routeViewMode } = settings - const { isConnected } = useRemixForgeSocket() + //const { isConnected } = useRemixForgeSocket() const { detachedWindow } = useDetachedWindowControls() const [activeRoute, setActiveRoute] = useState(null) const [routes] = useState(createExtendedRoutes()) @@ -67,7 +66,8 @@ const RoutesTab = () => {
) : ( - {isConnected && ( + {/* TODO bring back */} + {false && ( Add a new route to the project diff --git a/src/client/tabs/TerminalTab.tsx b/src/client/tabs/TerminalTab.tsx index 0909b97..1858f90 100644 --- a/src/client/tabs/TerminalTab.tsx +++ b/src/client/tabs/TerminalTab.tsx @@ -4,7 +4,6 @@ import clsx from "clsx" import { Icon } from "../components/icon/Icon.js" import type { Terminal as TerminalType } from "../context/terminal/types.js" import { useTerminalContext } from "../context/useRDTContext.js" -import { useRemixForgeSocket } from "../hooks/useRemixForgeSocket.js" import { useTerminalShortcuts } from "../hooks/useTerminalShortcuts.js" interface TerminalProps { @@ -37,39 +36,8 @@ const Terminal = ({ onClose, terminal, projectCommands }: TerminalProps) => { useEffect(() => { ref.current?.scrollTo({ top: ref.current.scrollHeight }) }, [terminal.output]) - - const { sendJsonMessage } = useRemixForgeSocket({ - onMessage: (message) => { - try { - const data = JSON.parse(message.data) - // Check if command was sent from this terminal - const isThisTerminalCommand = data.type === "terminal_command" && data.terminalId === terminal.id - - if (isThisTerminalCommand) { - const processDone = data.subtype === "ERROR" || data.subtype === "EXIT" || data.subtype === "CLOSE" - const hasOutputData = data.subtype === "DATA" || data.subtype === "ERROR" - // set the process ID if it exists so we can terminate it if we want - if (data.processId) { - setProcessId(terminal.id, data.processId) - } - // Process done => unlock terminal - if (processDone) { - setProcessId(terminal.id, undefined) - toggleTerminalLock(terminal.id, false) - } - // Add output to terminal - if (hasOutputData) { - addTerminalOutput(data.terminalId, { - type: data.subtype === "ERROR" ? "error" : "output", - value: data.data, - }) - } - } - } catch (e) { - // console.log(e); - } - }, - }) + // TODO bring back + const sendJsonMessage = (params: any) => {} const { onKeyDown } = useTerminalShortcuts({ onSubmit, setCommand, @@ -146,44 +114,34 @@ const Terminal = ({ onClose, terminal, projectCommands }: TerminalProps) => { } const TerminalTab = () => { - const { terminals, addOrRemoveTerminal } = useTerminalContext() - const [projectCommands, setProjectCommands] = useState>() - const { sendJsonMessage } = useRemixForgeSocket({ - onMessage: (message) => { - try { - const data = JSON.parse(message.data) - if (data.type === "commands") { - setProjectCommands(data.data) - } - } catch (e) { - // console.log(e); - } - }, - }) - - useEffect(() => { - sendJsonMessage({ type: "commands" }) - }, [sendJsonMessage]) - return ( -
- {terminals.length < 3 && ( - - )} - {/* */} - {terminals.map((terminal) => ( - addOrRemoveTerminal(terminal.id)} - key={terminal.id} - /> - ))} -
- ) + return
+ // const { terminals, addOrRemoveTerminal } = useTerminalContext() + // const [projectCommands, setProjectCommands] = useState>() + // //const sendJsonMessage = (params: any) =>{} + // // TODO bring back + // // useEffect(() => { + // // sendJsonMessage({ type: "commands" }) + // // }, [sendJsonMessage]) + // return ( + //
+ // {terminals.length < 3 && ( + // + // )} + // {/* */} + // {terminals.map((terminal) => ( + // addOrRemoveTerminal(terminal.id)} + // key={terminal.id} + // /> + // ))} + //
+ // ) } export { TerminalTab }