Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}