some tweaks

main
Antonio De Lucreziis 2 years ago
parent 0896799b34
commit 58b6b6ed18

@ -0,0 +1,9 @@
{
"printWidth": 120,
"singleQuote": true,
"quoteProps": "consistent",
"tabWidth": 4,
"semi": false,
"arrowParens": "avoid",
"proseWrap": "always"
}

@ -1 +1 @@
# ReplIt Clone # ReplIt Clone

@ -1,23 +1,12 @@
import { useSignal, signal, useComputed, batch, effect } from '@preact/signals' import { useSignal, signal, useComputed, batch, effect, Signal } from '@preact/signals'
import clsx from 'clsx'
import { TreeView, EXAMPLE_TREE } 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 clsx from 'clsx' import { useEffect } from 'preact/hooks'
import { useEffect, useState } from 'preact/hooks'
import { fetchJson } from '../utils.js'
import { Terminal } from './Terminal.jsx' import { Terminal } from './Terminal.jsx'
function ensurePrefix(s, prefix) { import { clamp, fetchJson, stripPrefix } from '@/client/utils.js'
return s.slice(0, prefix.length) === prefix ? s : prefix + s
}
function stripPrefix(s, prefix) {
return s.slice(0, prefix.length) === prefix ? s.slice(prefix.length) : s
}
function clamp(min, value, max) {
return Math.max(min, Math.min(value, max))
}
export const Ide = ({}) => { export const Ide = ({}) => {
const ws = useSignal(null) const ws = useSignal(null)
@ -38,16 +27,8 @@ export const Ide = ({}) => {
const activeTab = useSignal(null) const activeTab = useSignal(null)
const tabs = useSignal([ /** @type {Signal<{ id: string, content: Signal<string> }[]>} */
// { const tabs = useSignal([])
// id: '/project/main.c',
// content: signal('...main.c...'),
// },
// {
// id: '/project/data.csv',
// content: signal('...data.csv...'),
// },
])
const activePath = useComputed(() => { const activePath = useComputed(() => {
if (activeTab.value === null) return null if (activeTab.value === null) return null
@ -61,15 +42,6 @@ export const Ide = ({}) => {
return tabs.value[activeTab.value].content return tabs.value[activeTab.value].content
}) })
effect(() => {
const path = activePath.value
const contentSig = activeContent.value
if (contentSig === null || path === null) return
console.log(path, contentSig.value)
})
return ( return (
<div class="ide"> <div class="ide">
<div class="sidebar"> <div class="sidebar">
@ -161,27 +133,44 @@ export const Ide = ({}) => {
<button class="icon"> <button class="icon">
<div class="material-symbols-outlined">terminal</div> <div class="material-symbols-outlined">terminal</div>
</button> </button>
<button <div class="popup-region run-style">
class="run" <div class="region">
onClick={() => { <button class="compound">
console.log('Running file:', activePath.value) <span
if (!activePath.value) return class="part"
onClick={() => {
console.log('Running file:', activePath.value)
if (!activePath.value) return
const fullPath = '/project' + activePath.value const fullPath = '/project' + activePath.value
if (fullPath.endsWith('.sh')) { if (fullPath.endsWith('.sh')) {
ws.value.send(`sh ${fullPath}\n`) ws.value.send(`sh ${fullPath}\n`)
return return
} }
if (fullPath.endsWith('.js')) { if (fullPath.endsWith('.js')) {
ws.value.send(`node ${fullPath}\n`) ws.value.send(`node ${fullPath}\n`)
return return
} }
}} }}
> >
<div class="material-symbols-outlined">play_arrow</div> Run
Run File </span>
</button> <span class="part">
<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>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="tabbed-editor"> <div class="tabbed-editor">

@ -40,6 +40,7 @@ export const Terminal = memo(({ ws }) => {
<div <div
class="terminal" class="terminal"
ref={el => { ref={el => {
if (!el) return
console.log('Starting terminal...') console.log('Starting terminal...')
term.open(el) term.open(el)

@ -1,31 +1,5 @@
import { useSignal } from '@preact/signals'
import clsx from 'clsx'
import { useEffect, useState } from 'preact/hooks' import { useEffect, useState } from 'preact/hooks'
export const EXAMPLE_TREE = {
type: 'root',
children: [
{
type: 'folder',
name: 'bin/',
children: [
{
type: 'file',
name: 'a.out',
},
],
},
{
type: 'file',
name: 'main.c',
},
{
type: 'file',
name: 'data.csv',
},
],
}
const flattenTree = (node, depth = 0, path = []) => { const flattenTree = (node, depth = 0, path = []) => {
if (node.type === 'root') { if (node.type === 'root') {
return node.children.flatMap(entry => flattenTree(entry, depth, [])) return node.children.flatMap(entry => flattenTree(entry, depth, []))
@ -127,11 +101,6 @@ const TreeViewNode = ({ listDir, actionOpenFile, node, depth, path }) => {
} }
} }
const ICONS = {
['folder']: 'folder',
['file']: 'description',
}
export const TreeView = ({ listDir, actionOpenFile, rootPath }) => { export const TreeView = ({ listDir, actionOpenFile, rootPath }) => {
return ( return (
<div class="tree-view"> <div class="tree-view">

@ -45,18 +45,13 @@ export class FitAddon implements ITerminalAddon {
// Force a full render // Force a full render
if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) { if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {
// core._renderCoordinator.clear() // core._renderCoordinator.clear()
this._terminal.resize(dims.cols, dims.rows) this._terminal.resize(dims.cols | 0, dims.rows | 0)
} }
} }
public proposeDimensions(): ITerminalDimensions | undefined { public proposeDimensions(): ITerminalDimensions | undefined {
if (!this._terminal) { if (!this._terminal) return undefined
return undefined if (!this._terminal.element) return undefined
}
if (!this._terminal.element.parentElement) {
return undefined
}
const dimEl = this._terminal.element.closest('[xterm-dimensions]') ?? this._terminal.element.parentElement const dimEl = this._terminal.element.closest('[xterm-dimensions]') ?? this._terminal.element.parentElement

@ -117,8 +117,6 @@ input[type='submit'],
[role='button'] { [role='button'] {
appearance: none; appearance: none;
outline: none; outline: none;
border: 1px solid #d9d9d9;
background: #fff;
display: grid; display: grid;
place-content: center; place-content: center;
@ -128,13 +126,20 @@ input[type='submit'],
font-size: 15px; font-size: 15px;
font-weight: 500; font-weight: 500;
color: var(--fg, #333);
background: var(--bg, #fff);
border: var(--brd, 1px solid #d9d9d9);
box-shadow: var(--shadow, 0 0.125rem 0.125rem 0 #00000008);
&:hover {
background: var(--bg-hover, #f8f8f8);
}
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
gap: 0.25rem; gap: 0.25rem;
border-radius: 0.25rem; border-radius: 0.25rem;
box-shadow: 0 0.125rem 0.125rem 0 #00000008;
min-height: 1.75rem; min-height: 1.75rem;
cursor: pointer; cursor: pointer;
@ -143,43 +148,120 @@ input[type='submit'],
padding-left: 0.125rem; padding-left: 0.125rem;
} }
&:hover {
background: #f8f8f8;
}
&.icon { &.icon {
padding: 0.25rem; padding: 0.25rem;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
} }
&.run { &.flat {
background: hsl(120, 35%, 46%); --bg: none;
border-color: hsl(120, 38%, 41%); --bg-hover: #0002;
color: #fff; --brd: none;
.material-symbols-outlined { min-height: auto;
font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 20; box-shadow: none;
&.icon {
padding: 0;
} }
}
box-shadow: 0 0.125rem 0.2rem 0 hsla(120, 40%, 53%, 0.37); &.compound {
padding: 0;
gap: 0;
&:hover { > .part {
background: hsl(120, 35%, 51%); display: grid;
place-content: center;
padding: 0.25rem 0.5rem;
background: var(--bg);
&:not(:last-child) {
border-right: var(--brd);
}
&:hover {
background: var(--bg-hover);
}
&:has(> .material-symbols-outlined:first-child) {
padding-left: 0.25rem;
}
&:has(> .material-symbols-outlined:last-child) {
padding-right: 0.25rem;
}
} }
} }
}
&.flat { .run-style {
min-height: auto; --fg: #fff;
border: none;
background: none;
box-shadow: none;
&.icon { --bg: hsl(120, 35%, 46%);
padding: 0; --bg-hover: hsl(120, 35%, 51%);
--brd: 1px solid hsl(120, 38%, 41%);
--shadow: 0 0.125rem 0.2rem 0 hsla(120, 40%, 53%, 0.37);
.material-symbols-outlined {
font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 20;
}
}
.popup-region {
position: relative;
display: grid;
> .region {
display: grid;
}
> .popup {
position: absolute;
background: #fff;
border: 1px solid #d9d9d9;
border-radius: 0.35rem;
box-shadow: 0 0.125rem 0.25rem 0 #00000030;
overflow: clip;
z-index: 1000;
&.anchor-mode-1 {
top: calc(100% + 0.25rem);
right: 0;
}
}
}
.menu {
display: grid;
grid-auto-flow: row;
font-size: 15px;
font-weight: 500;
border: var(--brd, 1px solid #d9d9d9);
> .item {
display: grid;
padding: 0.25rem 0.5rem;
cursor: pointer;
color: var(--fg, #333);
background: var(--bg, #fff);
&:not(:last-child) {
border-bottom: var(--brd);
} }
&:hover { &:hover {
background: #0002; background: var(--bg-hover, #00000006);
} }
} }
} }
@ -533,7 +615,7 @@ input[type='submit'],
> .status { > .status {
border-top: 1px solid #d9d9d9; border-top: 1px solid #d9d9d9;
padding: 0.125rem 0.25rem; padding: 0.25rem 0.25rem;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

@ -19,3 +19,11 @@ export function useEventListener(target, event, handler, deps = []) {
} }
}, deps) }, deps)
} }
export function stripPrefix(s, prefix) {
return s.slice(0, prefix.length) === prefix ? s.slice(prefix.length) : s
}
export function clamp(min, value, max) {
return Math.max(min, Math.min(value, max))
}