Skip to content

Commit

Permalink
styling
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisjofrank committed Nov 10, 2024
1 parent b5cc371 commit 404e61c
Show file tree
Hide file tree
Showing 26 changed files with 1,355 additions and 1,275 deletions.
71 changes: 46 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,41 @@

This is a code sandbox example that uses:

* React - for the WebUI
* Vite - for a react dev server
* Deno - as the backend runtime
* Oak - as the middleware framework for the API
* Deno Deploy - to host the code sandbox
* Deno Subhosting - to host the code *executed using the sandbox*
- React - for the WebUI
- Vite - for a react dev server
- Deno - as the backend runtime
- Oak - as the middleware framework for the API
- Deno Deploy - to host the code sandbox
- Deno Subhosting - to host the code _executed using the sandbox_

This is a stack that'll work if you want to build something like *CodePen*, *Glitch*, *JSFiddle*, or Edge Functions (Cloudflare, Vercel etc).
This is a stack that'll work if you want to build something like _CodePen_,
_Glitch_, _JSFiddle_, or Edge Functions (Cloudflare, Vercel etc).

## What it does

The homepage of the app displays a snippet of a program using the `Deno.serve` API to create a Deno web app. When you click `Create Interactive Sandbox` the code you see will be pushed to the backend `API` which will create a `Deno Subhosting Instance` and startup the code.
The homepage of the app displays a snippet of a program using the `Deno.serve`
API to create a Deno web app. When you click `Create Interactive Sandbox` the
code you see will be pushed to the backend `API` which will create a
`Deno Subhosting Instance` and startup the code.

You'll be redirected to an editor page at this point, where you can edit and save the code in real-time, each save resulting in a new `Deno Subhosting Deployment` with a distinct URL that you can share with others.
You'll be redirected to an editor page at this point, where you can edit and
save the code in real-time, each save resulting in a new
`Deno Subhosting Deployment` with a distinct URL that you can share with others.

A real "code sandbox" experience would likely clean up the last n deployments after a certain time period, but this is a simplified example.
A real "code sandbox" experience would likely clean up the last n deployments
after a certain time period, but this is a simplified example.

### Pre-Requisites

You'll need:

* Deno installed
* A Deno Deploy account
* A Deno Deploy access token
* A Deno Subhosting Account with an Organization created
- Deno installed
- A Deno Deploy account
- A Deno Deploy access token
- A Deno Subhosting Account with an Organization created

Once you have your accounts, you'll need to create a `.env` file in the root of the project with the following contents:
Once you have your accounts, you'll need to create a `.env` file in the root of
the project with the following contents:

```text
DEPLOY_ORG_ID=283....
Expand All @@ -43,7 +51,8 @@ With your `Org Id` and `Access Token` in place so you can run the app.
deno task start
```

This will start the Vite server and the Oak server, and you can visit `http://localhost:3000` to see the app running.
This will start the Vite server and the Oak server, and you can visit
`http://localhost:3000` to see the app running.

### How to deploy

Expand All @@ -53,22 +62,34 @@ You can create all the required build artifacts by running
deno task build
```

This will use `Deno` to execute `Vite` to build the frontend components for the react app. The built react app will be created at `./client/dist` and the backend code will be in `./server`.
This will use `Deno` to execute `Vite` to build the frontend components for the
react app. The built react app will be created at `./client/dist` and the
backend code will be in `./server`.

If you're deploying with Deno Deploy, you'll want to configure the following settings:
If you're deploying with Deno Deploy, you'll want to configure the following
settings:

* Build - `deno task build`
* Entrypoint - `./server/main.ts`
- Build - `deno task build`
- Entrypoint - `./server/main.ts`

In production, the Deno app will serve both the API, and the static files for the react app.
You'll see the API served at `/api` and the static files served at `/`.
In production, the Deno app will serve both the API, and the static files for
the react app. You'll see the API served at `/api` and the static files served
at `/`.

### Development Notes

In development, the `Vite dev server` is configured to proxy across to the Deno server for the API calls so if you've used `Vite` before, everything will just work as expected.
In development, the `Vite dev server` is configured to proxy across to the Deno
server for the API calls so if you've used `Vite` before, everything will just
work as expected.

If you're looking to diagnose issues, you can run `deno task serve` to start a production-like build that doesn't require a running instance of `Vite` to serve the frontend.
If you're looking to diagnose issues, you can run `deno task serve` to start a
production-like build that doesn't require a running instance of `Vite` to serve
the frontend.

### React Notes

This example uses the latest version of `React` and is configured for HMR (Hot Module Replacement) so you can see changes in real-time as you develop. `React Router` is also included to handle the routing for the app, along with appropriate wildcard routing configuration so that refreshing the page will still load the app.
This example uses the latest version of `React` and is configured for HMR (Hot
Module Replacement) so you can see changes in real-time as you develop.
`React Router` is also included to handle the routing for the app, along with
appropriate wildcard routing configuration so that refreshing the page will
still load the app.
2 changes: 1 addition & 1 deletion client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
</html>
159 changes: 137 additions & 22 deletions client/src/App.css
Original file line number Diff line number Diff line change
@@ -1,48 +1,163 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: left;
}
--editor-width: 60%;
}

.editor {
display: flex;
flex-direction: column;
border-radius: 5px;

height: 60vh;
width: 100%;
min-width: 50vw;
padding-left: calc(var(--editor-width) + 2rem);
}

textarea {
width: 100%;
min-height: 15rem;
padding: 0.5em;
font-size: 1em;
font-family: 'Courier New', Courier, monospace;
font-family: "Courier New", Courier, monospace;
}

.console {
height: calc(100vh - 12rem);
margin-top: 2rem;
border-radius: 1rem;
overflow: hidden;
}

iframe {
.sandbox {
width: 100%;
height: 100%;
margin: 0;
height: calc(100vh - 14rem);
border: 0;
border-bottom-left-radius: .5rem;
border-bottom-right-radius: .5rem;
background-color: light-dark(var(--light-primary), var(--dark-primary));
padding: 1.6rem;
font-size: 14pt;
background-color: rgba(255,255,255,0.6);
color: #272822;
}

.url {
margin-top: 1rem;
padding: .5rem 1rem;
font-size: small;
background-color: light-dark(var(--light-tertiary), var(--dark-tertiary));
padding: 1rem 1.6rem;
font-size: 14pt;
background-color: #a6e22e;
a {
text-decoration: none;
color: light-dark(var(--light-text), var(--dark-text));
color: #272822;
&:hover {
text-decoration: underline;
color: light-dark(var(--light-primary), var(--dark-primary));
}
}
}

#editing, #highlighting {
position: absolute;
top: 0;
left: 0;
margin: 1rem;
padding: 1rem;
border: 0;
width: var(--editor-width);
height: calc(100vh - 2rem);
}

#editing, #highlighting, #highlighting * {
/* Also add text styles to highlighting tokens */
font-size: 14pt;
font-family: monospace;
line-height: 16pt;
}

#editing {
z-index: 1;
color: transparent;
background: transparent;
caret-color: white; /* Or choose your favorite color */
}

#highlighting {
z-index: 0;
}

/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism-okaidia&languages=clike+javascript+typescript */
code[class*="language-"], pre[class*="language-"] {
color: #f8f8f2;
background: 0 0;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"] {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"], pre[class*="language-"] {
background: #272822;
}
:not(pre) > code[class*="language-"] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
.token.cdata, .token.comment, .token.doctype, .token.prolog {
color: #8292a2;
}
.token.punctuation {
color: #f8f8f2;
}
.token.namespace {
opacity: 0.7;
}
.token.constant, .token.deleted, .token.property, .token.symbol, .token.tag {
color: #f92672;
}
.token.boolean, .token.number {
color: #ae81ff;
}
.token.attr-name,
.token.builtin,
.token.char,
.token.inserted,
.token.selector,
.token.string {
color: #a6e22e;
}
.language-css .token.string,
.style .token.string,
.token.entity,
.token.operator,
.token.url,
.token.variable {
color: #f8f8f2;
}
.token.atrule, .token.attr-value, .token.class-name, .token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.important, .token.regex {
color: #fd971f;
}
.token.bold, .token.important {
font-weight: 700;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
11 changes: 5 additions & 6 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useState, useEffect } from 'react'
import './App.css'
import React from "react";
import "./App.css";

function App(props: { children: React.ReactNode }) {
return (
<div className="App">
{ props.children }
<div className="console"></div>
{props.children}
</div>
)
);
}

export default App
export default App;
61 changes: 32 additions & 29 deletions client/src/api/ApiClient.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import doUntil from "./doUntil.ts";

export default class ApiClient {
constructor(private readonly baseUrl: string = "") { }

public async getSampleCode(): Promise<SampleCode> {
const response = await fetch(`${this.baseUrl}/api/sample`);
return (await response.json()).code;
constructor(private readonly baseUrl: string = "") {}

public async getSampleCode(): Promise<string> {
const response = await fetch(`${this.baseUrl}/api/sample`);
return (await response.json()).code;
}

public async deployProject(
code: string,
id?: string,
): Promise<ProjectDeployment> {
const idSuffix = id ? `/${id}` : "";

const response = await fetch(`${this.baseUrl}/api/project${idSuffix}`, {
method: "POST",
body: JSON.stringify({ code }),
});

if (!response.ok) {
throw new Error("Failed to deploy project");
}

public async deployProject(code: string, id?: string): Promise<ProjectDeployment> {
const idSuffix = id ? `/${id}` : '';

const response = await fetch(`${this.baseUrl}/api/project${idSuffix}`, {
method: 'POST',
body: JSON.stringify({ code })
});
return await response.json();
}

if (!response.ok) {
throw new Error('Failed to deploy project');
}

return await response.json();
}
public async getDeployment(id: string): Promise<ProjectDeployment> {
const response = await fetch(`${this.baseUrl}/api/deployment/${id}`);
return await response.json();
}

public async getDeployment(id: string): Promise<ProjectDeployment> {
const response = await fetch(`${this.baseUrl}/api/deployment/${id}`);
return await response.json();
}

public async waitForDeployment(deploymentId: string) {
return await doUntil(async () => {
return await this.getDeployment(deploymentId);
}, (response) => response.deployment.status !== 'pending');
}
}
public async waitForDeployment(deploymentId: string) {
return await doUntil(async () => {
return await this.getDeployment(deploymentId);
}, (response) => response.deployment.status !== "pending");
}
}
Loading

0 comments on commit 404e61c

Please sign in to comment.