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

@ -62,7 +62,7 @@ const TreeViewNode = ({ listDir, actionOpenFile, node, depth, path }) => {
class="entry folder" class="entry folder"
style={{ '--depth': depth }} style={{ '--depth': depth }}
title={path} title={path}
onClick={() => setCollapsed(c => !c)} onMouseDown={() => setCollapsed(c => !c)}
> >
<div class="material-symbols-outlined">folder</div> <div class="material-symbols-outlined">folder</div>
<div class="name">{node.name}/</div> <div class="name">{node.name}/</div>
@ -88,7 +88,7 @@ const TreeViewNode = ({ listDir, actionOpenFile, node, depth, path }) => {
class="entry file" class="entry file"
style={{ '--depth': depth }} style={{ '--depth': depth }}
title={path} title={path}
onClick={() => { onMouseDown={() => {
console.log('open', path) console.log('open', path)
actionOpenFile?.(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 { .run-style {
--fg: #fff; --fg: #fff;
@ -400,6 +412,15 @@ input[type='submit'],
.logo { .logo {
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
a {
color: #333;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
} }
> .spacer { > .spacer {
@ -545,7 +566,7 @@ input[type='submit'],
border-bottom: 1px solid #d9d9d9; border-bottom: 1px solid #d9d9d9;
&:hover { &: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 */ /* Layout */
body { body {

@ -27,3 +27,18 @@ export function stripPrefix(s, prefix) {
export function clamp(min, value, max) { export function clamp(min, value, max) {
return Math.max(min, Math.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' import Base from '../layouts/Base.astro'
--- ---
<Base> <Base title="PHC / Run">
<h1>docker+xterm.js</h1> <div class="homepage">
<div id="terminal"></div> <section class="hero-section">
<h1>PHC / Run</h1>
<script> <h2>A platform to easily create, run and share code projects and prototypes</h2>
import '../client/term.js' <img src="/public/screenshot-1.png" alt="phc run screenshot" />
</script> </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> </Base>