Skip to content

Commit fd6221c

Browse files
committed
Add recommended dimension & allow opening apps in canvas
1 parent 115153e commit fd6221c

File tree

19 files changed

+268
-144
lines changed

19 files changed

+268
-144
lines changed

.changeset/clean-mangos-swim.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@pulse-editor/shared-utils": patch
3+
"@pulse-editor/react-api": patch
4+
---
5+
6+
Add recommended display dimension in canvas view

.changeset/pre.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"beige-pandas-rhyme",
1818
"calm-rivers-march",
1919
"chatty-trains-beam",
20+
"clean-mangos-swim",
2021
"cruel-waves-double",
2122
"cruel-zoos-play",
2223
"curvy-places-wash",

npm-packages/react-api/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# @pulse-editor/react-api
22

3+
## 0.1.1-alpha.31
4+
5+
### Patch Changes
6+
7+
- Add recommended display dimension in canvas view
8+
- Updated dependencies
9+
- @pulse-editor/shared-utils@0.1.1-alpha.31
10+
311
## 0.1.1-alpha.30
412

513
### Patch Changes

npm-packages/react-api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pulse-editor/react-api",
3-
"version": "0.1.1-alpha.30",
3+
"version": "0.1.1-alpha.31",
44
"main": "dist/main.js",
55
"files": [
66
"dist"
@@ -38,7 +38,7 @@
3838
"typescript-eslint": "^8.30.1"
3939
},
4040
"peerDependencies": {
41-
"@pulse-editor/shared-utils": "0.1.1-alpha.30",
41+
"@pulse-editor/shared-utils": "0.1.1-alpha.31",
4242
"react": "^19.0.0",
4343
"react-dom": "^19.0.0"
4444
}

npm-packages/shared-utils/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @pulse-editor/shared-utils
22

3+
## 0.1.1-alpha.31
4+
5+
### Patch Changes
6+
7+
- Add recommended display dimension in canvas view
8+
39
## 0.1.1-alpha.30
410

511
### Patch Changes

npm-packages/shared-utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pulse-editor/shared-utils",
3-
"version": "0.1.1-alpha.30",
3+
"version": "0.1.1-alpha.31",
44
"main": "dist/main.js",
55
"files": [
66
"dist"

npm-packages/shared-utils/src/types/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ export type ExtensionConfig = {
131131
commandsInfoList?: CommandInfo[];
132132
// Visibility
133133
visibility: string;
134+
// Recommended dimensions for app view in canvas
135+
recommendedHeight?: number;
136+
recommendedWidth?: number;
134137
};
135138

136139
export type CommandInfo = {

web/.env.example

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Server backend URL.
2+
# Note: This environment variable is not intended to be
3+
# used if you are not developing Pulse Editor's managed services.
4+
NEXT_PUBLIC_BACKEND_URL="<server_backend_url>"
5+
6+
# This is used to distribute module federation modules.
7+
# You can replace this with your own federated apps CDN URL,
8+
# or use Pulse Editor's CDN for extension registry:
9+
# https://cdn.pulse-editor.com
10+
NEXT_PUBLIC_CDN_URL="https://cdn.pulse-editor.com"
11+
# This is the Azure Storage container name used to store
12+
# extension registry data. You can replace this with your own
13+
# storage container if you are self-hosting the extension registry.
14+
NEXT_PUBLIC_STORAGE_CONTAINER="extension"

web/components/extension/extension-loader.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,12 @@ export default function ExtensionLoader({
2626
? `/extension.html?remoteOrigin=${remoteOrigin}&moduleId=${moduleId}&moduleVersion=${moduleVersion}&viewId=${viewId}`
2727
: `/extension?remoteOrigin=${remoteOrigin}&moduleId=${moduleId}&moduleVersion=${moduleVersion}&viewId=${viewId}`;
2828

29-
return <iframe ref={iframeRef} className="h-full w-full" src={src} />;
29+
return (
30+
<iframe
31+
ref={iframeRef}
32+
className="h-full w-full"
33+
src={src}
34+
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
35+
/>
36+
);
3037
}

web/components/views/canvas/canvas-view.tsx

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
addEdge,
77
NodeChange,
88
EdgeChange,
9+
Node as ReactFlowNode,
10+
Edge as ReactFlowEdge,
911
} from "@xyflow/react";
1012
import "@xyflow/react/dist/style.css";
1113
import { useState, useCallback, useEffect } from "react";
@@ -19,24 +21,23 @@ import {
1921
} from "@/lib/types";
2022
import { useMenuActions } from "@/lib/hooks/use-menu-actions";
2123
import AppNode from "./nodes/app-node";
22-
import { v4 } from "uuid";
23-
24-
const initialNodes = [
25-
{
26-
id: "n1",
27-
position: { x: 200, y: 0 },
28-
data: {
29-
label: "Node 1",
30-
config: {
31-
viewId: v4(),
32-
app: "https://cdn.pulse-editor.com/extension/spin_wheel/0.0.1/",
33-
},
34-
},
35-
type: "appNode",
36-
},
37-
{ id: "n2", position: { x: 0, y: 100 }, data: { label: "Node 2" } },
38-
];
39-
const initialEdges = [{ id: "n1-n2", source: "n1", target: "n2" }];
24+
25+
// const initialNodes = [
26+
// {
27+
// id: "n1",
28+
// position: { x: 200, y: 0 },
29+
// data: {
30+
// label: "Node 1",
31+
// config: {
32+
// viewId: v4(),
33+
// app: "https://cdn.pulse-editor.com/extension/spin_wheel/0.0.1/",
34+
// },
35+
// },
36+
// type: "appNode",
37+
// },
38+
// { id: "n2", position: { x: 0, y: 100 }, data: { label: "Node 2" } },
39+
// ];
40+
// const initialEdges = [{ id: "n1-n2", source: "n1", target: "n2" }];
4041

4142
const appInfo: AppInfoModalContent = {
4243
name: "Pulse Editor",
@@ -64,22 +65,13 @@ export default function CanvasView({
6465
openViewInFullScreen: (config: AppViewConfig) => void;
6566
}) {
6667
const { openAppInfoModal } = useAppInfo();
67-
const [nodes, setNodes] = useState(initialNodes);
68-
const [edges, setEdges] = useState(initialEdges);
69-
70-
const onNodesChange = useCallback(
71-
(
72-
changes: NodeChange<{
73-
id: string;
74-
position: { x: number; y: number };
75-
data: { label: string };
76-
}>[],
77-
) => {
78-
console.log("Node changes:", changes);
79-
setNodes((nodesSnapshot) => applyNodeChanges(changes, nodesSnapshot));
80-
},
81-
[],
82-
);
68+
const [nodes, setNodes] = useState<ReactFlowNode[]>([]);
69+
const [edges, setEdges] = useState<ReactFlowEdge[]>([]);
70+
71+
const onNodesChange = useCallback((changes: NodeChange<ReactFlowNode>[]) => {
72+
console.log("Node changes:", changes);
73+
setNodes((nodesSnapshot) => applyNodeChanges(changes, nodesSnapshot));
74+
}, []);
8375
const onEdgesChange = useCallback(
8476
(changes: EdgeChange<{ id: string; source: string; target: string }>[]) =>
8577
setEdges((edgesSnapshot) => applyEdgeChanges(changes, edgesSnapshot)),
@@ -91,19 +83,6 @@ export default function CanvasView({
9183
[],
9284
);
9385

94-
async function exportWorkflow() {
95-
const workflow = { nodes, edges };
96-
const blob = new Blob([JSON.stringify(workflow, null, 2)], {
97-
type: "application/json",
98-
});
99-
const url = URL.createObjectURL(blob);
100-
const a = document.createElement("a");
101-
a.href = url;
102-
a.download = "workflow.json";
103-
a.click();
104-
URL.revokeObjectURL(url);
105-
}
106-
10786
const createAppNode = useCallback(
10887
(props: any) => {
10988
return <AppNode {...props} openViewInFullScreen={openViewInFullScreen} />;
@@ -112,6 +91,7 @@ export default function CanvasView({
11291
);
11392

11493
const { registerMenuAction, unregisterMenuAction } = useMenuActions();
94+
11595
// Register menu actions
11696
useEffect(() => {
11797
console.log("CanvasView rendered: registering menu actions");
@@ -172,8 +152,46 @@ export default function CanvasView({
172152
};
173153
}, []);
174154

155+
useEffect(() => {
156+
if (config) {
157+
const appConfigs: ReactFlowNode[] =
158+
config.nodes?.map((appConfig) => ({
159+
id: appConfig.viewId,
160+
position: {
161+
x: 0,
162+
y: 0,
163+
},
164+
data: {
165+
label: appConfig.app,
166+
config: appConfig,
167+
},
168+
type: "appNode",
169+
height: appConfig.recommendedHeight ?? 360,
170+
width: appConfig.recommendedWidth ?? 640,
171+
})) ?? [];
172+
173+
setNodes(appConfigs);
174+
}
175+
}, [config]);
176+
177+
async function exportWorkflow() {
178+
const workflow = { nodes, edges };
179+
const blob = new Blob([JSON.stringify(workflow, null, 2)], {
180+
type: "application/json",
181+
});
182+
const url = URL.createObjectURL(blob);
183+
const a = document.createElement("a");
184+
a.href = url;
185+
a.download = "workflow.json";
186+
a.click();
187+
URL.revokeObjectURL(url);
188+
}
189+
175190
return (
176-
<div className="bg-default text-default-foreground relative h-full w-full">
191+
<div
192+
className="bg-default text-default-foreground relative h-full w-full"
193+
id={`canvas-${config?.viewId}`}
194+
>
177195
<ReactFlow
178196
nodes={nodes}
179197
edges={edges}

0 commit comments

Comments
 (0)