some tweaks
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "consistent",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"arrowParens": "avoid",
|
||||
"proseWrap": "always"
|
||||
}
|
||||
@@ -1,23 +1,12 @@
|
||||
import { useSignal, signal, useComputed, batch, effect } from '@preact/signals'
|
||||
|
||||
import { TreeView, EXAMPLE_TREE } from '@/client/components/TreeView.jsx'
|
||||
import { Editor } from '@/client/components/Editor.jsx'
|
||||
import { useSignal, signal, useComputed, batch, effect, Signal } from '@preact/signals'
|
||||
import clsx from 'clsx'
|
||||
import { useEffect, useState } from 'preact/hooks'
|
||||
import { fetchJson } from '../utils.js'
|
||||
|
||||
import { TreeView } from '@/client/components/TreeView.jsx'
|
||||
import { Editor } from '@/client/components/Editor.jsx'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
import { Terminal } from './Terminal.jsx'
|
||||
|
||||
function ensurePrefix(s, prefix) {
|
||||
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))
|
||||
}
|
||||
import { clamp, fetchJson, stripPrefix } from '@/client/utils.js'
|
||||
|
||||
export const Ide = ({}) => {
|
||||
const ws = useSignal(null)
|
||||
@@ -38,16 +27,8 @@ export const Ide = ({}) => {
|
||||
|
||||
const activeTab = useSignal(null)
|
||||
|
||||
const tabs = useSignal([
|
||||
// {
|
||||
// id: '/project/main.c',
|
||||
// content: signal('...main.c...'),
|
||||
// },
|
||||
// {
|
||||
// id: '/project/data.csv',
|
||||
// content: signal('...data.csv...'),
|
||||
// },
|
||||
])
|
||||
/** @type {Signal<{ id: string, content: Signal<string> }[]>} */
|
||||
const tabs = useSignal([])
|
||||
|
||||
const activePath = useComputed(() => {
|
||||
if (activeTab.value === null) return null
|
||||
@@ -61,15 +42,6 @@ export const Ide = ({}) => {
|
||||
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 (
|
||||
<div class="ide">
|
||||
<div class="sidebar">
|
||||
@@ -161,27 +133,44 @@ export const Ide = ({}) => {
|
||||
<button class="icon">
|
||||
<div class="material-symbols-outlined">terminal</div>
|
||||
</button>
|
||||
<button
|
||||
class="run"
|
||||
onClick={() => {
|
||||
console.log('Running file:', activePath.value)
|
||||
if (!activePath.value) return
|
||||
<div class="popup-region run-style">
|
||||
<div class="region">
|
||||
<button class="compound">
|
||||
<span
|
||||
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')) {
|
||||
ws.value.send(`sh ${fullPath}\n`)
|
||||
return
|
||||
}
|
||||
if (fullPath.endsWith('.js')) {
|
||||
ws.value.send(`node ${fullPath}\n`)
|
||||
return
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="material-symbols-outlined">play_arrow</div>
|
||||
Run File
|
||||
</button>
|
||||
if (fullPath.endsWith('.sh')) {
|
||||
ws.value.send(`sh ${fullPath}\n`)
|
||||
return
|
||||
}
|
||||
if (fullPath.endsWith('.js')) {
|
||||
ws.value.send(`node ${fullPath}\n`)
|
||||
return
|
||||
}
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</span>
|
||||
<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 class="tabbed-editor">
|
||||
|
||||
@@ -40,6 +40,7 @@ export const Terminal = memo(({ ws }) => {
|
||||
<div
|
||||
class="terminal"
|
||||
ref={el => {
|
||||
if (!el) return
|
||||
console.log('Starting terminal...')
|
||||
|
||||
term.open(el)
|
||||
|
||||
@@ -1,31 +1,5 @@
|
||||
import { useSignal } from '@preact/signals'
|
||||
import clsx from 'clsx'
|
||||
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 = []) => {
|
||||
if (node.type === 'root') {
|
||||
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 }) => {
|
||||
return (
|
||||
<div class="tree-view">
|
||||
|
||||
@@ -45,18 +45,13 @@ export class FitAddon implements ITerminalAddon {
|
||||
// Force a full render
|
||||
if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {
|
||||
// core._renderCoordinator.clear()
|
||||
this._terminal.resize(dims.cols, dims.rows)
|
||||
this._terminal.resize(dims.cols | 0, dims.rows | 0)
|
||||
}
|
||||
}
|
||||
|
||||
public proposeDimensions(): ITerminalDimensions | undefined {
|
||||
if (!this._terminal) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (!this._terminal.element.parentElement) {
|
||||
return undefined
|
||||
}
|
||||
if (!this._terminal) return undefined
|
||||
if (!this._terminal.element) return undefined
|
||||
|
||||
const dimEl = this._terminal.element.closest('[xterm-dimensions]') ?? this._terminal.element.parentElement
|
||||
|
||||
|
||||
+110
-28
@@ -117,8 +117,6 @@ input[type='submit'],
|
||||
[role='button'] {
|
||||
appearance: none;
|
||||
outline: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
background: #fff;
|
||||
|
||||
display: grid;
|
||||
place-content: center;
|
||||
@@ -128,13 +126,20 @@ input[type='submit'],
|
||||
font-size: 15px;
|
||||
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;
|
||||
gap: 0.25rem;
|
||||
|
||||
border-radius: 0.25rem;
|
||||
|
||||
box-shadow: 0 0.125rem 0.125rem 0 #00000008;
|
||||
|
||||
min-height: 1.75rem;
|
||||
|
||||
cursor: pointer;
|
||||
@@ -143,43 +148,120 @@ input[type='submit'],
|
||||
padding-left: 0.125rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
&.icon {
|
||||
padding: 0.25rem;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
&.run {
|
||||
background: hsl(120, 35%, 46%);
|
||||
border-color: hsl(120, 38%, 41%);
|
||||
color: #fff;
|
||||
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 20;
|
||||
}
|
||||
|
||||
box-shadow: 0 0.125rem 0.2rem 0 hsla(120, 40%, 53%, 0.37);
|
||||
|
||||
&:hover {
|
||||
background: hsl(120, 35%, 51%);
|
||||
}
|
||||
}
|
||||
|
||||
&.flat {
|
||||
--bg: none;
|
||||
--bg-hover: #0002;
|
||||
--brd: none;
|
||||
|
||||
min-height: auto;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
|
||||
&.icon {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.compound {
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
|
||||
> .part {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.run-style {
|
||||
--fg: #fff;
|
||||
|
||||
--bg: hsl(120, 35%, 46%);
|
||||
--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 {
|
||||
background: #0002;
|
||||
background: var(--bg-hover, #00000006);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -533,7 +615,7 @@ input[type='submit'],
|
||||
> .status {
|
||||
border-top: 1px solid #d9d9d9;
|
||||
|
||||
padding: 0.125rem 0.25rem;
|
||||
padding: 0.25rem 0.25rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -19,3 +19,11 @@ export function useEventListener(target, event, handler, 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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user