Skip to content

Commit

Permalink
feat: extend ipc (#2)
Browse files Browse the repository at this point in the history
* docs(utils): add docs to helpers

* feat(utils): add pc types to utils

* feat(react): make scrollbars accept a ref

* chore(react):add missing dependencies

* feat(react): extend sdk hook for new API

* feat(joy): make scrollbars optional

* refactor: cleanup types
  • Loading branch information
pixelass committed Mar 8, 2024
1 parent 21f1952 commit 3d40e32
Show file tree
Hide file tree
Showing 13 changed files with 244 additions and 75 deletions.
8 changes: 3 additions & 5 deletions packages/joy/package.json
Expand Up @@ -49,27 +49,25 @@
"watch": "tsc --watch --project tsconfig.production.json"
},
"dependencies": {
"@captn/react": "^0.2.1",
"@captn/theme": "^0.2.1",
"@captn/utils": "^0.2.1",
"@mui/base": "5.0.0-beta.37",
"@mui/icons-material": "5.15.11",
"@mui/joy": "5.0.0-beta.29",
"@mui/material": "5.15.11",
"react-custom-scrollbars": "^4.2.1"
"@mui/material": "5.15.11"
},
"devDependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@mui/joy": "5.0.0-beta.29",
"@types/react": "^18.2.61",
"@types/react-custom-scrollbars": "^4.0.13",
"@types/react-dom": "^18.2.19",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"peerDependencies": {
"@emotion/react": ">=11.11.3",
"@emotion/styled": ">=11",
"@mui/joy": ">=5.0.0-beta.29",
"react": ">=18",
"react-dom": ">=18"
},
Expand Down
5 changes: 1 addition & 4 deletions packages/joy/src/app-frame/index.tsx
@@ -1,4 +1,3 @@
import { CustomScrollbars } from "@captn/react/custom-scrollbars";
import Box from "@mui/joy/Box";
import Sheet, { SheetProps } from "@mui/joy/Sheet";
import { ReactNode } from "react";
Expand Down Expand Up @@ -27,9 +26,7 @@ export function AppFrame({
}}
>
{titleBar}
<Box sx={{ flex: 1, position: "relative" }}>
<CustomScrollbars>{children}</CustomScrollbars>
</Box>
<Box sx={{ flex: 1, position: "relative" }}>{children}</Box>
</Sheet>
);
}
3 changes: 3 additions & 0 deletions packages/joy/src/captn.d.ts
@@ -0,0 +1,3 @@
/// <reference types="@captn/utils/types" />

// NOTE: This file should not be edited
10 changes: 1 addition & 9 deletions packages/joy/src/types.ts
@@ -1,13 +1,5 @@
import type { PaletteRange } from "@mui/joy/styles";

declare global {
interface Window {
ipc: {
send(channel: string, value?: unknown): void;
on(channel: string, callback: (...args: any[]) => void): () => void;
};
}
}
import "@captn/utils/types";

declare module "@mui/joy/styles" {
interface ColorPalettePropOverrides {
Expand Down
4 changes: 3 additions & 1 deletion packages/react/package.json
Expand Up @@ -41,10 +41,12 @@
"watch": "tsc --watch --project tsconfig.production.json"
},
"dependencies": {
"@captn/utils": "^0.2.1"
"@captn/utils": "^0.2.1",
"react-custom-scrollbars": "^4.2.1"
},
"devDependencies": {
"@types/react": "^18.2.61",
"@types/react-custom-scrollbars": "^4.0.13",
"@types/react-dom": "^18.2.19",
"react": "^18.2.0",
"react-dom": "^18.2.0"
Expand Down
3 changes: 3 additions & 0 deletions packages/react/src/captn.d.ts
@@ -0,0 +1,3 @@
/// <reference types="@captn/utils/types" />

// NOTE: This file should not be edited
55 changes: 27 additions & 28 deletions packages/react/src/custom-scrollbars/index.tsx
@@ -1,31 +1,30 @@
import Box from "@mui/joy/Box";
import type { CSSProperties, ReactNode } from "react";
import { CSSProperties, forwardRef, ReactNode } from "react";
import { Scrollbars } from "react-custom-scrollbars";

export function CustomScrollbars({
style,
children,
}: {
style?: CSSProperties;
children?: ReactNode;
}) {
return (
<Scrollbars
autoHide
universal
style={{ ...style, overflow: "hidden" }}
renderThumbVertical={properties => (
<Box
{...properties}
className="thumb-vertical"
sx={theme => ({
bgcolor: "text.secondary",
zIndex: theme.zIndex.badge + 1,
})}
/>
)}
>
{children}
</Scrollbars>
);
}
export const CustomScrollbars = forwardRef<
Scrollbars,
{
style?: CSSProperties;
children?: ReactNode;
}
>(({ style, children }, ref) => (
<Scrollbars
ref={ref}
autoHide
universal
style={{ ...style, overflow: "hidden" }}
renderThumbVertical={properties => (
<Box
{...properties}
className="thumb-vertical"
sx={theme => ({
bgcolor: "text.secondary",
zIndex: theme.zIndex.badge + 1,
})}
/>
)}
>
{children}
</Scrollbars>
));
9 changes: 0 additions & 9 deletions packages/react/src/types.ts
Expand Up @@ -2,12 +2,3 @@ export interface SDKMessage<T> {
action: string;
payload: T;
}

declare global {
interface Window {
ipc: {
send(channel: string, value?: unknown): void;
on(channel: string, callback: (...args: any[]) => void): () => void;
};
}
}
71 changes: 52 additions & 19 deletions packages/react/src/use-sdk/index.ts
@@ -1,16 +1,17 @@
import { APP_MESSAGE_KEY } from "@captn/utils/constants";
import { IPCHandlers } from "@captn/utils/types";
import { useCallback, useEffect, useRef } from "react";

import { SDKMessage } from "../types";

/**
* Provides a React hook for establishing and managing Inter-Process Communication (IPC)
* with an SDK. This hook enables sending messages to and receiving messages from the SDK,
* incorporating robust error handling. It abstracts the intricacies of IPC messaging,
* facilitating communication with minimal setup. React's `useCallback`, `useEffect`,
* and `useRef` hooks are utilized to maintain an efficient, memory-safe implementation.
* Error handling is deeply integrated, allowing consumers to appropriately respond to
* communication issues.
* with an SDK. This hook facilitates sending messages to and receiving messages from the SDK,
* handling files, and navigating directories, thereby incorporating robust error handling and
* abstracting the intricacies of IPC messaging for minimal setup. React's `useCallback`,
* `useEffect`, and `useRef` hooks are utilized to ensure an efficient, memory-safe implementation.
* Error handling is deeply integrated, enabling consumers to appropriately respond to communication
* and operation issues.
*
* @template T - The type of messages being sent to the SDK, constrained to be an object
* with string keys.
Expand All @@ -19,24 +20,28 @@ import { SDKMessage } from "../types";
*
* @param {string} appId - A unique identifier for the application or SDK instance. This ID
* is used to construct the IPC channel name, ensuring correct message routing.
* @param {Object} options - Configuration options for the hook.
* @param {Object} options - Configuration options for the hook, including callbacks for
* message reception and error handling.
* @param {Function} [options.onMessage] - An optional callback function invoked when a
* message is received from the SDK. The received message is passed as an argument,
* allowing the consuming component to process or react to it.
* @param {Function} [options.onError] - An optional callback function for handling errors
* that occur during IPC communication, including message sending failures or subscription
* issues. The error object is passed to this callback.
* that occur during IPC communication or file operations, including message sending failures,
* subscription issues, or file operation errors. The error object is passed to this callback.
*
* @returns {{ channel: string, send: (message: SDKMessage<T>) => void }}
* An object containing:
* @returns An object containing:
* - `channel`: The constructed IPC channel name for debugging or informational purposes.
* - `send`: A function to send messages to the SDK. This function is memoized and designed
* to internally catch and handle errors, invoking `onError` if provided.
* - `getFilePath`: A function to get the path of a specific file, leveraging the IPC mechanism.
* - `getDirectoryPath`: A function to get the path of a specific directory, leveraging the IPC mechanism.
* - `readFile`: A function to read the content of a file, encapsulating the IPC file reading capabilities.
* - `writeFile`: A function to write content to a file, encapsulating the IPC file writing capabilities.
*
* @example
* // Using the `useSDK` hook within a component
* const App = () => {
* const { send } = useSDK("myAppId", {
* const { send, readFile, writeFile } = useSDK("myAppId", {
* onMessage: (message) => {
* console.log("Received message from SDK:", message.action, message.payload);
* },
Expand All @@ -45,10 +50,26 @@ import { SDKMessage } from "../types";
* }
* });
*
* // Example usage of readFile and writeFile
* const handleFileOperations = async () => {
* try {
* const filePath = await writeFile('test.txt', 'Hello SDK!', { encoding: 'utf8' });
* const content = await readFile(filePath, 'utf8');
* console.log('File content:', content);
* } catch (error) {
* console.error('File operation error:', error);
* }
* };
*
* return (
* <button onClick={() => send({ action: "greet", payload: "Hello, SDK!" })}>
* Send Message
* </button>
* <div>
* <button onClick={() => send({ action: "greet", payload: "Hello, SDK!" })}>
* Send Message
* </button>
* <button onClick={handleFileOperations}>
* Test File Operations
* </button>
* </div>
* );
* };
*
Expand All @@ -61,16 +82,23 @@ import { SDKMessage } from "../types";
* - The hook manages subscriptions to prevent memory leaks, automatically cleaning up on
* component unmount.
* - While the hook simplifies IPC communication, a basic understanding of IPC mechanisms
* is beneficial for effective use and handling potential edge cases or errors.
* and file operations is beneficial for effective use and handling potential edge cases
* or errors.
*/

export function useSDK<T, R>(
appId: string,
{
onMessage,
onError,
}: { onMessage?(message: SDKMessage<R>): void; onError?(error: Error): void }
): { channel: string; send: (message: SDKMessage<T>) => void } {
): {
channel: string;
send(message: SDKMessage<T>): void;
getFilePath: IPCHandlers["getFilePath"];
getDirectoryPath: IPCHandlers["getFilePath"];
readFile: IPCHandlers["readFile"];
writeFile: IPCHandlers["writeFile"];
} {
const onMessageReference = useRef(onMessage);
const onErrorReference = useRef(onError);

Expand All @@ -89,6 +117,11 @@ export function useSDK<T, R>(
[appId]
);

const getFilePath = useCallback(window.ipc.getFilePath, []);
const getDirectoryPath = useCallback(window.ipc.getDirectoryPath, []);
const readFile = useCallback(window.ipc.readFile, []);
const writeFile = useCallback(window.ipc.writeFile, []);

useEffect(() => {
onMessageReference.current = onMessage;
}, [onMessage]);
Expand All @@ -114,5 +147,5 @@ export function useSDK<T, R>(
}
}, [channel]);

return { channel, send };
return { channel, send, getFilePath, getDirectoryPath, readFile, writeFile };
}
4 changes: 4 additions & 0 deletions packages/utils/package.json
Expand Up @@ -26,6 +26,10 @@
"./constants": {
"types": "./dist/constants.d.ts",
"default": "./dist/constants.js"
},
"./types": {
"types": "./dist/types.d.ts",
"default": "./dist/types.js"
}
},
"files": [
Expand Down

0 comments on commit 3d40e32

Please sign in to comment.