|
1 | 1 | # Workspace Example |
2 | 2 |
|
3 | 3 | This is an Example of a workspace embedded in HTML/Markdown that will persist it's code in browser local storage. |
4 | | -<lively-script><script>import focalStorage from 'src/external/focalStorage.js'; import {SocketSingleton} from 'src/components/mle/socket.js'; const idMap = new Map(); const enclosingDiv = <div />; var mle = {}; async function startUp(){ await SocketSingleton.get(); let i = 0; while(await focalStorage.getItem(`markdown_workspace_${i}`)){ await new Pane(i, await focalStorage.getItem(`markdown_workspace_${i}_kind`)).create(); i++; } } class Pane { constructor(id, kind){ if(id !== undefined){ this._id = id; this.kind = kind; } else { this._id = idMap.size; } idMap.set(this.textStorageId, this); } onDoIt() { this.saveText() this.workspace.tryBoundEval(this.workspace.value) } async onReset() { if(this.kind) this.socket = await SocketSingleton.reset(); this.logarea.value = "LOGS"; this.drawarea.innerHTML = ""; } get defaultText() { return "sql`SELECT * FROM students`" } get textStorageId() { return `markdown_workspace_${this._id}` } async loadText() { var loaded = await focalStorage.getItem(this.textStorageId); if (loaded) return loaded; return this.defaultText; } async saveText() { focalStorage.setItem(this.textStorageId, this.workspace.value); focalStorage.setItem(`${this.textStorageId}_kind`, this.kind); } log(s) { this.logarea.value += s + ""; } async create() { // #TODO #META style and pre tags are problematic in Markdown scripts this.kind = this.kind === undefined ? await lively.confirm("MLE Workspace? Ok for yes, Cancel for No.") : this.kind; var style = document.createElement("style"); style.textContent = ` lively-code-mirror { border: 1px solid gray; flex: 4; }`; var buttons = <div> <button click={() => {this.onDoIt()} }>DoIt</button> <button click={() => {this.onReset()} }>reset</button> </div>; this.workspace = await (<lively-code-mirror></lively-code-mirror>); this.workspace.value = await this.loadText(); this.workspace.doitContext = this; this.logarea = <textarea disabled style="flex: 2;"/>; this.logarea.value = "LOGS"; this.drawarea = <div></div>; if(this.kind){ this.workspace.boundEval = async function(s) { this.socket = this.socket || await SocketSingleton.get(); this.socket.emit("test", {id: this.textStorageId, func: "evaluate", parameters: [s]}); const value = await new Promise((res) => { this.socket.on("result", r => { if(!r) return; if(r.id !== this.textStorageId) return; console.log(r.data) res(JSON.parse(r.data)); }); }); this.outData = value; Object.defineProperty(window, `$${this._id}`, {configurable: true, value}); this.log(JSON.stringify(value)); this.saveText(); return {value}; } this.workspace.boundEval = this.workspace.boundEval.bind(this); } enclosingDiv.appendChild( <div style="padding: 10px; width:90%;"> {style} <h4>{this.kind ? "MLE" : "DOM"} Workspace ${this._id}</h4> {buttons} <div style="display: flex;"> {this.workspace} {this.logarea} </div> {this.drawarea} </div> ); } } startUp().then(t => <div> <button click={() => new Pane().create()}>New workspace</button> {enclosingDiv} </div>)</script> </lively-script> |
| 4 | +<script> |
| 5 | +import focalStorage from 'src/external/focalStorage.js'; |
| 6 | +import {SocketSingleton} from 'src/components/mle/socket.js'; |
| 7 | +const idMap = new Map(); |
| 8 | +const enclosingDiv = <div />; |
| 9 | +var mle = {}; |
| 10 | +async function startUp(){ |
| 11 | + await SocketSingleton.get(); |
| 12 | + let i = 0; |
| 13 | + while(await focalStorage.getItem(`markdown_workspace_${i}`)){ |
| 14 | + await new Pane(i, await focalStorage.getItem(`markdown_workspace_${i}_kind`)).create(); |
| 15 | + i++; |
| 16 | + } |
| 17 | +} |
| 18 | +class Pane { |
| 19 | + constructor(id, kind){ |
| 20 | + if(id !== undefined){ |
| 21 | + this._id = id; |
| 22 | + this.kind = kind; |
| 23 | + } else { |
| 24 | + this._id = idMap.size; |
| 25 | + } |
| 26 | + idMap.set(this.textStorageId, this); |
| 27 | + } |
| 28 | + |
| 29 | + onDoIt() { |
| 30 | + this.saveText(); |
| 31 | + this.workspace.tryBoundEval(this.workspace.value); |
| 32 | + } |
| 33 | + |
| 34 | + async onReset() { |
| 35 | + if(this.kind) this.socket = await SocketSingleton.reset(); |
| 36 | + this.logarea.value = "LOGS"; |
| 37 | + this.drawarea.innerHTML = ""; |
| 38 | + } |
| 39 | + |
| 40 | + get defaultText() { |
| 41 | + return "sql`SELECT * FROM students`" |
| 42 | + } |
| 43 | + |
| 44 | + get textStorageId() { |
| 45 | + return `markdown_workspace_${this._id}` |
| 46 | + } |
| 47 | + |
| 48 | + async loadText() { |
| 49 | + var loaded = await focalStorage.getItem(this.textStorageId); |
| 50 | + if (loaded) return loaded; |
| 51 | + return this.defaultText; |
| 52 | + } |
| 53 | + |
| 54 | + async saveText() { |
| 55 | + focalStorage.setItem(this.textStorageId, this.workspace.value); |
| 56 | + focalStorage.setItem(`${this.textStorageId}_kind`, this.kind); |
| 57 | + } |
| 58 | + |
| 59 | + log(s) { |
| 60 | + this.logarea.value += s + ""; |
| 61 | + } |
| 62 | + |
| 63 | + async create() { |
| 64 | + // #TODO #META style and pre tags are problematic in Markdown scripts |
| 65 | + this.kind = this.kind === undefined ? await lively.confirm("MLE Workspace? Ok for yes, Cancel for No.") : this.kind; |
| 66 | + var style = document.createElement("style"); |
| 67 | + style.textContent = ` lively-code-mirror { border: 1px solid gray; flex: 4; }`; |
| 68 | + var buttons = <div> |
| 69 | + <button click={() => {this.onDoIt()} }>DoIt</button> |
| 70 | + <button click={() => {this.onReset()} }>reset</button> |
| 71 | + </div>; |
| 72 | + this.workspace = await (<lively-code-mirror></lively-code-mirror>); |
| 73 | + this.workspace.value = await this.loadText(); |
| 74 | + this.workspace.doitContext = this; |
| 75 | + this.logarea = <textarea disabled style="flex: 2;"/>; |
| 76 | + this.logarea.value = "LOGS"; |
| 77 | + this.drawarea = <div></div>; |
| 78 | + if(this.kind){ |
| 79 | + this.workspace.boundEval = async function(s) { |
| 80 | + this.socket = this.socket || await SocketSingleton.get(); |
| 81 | + this.socket.emit("test", { |
| 82 | + id: this.textStorageId, |
| 83 | + func: "evaluate", |
| 84 | + parameters: [s] |
| 85 | + }); |
| 86 | + const value = await new Promise((res) => { |
| 87 | + this.socket.on("result", r => { |
| 88 | + if(!r) return; |
| 89 | + if(r.id !== this.textStorageId) return; |
| 90 | + res(JSON.parse(r.data)); |
| 91 | + }); |
| 92 | + }); |
| 93 | + this.outData = value; |
| 94 | + Object.defineProperty(window, `$${this._id}`, {configurable: true, value}); |
| 95 | + this.log(JSON.stringify(value)); |
| 96 | + this.saveText(); |
| 97 | + return {value}; |
| 98 | + } |
| 99 | + this.workspace.boundEval = this.workspace.boundEval.bind(this); |
| 100 | + } |
| 101 | + |
| 102 | + enclosingDiv.appendChild(<div style="padding: 10px; width:90%;"> |
| 103 | + {style} |
| 104 | + <h4>{this.kind ? "MLE" : "DOM"} Workspace ${this._id}</h4> |
| 105 | + {buttons} |
| 106 | + <div style="display: flex;"> |
| 107 | + {this.workspace} |
| 108 | + {this.logarea} |
| 109 | + </div> |
| 110 | + {this.drawarea} |
| 111 | + </div>); |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +startUp().then(t => <div> |
| 116 | + <button click={() => new Pane().create()}>New workspace</button> |
| 117 | + {enclosingDiv} |
| 118 | +</div>) |
| 119 | +</script> |
| 120 | + |
| 121 | +Example Query: |
| 122 | +``` |
| 123 | +const projects = sql`SELECT * FROM projects where id < 20`.map( |
| 124 | + ({id, url}) => { |
| 125 | + const prs = sql`SELECT * FROM pull_requests where head_repo_id=${id}`.map(({id}) => { |
| 126 | + const events = sql`SELECT * FROM pull_request_history where pull_request_id=${id}`.reduce( |
| 127 | + ({opened, last}, {created_at, action}) => { |
| 128 | + if(action.startsWith("opened")){ |
| 129 | + if(!opened || opened > created_at) { |
| 130 | + return {opened: created_at, last}; |
| 131 | + } |
| 132 | + } |
| 133 | + if(action.startsWith("closed") || action.startsWith("merged")){ |
| 134 | + if(!last || last < created_at){ |
| 135 | + return {opened, last: created_at} |
| 136 | + } |
| 137 | + } |
| 138 | + return {opened, last}; |
| 139 | + }, {opened: false, last: false}); |
| 140 | + return events.last - events.opened; |
| 141 | + }).reduce(({count, sum}, curr) => ({count: count+1, sum: sum+curr}), {count: 0, sum: 0}); |
| 142 | + return {repo: url, avg: prs.sum/prs.count}; |
| 143 | + } |
| 144 | +).reduce((prev, curr) => { |
| 145 | + if(!curr.avg || curr.avg <0) return prev; |
| 146 | + if(prev.length < 3) return [...prev, curr]; |
| 147 | + const t = prev.sort((a, b) => a.avg-b.avg); |
| 148 | + if(t[0].avg < curr.avg){ |
| 149 | + [...t.slice(1), curr] |
| 150 | + } |
| 151 | + return t; |
| 152 | +}, []).map(({repo, ...t}) => ({...t, repo:repo.split("/").slice(4).join("/")})); |
| 153 | +projects |
| 154 | +``` |
0 commit comments