Created
October 8, 2020 14:05
-
-
Save dralletje/9ae74e22b535ee83382eb411178a913c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### A Pluto.jl notebook ### | |
# v0.11.14 | |
using Markdown | |
using InteractiveUtils | |
# ╔═╡ cf2958da-f8da-11ea-3e03-5fd8e443ce37 | |
html"""<script> | |
let firebase = await require.alias({ | |
"@firebase/app": "https://www.gstatic.com/firebasejs/6.3.1/firebase-app.js", | |
"@firebase/firestore": | |
"https://www.gstatic.com/firebasejs/6.3.1/firebase-auth.js", | |
"@firebase/database": | |
"https://www.gstatic.com/firebasejs/6.3.1/firebase-database.js" | |
})("@firebase/app", "@firebase/firestore", "@firebase/database") | |
var firebaseConfig = { | |
apiKey: "AIzaSyDjOnfUxXqV3mcunbn6z8bdztGXOMxTH3M", | |
authDomain: "plutojl.firebaseapp.com", | |
databaseURL: "https://plutojl.firebaseio.com", | |
projectId: "plutojl", | |
storageBucket: "plutojl.appspot.com", | |
messagingSenderId: "1096981201955", | |
appId: "1:1096981201955:web:187dfe8dc4f22602e2dd96", | |
measurementId: "G-0MQBLBDYX5" | |
}; | |
let app = firebase.initializeApp(firebaseConfig); | |
await app.auth().signInAnonymously() | |
window.app = app; | |
console.log('Logged in'); | |
""" | |
# ╔═╡ 91ce3b70-f8e1-11ea-25c6-07873559b55e | |
html""" | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script> | |
<script> | |
if (window.app == null) return html` | |
<pre>You have to reload this cell</pre> | |
` | |
let notebook_id = currentScript.closest('pluto-notebook').id; | |
let user_id = app.auth().currentUser.uid; | |
app.database().ref(`notebooks/${notebook_id}/users/${user_id}`).onDisconnect().remove() | |
invalidation.then(() => { | |
return; | |
let ref = app.database().ref(`notebooks/${notebook_id}/users/${user_id}`) | |
ref.remove() | |
ref.onDisconnect().cancel() | |
}) | |
let addEventListener = ({ target, event, handler, passive }) => { | |
target.addEventListener(event, handler, { passive: passive }) | |
invalidation.then(() => { | |
target.removeEventListener(event, handler) | |
}) | |
} | |
addEventListener({ | |
target: document, | |
event: "focusin", | |
passive: true, | |
handler: (e) => { | |
let cell_node = e.target.closest('pluto-cell'); | |
let notebook_id = cell_node.closest('pluto-notebook').id; | |
let cell_id = cell_node.id; | |
let user_id = app.auth().currentUser.uid; | |
app.database() | |
.ref(`notebooks/${notebook_id}/users/${user_id}`) | |
.update({ cell_in_focus: cell_id }); | |
}, | |
}) | |
addEventListener({ | |
target: window, | |
event: "scroll", | |
passive: true, | |
handler: _.debounce((e) => { | |
if (invalidation.isInvalidated) return | |
let notebook_id = currentScript.closest('pluto-notebook')?.id; | |
let user_id = app.auth().currentUser.uid; | |
if (notebook_id == null) return; | |
app.database() | |
.ref(`notebooks/${notebook_id}/users/${user_id}`) | |
.update({ | |
viewport: [ | |
document.documentElement.scrollTop, | |
document.documentElement.scrollTop + document.documentElement.clientHeight | |
] | |
}); | |
}, 500), | |
}) | |
let track_cell_state = _.debounce(() => { | |
if (invalidation.isInvalidated) return | |
let selection = document.getSelection(); | |
let cm_node = selection.anchorNode.closest('pluto-input > .CodeMirror') | |
let code_mirror = cm_node.CodeMirror | |
let start_cursor = code_mirror.getCursor(true) | |
let end_cursor = code_mirror.getCursor(false) | |
let cell_node = cm_node.closest('pluto-cell'); | |
let notebook_id = cell_node.closest('pluto-notebook').id; | |
let cell_id = cell_node.id; | |
let user_id = app.auth().currentUser.uid; | |
let changed_cells = [...document.querySelectorAll('pluto-cell.code_differs')].map(x => x.id) | |
console.log('changed_cells:', changed_cells) | |
app.database() | |
.ref(`notebooks/${notebook_id}/users/${user_id}`) | |
.update({ | |
changed_cells: changed_cells, | |
selection: { | |
cell: cell_id, | |
start: { line: start_cursor.line, column: start_cursor.ch }, | |
end: { line: end_cursor.line, column: end_cursor.ch }, | |
} | |
}); | |
}, 500) | |
addEventListener({ | |
target: document, | |
event: 'selectionchange', | |
passive: true, | |
handler: track_cell_state, | |
}) | |
addEventListener({ | |
target: document, | |
event: 'keyup', | |
passive: true, | |
handler: track_cell_state, | |
}) | |
addEventListener({ | |
target: document, | |
event: 'mouseup', | |
passive: true, | |
handler: track_cell_state, | |
}) | |
""" | |
# ╔═╡ c4f7460e-f8f5-11ea-1222-3768024237da | |
html""" | |
<style> | |
.styled-background { | |
background-color: red; | |
} | |
</style> | |
<script id="main"> | |
if (window.app == null) return html` | |
<pre>You need to reload this cell</pre> | |
` | |
let Preact = await import("https://cdn.jsdelivr.net/npm/htm@3/preact/standalone.module.js") | |
let renderComponent = (element = DOM.element('div'), component) => { | |
Preact.render(Preact.html`<${component} />`, element) | |
invalidation.then(() => { | |
Preact.render(null, element) | |
}) | |
return element | |
} | |
let onValue = ({ ref, onValue }) => { | |
let subscription = ref.on('value', snapshot => onValue(snapshot.val())) | |
invalidation.then(() => { | |
ref.off('value', subscription); | |
}) | |
} | |
let toRGB = (string) => { | |
var hash = 0; | |
for (let character of string) { | |
hash = character.charCodeAt(0) + ((hash << 5) - hash); | |
hash = hash & hash; | |
} | |
var rgb = [0, 0, 0]; | |
for (var i = 0; i < 3; i++) { | |
var value = Math.abs(((hash * i) & 255)); | |
rgb[i] = value; | |
} | |
return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, 0.2)`; | |
} | |
let { html: jsx, useEffect, useState, useRef } = Preact; | |
let notebook_id = currentScript.closest('pluto-notebook').id; | |
let element = this?.gutter_element ?? DOM.element('div') | |
// let element = DOM.element('div') | |
document.body.appendChild(element) | |
let MarkText = ({ cell, start, end, color }) => { | |
Preact.useEffect(() => { | |
let editor = document.getElementById(cell).querySelector(`.CodeMirror`).CodeMirror | |
let x = editor.markText( | |
{ line: start.line, ch: start.column }, | |
{ line: end.line, ch: end.column }, | |
{ css: `background-color: ${color}` } | |
); | |
return () => x.clear() | |
}, [cell, start.line, start.character, end.line, end.character]) | |
return null | |
} | |
let LockCell = ({ cell_id, color }) => { | |
Preact.useEffect(() => { | |
let cell_node = document.getElementById(cell_id) | |
let codemirror_node = cell_node.querySelector('.CodeMirror') | |
let cm_instance = codemirror_node.CodeMirror; | |
console.log('#10') | |
cm_instance.setOption("readOnly", true) | |
codemirror_node.style.backgroundColor = color | |
cm_instance.readOnly_listeners = (cm_instance.readOnly_listeners ?? 0) + 1 | |
return () => { | |
cm_instance.readOnly_listeners = cm_instance.readOnly_listeners - 1 | |
if (cm_instance.readOnly_listeners === 0) { | |
cm_instance.setOption("readOnly", false); | |
codemirror_node.style.backgroundColor = "" | |
} | |
} | |
}, [cell_id, color]) | |
return null; | |
} | |
let GUTTER_VERTICAL_MARGIN = 40; | |
let ViewportGutter = ({ viewport, title, color }) => { | |
return jsx` | |
<div | |
title=${title} | |
style=${{ | |
transition: 'transform .5s', | |
marginLeft: 5, | |
transform: `translateY(${viewport[0] + GUTTER_VERTICAL_MARGIN}px)`, | |
height: viewport[1] - viewport[0] - GUTTER_VERTICAL_MARGIN * 2, | |
width: 5, | |
backgroundColor: color, | |
}} | |
/> | |
` | |
} | |
renderComponent(element, () => { | |
let [notebook, set_notebook] = useState(null) | |
useEffect(() => { | |
onValue({ | |
ref: app.database().ref(`notebooks/${notebook_id}`), | |
onValue: (value) => { | |
window.requestAnimationFrame(() => { | |
set_notebook(value) | |
}) | |
}, | |
}) | |
}, []); | |
console.log('notebook:', notebook) | |
return jsx` | |
<div | |
style=${{ | |
position: 'absolute', | |
top: 0, bottom: 0, | |
left: 0, | |
display: 'flex', | |
flexDirection: 'row', | |
overflowY: 'hidden', | |
}} | |
> | |
${Object.entries(notebook?.users ?? {}) | |
.filter(([id, user]) => id !== app.auth().currentUser.uid) | |
.map(([id, user]) => jsx` | |
<${Preact.Fragment} key=${id}> | |
${user.viewport && jsx` | |
<${ViewportGutter} | |
key={id} | |
title={id} | |
viewport=${user.viewport} | |
color=${toRGB(id)} | |
/> | |
`} | |
${(user.changed_cells ?? []).map(cell_id => jsx` | |
<${LockCell} | |
key=${cell_id} | |
cell_id=${cell_id} | |
color=${toRGB(id)} | |
/> | |
`)} | |
${user.selection && jsx` | |
<${MarkText} | |
...${user.selection} | |
color=${toRGB(id)} | |
/> | |
`} | |
</${Preact.Fragment}> | |
`)} | |
</div> | |
` | |
}) | |
let return_element = html` | |
<pre>Running UI</pre> | |
` | |
return_element.gutter_element = element | |
return return_element | |
""" | |
# ╔═╡ Cell order: | |
# ╠═cf2958da-f8da-11ea-3e03-5fd8e443ce37 | |
# ╠═91ce3b70-f8e1-11ea-25c6-07873559b55e | |
# ╠═c4f7460e-f8f5-11ea-1222-3768024237da |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment