mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: scratchpad sidebar
This commit is contained in:
1 parent
981f8a4b9d
commit
e3a5543d9f
14 files changed
+239
-65
No files matched your search
@@ -106,7 +106,20 @@ export default function (env = {}) {
|
|||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.(svg|ttf|eot|woff|woff2|png|jpg|jpeg|gif)$/,
|
test: /\.svg$/i,
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
issuer: /\.[jt]sx?$/,
|
||||||
|
resourceQuery: /react/,
|
||||||
|
use: { loader: '@svgr/webpack', options: { icon: true } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: [fileLoader()],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ttf|eot|woff|woff2|png|jpg|jpeg|gif)$/,
|
||||||
use: [fileLoader()],
|
use: [fileLoader()],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
|
||||||
|
const _hooks: Record<keyof any, Array<(...args: any[]) => any>> = {};
|
||||||
|
|
||||||
|
function isBailed(value: any) {
|
||||||
|
return value !== null && value !== false && value !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Disposable = () => void;
|
||||||
|
export type VoidReturn = Promise<any> | any;
|
||||||
|
|
||||||
|
export interface EventMap extends Record<string, any> {
|
||||||
|
'scratchpadEditorCreate': (editor, monaco) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHooks<K extends keyof EventMap>(name: K) {
|
||||||
|
const hooks = _hooks[name] || (_hooks[name] = []);
|
||||||
|
if (hooks.length >= 2048) {
|
||||||
|
console.warn(
|
||||||
|
'max listener count (2048) for event "%s" exceeded, which may be caused by a memory leak',
|
||||||
|
name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
|
const index = (_hooks[name] || []).findIndex((callback) => callback === listener);
|
||||||
|
if (index >= 0) {
|
||||||
|
_hooks[name].splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
|
getHooks(name).push(listener);
|
||||||
|
return () => removeListener(name, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prependListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
|
getHooks(name).unshift(listener);
|
||||||
|
return () => removeListener(name, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function once<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
|
let dispose;
|
||||||
|
function _listener(...args: any[]) {
|
||||||
|
dispose();
|
||||||
|
return listener.apply(this, args);
|
||||||
|
}
|
||||||
|
_listener.toString = () => `// Once \n${listener.toString()}`;
|
||||||
|
dispose = addListener(name, _listener);
|
||||||
|
return dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function on<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
|
return addListener(name, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function off<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
|
return removeListener(name, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parallel<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): Promise<void> {
|
||||||
|
const tasks: Promise<any>[] = [];
|
||||||
|
for (const callback of _hooks[name] || []) {
|
||||||
|
tasks.push(callback.apply(this, args));
|
||||||
|
}
|
||||||
|
await Promise.all(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emit<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>) {
|
||||||
|
return parallel(name, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function serial<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): Promise<void> {
|
||||||
|
const hooks = Array.from(_hooks[name] || []);
|
||||||
|
for (const callback of hooks) {
|
||||||
|
await callback.apply(this, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function bail<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): Promise<ReturnType<EventMap[K]>> {
|
||||||
|
const hooks = Array.from(_hooks[name] || []);
|
||||||
|
for (const callback of hooks) {
|
||||||
|
let result = callback.apply(this, args);
|
||||||
|
if (result instanceof Promise) result = await result;
|
||||||
|
if (isBailed(result)) return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ export async function load(features = ['markdown']) {
|
|||||||
for (const feat of features) {
|
for (const feat of features) {
|
||||||
if (loaded.includes(feat)) continue;
|
if (loaded.includes(feat)) continue;
|
||||||
if (!loaders[feat] && !window.externalModules[`monaco-${feat}`]) {
|
if (!loaders[feat] && !window.externalModules[`monaco-${feat}`]) {
|
||||||
console.error('Unknown monaco feature:', feat);
|
console.warn('Unknown monaco feature:', feat);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
s = Date.now();
|
s = Date.now();
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
|
||||||
export default function DomComponent(props: React.HTMLAttributes<HTMLDivElement> & { childDom: HTMLElement }) {
|
const tempDoms = {};
|
||||||
|
|
||||||
|
export default function DomComponent(props: React.HTMLAttributes<HTMLDivElement> & { id?: string, childDom: HTMLElement }) {
|
||||||
const ref = React.useRef<HTMLDivElement>();
|
const ref = React.useRef<HTMLDivElement>();
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
ref.current.appendChild(props.childDom);
|
let dom = props.childDom;
|
||||||
|
if (props.id && dom) tempDoms[props.id] = dom;
|
||||||
|
else if (props.id && !dom && tempDoms[props.id]) dom = tempDoms[props.id];
|
||||||
|
ref.current.appendChild(dom);
|
||||||
return () => {
|
return () => {
|
||||||
$(ref.current).empty();
|
$(ref.current).empty();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
|||||||
);
|
);
|
||||||
window.editor = this.editor;
|
window.editor = this.editor;
|
||||||
window.monaco = monaco;
|
window.monaco = monaco;
|
||||||
|
window.Hydro.bus.emit('scratchpadEditorCreate', this.editor, monaco);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function ScratchpadSidebar({ name }) {
|
|
||||||
const Component = window.Hydro.scratchpad?.[name];
|
|
||||||
const [state, setState] = React.useState(0);
|
|
||||||
if (Component) return <Component />;
|
|
||||||
setTimeout(() => setState(state + 1), 1000);
|
|
||||||
return <div>No registered providers.</div>;
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,6 @@ import Toolbar, {
|
|||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
pretestVisible: state.ui.pretest.visible,
|
pretestVisible: state.ui.pretest.visible,
|
||||||
sidebarVisible: state.ui.sidebar.visible,
|
|
||||||
recordsVisible: state.ui.records.visible,
|
recordsVisible: state.ui.records.visible,
|
||||||
isPosting: state.ui.isPosting,
|
isPosting: state.ui.isPosting,
|
||||||
isRunning: state.pretest.isRunning,
|
isRunning: state.pretest.isRunning,
|
||||||
@@ -185,16 +184,6 @@ export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadTool
|
|||||||
{i18n('Records')}
|
{i18n('Records')}
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
)}
|
)}
|
||||||
{/* <ToolbarButton
|
|
||||||
activated={this.props.sidebarVisible}
|
|
||||||
onClick={() => this.props.togglePanel('sidebar')}
|
|
||||||
data-global-hotkey="alt+w"
|
|
||||||
data-tooltip={`${i18n('Toggle Sidebar')} (Alt+L)`}
|
|
||||||
>
|
|
||||||
<Icon name="edit" />
|
|
||||||
{' '}
|
|
||||||
{i18n('Pretest')}
|
|
||||||
</ToolbarButton> */}
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
.scratchpad__tablist
|
||||||
|
user-select: none
|
||||||
|
|
||||||
|
div
|
||||||
|
display: flex
|
||||||
|
width: 50px
|
||||||
|
height: 50px
|
||||||
|
font-size: 28px
|
||||||
|
flex-direction: row
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
margin: rem(8px) 0
|
||||||
|
fill: #666
|
||||||
|
color: #666
|
||||||
|
cursor: pointer
|
||||||
|
&:hover
|
||||||
|
fill: #000
|
||||||
|
color: #000
|
||||||
|
|
||||||
|
div.scratchpad__tab-active
|
||||||
|
border-left: 4px solid #000
|
||||||
|
fill: #000
|
||||||
|
color: #000
|
||||||
|
cursor: default
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SplitPane from 'react-split-pane';
|
import SplitPane from 'react-split-pane';
|
||||||
import { connect } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import SplitPaneFillOverlay from 'vj/components/react-splitpane/SplitPaneFillOverlayComponent';
|
import SplitPaneFillOverlay from 'vj/components/react-splitpane/SplitPaneFillOverlayComponent';
|
||||||
|
import ProblemIcon from '@vscode/codicons/src/icons/file.svg?react';
|
||||||
import Dom from 'vj/components/react/DomComponent';
|
import Dom from 'vj/components/react/DomComponent';
|
||||||
import ScratchpadToolbar from './ScratchpadToolbarContainer';
|
import ScratchpadToolbar from './ScratchpadToolbarContainer';
|
||||||
import ScratchpadEditor from './ScratchpadEditorContainer';
|
import ScratchpadEditor from './ScratchpadEditorContainer';
|
||||||
import ScratchpadPretest from './ScratchpadPretestContainer';
|
import ScratchpadPretest from './ScratchpadPretestContainer';
|
||||||
import ScratchpadRecords from './ScratchpadRecordsContainer';
|
import ScratchpadRecords from './ScratchpadRecordsContainer';
|
||||||
import ScratchpadSidebar from './ScratchpadSidebar';
|
|
||||||
|
|
||||||
function buildNestedPane([a, ...panes], mode = 'horizontal') {
|
function buildNestedPane([a, ...panes], mode = 'horizontal') {
|
||||||
const elements = [
|
const elements = [
|
||||||
@@ -31,12 +31,36 @@ function buildNestedPane([a, ...panes], mode = 'horizontal') {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const pages = {
|
||||||
ui: state.ui,
|
problem: {
|
||||||
});
|
icon: () => <ProblemIcon />,
|
||||||
|
component: () => {
|
||||||
|
console.log($('.problem-content'));
|
||||||
|
return <Dom childDom={$('.problem-content').get(0)} id="problem-content" />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
let rerenderCallback = null;
|
||||||
changeUiSize: _.debounce((uiElement, size) => {
|
export function addPage(key, icon, component) {
|
||||||
|
pages[key] = {
|
||||||
|
icon,
|
||||||
|
component,
|
||||||
|
};
|
||||||
|
rerenderCallback?.();
|
||||||
|
}
|
||||||
|
window.Hydro.scratchpad = { addPage, pages };
|
||||||
|
|
||||||
|
export default function ScratchpadContainer() {
|
||||||
|
const [, updateState] = React.useState();
|
||||||
|
const forceUpdate = React.useCallback(() => updateState({}), []);
|
||||||
|
React.useEffect(() => {
|
||||||
|
rerenderCallback = forceUpdate;
|
||||||
|
}, []);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const ui = useSelector((state) => state.ui, _.isEqual);
|
||||||
|
|
||||||
|
const handleChangeSize = _.debounce((uiElement, size) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SCRATCHPAD_UI_CHANGE_SIZE',
|
type: 'SCRATCHPAD_UI_CHANGE_SIZE',
|
||||||
payload: {
|
payload: {
|
||||||
@@ -44,52 +68,67 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
size,
|
size,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, 500),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadContainer extends React.PureComponent {
|
|
||||||
handleChangeSize(uiElement, size) {
|
|
||||||
this.props.changeUiSize(uiElement, size);
|
|
||||||
$('#scratchpad').trigger('vjScratchpadRelayout');
|
$('#scratchpad').trigger('vjScratchpadRelayout');
|
||||||
this.props.ui.pretest.visible = !!this.props.ui.pretest.visible;
|
forceUpdate();
|
||||||
this.forceUpdate();
|
}, 500);
|
||||||
}
|
const switchToPage = (target) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'SCRATCHPAD_SWITCH_TO_PAGE',
|
||||||
|
payload: target,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
return (
|
<SplitPane
|
||||||
|
split="vertical"
|
||||||
|
primary="first"
|
||||||
|
defaultSize={50}
|
||||||
|
allowResize={false}
|
||||||
|
>
|
||||||
|
<div className="scratchpad__tablist">
|
||||||
|
{Object.keys(pages).map((key) => {
|
||||||
|
const Component = pages[key].icon;
|
||||||
|
return (
|
||||||
|
<div key={key} className={key === ui.activePage ? 'scratchpad__tab-active' : ''} onClick={() => switchToPage(key)}>
|
||||||
|
<Component />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
<SplitPane
|
<SplitPane
|
||||||
defaultSize={this.props.ui.main.size}
|
defaultSize={ui.main.size}
|
||||||
minSize={250}
|
minSize={250}
|
||||||
split="vertical"
|
split="vertical"
|
||||||
primary="second"
|
primary="second"
|
||||||
onChange={(size) => this.handleChangeSize('main', size)}
|
onChange={(size) => handleChangeSize('main', size)}
|
||||||
>
|
>
|
||||||
<SplitPane
|
<div className="scratchpad__problem">
|
||||||
defaultSize={this.props.ui.sidebar.visible ? this.props.ui.sidebar.size : 0}
|
{Object.keys(pages).map((key) => {
|
||||||
split="vertical"
|
const Component = pages[key].component;
|
||||||
primary="second"
|
return (
|
||||||
onChange={(size) => this.handleChangeSize('sidebar', size)}
|
<div key={key} style={{ display: key === ui.activePage ? 'block' : 'none' }}>
|
||||||
>
|
<Component />
|
||||||
<Dom className="scratchpad__problem" childDom={$('.problem-content').get(0)} />
|
</div>
|
||||||
<ScratchpadSidebar name="debug" />
|
);
|
||||||
</SplitPane>
|
})}
|
||||||
|
</div>
|
||||||
{buildNestedPane([
|
{buildNestedPane([
|
||||||
<SplitPaneFillOverlay key="editor" className="flex-col">
|
<SplitPaneFillOverlay key="editor" className="flex-col">
|
||||||
<ScratchpadToolbar />
|
<ScratchpadToolbar />
|
||||||
<ScratchpadEditor />
|
<ScratchpadEditor />
|
||||||
</SplitPaneFillOverlay>,
|
</SplitPaneFillOverlay>,
|
||||||
{
|
{
|
||||||
props: this.props.ui.pretest,
|
props: ui.pretest,
|
||||||
onChange: (size) => this.handleChangeSize('pretest', size),
|
onChange: (size) => handleChangeSize('pretest', size),
|
||||||
element: <ScratchpadPretest key="pretest" />,
|
element: <ScratchpadPretest key="pretest" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
props: this.props.ui.records,
|
props: ui.records,
|
||||||
onChange: (size) => this.handleChangeSize('records', size),
|
onChange: (size) => handleChangeSize('records', size),
|
||||||
element: <ScratchpadRecords key="records" />,
|
element: <ScratchpadRecords key="records" />,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
);
|
</SplitPane>
|
||||||
}
|
);
|
||||||
});
|
}
|
||||||
@@ -3,12 +3,14 @@ import ui from './ui';
|
|||||||
import editor from './editor';
|
import editor from './editor';
|
||||||
import pretest from './pretest';
|
import pretest from './pretest';
|
||||||
import records from './records';
|
import records from './records';
|
||||||
|
import state from './state';
|
||||||
|
|
||||||
const reducer = combineReducers({
|
const reducer = combineReducers({
|
||||||
ui,
|
ui,
|
||||||
editor,
|
editor,
|
||||||
pretest,
|
pretest,
|
||||||
records,
|
records,
|
||||||
|
state,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default reducer;
|
export default reducer;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export default function reducer(state = {}, action) {
|
||||||
|
if (action.type === 'SCRATCHPAD_STATE_UPDATE') {
|
||||||
|
const { key, value } = action.payload;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
@@ -11,10 +11,6 @@ export default function reducer(state = {
|
|||||||
: false,
|
: false,
|
||||||
size: 200,
|
size: 200,
|
||||||
},
|
},
|
||||||
sidebar: {
|
|
||||||
visible: false,
|
|
||||||
size: 100,
|
|
||||||
},
|
|
||||||
records: {
|
records: {
|
||||||
visible: UiContext.canViewRecord && localStorage.getItem('scratchpad/records') === 'true',
|
visible: UiContext.canViewRecord && localStorage.getItem('scratchpad/records') === 'true',
|
||||||
size: 100,
|
size: 100,
|
||||||
@@ -23,6 +19,7 @@ export default function reducer(state = {
|
|||||||
isPosting: false,
|
isPosting: false,
|
||||||
waitSec: 0,
|
waitSec: 0,
|
||||||
isWaiting: false,
|
isWaiting: false,
|
||||||
|
activePage: 'problem',
|
||||||
}, action) {
|
}, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'SCRATCHPAD_UI_CHANGE_SIZE': {
|
case 'SCRATCHPAD_UI_CHANGE_SIZE': {
|
||||||
@@ -122,6 +119,12 @@ export default function reducer(state = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case 'SCRATCHPAD_SWITCH_TO_PAGE': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activePage: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import * as bus from './bus';
|
||||||
|
|
||||||
window.Hydro = {
|
window.Hydro = {
|
||||||
extraPages: [],
|
extraPages: [],
|
||||||
preload: [],
|
preload: [],
|
||||||
@@ -5,6 +7,7 @@ window.Hydro = {
|
|||||||
utils: {},
|
utils: {},
|
||||||
node_modules: {},
|
node_modules: {},
|
||||||
version: process.env.VERSION,
|
version: process.env.VERSION,
|
||||||
|
bus,
|
||||||
};
|
};
|
||||||
window.externalModules = {};
|
window.externalModules = {};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.38.29",
|
"version": "4.38.30",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "hydro.js",
|
"main": "hydro.js",
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"@fontsource/source-code-pro": "^4.5.10",
|
"@fontsource/source-code-pro": "^4.5.10",
|
||||||
"@fontsource/ubuntu-mono": "^4.5.10",
|
"@fontsource/ubuntu-mono": "^4.5.10",
|
||||||
"@hydrooj/utils": "workspace:*",
|
"@hydrooj/utils": "workspace:*",
|
||||||
|
"@svgr/webpack": "^5",
|
||||||
"@types/gulp-if": "^0.0.34",
|
"@types/gulp-if": "^0.0.34",
|
||||||
"@types/jquery": "^3.5.14",
|
"@types/jquery": "^3.5.14",
|
||||||
"@types/json-schema": "^7.0.11",
|
"@types/json-schema": "^7.0.11",
|
||||||
@@ -111,6 +112,7 @@
|
|||||||
"webpackbar": "^5.0.2"
|
"webpackbar": "^5.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vscode/codicons": "^0.0.31",
|
||||||
"ansi_up": "^5.1.0",
|
"ansi_up": "^5.1.0",
|
||||||
"esbuild": "0.14.3",
|
"esbuild": "0.14.3",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
|||||||
Reference in new issue
Block a user