Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend ipc #2

Merged
merged 7 commits into from Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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