Merge pull request #3 from commandlinedev/CLI-9-Create-a-new-Plugin-called-CodeRenderer

Cli 9 create a new plugin called code renderer
This commit is contained in:
anandamarsh
2023-08-21 21:31:31 -07:00
committed by GitHub
9 changed files with 6340 additions and 5013 deletions
+1
View File
@@ -4,6 +4,7 @@
"main": "dist/emain.js",
"license": "Proprietary",
"dependencies": {
"@monaco-editor/react": "^4.5.1",
"autobind-decorator": "^2.4.0",
"classnames": "^2.3.1",
"dayjs": "^1.11.3",
+1220 -845
View File
File diff suppressed because it is too large Load Diff
+4032 -3346
View File
File diff suppressed because it is too large Load Diff
+93 -76
View File
@@ -1,93 +1,110 @@
import {RendererPluginType} from "./types";
import {SimpleImageRenderer} from "./view/image";
import {SimpleMarkdownRenderer} from "./view/markdown";
import {SimpleJsonRenderer} from "./view/json";
import {OpenAIRenderer, OpenAIRendererModel} from "./view/openai";
import {isBlank} from "./util";
import {sprintf} from "sprintf-js";
import { RendererPluginType } from "./types";
import { SimpleImageRenderer } from "./view/image";
import { SimpleMarkdownRenderer } from "./view/markdown";
import { SimpleJsonRenderer } from "./view/json";
import { SourceCodeRenderer } from "./view/code";
import { OpenAIRenderer, OpenAIRendererModel } from "./view/openai";
import { isBlank } from "./util";
import { sprintf } from "sprintf-js";
const ImagePlugin : RendererPluginType = {
name: "image",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["image/*"],
simpleComponent: SimpleImageRenderer,
const ImagePlugin: RendererPluginType = {
name: "image",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["image/*"],
simpleComponent: SimpleImageRenderer,
};
const MarkdownPlugin : RendererPluginType = {
name: "markdown",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["text/markdown"],
simpleComponent: SimpleMarkdownRenderer,
const MarkdownPlugin: RendererPluginType = {
name: "markdown",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["text/markdown"],
simpleComponent: SimpleMarkdownRenderer,
};
const JsonPlugin : RendererPluginType = {
name: "json",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["application/json"],
simpleComponent: SimpleJsonRenderer,
const JsonPlugin: RendererPluginType = {
name: "json",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["application/json"],
simpleComponent: SimpleJsonRenderer,
};
const OpenAIPlugin : RendererPluginType = {
name: "openai",
rendererType: "full",
heightType: "pixels",
dataType: "model",
collapseType: "remove",
hidePrompt: true,
globalCss: null,
mimeTypes: ["application/json"],
fullComponent: OpenAIRenderer,
modelCtor: () => new OpenAIRendererModel(),
const CodePlugin: RendererPluginType = {
name: "code",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["text/plain"],
simpleComponent: SourceCodeRenderer,
};
const OpenAIPlugin: RendererPluginType = {
name: "openai",
rendererType: "full",
heightType: "pixels",
dataType: "model",
collapseType: "remove",
hidePrompt: true,
globalCss: null,
mimeTypes: ["application/json"],
fullComponent: OpenAIRenderer,
modelCtor: () => new OpenAIRendererModel(),
};
class PluginModelClass {
rendererPlugins : RendererPluginType[] = [];
registerRendererPlugin(plugin : RendererPluginType) {
if (isBlank(plugin.name)) {
throw new Error("invalid plugin, no name");
}
if (plugin.name == "terminal" || plugin.name == "none") {
throw new Error(sprintf("invalid plugin, name '%s' is reserved", plugin.name));
}
let existingPlugin = this.getRendererPluginByName(plugin.name);
if (existingPlugin != null) {
throw new Error(sprintf("plugin with name %s already registered", plugin.name));
}
this.rendererPlugins.push(plugin);
}
rendererPlugins: RendererPluginType[] = [];
getRendererPluginByName(name : string) : RendererPluginType {
for (let i=0; i<this.rendererPlugins.length; i++) {
let plugin = this.rendererPlugins[i];
if (plugin.name == name) {
return plugin;
}
}
return null;
registerRendererPlugin(plugin: RendererPluginType) {
if (isBlank(plugin.name)) {
throw new Error("invalid plugin, no name");
}
if (plugin.name == "terminal" || plugin.name == "none") {
throw new Error(
sprintf("invalid plugin, name '%s' is reserved", plugin.name)
);
}
let existingPlugin = this.getRendererPluginByName(plugin.name);
if (existingPlugin != null) {
throw new Error(
sprintf("plugin with name %s already registered", plugin.name)
);
}
this.rendererPlugins.push(plugin);
}
getRendererPluginByName(name: string): RendererPluginType {
for (let i = 0; i < this.rendererPlugins.length; i++) {
let plugin = this.rendererPlugins[i];
if (plugin.name == name) {
return plugin;
}
}
return null;
}
}
let PluginModel : PluginModelClass = null;
let PluginModel: PluginModelClass = null;
if ((window as any).PluginModel == null) {
PluginModel = new PluginModelClass();
PluginModel.registerRendererPlugin(ImagePlugin);
PluginModel.registerRendererPlugin(MarkdownPlugin);
PluginModel.registerRendererPlugin(JsonPlugin);
PluginModel.registerRendererPlugin(OpenAIPlugin);
(window as any).PluginModel = PluginModel;
PluginModel = new PluginModelClass();
PluginModel.registerRendererPlugin(ImagePlugin);
PluginModel.registerRendererPlugin(MarkdownPlugin);
PluginModel.registerRendererPlugin(JsonPlugin);
PluginModel.registerRendererPlugin(CodePlugin);
PluginModel.registerRendererPlugin(OpenAIPlugin);
(window as any).PluginModel = PluginModel;
}
export {PluginModel};
export { PluginModel };
+205 -197
View File
@@ -39,7 +39,7 @@
@import "views.less";
@import "sidebar.less";
@import "modals.less"; // includes settings
@import "comps.less"; // includes terminal
@import "comps.less"; // includes terminal
@import "tabs.less";
@import "cmdinput.less";
@import "lines.less";
@@ -47,251 +47,259 @@
// global settings / overrides
:root {
--fa-style-family: "Font Awesome 6 Sharp";
--fa-style-family: "Font Awesome 6 Sharp";
}
html, body, #app, #main {
background-color: #000;
height: 100vh;
html,
body,
#app,
#main {
background-color: #000;
height: 100vh;
}
.content {
a:hover {
color: #485fc7;
}
a:hover {
color: #485fc7;
}
}
body {
overflow: hidden;
overflow: hidden;
}
body::-webkit-scrollbar {
display: none;
display: none;
}
*::-webkit-scrollbar {
background-color: #777;
width: 5px;
height: 5px;
background-color: #777;
width: 5px;
height: 5px;
}
*::-webkit-scrollbar-thumb {
background: white;
background: white;
}
input[type=checkbox] {
cursor: pointer;
input[type="checkbox"] {
cursor: pointer;
}
// main layout
#main {
height: 100vh;
height: 100vh;
display: flex;
flex-direction: column;
.main-content {
display: flex;
flex-direction: column;
flex-direction: row;
background-color: black;
height: 100%;
.main-content {
display: flex;
flex-direction: row;
background-color: black;
height: 100%;
.session-view,
.history-view,
.bookmarks-view {
flex-grow: 1;
display: flex;
flex-direction: column;
min-width: 300px;
position: relative;
.session-view, .history-view, .bookmarks-view {
flex-grow: 1;
display: flex;
flex-direction: column;
min-width: 300px;
position: relative;
&.is-hidden {
display: none;
}
}
.screen-view {
flex-grow: 1;
border-right: 1px solid #ccc;
position: relative;
}
.window-view {
display: flex;
flex-direction: column;
position: relative;
.rendermode-tag {
position: absolute;
top: 0;
right: 0;
background-color: rgba(78, 154, 6, 0.65);
color: black;
padding: 2px 8px 2px 4px;
border-bottom-left-radius: 5px;
z-index: 10;
font-size: 12px;
&.is-active {
color: #ccc;
}
.render-mode {
padding-top: 2px;
font-size: 16px;
position: relative;
cursor: pointer;
color: #ccc;
&:hover {
color: white;
}
}
}
.share-tag {
color: #ccc;
position: absolute;
top: 0;
left: 40%;
background-color: darken(rgb(0, 177, 10), 20%);
padding: 2px 8px 2px 4px;
z-index: 11;
font-size: 12px;
/* border-radius: 0 0 5px 5px; */
opacity: 0.8;
display: flex;
flex-direction: column;
.share-tag-link {
margin-top: 10px;
display: none;
}
&:hover {
.share-tag-title {
font-size: 14px;
font-weight: bold;
}
opacity: 1.0;
padding: 20px;
width: 250px;
border: 1px solid #ccc;
border-top: 0;
.share-tag-link {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
}
}
.window-empty {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 10px;
height: 100%;
color: #ccc;
.mono-font();
code {
background-color: black;
color: #4e9a06;
}
&.should-fade {
opacity: 1.0;
animation: fade-in 2.5s;
}
}
}
&.is-hidden {
display: none;
}
}
.screen-view {
flex-grow: 1;
border-right: 1px solid #ccc;
position: relative;
}
.window-view {
display: flex;
flex-direction: column;
position: relative;
.rendermode-tag {
position: absolute;
top: 0;
right: 0;
background-color: rgba(78, 154, 6, 0.65);
color: black;
padding: 2px 8px 2px 4px;
border-bottom-left-radius: 5px;
z-index: 10;
font-size: 12px;
&.is-active {
color: #ccc;
}
.render-mode {
padding-top: 2px;
font-size: 16px;
position: relative;
cursor: pointer;
color: #ccc;
&:hover {
color: white;
}
}
}
.share-tag {
color: #ccc;
position: absolute;
top: 0;
left: 40%;
background-color: darken(rgb(0, 177, 10), 20%);
padding: 2px 8px 2px 4px;
z-index: 11;
font-size: 12px;
/* border-radius: 0 0 5px 5px; */
opacity: 0.8;
display: flex;
flex-direction: column;
.share-tag-link {
margin-top: 10px;
display: none;
}
&:hover {
.share-tag-title {
font-size: 14px;
font-weight: bold;
}
opacity: 1;
padding: 20px;
width: 250px;
border: 1px solid #ccc;
border-top: 0;
.share-tag-link {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
}
}
.window-empty {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 10px;
height: 100%;
color: #ccc;
.mono-font();
code {
background-color: black;
color: #4e9a06;
}
&.should-fade {
opacity: 1;
animation: fade-in 2.5s;
}
}
}
}
}
.remote-field .remote-status {
top: 4px;
top: 4px;
}
.image-renderer {
padding: 10px;
img {
display: block;
}
padding: 10px;
img {
display: block;
}
}
.renderer-container {
.error-container {
color: @term-red;
font-size: 14px;
padding: 5px;
}
.error-container {
color: @term-red;
font-size: 14px;
padding: 5px;
}
.scroller {
overflow: auto;
display: flex;
flex-direction: row;
}
.scroller {
overflow: auto;
display: flex;
flex-direction: row;
}
}
.renderer-container.code-renderer {
margin-top: 10px;
}
.renderer-container.json-renderer {
padding: 10px;
font-size: 12px;
padding: 10px;
font-size: 12px;
}
.markdown-renderer .markdown {
padding: 5px;
line-height: 1.5;
width: fit-content;
padding: 5px;
line-height: 1.5;
width: fit-content;
blockquote {
background-color: #222;
}
blockquote {
background-color: #222;
}
code {
background-color: #222;
font-size: 14px;
}
code {
background-color: #222;
font-size: 14px;
}
pre {
background-color: #222;
margin: 2px 10px 6px 10px;
padding: 4px 4px 4px 6px;
}
pre {
background-color: #222;
margin: 2px 10px 6px 10px;
padding: 4px 4px 4px 6px;
}
}
.openai-renderer {
.openai-message {
display: flex;
flex-direction: row;
justify-content: flex-start;
.openai-role {
color: @term-bright-green;
font-weight: bold;
width: 100px;
}
.openai-message {
display: flex;
flex-direction: row;
justify-content: flex-start;
.openai-role.openai-role-assistant {
color: @term-bright-white;
}
.openai-content-user {
white-space: pre;
color: white;
}
.openai-content-assistant {
color: white;
}
.openai-role-error {
color: @term-bright-red;
}
.openai-content-error {
color: @term-bright-red;
}
.openai-role {
color: @term-bright-green;
font-weight: bold;
width: 100px;
}
.openai-role.openai-role-assistant {
color: @term-bright-white;
}
.openai-content-user {
white-space: pre;
color: white;
}
.openai-content-assistant {
color: white;
}
.openai-role-error {
color: @term-bright-red;
}
.openai-content-error {
color: @term-bright-red;
}
}
}
+196 -149
View File
@@ -1,182 +1,229 @@
import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
import type {RendererModelInitializeParams, TermOptsType, RendererContext, RendererOpts, SimpleBlobRendererComponent, RendererModelContainerApi, RendererPluginType, PtyDataType, RendererModel, RendererOptsUpdate, LineType, TermContextUnion, RendererContainerType} from "./types";
import {PacketDataBuffer} from "./ptydata";
import {debounce, throttle} from "throttle-debounce";
import { sprintf } from "sprintf-js";
import { boundMethod } from "autobind-decorator";
import {
If,
For,
When,
Otherwise,
Choose,
} from "tsx-control-statements/components";
import type {
RendererModelInitializeParams,
TermOptsType,
RendererContext,
RendererOpts,
SimpleBlobRendererComponent,
RendererModelContainerApi,
RendererPluginType,
PtyDataType,
RendererModel,
RendererOptsUpdate,
LineType,
TermContextUnion,
RendererContainerType,
} from "./types";
import { PacketDataBuffer } from "./ptydata";
import { debounce, throttle } from "throttle-debounce";
type OV<V> = mobx.IObservableValue<V>;
type CV<V> = mobx.IComputedValue<V>;
class SimpleBlobRendererModel {
context : RendererContext;
opts : RendererOpts;
isDone : OV<boolean>;
api : RendererModelContainerApi;
savedHeight : number;
loading : OV<boolean>;
loadError : OV<string> = mobx.observable.box(null, {name: "renderer-loadError"});
ptyData : PtyDataType;
ptyDataSource : (termContext : TermContextUnion) => Promise<PtyDataType>;
initialize(params : RendererModelInitializeParams) : void {
this.loading = mobx.observable.box(true, {name: "renderer-loading"});
this.isDone = mobx.observable.box(params.isDone, {name: "renderer-isDone"});
this.context = params.context;
this.opts = params.opts;
this.api = params.api;
this.savedHeight = params.savedHeight;
this.ptyDataSource = params.ptyDataSource;
if (this.isDone.get()) {
setTimeout(() => this.reload(0), 10);
}
}
context: RendererContext;
opts: RendererOpts;
isDone: OV<boolean>;
api: RendererModelContainerApi;
savedHeight: number;
loading: OV<boolean>;
loadError: OV<string> = mobx.observable.box(null, {
name: "renderer-loadError",
});
ptyData: PtyDataType;
ptyDataSource: (termContext: TermContextUnion) => Promise<PtyDataType>;
dispose() : void {
return;
}
giveFocus() : void {
return;
initialize(params: RendererModelInitializeParams): void {
this.loading = mobx.observable.box(true, { name: "renderer-loading" });
this.isDone = mobx.observable.box(params.isDone, {
name: "renderer-isDone",
});
this.context = params.context;
this.opts = params.opts;
this.api = params.api;
this.savedHeight = params.savedHeight;
this.ptyDataSource = params.ptyDataSource;
if (this.isDone.get()) {
setTimeout(() => this.reload(0), 10);
}
}
updateOpts(update : RendererOptsUpdate) : void {
Object.assign(this.opts, update);
}
dispose(): void {
return;
}
updateHeight(newHeight : number) : void {
if (this.savedHeight != newHeight) {
this.savedHeight = newHeight;
this.api.saveHeight(newHeight);
}
giveFocus(): void {
return;
}
updateOpts(update: RendererOptsUpdate): void {
Object.assign(this.opts, update);
}
updateHeight(newHeight: number): void {
if (this.savedHeight != newHeight) {
this.savedHeight = newHeight;
this.api.saveHeight(newHeight);
}
setIsDone() : void {
if (this.isDone.get()) {
return;
}
}
setIsDone(): void {
if (this.isDone.get()) {
return;
}
mobx.action(() => {
this.isDone.set(true);
})();
this.reload(0);
}
reload(delayMs: number): void {
mobx.action(() => {
this.loading.set(true);
})();
let rtnp = this.ptyDataSource(this.context);
if (rtnp == null) {
console.log(
"no promise returned from ptyDataSource (simplerenderer)",
this.context
);
return;
}
rtnp
.then((ptydata) => {
setTimeout(() => {
this.ptyData = ptydata;
mobx.action(() => {
this.loading.set(false);
this.loadError.set(null);
})();
}, delayMs);
})
.catch((e) => {
console.log("error loading data", e);
mobx.action(() => {
this.isDone.set(true);
this.loadError.set("error loading data: " + e);
})();
this.reload(0);
}
});
}
reload(delayMs : number) : void {
mobx.action(() => {
this.loading.set(true);
})();
let rtnp = this.ptyDataSource(this.context);
if (rtnp == null) {
console.log("no promise returned from ptyDataSource (simplerenderer)", this.context);
return;
}
rtnp.then((ptydata) => {
setTimeout(() => {
this.ptyData = ptydata;
mobx.action(() => {
this.loading.set(false);
this.loadError.set(null);
})();
}, delayMs);
}).catch((e) => {
console.log("error loading data", e);
mobx.action(() => {
this.loadError.set("error loading data: " + e);
})();
});
}
receiveData(pos : number, data : Uint8Array, reason? : string) : void {
// this.dataBuf.receiveData(pos, data, reason);
}
receiveData(pos: number, data: Uint8Array, reason?: string): void {
// this.dataBuf.receiveData(pos, data, reason);
}
}
@mobxReact.observer
class SimpleBlobRenderer extends React.Component<{rendererContainer : RendererContainerType, lineId : string, plugin : RendererPluginType, onHeightChange : () => void, initParams : RendererModelInitializeParams}, {}> {
model : SimpleBlobRendererModel;
wrapperDivRef : React.RefObject<any> = React.createRef();
rszObs : ResizeObserver;
updateHeight_debounced : (newHeight : number) => void;
class SimpleBlobRenderer extends React.Component<
{
rendererContainer: RendererContainerType;
lineId: string;
plugin: RendererPluginType;
onHeightChange: () => void;
initParams: RendererModelInitializeParams;
},
{}
> {
model: SimpleBlobRendererModel;
wrapperDivRef: React.RefObject<any> = React.createRef();
rszObs: ResizeObserver;
updateHeight_debounced: (newHeight: number) => void;
constructor(props : any) {
super(props);
let {rendererContainer, lineId, plugin, initParams} = this.props;
this.model = new SimpleBlobRendererModel();
this.model.initialize(initParams);
rendererContainer.registerRenderer(lineId, this.model);
this.updateHeight_debounced = debounce(1000, this.updateHeight.bind(this));
}
constructor(props: any) {
super(props);
let { rendererContainer, lineId, plugin, initParams } = this.props;
this.model = new SimpleBlobRendererModel();
this.model.initialize(initParams);
rendererContainer.registerRenderer(lineId, this.model);
this.updateHeight_debounced = debounce(1000, this.updateHeight.bind(this));
}
updateHeight(newHeight : number) : void {
this.model.updateHeight(newHeight);
}
updateHeight(newHeight: number): void {
this.model.updateHeight(newHeight);
}
handleResize(entries : ResizeObserverEntry[]) : void {
if (this.model.loading.get()) {
return;
}
if (this.props.onHeightChange) {
this.props.onHeightChange();
}
if (!this.model.loading.get() && this.wrapperDivRef.current != null) {
let height = this.wrapperDivRef.current.offsetHeight;
this.updateHeight_debounced(height);
}
handleResize(entries: ResizeObserverEntry[]): void {
if (this.model.loading.get()) {
return;
}
if (this.props.onHeightChange) {
this.props.onHeightChange();
}
if (!this.model.loading.get() && this.wrapperDivRef.current != null) {
let height = this.wrapperDivRef.current.offsetHeight;
this.updateHeight_debounced(height);
}
}
checkRszObs() {
if (this.rszObs != null) {
return;
}
if (this.wrapperDivRef.current == null) {
return;
}
this.rszObs = new ResizeObserver(this.handleResize.bind(this));
this.rszObs.observe(this.wrapperDivRef.current);
checkRszObs() {
if (this.rszObs != null) {
return;
}
componentDidMount() {
this.checkRszObs();
if (this.wrapperDivRef.current == null) {
return;
}
this.rszObs = new ResizeObserver(this.handleResize.bind(this));
this.rszObs.observe(this.wrapperDivRef.current);
}
componentWillUnmount() {
let {rendererContainer, lineId} = this.props;
rendererContainer.unloadRenderer(lineId);
if (this.rszObs != null) {
this.rszObs.disconnect();
this.rszObs = null;
}
}
componentDidMount() {
this.checkRszObs();
}
componentDidUpdate() {
this.checkRszObs();
componentWillUnmount() {
let { rendererContainer, lineId } = this.props;
rendererContainer.unloadRenderer(lineId);
if (this.rszObs != null) {
this.rszObs.disconnect();
this.rszObs = null;
}
}
render() {
let {plugin} = this.props;
let model = this.model;
if (model.loading.get()) {
let height = this.model.savedHeight;
return (<div ref={this.wrapperDivRef} style={{minHeight: height}}>...</div>);
}
let Comp = plugin.simpleComponent;
if (Comp == null) {
<div ref={this.wrapperDivRef}>
(no component found in plugin)
</div>
}
let dataBlob = new Blob([model.ptyData.data]);
let simpleModel = (model as SimpleBlobRendererModel);
return (
<div ref={this.wrapperDivRef}>
<Comp data={dataBlob} context={simpleModel.context} opts={simpleModel.opts} savedHeight={simpleModel.savedHeight}/>
</div>
);
componentDidUpdate() {
this.checkRszObs();
}
render() {
let { plugin } = this.props;
let model = this.model;
if (model.loading.get()) {
let height = this.model.savedHeight;
return (
<div ref={this.wrapperDivRef} style={{ minHeight: height }}>
...
</div>
);
}
let Comp = plugin.simpleComponent;
if (Comp == null) {
<div ref={this.wrapperDivRef}>(no component found in plugin)</div>;
}
let dataBlob = new Blob([model.ptyData.data]);
let simpleModel = model as SimpleBlobRendererModel;
let { festate, cmdstr } = this.props.initParams.rawCmd;
return (
<div ref={this.wrapperDivRef}>
<Comp
cwd={festate.cwd}
cmdstr={cmdstr}
data={dataBlob}
context={simpleModel.context}
opts={simpleModel.opts}
savedHeight={simpleModel.savedHeight}
/>
</div>
);
}
}
export {SimpleBlobRendererModel, SimpleBlobRenderer};
export { SimpleBlobRendererModel, SimpleBlobRenderer };
+479 -400
View File
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
import * as React from "react";
import * as mobx from "mobx";
import * as mobxReact from "mobx-react";
import { RendererContext, RendererOpts } from "../types";
import Editor from "@monaco-editor/react";
/**
* Note: As mentioned in https://www.npmjs.com/package/@monaco-editor/react#for-electron-users,
* Monaco gets loaded from CDN. This may be problematic if we are behind a firewall etc. If this happens,
* We need to serve Monaco from node_modules instead
*/
type OV<V> = mobx.IObservableValue<V>;
const MaxJsonSize = 50000;
@mobxReact.observer
class SourceCodeRenderer extends React.Component<
{
data: Blob;
path: String;
context: RendererContext;
opts: RendererOpts;
savedHeight: number;
},
{}
> {
code: OV<any> = mobx.observable.box(null, {
name: "code",
deep: false,
});
language: OV<any> = mobx.observable.box(null, {
name: "language",
deep: false,
});
editorRef;
constructor(props) {
super(props);
this.editorRef = React.createRef();
}
componentDidMount() {
let prtn = this.props.data.text();
prtn.then((text) => this.code.set(text));
}
handleEditorDidMount = (editor, monaco) => {
const extension = this.props.cmdstr.split(".").pop();
const detectedLanguage = monaco.languages
.getLanguages()
.find(
(lang) => lang.extensions && lang.extensions.includes("." + extension)
);
if (detectedLanguage) {
this.editorRef.current = editor;
const model = editor.getModel();
if (model) {
monaco.editor.setModelLanguage(model, detectedLanguage.id);
this.language.set(detectedLanguage.id);
}
}
};
render() {
let opts = this.props.opts;
let maxWidth = opts.maxSize.width;
let minWidth = opts.maxSize.width;
if (minWidth > 1000) {
minWidth = 1000;
}
let lang = this.language.get();
let code = this.code.get();
if (!code) return <></>;
return (
<div className="renderer-container code-renderer">
<div className="scroller" style={{ maxHeight: opts.maxSize.height }}>
<Editor
height="30vh"
theme="hc-black"
defaultLanguage={lang}
defaultValue={code}
onMount={this.handleEditorDidMount}
options={{
scrollBeyondLastLine: false,
fontSize: "14px",
}}
/>
</div>
</div>
);
}
}
export { SourceCodeRenderer };
+19
View File
@@ -1594,6 +1594,20 @@
dependencies:
cross-spawn "^7.0.1"
"@monaco-editor/loader@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca"
integrity sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==
dependencies:
state-local "^1.0.6"
"@monaco-editor/react@^4.5.1":
version "4.5.1"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.5.1.tgz#fbc76c692aee9a33b9ab24ae0c5f219b8f002fdb"
integrity sha512-NNDFdP+2HojtNhCkRfE6/D6ro6pBNihaOzMbGK84lNWzRu+CfBjwzGt4jmnqimLuqp5yE5viHS2vi+QOAnD5FQ==
dependencies:
"@monaco-editor/loader" "^1.3.3"
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
version "2.1.8-no-fsevents.3"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
@@ -7064,6 +7078,11 @@ stack-trace@0.0.x:
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==
state-local@^1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5"
integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"