wiki-plugin-mech 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/mech.js +134 -11
- package/package.json +1 -1
package/client/mech.js
CHANGED
|
@@ -170,20 +170,72 @@
|
|
|
170
170
|
return trouble(elem,`"${arg}" doesn't name an item we can preview`)
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
-
const title = "Mech Preview"
|
|
173
|
+
const title = "Mech Preview" + (state.tick ? ` ${state.tick}` : '')
|
|
174
174
|
const page = {title,story}
|
|
175
|
-
const options = {}
|
|
175
|
+
const options = {$page:$(elem.closest('.page'))}
|
|
176
176
|
wiki.showResult(wiki.newPage(page), options)
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
function neighbors_emit ({elem,command,args,state}) {
|
|
180
|
+
const want = args[0]
|
|
181
|
+
if(state.debug) console.log({neighborhoodObject:wiki.neighborhoodObject})
|
|
182
|
+
const have = Object.entries(wiki.neighborhoodObject.sites)
|
|
183
|
+
.filter(([domain,site]) => !site.sitemapRequestInflight && (!want || domain.includes(want)))
|
|
184
|
+
.map(([domain,site]) => (site.sitemap||[])
|
|
185
|
+
.map(info => Object.assign({domain},info)))
|
|
186
|
+
state.neighborhood = have.flat()
|
|
187
|
+
.sort((a,b) => b.date - a.date)
|
|
188
|
+
elem.innerHTML = command + ` ⇒ ${state.neighborhood.length} pages, ${have.length} sites`
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function walk_emit ({elem,command,args,state}) {
|
|
192
|
+
const steps = Object.groupBy(walks(state.neighborhood),({graph})=>graph?'some':'none')
|
|
193
|
+
if(state.debug) console.log({steps})
|
|
194
|
+
const nodes = (steps.some||[]).map(({graph}) => graph.nodes).flat()
|
|
195
|
+
elem.innerHTML = command + ` ⇒ ${(steps.some||[]).length} aspects, ${(steps.none||[]).length} empty, ${nodes.length} nodes`
|
|
196
|
+
const item = elem.closest('.item')
|
|
197
|
+
item.classList.add('aspect-source')
|
|
198
|
+
item.aspectData = () => (steps.some||[])
|
|
199
|
+
state.aspect = [{div:item,result:steps.some}]
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function tick_emit ({elem,args,body,state}) {
|
|
203
|
+
if(elem.innerHTML.match(/button/)) return
|
|
204
|
+
if (!body?.length) return trouble(elem,'TICK expects indented blocks to follow.')
|
|
205
|
+
const count = args[0] || '1'
|
|
206
|
+
if (!count.match(/^[1-9][0-9]?$/)) return trouble(elem,"TICK expects a count from 1 to 99")
|
|
207
|
+
let clock = null
|
|
208
|
+
elem.innerHTML += '<button style="border-width:0;">◉</button>'
|
|
209
|
+
elem.querySelector('button').addEventListener('click',event => {
|
|
210
|
+
state.debug = event.shiftKey
|
|
211
|
+
if(clock){
|
|
212
|
+
clock = clearInterval(clock)
|
|
213
|
+
delete state.tick
|
|
214
|
+
} else {
|
|
215
|
+
state.tick = +count
|
|
216
|
+
run(body,state)
|
|
217
|
+
clock = setInterval(()=>{
|
|
218
|
+
if(state.debug) console.log({tick:state.tick})
|
|
219
|
+
if(--state.tick > 0)
|
|
220
|
+
run(body,state)
|
|
221
|
+
else
|
|
222
|
+
clock = clearInterval(clock)
|
|
223
|
+
},1000)
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
179
228
|
const blocks = {
|
|
180
|
-
CLICK:
|
|
181
|
-
HELLO:
|
|
182
|
-
FROM:
|
|
183
|
-
SENSOR: {emit:sensor_emit
|
|
184
|
-
REPORT: {emit:report_emit
|
|
185
|
-
SOURCE: {emit:source_emit
|
|
186
|
-
PREVIEW:
|
|
229
|
+
CLICK: {emit:click_emit},
|
|
230
|
+
HELLO: {emit:hello_emit},
|
|
231
|
+
FROM: {emit:from_emit},
|
|
232
|
+
SENSOR: {emit:sensor_emit},
|
|
233
|
+
REPORT: {emit:report_emit},
|
|
234
|
+
SOURCE: {emit:source_emit},
|
|
235
|
+
PREVIEW: {emit:preview_emit},
|
|
236
|
+
NEIGHBORS:{emit:neighbors_emit},
|
|
237
|
+
WALK: {emit:walk_emit},
|
|
238
|
+
TICK: {emit:tick_emit}
|
|
187
239
|
}
|
|
188
240
|
|
|
189
241
|
function run (nest,state={}) {
|
|
@@ -215,7 +267,7 @@
|
|
|
215
267
|
const lines = item.text.split(/\n/)
|
|
216
268
|
const nest = tree(lines,[],0)
|
|
217
269
|
const html = format(nest)
|
|
218
|
-
const state = {$item}
|
|
270
|
+
const state = {$item} // deprecated. use elem.closest('.item')
|
|
219
271
|
$item.append(`<div style="background-color:#eee;padding:15px;border-top:8px;">${html}</div>`)
|
|
220
272
|
run(nest,state)
|
|
221
273
|
}
|
|
@@ -260,7 +312,7 @@
|
|
|
260
312
|
function dotify(graph) {
|
|
261
313
|
const tip = props => Object.entries(props).filter(e => e[1]).map(e => `${e[0]}: ${e[1]}`).join("\\n")
|
|
262
314
|
const nodes = graph.nodes.map((node,id) => {
|
|
263
|
-
const label = `${node.type}\\n${node.props.name}`
|
|
315
|
+
const label = node.type ? `${node.type}\\n${node.props.name}` : node.props.name
|
|
264
316
|
return `${id} [label="${label}" ${(node.props.url||node.props.tick)?`URL="${node.props.url||'#'}" target="_blank"`:''} tooltip="${tip(node.props)}"]`
|
|
265
317
|
})
|
|
266
318
|
const edges = graph.rels.map(rel => {
|
|
@@ -275,5 +327,76 @@
|
|
|
275
327
|
'}'].join("\n")
|
|
276
328
|
}
|
|
277
329
|
|
|
330
|
+
// inspired by aspects-of-recent-changes/roster-graphs.html
|
|
331
|
+
function walks(neighborhood) {
|
|
332
|
+
const prob = n => Math.floor(n * Math.abs(Math.random()-Math.random()))
|
|
333
|
+
const rand = a => a[prob(a.length)]
|
|
334
|
+
const domains = neighborhood
|
|
335
|
+
.map(info => info.domain)
|
|
336
|
+
.filter(uniq)
|
|
337
|
+
return domains
|
|
338
|
+
.map(domain => {
|
|
339
|
+
const name = domain.split('.').slice(0,3).join('.')
|
|
340
|
+
const done = new Set()
|
|
341
|
+
const graph = new Graph()
|
|
342
|
+
let nid = 0
|
|
343
|
+
const here = neighborhood
|
|
344
|
+
.filter(info => info.domain==domain && ('links' in info))
|
|
345
|
+
if(!here.length) return {name,graph:null}
|
|
346
|
+
const find = slug => neighborhood.find(info => info.slug == slug)
|
|
347
|
+
const node = info => {
|
|
348
|
+
nid = graph.addNode('',{
|
|
349
|
+
name:info.title.replaceAll(/ /g,"\n"),
|
|
350
|
+
title:info.title,
|
|
351
|
+
site:domain,
|
|
352
|
+
links:Object.keys(info.links||{}).filter(slug => find(slug))})
|
|
353
|
+
return nid}
|
|
354
|
+
const rel = (here,there) => graph.addRel('',here,there)
|
|
355
|
+
const links = nid => graph.nodes[nid].props.links.filter(slug => !done.has(slug))
|
|
356
|
+
const start = rand(here)
|
|
357
|
+
done.add(start.slug)
|
|
358
|
+
node(start)
|
|
359
|
+
for (n=5;n>0;n--) {
|
|
360
|
+
try {
|
|
361
|
+
const slugs = links(nid)
|
|
362
|
+
const slug = rand(slugs)
|
|
363
|
+
done.add(slug)
|
|
364
|
+
const info = find(slug)
|
|
365
|
+
rel(nid,node(info))}
|
|
366
|
+
catch (e) {}
|
|
367
|
+
}
|
|
368
|
+
return {name,graph}
|
|
369
|
+
})
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// adapted from graph/src/graph.js
|
|
373
|
+
class Graph {
|
|
374
|
+
|
|
375
|
+
constructor(nodes=[], rels=[]) {
|
|
376
|
+
this.nodes = nodes;
|
|
377
|
+
this.rels = rels;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
addNode(type, props={}){
|
|
381
|
+
const obj = {type, in:[], out:[], props};
|
|
382
|
+
this.nodes.push(obj);
|
|
383
|
+
return this.nodes.length-1;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
addRel(type, from, to, props={}) {
|
|
387
|
+
const obj = {type, from, to, props};
|
|
388
|
+
this.rels.push(obj);
|
|
389
|
+
const rid = this.rels.length-1;
|
|
390
|
+
this.nodes[from].out.push(rid)
|
|
391
|
+
this.nodes[to].in.push(rid);
|
|
392
|
+
return rid;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
stringify(...args) {
|
|
396
|
+
const obj = { nodes: this.nodes, rels: this.rels }
|
|
397
|
+
return JSON.stringify(obj, ...args)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
}
|
|
278
401
|
|
|
279
402
|
}).call(this)
|