Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@
"shiny-doodles-jump",
"slick-roses-fix",
"social-donkeys-cross",
"soft-cases-share",
"stale-groups-poke",
"tender-jeans-occur",
"true-suits-fly",
"vast-places-rhyme",
"wicked-spoons-fry"
]
}
6 changes: 6 additions & 0 deletions .changeset/soft-cases-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pulse-editor/shared-utils": patch
"@pulse-editor/react-api": patch
---

Add queue to command react hook
6 changes: 6 additions & 0 deletions .changeset/vast-places-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pulse-editor/shared-utils": patch
"@pulse-editor/react-api": patch
---

Add env hook
16 changes: 16 additions & 0 deletions npm-packages/react-api/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# @pulse-editor/react-api

## 0.1.1-alpha.36

### Patch Changes

- Add queue to command react hook
- Updated dependencies
- @pulse-editor/[email protected]

## 0.1.1-alpha.35

### Patch Changes

- Add env hook
- Updated dependencies
- @pulse-editor/[email protected]

## 0.1.1-alpha.34

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions npm-packages/react-api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pulse-editor/react-api",
"version": "0.1.1-alpha.34",
"version": "0.1.1-alpha.36",
"main": "dist/main.js",
"files": [
"dist"
Expand Down Expand Up @@ -38,7 +38,7 @@
"typescript-eslint": "^8.30.1"
},
"peerDependencies": {
"@pulse-editor/shared-utils": "0.1.1-alpha.34",
"@pulse-editor/shared-utils": "0.1.1-alpha.36",
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
Expand Down
56 changes: 38 additions & 18 deletions npm-packages/react-api/src/hooks/editor/use-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
IMCMessageTypeEnum,
ReceiverHandler,
} from "@pulse-editor/shared-utils";
import { useEffect, useRef, useState } from "react";
import useIMC from "../../lib/use-imc";
import { useEffect, useState } from "react";

/**
* Register an extension command to listen to IMC messages from the core,
Expand All @@ -17,33 +17,38 @@ import { useEffect, useState } from "react";
*/
export default function useCommand(
commandInfo: CommandInfo,
callbackHandler?: (args: any) => Promise<string | void>
callbackHandler?: (args: any) => Promise<string | void>,
isExtReady: boolean = true
) {
const { isReady, imc } = useIMC(getReceiverHandlerMap());

const [handler, setHandler] = useState<
((args: any) => Promise<any>) | undefined
>(undefined);

// Queue to hold commands until extension is ready
const commandQueue = useRef<{ args: any; resolve: (v: any) => void }[]>([]);

async function executeCommand(args: any) {
if (!handler) return;

const res = await handler(args);
return res;
}

function getReceiverHandlerMap() {
const receiverHandlerMap = new Map<IMCMessageTypeEnum, ReceiverHandler>([
[
IMCMessageTypeEnum.EditorRunExtCommand,
async (senderWindow: Window, message: IMCMessage) => {
async (_senderWindow: Window, message: IMCMessage) => {
if (!commandInfo) {
throw new Error("Extension command is not available");
}

const {
name,
args,
}: {
name: string;
args: any;
} = message.payload;
const { name, args }: { name: string; args: any } = message.payload;

if (name === commandInfo.name) {
// Check if the parameters match the command's parameters
// Validate parameters
const commandParameters = commandInfo.parameters;
if (
Object.keys(args).length !== Object.keys(commandParameters).length
Expand All @@ -54,6 +59,7 @@ export default function useCommand(
}, got ${Object.keys(args).length}`
);
}

for (const [key, value] of Object.entries(args)) {
if (commandInfo.parameters[key] === undefined) {
throw new Error(`Invalid parameter: ${key}`);
Expand All @@ -67,23 +73,37 @@ export default function useCommand(
}
}

// Execute the command handler with the parameters
if (handler) {
const res = await handler(args);
if (res) {
return res;
}
// If extension is ready, execute immediately
if (isExtReady) {
return await executeCommand(args);
}

// Otherwise, queue the command and return when executed
return new Promise((resolve) => {
commandQueue.current.push({ args, resolve });
});
}
},
],
]);
return receiverHandlerMap;
}

// Flush queued commands when isExtReady becomes true
useEffect(() => {
if (isExtReady && commandQueue.current.length > 0) {
const pending = [...commandQueue.current];
commandQueue.current = [];
pending.forEach(async ({ args, resolve }) => {
const res = await executeCommand(args);
resolve(res);
});
}
}, [isExtReady]);

useEffect(() => {
imc?.updateReceiverHandlerMap(getReceiverHandlerMap());
}, [handler, imc]);
}, [handler, imc, isExtReady]);

useEffect(() => {
setHandler(() => callbackHandler);
Expand Down
26 changes: 26 additions & 0 deletions npm-packages/react-api/src/hooks/editor/use-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { IMCMessage, IMCMessageTypeEnum } from "@pulse-editor/shared-utils";
import { useEffect, useState } from "react";
import useIMC from "../../lib/use-imc";

export default function usePulseEnv() {
const receiverHandlerMap = new Map<
IMCMessageTypeEnum,
(senderWindow: Window, message: IMCMessage) => Promise<void>
>();

const { imc, isReady } = useIMC(receiverHandlerMap);
const [envs, setEnvs] = useState<Record<string, string>>({});

useEffect(() => {
if (isReady) {
imc?.sendMessage(IMCMessageTypeEnum.EditorGetEnv).then((env) => {
setEnvs(env);
});
}
}, [isReady]);

return {
isReady,
envs,
};
}
16 changes: 9 additions & 7 deletions npm-packages/react-api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import useAgentTools from "./hooks/agent/use-agent-tools";
import useAgents from "./hooks/agent/use-agents";
import useCommand from "./hooks/editor/use-command";
import useFileView from "./hooks/editor/use-file-view";
import useLoading from "./hooks/editor/use-loading";
import useNotification from "./hooks/editor/use-notification";
import useTheme from "./hooks/editor/use-theme";
import useToolbar from "./hooks/editor/use-toolbar";
import useCommand from "./hooks/editor/use-command";

import useImageGen from "./hooks/ai-modality/use-image-gen";
import useLLM from "./hooks/ai-modality/use-llm";
import useOCR from "./hooks/ai-modality/use-ocr";
import useSTT from "./hooks/ai-modality/use-stt";
import useTTS from "./hooks/ai-modality/use-tts";
import useVideoGen from "./hooks/ai-modality/use-video-gen";
import usePulseEnv from "./hooks/editor/use-env";
import useTerminal from "./hooks/terminal/use-terminal";

export {
useAgentTools,
useAgents,
useCommand,
useFileView,
useNotification,
useTheme,
useToolbar,
useImageGen,
useVideoGen,
useLLM,
useLoading,
useNotification,
useOCR,
usePulseEnv,
useSTT,
useTTS,
useCommand,
useTerminal,
useLoading,
useTheme,
useToolbar,
useVideoGen,
};
12 changes: 12 additions & 0 deletions npm-packages/shared-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# @pulse-editor/shared-utils

## 0.1.1-alpha.36

### Patch Changes

- Add queue to command react hook

## 0.1.1-alpha.35

### Patch Changes

- Add env hook

## 0.1.1-alpha.34

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion npm-packages/shared-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pulse-editor/shared-utils",
"version": "0.1.1-alpha.34",
"version": "0.1.1-alpha.36",
"main": "dist/main.js",
"files": [
"dist"
Expand Down
2 changes: 2 additions & 0 deletions npm-packages/shared-utils/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export enum IMCMessageTypeEnum {
EditorThemeUpdate = "editor-theme-update",
// Send notification
EditorShowNotification = "editor-show-notification",
// Get environment variables
EditorGetEnv = "editor-get-env",
// #endregion

// #region Platform API interaction messages (require OS-like environment)
Expand Down
34 changes: 31 additions & 3 deletions web/components/explorer/app/app-explorer.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import ExtensionPreview from "@/components/extension/extension-preview";
import { EditorContext } from "@/components/providers/editor-context-provider";
import { useScreenSize } from "@/lib/hooks/use-screen-size";
import { useTabViewManager } from "@/lib/hooks/use-tab-view-manager";
import { AppViewConfig } from "@/lib/types";
import { Button } from "@heroui/react";
import { useContext } from "react";
import { v4 } from "uuid";

export default function AppExplorer() {
const editorContext = useContext(EditorContext);

const { createAppViewInCanvasView } = useTabViewManager();
const { isLandscape } = useScreenSize();

const extensions = editorContext?.persistSettings?.extensions ?? [];

const previews = extensions.map((ext, index) => (
<div key={index} className="w-full h-fit">
<div
key={index}
className="w-full h-fit"
draggable
onDragStart={(e) => {
e.dataTransfer.setData("text/plain", JSON.stringify(ext));
}}
>
<ExtensionPreview
extension={ext}
isShowInstalledChip={false}
Expand All @@ -28,18 +38,36 @@ export default function AppExplorer() {
recommendedWidth: ext.config.recommendedWidth,
};
createAppViewInCanvasView(config);
console.log("Is Landscape:", isLandscape);
if (!isLandscape) {
editorContext?.setEditorStates((prev) => ({
...prev,
isSideMenuOpen: false,
}));
}
}}
/>
</div>
));

return (
<div className="p-4 h-full">
<div className="h-full grid grid-rows-[max-content_auto_max-content] gap-y-2">
<p className="text-center">Tap or drag an extension to open it.</p>

<div className="mt-4 grid grid-cols-2 gap-2 h-full w-full overflow-y-auto">
<div className="grid grid-cols-2 gap-2 h-full w-full overflow-y-auto overflow-x-hidden px-4">
{previews}
</div>
<Button
className="mx-4 mb-2"
onPress={() => {
editorContext?.setEditorStates((prev) => ({
...prev,
isMarketplaceOpen: true,
}));
}}
>
Browse More Apps
</Button>
</div>
);
}
18 changes: 1 addition & 17 deletions web/components/explorer/file-system/fs-explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,7 @@ export default function FileSystemExplorer({

// Browse inside a project
return (
<div className="relative h-full w-full">
{/* <div className="flex w-full justify-center">
<div className="w-fit">
<Tabs
tabItems={tabItems}
selectedItem={tabItems[selectedTabIndex]}
setSelectedItem={(item) => {
const index = tabItems.findIndex(
(tab) => tab.name === item?.name,
);
setSelectedTabIndex(index !== -1 ? index : 0);
}}
isClosable={false}
/>
</div>
</div> */}

<div className="relative h-full w-full px-2 py-1">
<div className="flex h-full w-full flex-col space-y-2">
<div className="bg-default text-default-foreground flex h-10 w-full items-center rounded-xl px-3">
<div className="flex w-full">
Expand Down
Loading