finishing touches to editor and started homepage

main
Antonio De Lucreziis 2 years ago
parent 97db71878f
commit ec281d2eba

@ -8,6 +8,10 @@ export default defineConfig({
integrations: [preact()],
output: 'server',
devToolbar: {
enabled: false,
},
adapter: node({
mode: 'standalone',
}),

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

@ -3,10 +3,10 @@ import clsx from 'clsx'
import { TreeView } from '@/client/components/TreeView.jsx'
import { Editor } from '@/client/components/Editor.jsx'
import { useEffect } from 'preact/hooks'
import { useEffect, useRef } from 'preact/hooks'
import { Terminal } from './Terminal.jsx'
import { clamp, fetchJson, stripPrefix } from '@/client/utils.js'
import { clamp, fetchJson, stripPrefix, useClickOutside } from '@/client/utils.js'
export const Ide = ({}) => {
const ws = useSignal(null)
@ -42,10 +42,20 @@ export const Ide = ({}) => {
return tabs.value[activeTab.value].content
})
const runCompoundButtonOpen = useSignal(false)
const runCompoundPopupRef = useRef(null)
useClickOutside(runCompoundPopupRef, () => {
runCompoundButtonOpen.value = false
})
return (
<div class="ide">
<div class="sidebar">
<div class="logo">PHC / Run</div>
<div class="logo">
<a href="/">PHC / Run</a>
</div>
<div class="flex-row">
<button class="icon">
@ -82,6 +92,12 @@ export const Ide = ({}) => {
console.log('content', content)
batch(() => {
const existingIndex = tabs.value.findIndex(tab => tab.id === path)
if (existingIndex !== -1) {
activeTab.value = existingIndex
return
}
tabs.value = [
...tabs.value,
{
@ -120,8 +136,8 @@ export const Ide = ({}) => {
alt="profile picture"
/>
</div>
<div class="username">{'aziis98'}</div>
<div class="email">{'aziis98'}@exmaple.com</div>
<div class="username">{'username'}</div>
<div class="email">{'username'}@exmaple.com</div>
</div>
</div>
<div class="header">
@ -156,20 +172,28 @@ export const Ide = ({}) => {
>
Run
</span>
<span class="part">
<span
class="part"
onMouseDown={e => {
e.stopPropagation()
runCompoundButtonOpen.value = !runCompoundButtonOpen.value
}}
>
<span class="material-symbols-outlined">keyboard_arrow_down</span>
</span>
</button>
</div>
<div class="popup anchor-mode-1">
<div class="menu">
<div class="item">Run</div>
<div class="item">Debug</div>
<div class="item">Test</div>
<div class="item">Profile</div>
<div class="item">Bench</div>
{runCompoundButtonOpen.value && (
<div class="popup anchor-mode-1" ref={runCompoundPopupRef}>
<div class="menu">
<div class="item">Run</div>
<div class="item">Debug</div>
<div class="item">Test</div>
<div class="item">Profile</div>
<div class="item">Bench</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
@ -184,7 +208,7 @@ export const Ide = ({}) => {
<div class="buttons">
<button
class="flat icon"
onClick={e => {
onMouseDown={e => {
e.stopPropagation()
batch(() => {

@ -62,7 +62,7 @@ const TreeViewNode = ({ listDir, actionOpenFile, node, depth, path }) => {
class="entry folder"
style={{ '--depth': depth }}
title={path}
onClick={() => setCollapsed(c => !c)}
onMouseDown={() => setCollapsed(c => !c)}
>
<div class="material-symbols-outlined">folder</div>
<div class="name">{node.name}/</div>
@ -88,7 +88,7 @@ const TreeViewNode = ({ listDir, actionOpenFile, node, depth, path }) => {
class="entry file"
style={{ '--depth': depth }}
title={path}
onClick={() => {
onMouseDown={() => {
console.log('open', path)
actionOpenFile?.(path)
}}

@ -196,6 +196,18 @@ input[type='submit'],
}
}
a,
a:visited {
color: #8000ff;
text-decoration: none;
font-weight: 600;
&:hover {
text-decoration: underline solid 2px;
}
}
.run-style {
--fg: #fff;
@ -400,6 +412,15 @@ input[type='submit'],
.logo {
font-size: 24px;
font-weight: 700;
a {
color: #333;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
> .spacer {
@ -545,7 +566,7 @@ input[type='submit'],
border-bottom: 1px solid #d9d9d9;
&:hover {
background: #f8f8f8;
background: #f4f4f4;
}
}
}
@ -625,6 +646,126 @@ input[type='submit'],
}
}
.homepage {
display: grid;
grid-auto-flow: row;
gap: 1rem;
padding: 1rem;
place-content: center;
background: linear-gradient(180deg, #f5efff 0%, #fafcfe 100%);
> section {
display: grid;
grid-auto-flow: row;
grid-auto-rows: auto;
align-content: start;
padding: 3rem 0;
gap: 3rem;
min-height: 100vh;
max-width: 65rem;
h2 {
font-size: 2rem;
font-weight: 700;
text-align: center;
}
&.hero-section {
place-content: center;
place-items: center;
min-height: calc(100vh + 20rem);
padding-top: 20vh;
h1 {
font-size: 3rem;
font-weight: 700;
text-align: center;
}
h2 {
font-size: 1.5rem;
font-weight: 300;
text-align: center;
}
img {
margin-top: 3rem;
width: 100%;
max-width: 65rem;
border: 1px solid #d9d9d9;
border-radius: 0.5rem;
box-shadow: 0 0 0.25rem 0 #680bff08, 0 0.5rem 1rem 0 #680bff10;
}
}
&:not(:first-child) {
border-top: 2px solid #d9d9d9;
}
> .features {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
gap: 1rem;
width: 100%;
> .feature-item {
display: grid;
grid-template-rows: auto 1fr;
gap: 0.5rem;
/* width: 100%; */
padding: 1rem;
border: 1px solid #d9d9d9;
border-radius: 0.5rem;
box-shadow: 0 0.125rem 0.25rem 0 #00000010;
h3 {
font-size: 1.25rem;
font-weight: 500;
}
p {
font-size: 1rem;
font-weight: 300;
}
}
}
}
}
footer {
display: grid;
gap: 1rem;
padding: 1rem;
place-content: center;
font-size: 14px;
font-weight: 400;
border-top: 1px solid #d9d9d9;
}
/* Layout */
body {

@ -27,3 +27,18 @@ export function stripPrefix(s, prefix) {
export function clamp(min, value, max) {
return Math.max(min, Math.min(value, max))
}
export function useClickOutside(ref, handler) {
useEffect(() => {
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
handler()
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [ref, handler])
}

@ -4,11 +4,49 @@ import '../client/style.css'
import Base from '../layouts/Base.astro'
---
<Base>
<h1>docker+xterm.js</h1>
<div id="terminal"></div>
<script>
import '../client/term.js'
</script>
<Base title="PHC / Run">
<div class="homepage">
<section class="hero-section">
<h1>PHC / Run</h1>
<h2>A platform to easily create, run and share code projects and prototypes</h2>
<img src="/public/screenshot-1.png" alt="phc run screenshot" />
</section>
<section>
<h2>Features</h2>
<div class="features">
<div class="feature-item">
<h3>Multiple Language Support</h3>
<p>
PHC / Run supports multiple languages, including Python, JavaScript, Ruby, Java, C, C++, and
many more.
</p>
</div>
<div class="feature-item">
<h3>Share Code</h3>
<p>Share a link to your code with anyone, and they can run it in their browser.</p>
</div>
<div class="feature-item">
<h3>Collaborate</h3>
<p>Invite others to collaborate on your code projects.</p>
</div>
<div class="feature-item">
<h3>Run Code</h3>
<p>Run your code in the browser and see the output instantly without having to install anything.</p>
</div>
<div class="feature-item">
<h3>Docker Support</h3>
<p>All code runs in docker containers, so you can run any code without worrying about security.</p>
</div>
<div class="feature-item">
<h3>Open Source</h3>
<p>PHC / Run is licensed under the AGPL license, so you can self-host it if you want.</p>
</div>
</div>
</section>
</div>
<footer>
<p>
<a href="/login">Login</a> to get started.
</p>
</footer>
</Base>