Skip to content

Commit

Permalink
feat: terminal
Browse files Browse the repository at this point in the history
  • Loading branch information
o-az committed Sep 10, 2024
1 parent 628c350 commit 74229ef
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 149 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ db.sqlite
db.sqlite3*

move/*/build
.tsup
3 changes: 1 addition & 2 deletions app/src/lib/components/connect/connection.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ const onCopyClick = () => [toggleCopy(), setTimeout(() => toggleCopy(), 1_500)]
let sanitizeWalletInformation =
chainWalletsInformation.filter(
(predicate, index, array) =>
array.findIndex(t => t.name.toLowerCase().startsWith(predicate.name.toLowerCase())) ===
index,
array.findIndex(t => t.name.toLowerCase().startsWith(predicate.name.toLowerCase())) === index
) ?? chainWalletsInformation
$: walletListToRender =
Expand Down
2 changes: 1 addition & 1 deletion docs/docs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
packages = {
docs = mkCi false (unstablePkgs.buildNpmPackage {
npmDepsHash = "sha256-3Ip3T+Kfc5X2gZ/KzgLWTtfnQf7lCK+CsoTKwAB9ynw=";
npmDepsHash = "sha256-nnugNuwQybGENX7QAna0oZgtenUGsHaxKkIMxl/HrF8=";
src = ./.;
srcs = [ ./. ./../evm/. ./../networks/genesis/. ./../versions/. ];
sourceRoot = "docs";
Expand Down
8 changes: 4 additions & 4 deletions docs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@astrojs/vue": "^4.5.0",
"@expressive-code/plugin-collapsible-sections": "^0.36.1",
"@expressive-code/plugin-line-numbers": "^0.36.1",
"@unionlabs/client": "^0.0.17",
"@unionlabs/client": "^0.0.18",
"@webcontainer/api": "^1.3.0-internal.7",
"@xterm/addon-clipboard": "^0.1.0",
"@xterm/addon-fit": "^0.10.0",
Expand Down
134 changes: 36 additions & 98 deletions docs/src/components/Terminal.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
---
import "@xterm/xterm/css/xterm.css"
interface Props {}
interface Props {
webcontainerFiles?: string
}
const { webcontainerFiles } = Astro.props
---

<pre
style="display: none;"
id="webcontainer-files"
data-webcontainer-files="">
{webcontainerFiles}
</pre>

<textarea
required={true}
readonly={false}
Expand All @@ -24,99 +35,30 @@ interface Props {}
<iframe class="hidden" allow="cross-origin-isolated"></iframe>

<script>
import dedent from 'ts-dedent'
// import { highlightCode } from '#/lib/highlight-code.ts'
import {
WebContainer,
type FileNode,
type FileSystemTree,
type WebContainerProcess,
} from '@webcontainer/api'
import { initiateTerminal, type Terminal } from '#/lib/xterm/terminal'
import { WebContainer, type FileSystemTree, type WebContainerProcess } from '@webcontainer/api'

const files = {
'mod.ts': {
file: {
contents: dedent(/* ts */ `
import { http } from 'viem'
import { arbitrumSepolia } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
import { createUnionClient } from '@unionlabs/client'

const account = privateKeyToAccount(\`0x$\{process.env.PRIVATE_KEY}\`)

const client = createUnionClient({
account,
chainId: '421614',
transport: http('https://sepolia-rollup.arbitrum.io/rpc'),
})

const gasResponse = await client.simulateTransaction({
amount: 1n,
autoApprove: true,
destinationChainId: 'stride-internal-1',
denomAddress: '0xb1d4538b4571d411f07960ef2838ce337fe1e80e', // LINK
receiver: 'stride14qemq0vw6y3gc3u3e0aty2e764u4gs5l66hpe3'
})

if (gasResponse.isErr()) {
console.error(gasResponse.error)
process.exit(1)
}

console.info(\`gas: \${gasResponse.value\}\`)
`).trim(),
},
},
'.npmrc': {
file: {
contents: dedent(`
engine-strict=true
npm_config_yes=true
auto-install-peers=true
enable-pre-post-scripts=true
strict-peer-dependencies=false
node-options="--no-warnings"
`).trim(),
},
},
'package.json': {
file: {
contents: dedent(/* json */ `
{
"name": "demo",
"type": "module",
"dependencies": {
"tsx": "latest",
"viem": "latest",
"@unionlabs/client": "latest"
}
}
`).trim(),
},
},
} satisfies FileSystemTree

type FileName = keyof typeof files

const writeToWebContainerFile = async ({
path,
content,
}: {
path: FileName
content: string
}) => {
const writeToWebContainerFile = async ({ path, content }: { path: string; content: string }) => {
await webcontainerInstance.fs.writeFile(path, content)
}

async function installDependencies() {
// Install dependencies
const promises = [
webcontainerInstance.spawn('npm', ['install', '--global', 'bun']).catch(() => {}),
webcontainerInstance.spawn('npm', ['install']).catch(() => {}),
webcontainerInstance.spawn('npm', ['install']).catch(error => {
console.error('npm install failed', error)
}),
] as Array<Promise<WebContainerProcess>>
const installProcesses = await Promise.all(promises)
return installProcesses.map(installProcess => {
installProcess.output.pipeTo(
new WritableStream({
write: data => {
console.log(data)
},
write: (chunk, _controller) => console.info(chunk),
}),
)
// Wait for install command to exit
Expand All @@ -131,16 +73,12 @@ interface Props {}
})
shellProcess.output.pipeTo(
new WritableStream({
write: data => {
terminal.write(data)
},
write: (chunk, _controller) => terminal.write(chunk),
}),
)

const input = shellProcess.input.getWriter()
terminal.onData(data => {
input.write(data)
})
terminal.onData(data => input.write(data))

return shellProcess
}
Expand All @@ -150,7 +88,12 @@ interface Props {}
window.addEventListener('load', async () => {
if (!textareaElement || !terminalElement || !iframeElement) return

textareaElement.value = files['mod.ts'].file.contents
let files: FileSystemTree = {}
if (webcontainerFilesElement && webcontainerFilesElement.textContent) {
files = JSON.parse(webcontainerFilesElement.textContent) as FileSystemTree
}

textareaElement.value = (files['mod.ts'] as FileNode).file.contents.toString()
textareaElement.addEventListener('input', event => {
if (!event.target?.value) return
writeToWebContainerFile({ path: 'mod.ts', content: event.target.value })
Expand All @@ -168,42 +111,37 @@ interface Props {}

await webcontainerInstance.mount(files)

// webcontainerInstance.on('server-ready', (port, url) => {
// iframeElement.src = url
// })

const shellProcess = await startShell(terminal)
await installDependencies()
window.addEventListener('resize', () => {
fitAddon.fit()
shellProcess.resize({ cols: terminal.cols, rows: terminal.rows })
})

// const code = textareaElement.value
// const highlightedCode = await highlightCode({ code })
// textareaElement.insertAdjacentHTML("afterend", highlightedCode)

const xtermViewport = document.querySelector('div.xterm-viewport')
if (!xtermViewport) return
})

const iframeElement = document.querySelector('iframe')
const textareaElement = document.querySelector('textarea')
const terminalElement = document.querySelector('article#terminal')
const webcontainerFilesElement = document.querySelector('pre#webcontainer-files')
</script>

<style is:inline>
.sl-markdown-content {
margin-top: 0.3rem;
}

.xterm {
--at-apply: h-full;
--at-apply: p-3;
}
.xterm .xterm-viewport {
--at-apply: transition-theme;
}
canvas {
canvas,
.xterm-decoration-container {
margin-top: 0 !important;
}
.xterm,
Expand Down Expand Up @@ -233,7 +171,7 @@ interface Props {}
textarea {
width: 100%;
resize: none;
height: 35rem;
height: 48rem;
color: white;
padding: 0.5rem 1rem;
margin-bottom: 10px;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { dedent } from "ts-dedent"
import type { FileSystemTree } from "@webcontainer/api"

export const demoFiles = {
"mod.ts": {
file: {
contents: dedent(/* ts */ `
import { http } from "viem"
import { arbitrumSepolia } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"
import { createUnionClient, type TransferAssetsParameters } from "@unionlabs/client"
const account = privateKeyToAccount(\`0x$\{process.env["PRIVATE_KEY"]\}\`)
console.info(\`Account address: $\{JSON.stringify(account, undefined, 2)\}\`)
const client = createUnionClient({
account,
chainId: \`$\{arbitrumSepolia.id\}\`,
transport: http("https://sepolia-rollup.arbitrum.io/rpc")
})
const payload = {
amount: 1n,
autoApprove: false,
destinationChainId: "stride-internal-1",
receiver: "stride14qemq0vw6y3gc3u3e0aty2e764u4gs5l66hpe3",
denomAddress: "0xb1d4538b4571d411f07960ef2838ce337fe1e80e" // LINK
} satisfies TransferAssetsParameters<\`$\{typeof arbitrumSepolia.id\}\`>
const gasResponse = await client.simulateTransaction(payload)
if (gasResponse.isErr()) {
console.error(\`Gas estimation error: $\{gasResponse.error\}\`)
process.exit(1)
}
console.info(\`Gas estimation: $\{gasResponse.value\}\`)
const approval = await client.approveTransaction(payload)
if (approval.isErr()) {
console.error(\`Approval error: $\{approval.error\}\`)
process.exit(1)
}
console.info(\`Approval hash: $\{approval.value\}\`)
const transfer = await client.transferAsset(payload)
if (transfer.isErr()) {
console.error(\`Transfer error: $\{transfer.error\}\`)
process.exit(1)
}
console.info(\`Transfer hash: $\{transfer.value\}\`)
`).trim()
}
},
".npmrc": {
file: {
contents: dedent(`
engine-strict=true
npm_config_yes=true
auto-install-peers=true
enable-pre-post-scripts=true
strict-peer-dependencies=false
node-options="--no-warnings"
`).trim()
}
},
"package.json": {
file: {
contents: dedent(/* json */ `
{
"name": "demo",
"type": "module",
"dependencies": {
"tsx": "latest",
"viem": "latest",
"@unionlabs/client": "latest"
}
}
`).trim()
}
}
} satisfies FileSystemTree
Loading

0 comments on commit 74229ef

Please sign in to comment.