Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Render code + data using codemirror
  • Loading branch information
bfollington committed Jun 6, 2024
commit 7afc932b70c2b5dcdd9dbed5454f15110f37982e
311 changes: 311 additions & 0 deletions typescript/package-lock.json

Large diffs are not rendered by default.

25 changes: 18 additions & 7 deletions typescript/packages/lookslike-prototype/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@
},
"homepage": "https://github.com/commontoolsinc/labs#readme",
"devDependencies": {
"@commontools/module": "^0.0.1",
"@commontools/data": "^0.0.1",
"@commontools/io": "^0.0.1",
"@commontools/usuba-rt": "^0.0.1",
"@commontools/module": "^0.0.1",
"@commontools/runtime": "^0.0.1",
"@shoelace-style/shoelace": "^2.15.1",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"wireit": "^0.14.4",
"@commontools/usuba-rt": "^0.0.1",
"@instructor-ai/instructor": "^1.3.0",
"@shoelace-style/shoelace": "^2.15.1",
"@types/pretty": "^2.0.3",
"lit": "^3.1.3",
"openai": "^4.47.2",
"pretty": "^2.0.0",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"wireit": "^0.14.4"
},
"wireit": {
"build": {
Expand All @@ -49,5 +49,16 @@
"clean": {
"command": "rm -rf ./lib ./dist ./.wireit ./node_modules"
}
},
"dependencies": {
"@babel/parser": "^7.24.6",
"@codemirror/basic-setup": "^0.20.0",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/language": "^6.10.2",
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.27.0",
"codemirror": "^6.0.1",
"prettier": "^3.3.0"
}
}
53 changes: 49 additions & 4 deletions typescript/packages/lookslike-prototype/src/components/com-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const codePrompt = `
}
}
},
"body": "function() { return [{ label: 'Water my plants', checked: false }, { label: 'Buy milk', checked: true }]; }"
"body": "return [{ label: 'Water my plants', checked: false }, { label: 'Buy milk', checked: true }];"
}
]
\`\`\`
Expand All @@ -48,7 +48,7 @@ const codePrompt = `
"contentType": "text/javascript",
"in": {},
"outputType": {},
"body": "async function() { const todos = input('todos'); const newTodo = { label: 'water the plants', checked: false }; cost newTodos = [...todos, newTodo]; return newTodos; }"
"body": "const todos = input('todos'); const newTodo = { label: 'water the plants', checked: false }; cost newTodos = [...todos, newTodo]; return newTodos;"
}
]
\`\`\`
Expand Down Expand Up @@ -76,7 +76,7 @@ const codePrompt = `
}
}
},
"body": "function() { const todos = input('todos'); return todos.filter(todo => todo.checked); }"
"body": "const todos = input('todos'); return todos.filter(todo => todo.checked);"
}
]
\`\`\`
Expand All @@ -87,7 +87,52 @@ const codePrompt = `
ContentType should be "text/javascript" for code.
Always respond with code, even for static data. Wrap your response in a json block. Respond with nothing else.

render my todos" ->
---

"render each image by url" ->
images is an array of strings (URLs)

\`\`\`json
[
{
"id": "imageUi",
"contentType": "application/json+vnd.common.ui",
"in": {
"images": [".", "images"]
},
"outputType": {
"$id": "https://common.tools/ui.schema.json"
},
"body": {
"tag": "ul",
"props": {
"className": "image"
},
"children": [
"type": "repeat",
"binding": "images",
"template": {
"tag": "li",
"props": {},
"children": [
{
"tag": "img",
"props": {
"src": { type: 'string', binding: null },
}
}
],
}
]
}
}
]

Raw values can be passed through by setting binding to null.

---

"render my todos" ->

\`\`\`json
[
Expand Down
41 changes: 41 additions & 0 deletions typescript/packages/lookslike-prototype/src/components/com-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { EditorView, basicSetup } from "codemirror"
import { javascript } from "@codemirror/lang-javascript"
import { EditorState } from '@codemirror/state';
import { format } from '../format'

@customElement('com-code')
class CodeMirrorCodeViewer extends LitElement {
@property({ type: String }) code = '';

static styles = css`
:host {
display: block;
}
.editor {
border: 1px solid #ccc;
border-radius: 4px;
}
`;

async firstUpdated() {
const editorContainer = this.shadowRoot?.getElementById('editor');
if (editorContainer) {
const state = EditorState.create({
doc: await format(this.code),
extensions: [basicSetup, javascript()]
})
const editor = new EditorView({
state,
parent: editorContainer
})
}
}

render() {
return html`
<div id="editor" class="editor"></div>
`;
}
}
56 changes: 56 additions & 0 deletions typescript/packages/lookslike-prototype/src/components/com-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { EditorView, basicSetup } from "codemirror"
import { javascript } from "@codemirror/lang-javascript"
import { json } from "@codemirror/lang-json"
import { EditorState } from '@codemirror/state';
import { format } from '../format'
import { foldGutter, foldService, foldAll, foldEffect, foldedRanges } from "@codemirror/language";

@customElement('com-data')
class CodeMirrorDataViewer extends LitElement {
@property({ type: String }) data = '';

static styles = css`
:host {
display: block;
}
.editor {
border: 1px solid #ccc;
border-radius: 4px;
}
`;
state: EditorState;
editor: EditorView;

firstUpdated() {
const editorContainer = this.shadowRoot?.getElementById('editor');
if (editorContainer) {
this.state = EditorState.create({
doc: this.data,
extensions: [basicSetup, json()]
})
this.editor = new EditorView({
state: this.state,
parent: editorContainer
})
}
}

foldAll() {
const view = this.editor;
foldAll(view)
view.dispatch({});
}

updated() {
this.editor.dispatch({ changes: { from: 0, to: this.state.doc.length, insert: this.data } });
this.foldAll();
}

render() {
return html`
<div id="editor" class="editor"></div>
`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ function definitionToHtml(node: RecipeNode, context: any) {

if (node.contentType === 'text/javascript') {
const val = snapshot(context).outputs[node.id]
return html`<pre>${node.body}</pre><com-toggle><pre class="code">${JSON.stringify(val, null, 2)}</pre></com-toggle>`
return html`<com-code .code=${node.body}></com-code><com-data .data=${JSON.stringify(val, null, 2)}></com-data>`
}

if (node.contentType === 'application/json+vnd.common.ui') {
const el = createElement(node.body, snapshot(context).inputs[node.id] || {})

return html`<div>${unsafeHTML(el.outerHTML)}</div>
<com-toggle>
<pre class="code">${pretty(el.outerHTML)}</pre>
<pre class="code">${JSON.stringify(node.body, null, 2)}</pre>
<com-data .data=${pretty(el.outerHTML)}</com-data>
<com-data .data=${JSON.stringify(node.body, null, 2)}</com-data>
</com-toggle>`
}

Expand Down
19 changes: 10 additions & 9 deletions typescript/packages/lookslike-prototype/src/eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export async function run(src: string, inputs: { [key: string]: any }) {
const module = await rt.eval('text/javascript', code(src), io);

for (const key in inputs) {
io.write(key, infer(inputs[key]));
const input = infer(JSON.stringify(inputs[key]));
io.write(key, input);
}

console.log('Running the module:');
module.run();
const returnValue = io.read('__result__');
return returnValue?.val;
return JSON.parse(returnValue?.val);
}


Expand All @@ -36,17 +37,17 @@ const code = (src: string) => `
run() {
function input(key) {
const ref = read(key);
console.log('Reference:', ref);
console.log('read(' + key + '):', ref);
const value = ref?.deref()?.val;
console.log('Value:', value);
return value;
console.log('value(' + key + '):', value);
return JSON.parse(value);
}

console.log('Running!');
debugger;
const fn = ${src};
console.log('[begin]');
const fn = function() { ${src} };
const result = fn();
write('__result__', { tag: 'string', val: result });
write('__result__', { tag: 'string', val: JSON.stringify(result) });
console.log('[end]');
}
}

Expand Down
7 changes: 7 additions & 0 deletions typescript/packages/lookslike-prototype/src/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as prettier from 'prettier'
import prettierPluginBabel from 'prettier/plugins/babel'
import prettierPluginEstree from "prettier/plugins/estree";

export async function format(code: string) {
return await prettier.format(code, { semi: true, parser: "babel", plugins: [prettierPluginBabel, prettierPluginEstree] });
}
4 changes: 2 additions & 2 deletions typescript/packages/lookslike-prototype/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { demo } from './wasm.js'

export * as ComAppGrid from './components/com-app-grid.js'
export * as ComContent from './components/com-content.js'
export * as ComChat from './components/com-chat.js'
Expand All @@ -11,4 +9,6 @@ export * as ComButton from './components/com-button.js'
export * as ComUnibox from './components/com-unibox.js'
export * as ComEditor from './components/com-editor.js'
export * as ComToggle from './components/com-toggle.js'
export * as ComCode from './components/com-code.js'
export * as ComData from './components/com-data.js'
export * as ComApp from './components/com-app.js'
3 changes: 2 additions & 1 deletion typescript/packages/lookslike-prototype/src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export function createElement(node, context) {
element[key] = context[value.binding];
}
} else {
element[key] = context[value];
// the value is not in a subfield, context is the value
element[key] = context;
}
}
} else if (value["$id"] && value["$id"] === STREAM && value.name) {
Expand Down