ScrapJupyter
ScrapJupyter
https://gyazo.com/9548fb3de791020b5916c027488fd0c8
jupyter notebookみたいなので、ScrapJupyterという名前にした
cosenseに名前変わるし、あとjupyter notebook触らなくなったのでやっぱり名前変えようかなmiyamonz.icon
木星要素無いしね
ただ単にevalしてるだけなので自己責任でお願いします
しかし、それはそっくりそのままUserScriptを使う上での危険性と同じなので、わざわざリマインドすることでもないかもしれない
2021/3頃に書いたものからの変更 2024/10/16
log関数を用意して、その場で横に表示できるようにしました
その他いろいろ変更しました
自動実行あたりの設定の仕様をもうちょっと良くしたい
code:script.js
(()=>{
const title = 'ScrapJupyter'
const debug = (...args) => {
//console.log(title, ...args)
}
// 独自log, 右側に表示できる divid .cody-bodyに突っ込む const __log = (id) => (arg) => {
$([id=L${id}] .code-body [data-${title}-log]).remove()
const text = JSON.stringify(arg)
const result = $(<code data-${title}-log>)
.css('background', 'lightyellow')
.append( => ${text} )
const $icon = $('<i class="far fa-copy">').appendTo(result)
$icon.click(()=>{ navigator.clipboard.writeText(text) })
$([id=L${id}] .code-body).append(result)
//console.log(id, arg)
}
window.__log = __log;
const transpileLog = (line) => {
return line.text.replace(/^(\s)*log/, __log("${line.id}"))
}
// 現在のPageからcode blockの配列を作る
function getCodeBlocks() {
const lines = scrapbox.Page.lines ?? [];
return lines.reduce( (acc,line) => {
if(line?.codeBlock?.lang !== "js") return acc
if(line?.codeBlock?.start) {
}
if(line?.codeBlock) {
const text = transpileLog(line)
}
return acc;
},[])
}
const createButton = () => {
const r = $(<div data-${title}-button>)
.css({
'position': 'absolute',
'border': "solid 1px",
'border-radius': '50%',
'text-align': 'center',
'font-size': 'large',
})
.css({left: -40, top:0, 'z-index': 900})
.width(30).height(30)
.on('mousedown',function(){
$(this).css('color','red')
})
.on('mouseup',function(){
$(this).css('color','black')
})
.text("▶")
return r
}
//ボタンは divid .code-startの下に突っ込む const $button = (id) => $([id="L${id}"] .code-start [data-${title}-button])
const $buttons = () => $([id] .code-start [data-${title}-button])
const getOrCreateButton = (id) => {
const found = $button(id)
return found.length ? found : createButton()
}
const removeAllButtons = () => {
debug('removeAllButtons')
$buttons().remove()
}
function appendButtons() {
getCodeBlocks().forEach( block => {
const { id } = block
const b = getOrCreateButton(id)
b.click(()=>{run(block)})
$([id=L${id}] .code-start).append(b)
})
}
function run(block) {
debug('run', block)
const b = getOrCreateButton(block.id)
try {
(1,eval)(block.content)
b.css("background", "lightgreen")
return true
} catch (e) {
console.error(title, "eval error", block, e);
b.css("background", "red")
return false
}
}
function runAll() {
debug('runAll start')
for(const block of getCodeBlocks()){
const ok = run(block)
if(!ok) break;
}
debug('runAll end')
}
// disable when project is changed
const currentProject = scrapbox.Project.name;
function disableWhenProjectChange(){
if (scrapbox.Project.name !== currentProject) _disable()
}
function _enable() {
appendButtons()
scrapbox.on('lines:changed', appendButtons)
//scrapbox.on('lines:changed', runAll)
scrapbox.on('project:changed', disableWhenProjectChange)
}
function _disable(remove=true) {
if(remove) removeAllButtons()
scrapbox.off('lines:changed', appendButtons)
scrapbox.off('lines:changed', runAll)
scrapbox.off('project:changed', disableWhenProjectChange)
}
function loadScript() {
scrapbox.PageMenu.addMenu({
title,
image: off,
onClick: runAll
})
_enable()
}
// debug
const cache = (window.miyamonz ??= {})title ??= {}; cache.prev?.()
loadScript()
cache.prev = () => _disable(false)
})()
中身の解説
scrapbox.Page.linesの情報から無理やりコードを復元してるだけ
コードブロックのパーツを表すlineにはcodeBlockという変数が入ってる
その下にstart, end, lang, textあたりの情報が入ってる
start →コードブロックの先頭か否か
end →コードブロックの末端か否か
lang →コードの言語(javascriptとか)
text →その行のテキスト
start, endを見ながらtextを結合してるだけ
start見つけたら新規作成して、都度textを結合、endが見えたら終了
面倒だからindentの空白もtrimしてないよ 多分動くっしょ
eval
(1,eval)('hoge = 1')
これでwindow.hogeに入る
なんで?miyamonz.icon
特殊なlog関数を用意してる
実行前に行idを注入してる
いわばトランスパイル。単に文字列としていじってるだけだけど
引数に渡した中身をcosenseのエディタ上に表示してる
https://gyazo.com/1ca2eb1194ab5b90ef7666fe7a5a0996
以下考察
evalじゃない方法を考える
普通にapi経由で呼び出しだと、ファイル名が必要 & 同名は結合されて1つのファイルとなる
これはscrapboxの仕様
なので、ファイル名ごとに実行ボタンを配置するとかはたぶん可能 やってないけど
いつの間にか無名ファイルにもapiで取れるようになったので、実はそっちのほうがいいかも
全部に名前はつけたくないけど、個別のブロックごとに実行したいなら現状の方が良い
evalじゃないとwindowに変数や関数が保存できない
発展型
他にも、適当にcreateCanvasしたりしてp5.jsとか動かしたら楽しいかも? js実行するためにevalしたけど、なにか別のlispだとか, js上で処理できる言語を実行するのいいかも?
chrome拡張かなにかで外部に投げてみたい
http postすれば拡張も要らない?
scprapboxをなんかターンテーブルというか素材置場として、外部アプリに情報送信してDJみたいな感じ?をイメージしてる
雑に遊ぶ
code:js
jsなので動的に動けるとかはある
code:js
const n = Date.now()
window.open(${n})
Userscriptとして読み込むほどでもないものを直ちに実行したいときとかは便利かもしれない
まず突然コードブロックを書いてバシバシ実行しながらuserscriptを書く
いい感じになってきたらちゃんとimportして動くようにする、みたいな?
userscript自作するときに、書いたら即実行できるのはかなり便利