Skip to content

Commit

Permalink
Issue 313 (#316)
Browse files Browse the repository at this point in the history
* patch over some linter errors iwth codeblock

* checkpoint on codeblock output

* 2nd checkpoint on codeblock output

* 3rd cp

* checkpoint 4

* got output working

* got input working, but broke output in the process

* jeff's revisions, got it working

* revised output to be on state
  • Loading branch information
lritter79 authored Nov 19, 2024
1 parent 1b98212 commit 1d46c23
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 31 deletions.
112 changes: 85 additions & 27 deletions lib/com/codeblock.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { component } from "../model/components.ts";
import { Node } from "../model/mod.ts";


import { Workbench, Context } from "../workbench/mod.ts";
export interface CodeExecutor {
// executes the source and returns an output string.
// exceptions in execution should be caught and returned as a string.
async execute(source: string, options: ExecuteOptions): string;
execute(source: string, options: ExecuteOptions): Promise<string>;

canExecute(options: ExecuteOptions): boolean;
}
Expand All @@ -16,22 +14,25 @@ export interface ExecuteOptions {

// defaultExecutor can be replaced with an external service, etc
export let defaultExecutor: CodeExecutor = {
async execute(source: string, options: ExecuteOptions): Promise<string> {
async execute(
source: string,
options: ExecuteOptions
): Promise<string> {
if (options.language !== "javascript") {
return `Unsupported language: ${options.language}`;
}
return JSON.stringify(window.eval(source));
let output = window.eval(source);
//return JSON.stringify(output);
return output.toString();
},

canExecute(options: ExecuteOptions): boolean {
if (options.language === "javascript") {
return true;
}
return false;
}
}


},
};

@component
export class CodeBlock {
Expand All @@ -44,12 +45,23 @@ export class CodeBlock {
}

childrenView() {
return CodeEditor;
return CodeEditorWithOutput;
}

handleIcon(collapsed: boolean = false): any {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="node-bullet">
<svg
xmlns="http://www.w3.org/2000/svg"
width="15"
height="15"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="node-bullet"
>
<polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
</svg>
Expand All @@ -63,45 +75,91 @@ export class CodeBlock {
when: (ctx: Context) => {
if (!ctx.node) return false;
if (ctx.node.raw.Rel === "Fields") return false;
if (ctx.node.parent && ctx.node.parent.hasComponent(Document)) return false;
if (ctx.node.parent && ctx.node.parent.hasComponent(Document))
return false;
return true;
},
action: (ctx: Context) => {
const com = new CodeBlock();
ctx.node.addComponent(com);
ctx.node.changed();
workbench.workspace.setExpanded(ctx.path.head, ctx.path.node, true);
}
if (ctx?.node) {
ctx.node.addComponent(com);
ctx.node.changed();
workbench.workspace.setExpanded(
ctx.path.head,
ctx.path.node,
true
);
}
},
});
}

}


const CodeEditor = {
oncreate({dom, attrs: {path}}) {
oncreate(vnode) {
const {
dom,
attrs: { path },
} = vnode;
const snippet = path.node.getComponent(CodeBlock);

//@ts-ignore
dom.jarEditor = new window.CodeJar(dom, (editor) => {
// highlight.js does not trim old tags,
// let's do it by this hack.
editor.textContent = editor.textContent;
//@ts-ignore
window.hljs.highlightBlock(editor);
snippet.language = window.hljs.highlightAuto(editor.textContent).language || "";
snippet.language =
//@ts-ignore
window.hljs.highlightAuto(editor.textContent).language || "";
});
dom.jarEditor.updateCode(snippet.code);
dom.jarEditor.onUpdate(code => {
dom.jarEditor.onUpdate((code) => {
snippet.code = code;
path.node.changed();
});
},

view({attrs: {workbench, path}}) {
view({ attrs: { workbench, path } }) {
// this cancels the keydown on the outline node
// so you can use arrow keys normally
const onkeydown = (e) => e.stopPropagation();


return <div class="code-editor" onkeydown={onkeydown}></div>;
},
};

const Output = {
view({ dom, state, attrs: { path } }) {
const snippet = path.node.getComponent(CodeBlock);

let handleClick = async () => {
state.output = "Running...";
try {
const res = await defaultExecutor.execute(snippet.code, {
language: snippet.language,
});

// Update output using m.prop to ensure it's persistent across re-renders
state.output = res; // Call m.prop with the new value
} catch (error) {
state.output = error.toString();
}
};
return (
<div class="code-editor" onkeydown={onkeydown}></div>
)
<div className="code-editor-output">
<p>{state.output ? "Output: " + state.output : ""}</p>
<button type="button" onclick={handleClick}>
Run
</button>
</div>
);
},
};

class CodeEditorWithOutput {
view(vnode) {
return [m(CodeEditor, vnode.attrs), m(Output, vnode.attrs)];
}
}
}
26 changes: 22 additions & 4 deletions web/static/app/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -667,20 +667,38 @@ the top nav in a more elegant way at some point*/

/*------------CODE EDITOR------------*/

.code-editor {
border-radius: 6px;
.code-editor, .code-editor-output {
font-family: 'Source Code Pro', monospace;
font-size: 14px;
font-weight: 400;
height: 340px;
letter-spacing: normal;
line-height: 20px;
padding: 10px;
resize: none !important;
tab-size: 4;
margin-left: -0.5rem;
}

.code-editor {
border-top-left-radius: 6px;
border-top-right-radius: 6px;
height: 340px;
resize: none !important;

}

.code-editor.hljs {
padding: 10px;
}

.code-editor-output{
background: #232323;
color: #e6e1dc;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
border-top: 1px solid white;
display: flex !important;
justify-content: space-between;
p {
margin: 0;
}
}

0 comments on commit 1d46c23

Please sign in to comment.