diff --git a/app/deployments/page.tsx b/app/deployments/page.tsx
index a62aaa9..9c0aa8e 100644
--- a/app/deployments/page.tsx
+++ b/app/deployments/page.tsx
@@ -26,7 +26,7 @@ export default function Page() {
- Date and time
+ Date and time
Contract Address
diff --git a/app/workspace/[id]/page.tsx b/app/workspace/[id]/page.tsx
index cbbbced..55877fa 100644
--- a/app/workspace/[id]/page.tsx
+++ b/app/workspace/[id]/page.tsx
@@ -1,6 +1,8 @@
"use client";
+import { BuildDeployPanel } from "@/components/build-deploy-panel";
import TopBottom from "@/components/top-bottom";
+import { Button } from "@/components/ui/button";
import {
ResizablePanelGroup,
ResizablePanel,
@@ -14,20 +16,16 @@ import { TerminalContextProvider } from "react-terminal";
export default function Page() {
return (
-
- } bottom={Chat component
} />
-
-
-
- }
- bottom={
-
-
-
- }
- />
-
+
+
+
+
+
+
+
+ } bottom={} />
+
+
);
}
diff --git a/components/build-deploy-panel.tsx b/components/build-deploy-panel.tsx
new file mode 100644
index 0000000..5e2752c
--- /dev/null
+++ b/components/build-deploy-panel.tsx
@@ -0,0 +1,57 @@
+"use client";
+
+import { useState } from "react";
+import { Button } from "./ui/button";
+import { useCliCommands } from "./workspace/use-cli-commands";
+import useSWR from "swr";
+import { useParams } from "next/navigation";
+import { db } from "@/data/db";
+
+export function BuildDeployPanel() {
+ const commands = useCliCommands();
+ const [isBuilding, setIsBuilding] = useState(false);
+ const [isDeploying, setIsDeploying] = useState(false);
+
+ const { id } = useParams<{ id: string }>();
+ const { data: isDeployable } = useSWR(
+ id ? `deployable-${id}` : undefined,
+ async () => {
+ const ws = await db.workspaces.get(id);
+ return typeof ws?.dll === "string";
+ }
+ );
+
+ return (
+
+
+
+
+ );
+}
diff --git a/components/workspace/cli.tsx b/components/workspace/cli.tsx
index ea9b93f..a128812 100644
--- a/components/workspace/cli.tsx
+++ b/components/workspace/cli.tsx
@@ -1,116 +1,11 @@
"use client";
-import { build } from "@/data/build";
-import { db } from "@/data/db";
-import { useWallet } from "@/data/wallet";
import { useTheme } from "next-themes";
-import { useParams } from "next/navigation";
import { ReactTerminal } from "react-terminal";
-import { z } from "zod";
+import { useCliCommands } from "./use-cli-commands";
export default function Cli() {
- const { id } = useParams();
- const wallet = useWallet();
- const commands = {
- help: () => (
-
-
These are the available commands:
-
- - clear - clears the terminal
- - build - builds the current workspace
- - deploy - deploys the built smart contract
- -
- check txID - checks the result of transaction
-
-
-
- ),
- build: async () => {
- if (typeof id !== "string") throw new Error("id is not string");
- const start = `/workspace/${id}/`;
- const files = await db.files
- .filter((file) => file.path.startsWith(start))
- .toArray();
- const params = files
- .map((file) => ({
- path: decodeURIComponent(file.path.replace(start, "")),
- contents: file.contents,
- }))
- .filter((i) => i.path.startsWith("src"));
-
- const str = await build(params);
- await db.workspaces.update(id, { dll: str });
-
- return "Build successful.";
- },
- deploy: async () => {
- if (typeof id !== "string") return "Workspace id not found.";
- const { dll } = (await db.workspaces.get(id)) || {};
- if (!dll) return "Contract not built. Please build first.";
- if (!wallet) return "Wallet not ready.";
- try {
- await wallet.faucet();
- } catch (err) {}
- const { TransactionId } = await wallet.deploy(dll);
- try {
- const result = await wallet.getTxResult(TransactionId);
- return `TransactionId: ${TransactionId}, Status: ${result.Status}`;
- } catch (err) {
- return JSON.stringify(err, undefined, 2);
- }
- },
- check: async (id: string) => {
- if (!id) return `Please enter the Transaction ID.`;
- if (!wallet) return "Wallet not ready.";
- try {
- const result = await wallet.getTxResult(id);
- const logs = await wallet.getLogs(id);
- const { data } = z.object({ proposalId: z.string() }).safeParse(logs);
- if (!data?.proposalId) return "Missing proposalId.";
- const proposalInfo = await wallet.getProposalInfo(data?.proposalId);
- const releasedTxId = proposalInfo?.data.proposal.releasedTxId;
- const releasedTxLogs = releasedTxId
- ? await wallet.getLogs(releasedTxId)
- : undefined;
- const { data: contractAddressData } = z
- .object({ address: z.string() })
- .safeParse(releasedTxLogs);
-
- return (
- <>
-
-
- TransactionId: |
- {id} |
-
-
- Status: |
- {result.Status} |
-
-
- ProposalId: |
- {data?.proposalId} |
-
-
- Proposal Status: |
- {proposalInfo?.data.proposal.status} |
-
-
- Contract Address: |
-
- {proposalInfo?.data.proposal.status === "released"
- ? contractAddressData?.address
- : "-"}
- |
-
-
- >
- );
- } catch (err) {
- return JSON.stringify(err, undefined, 2);
- }
- },
- };
+ const commands = useCliCommands();
const { theme, systemTheme } = useTheme();
diff --git a/components/workspace/use-cli-commands.tsx b/components/workspace/use-cli-commands.tsx
new file mode 100644
index 0000000..8f303ea
--- /dev/null
+++ b/components/workspace/use-cli-commands.tsx
@@ -0,0 +1,187 @@
+import { build } from "@/data/build";
+import { db } from "@/data/db";
+import { useWallet } from "@/data/wallet";
+import { Loader2 } from "lucide-react";
+import Link from "next/link";
+import { useParams } from "next/navigation";
+import { useContext } from "react";
+import { TerminalContext } from "react-terminal";
+import { z } from "zod";
+
+export function useCliCommands() {
+ const terminalContext = useContext(TerminalContext);
+
+ const { id } = useParams<{ id: string }>();
+ const wallet = useWallet();
+ const commands = {
+ help: () => {
+ terminalContext.setBufferedContent(
+
+
These are the available commands:
+
+ - clear - clears the terminal
+ - build - builds the current workspace
+ - deploy - deploys the built smart contract
+ -
+ check txID - checks the result of transaction
+
+
+
+ );
+ },
+ build: async () => {
+ if (typeof id !== "string") throw new Error("id is not string");
+ await db.workspaces.update(id, { dll: undefined });
+ const start = `/workspace/${id}/`;
+ terminalContext.setBufferedContent(
+ <>
+ Loading files...
+ >
+ );
+ const files = (
+ await db.files.filter((file) => file.path.startsWith(start)).toArray()
+ )
+ .map((file) => ({
+ path: decodeURIComponent(file.path.replace(start, "")),
+ contents: file.contents,
+ }))
+ .filter((i) => i.path.startsWith("src"));
+
+ terminalContext.setBufferedContent(
+ <>
+ Loaded files: {files.map((i) => i.path).join(", ")}
+
+ Building...
+
+ >
+ );
+
+ try {
+ const str = await build(files);
+ if (typeof str === "string") {
+ await db.workspaces.update(id, { dll: str });
+ terminalContext.setBufferedContent(
+ <>
+ Build successful.
+ >
+ );
+ return;
+ } else {
+ terminalContext.setBufferedContent(
+ <>
+ {terminalContext.bufferedContent}
+ Build failed.
+ >
+ );
+ return;
+ }
+ } catch (err) {
+ if (err instanceof Error)
+ terminalContext.setBufferedContent(<>{err.message}>);
+ return;
+ }
+ },
+ deploy: async () => {
+ if (typeof id !== "string") {
+ terminalContext.setBufferedContent(
+ <>
+ Workspace {id} not found.
+ >
+ );
+ return;
+ }
+ const { dll } = (await db.workspaces.get(id)) || {};
+ if (!dll) {
+ terminalContext.setBufferedContent(
+ <>
+ Contract not built.
+ >
+ );
+ return;
+ }
+ if (!wallet) {
+ terminalContext.setBufferedContent(
+ <>
+ Wallet not ready.
+ >
+ );
+ return;
+ }
+ const { TransactionId } = await wallet.deploy(dll);
+ try {
+ const result = await wallet.getTxResult(TransactionId);
+ terminalContext.setBufferedContent(
+ <>
+ TransactionId: {TransactionId}
+ Status: {result.Status}
+
+ See all deployments
+
+ >
+ );
+ return;
+ } catch (err) {
+ terminalContext.setBufferedContent(
+ <>
+ {JSON.stringify(err, undefined, 2)}
+
+ >
+ );
+ return;
+ }
+ },
+ check: async (id: string) => {
+ if (!id) return `Please enter the Transaction ID.`;
+ if (!wallet) return "Wallet not ready.";
+ try {
+ const result = await wallet.getTxResult(id);
+ const logs = await wallet.getLogs(id);
+ const { data } = z.object({ proposalId: z.string() }).safeParse(logs);
+ if (!data?.proposalId) return "Missing proposalId.";
+ const proposalInfo = await wallet.getProposalInfo(data?.proposalId);
+ const releasedTxId = proposalInfo?.data.proposal.releasedTxId;
+ const releasedTxLogs = releasedTxId
+ ? await wallet.getLogs(releasedTxId)
+ : undefined;
+ const { data: contractAddressData } = z
+ .object({ address: z.string() })
+ .safeParse(releasedTxLogs);
+
+ return (
+ <>
+
+
+ TransactionId: |
+ {id} |
+
+
+ Status: |
+ {result.Status} |
+
+
+ ProposalId: |
+ {data?.proposalId} |
+
+
+ Proposal Status: |
+ {proposalInfo?.data.proposal.status} |
+
+
+ Contract Address: |
+
+ {proposalInfo?.data.proposal.status === "released"
+ ? contractAddressData?.address
+ : "-"}
+ |
+
+
+ >
+ );
+ } catch (err) {
+ return JSON.stringify(err, undefined, 2);
+ }
+ },
+ };
+
+ return commands;
+}
diff --git a/data/build.ts b/data/build.ts
index 05eab7d..d724e47 100644
--- a/data/build.ts
+++ b/data/build.ts
@@ -32,6 +32,7 @@ export async function build(files: FileContent[]) {
`${getBuildServerBaseUrl()}/playground/build`,
requestInit
);
+
if (!response.ok) {
const { message } = await response.json();
throw new Error(message);