wiki-plugin-mech 0.1.5 → 0.1.6

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.
Files changed (2) hide show
  1. package/client/mech.js +134 -11
  2. 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
+ 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: {emit:click_emit, bind:null},
181
- HELLO: {emit:hello_emit, bind:null},
182
- FROM: {emit:from_emit, bind:null},
183
- SENSOR: {emit:sensor_emit, bind:null},
184
- REPORT: {emit:report_emit, bind:null},
185
- SOURCE: {emit:source_emit, bind:null},
186
- PREVIEW: {emit:preview_emit, bind:null}
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-plugin-mech",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Federated Wiki - Mechanism Scripting Plugin",
5
5
  "keywords": [
6
6
  "mech",