fedwiki / wiki-client

Federated wiki client-side javascript as a npm module.
Other
117 stars 38 forks source link

could/should pageHandler emit an event announcing a put? #84

Open judell opened 9 years ago

judell commented 9 years ago

I am in the very early stage of exploring a plugin that inspects and interacts with the journal. It is modeled on the Activity plugin in that there is nothing in emit() and page-modifying code in bind(). For now, like Activity, it listens for new-neighbor-done, but it would rather react to a page edit. The DOMSubtreeModified event would be a standard way to do that but it does not seem to be well standardized. Could/should Wiki fire an event announcing an page edit? If so, might pageHandler's put method(s) be an appropriate source for such notification?

WardCunningham commented 9 years ago

http://ward.fed.wiki.org/view/three-layer-storage This doesn't yet address events. I would be interested to know what you would find useful and especially if there are common conventions.

judell commented 9 years ago

I don't have a general theory of how events could or should work in FW. I've only gotten this far: https://gist.github.com/judell/632cc730c689e1c3572a. It's a plugin, provisionally called Inspector, that for now just enumerates the paragraphs on its own wiki page. I'd like to get to the idea discussed here -- http://blog.jonudell.net/2014/12/30/federated-wiki-for-teaching-and-learning-basic-composition/ -- namely, a way to observe and discuss versions of a paragraph. The plugin could also just read the journal, and to display paragraph history will have to, but I began with DOM exploration because the sequence of paragraphs (with ids) is lying around, and I wanted to get a feel for interacting with the active wiki page.

Many questions remain unanswered. First, how to load this thing? It's modeled on the Activity plugin. I found I could drag one of those into a page, then edit the page's source and rename to Inspector to run my code. I guess it should have some visual representation even when empty/inactive? Even so, I wonder (as did FWH participants) about creating an Activity (or Reference) de novo versus dragging an instance from elsewhere.

Second, how to package this thing? Whether CoffeeScript or JavaScript there's a set of learnings around making a FW Node module that I don't have yet. So for now I'm just hacking this into a locally deployed server's wiki-plugin-inspector subdirectory.

Third, how to activate this thing. For now I just go into the debugger and type $('body').trigger('dom-change'). What the actual name of the event should be, and from whence it should emanate, dunno. I just imagine that completing an edit action within a wiki page within a FW page is a significant enough event in the life of the wiki page to warrant an announcement by JS eventing.

Fourth (not related to this issue but since I'm here) what's the deal with debugging this stuff? Where does client.max.js come from, and why (in Chrome) does it seem to only be used sometimes?

WardCunningham commented 9 years ago

You have a good start. You are asking all the right questions.

You're taking a "bare metal" approach which is good for understanding how things work. Let me suggest some steps to get your plugin on the screen.

    emit: function($item, item) {
        $item.append($("<p>Hello World</p>"));
    }, 

At this point the server should be able to serve your javascript whenever you visit the About Inspector Plugin page. The plugin will say hello. You might fix it to do its inspecting thing on click. Its easy and leaves you in control of when it runs. Later you can make it fully interactive for a better experience.

You should do the bare metal thing at least once for the experience but rest asured there is a better way. We've been using CoffeeScript for authoring code, Grunt for our build scripts, Mocha for build-time testing, and Federated Wiki for our documentation. There is a script that will set all of this up for you with one command: mkplugin.sh.

http://plugins.fed.wiki.org/view/welcome-visitors/view/about-plugins/view/make-plugin-script

I used this script last weekend to make the new Grep plugin. You can see how I went from working plugin to working plugin with each commit. By the third commit I had it doing something useful and I used it for an hour or two before I resumed coding the generalized version of Grep.

https://github.com/fedwiki/wiki-plugin-grep/commits/master

You will notice that even with this automation I still left a few things out that Paul fixed up for me. I've revised the script with the latest dev dependencies to match what Paul set up.

The html that gets the whole app running is served by wiki-node-server from it's views subdirectory.

https://github.com/fedwiki/wiki-node-server/blob/master/views/static.html

You'll want a dev copy of that installed too while developing. You can edit this html to load the un-minified version of client which helps when single stepping. I use chrome mostly but also debug in Firefox and Safari on occasion. I read Google's documentation on the chrome inspector once which made the whole debugging experience easier. @paul90 actually has a better handle on how all this works so he is a good one to ping for details. @nrn lives an breaths javascript so if you think things should be done differently he will agree. We're still catching up to the vision he laid out two years ago.

judell commented 9 years ago

Thanks Ward!

Stashing this here for future reference:

mkplugin.sh: https://gist.github.com/WardCunningham/fced775fcedcee133b32

judell commented 9 years ago

Forgot to ask: do you debug the server with WebStorm? node-inspector? Something else? I've used WebStorm before, don't have a license at the moment. node-inspector seems like it might work but haven't gotten it going yet.

Update: never mind, got node-inspector to work.

judell commented 9 years ago

I'm abusing this issue as a way of continuing the conversation in a public way vs. email.

Today I published an npm module, wiki-plugin-inspector, that seems to meet the minimum bar. (In other words, I can nuke it from wiki\node_modules, run npm install wiki-plugin-inspector, and it works.) Rough edges include:

When you add it, you have to type some scratch text in the edit box or it goes away. The scratch text is later ignored, so this is rather confusing to the poor user (and anyone who looks at the journal where the scratch text remains). What's the better way I haven't discovered?

I'm still depending on the new-neighbor-done event. For now, though, I'll only react to that once. If the page changes, you need to refresh the page to refresh the plugin. I tried linking the title to the plugin's core behavior but that didn't smell right, I think if I'm mucking around in the DOM I should be doing it only from bind() but in response to some other event as discussed above. But: please educate me!

Except by dragging to a scratch page

There is an npm wrapper, diff-match-patch-node. But I wanted to first understand the basics of a simple npm module before attempting a dependent submodule, so for now I am just embedding the code in the plugin.

WardCunningham commented 9 years ago

I don't think you want to run the textEditor since you have no text to edit.

The Factory menu assumes you are looking for some kind of markup. You should instantiate your plugin some other way. The mkplugin.sh script creates one for you and adds it to the about page. You can copy that one to each page you want to inspect. Shift-drag to copy instead of move. (Ignore that the visual feedback still looks like a move.)

You could smooth this activity for students by creating a template that already has the Inspector installed and maybe more instructions. When ever a page is created, templates are offered as an alternative to blank pages.

WardCunningham commented 9 years ago

Regarding refresh, you have all the capabilities of the dom at your disposal.

We've experimented with several protocols. The most advanced so far has plugins advertise capabilities by adding classes to the $item. Then any other plugin that finds that capability interesting can use what ever javascript protocol is implied by the declared class. This lets plugins cooperate without the knowledge of the core javascript.

The best example of this is in this video: http://video.fed.wiki.org/22-things-txtzyme-websocket-plugins.html

The D3 Line plugin is old and as a consequence understands multiple protocols. This makes reading the connection logic a little harder. Here is where if finds the Txtzyme plugin as its preferred data source. https://github.com/fedwiki/wiki-plugin-line/blob/master/client/line.coffee#L25-L30

This particular data source can provide updates to the data. It announces this change as a new 'sequence'. This is where the D3 Line subscribes to these updates. https://github.com/fedwiki/wiki-plugin-line/blob/master/client/line.coffee#L105-L117

The Txtzyme plugin sends this event when it has framed up enough datapoints from the attached hardware to make a complete sequence. This is handled here: https://github.com/fedwiki/wiki-plugin-txtzyme/blob/master/client/txtzyme.coffee#L133-L139

WardCunningham commented 9 years ago

Dragging unwanted items to a scratch page works.

I'd like to add a little icon in the footer for opening a scratch page titled Trash. This icon could also be a drop zone for items that would short-cut opening the Trash page. One can empty the Trash with fork-from-history of the create.

judell commented 9 years ago

Roger all that, thanks again Ward.

It instantiates now by shift-drag as documented on the about page.

Emit injects the plugin's title.

Bind wires hovering on the plugin's title to the refresh. So it's only done now when the user asks, and the user can ask repeatedly. An added advantage is the hover collapses any nodes that were expanded.

WardCunningham commented 9 years ago

I looked through your source on github and didn't see an obvious thing to test. Your functions are interacting with the wiki dom or interacting with the diff library. I'd guess if this worked once, it would work every time.

Exception handlers can get in the way of debugging. The core rendering wraps your emit and bind functions with an error handler that is pretty ok and gives both users and developers a next step.

judell commented 9 years ago

I realized that binding the hover event to the whole title makes it impossible to drag the title from about-inspector-plugin, which broke the new approach to instantiation. So I added a (refresh) cue and bound hover to that.

Then I discovered this interesting wrinkle:

http://wiki.elmcity.info/view/welcome-visitors/view/about-inspector-plugin

The plugin on the left was made by shift-dragging from the right. Now there are two elements in the page with the same data-id, which the plugin depends on. Hilarity ensues!

WardCunningham commented 9 years ago

Jon, you're nipping at my heals.

87 is a fix to the duplicate id problem.

judell commented 9 years ago

Sweet!

FWIW, it would be nice to soften the distinction between plugins that launch from the factory and ones that don't. The latter are much harder for people to discover. That's why I tried to put the inspector into the factory even though it was a poor fit for that mechanism.

I still don't understand the protocol well enough to see if there's a reasonable way to represent an instantiating plugin as something other than an edit box.

An alternative might be to add a section to the factory that lists plugins that instantiate from their about pages, and links their names to their about pages.

WardCunningham commented 9 years ago

Yes, that section should be labeled more.

There is an endpoint that will list the plugins installed in any given server. http://asia.wiki.org/system/plugins.json

The more selection should read all the about pages, present the synopsis for each possibility, and when one is selected, find the example from the about page and copy it in place of the factory.

There are cases where this will fail such as the new Grep plugin where every example is expensive. The fix here is to make a query that is not expensive and make that be the example.

judell commented 9 years ago

Yep, that'd be great. An added benefit would be more scrutiny of each plugin's synopsis.

judell commented 9 years ago

I made a start on this.

Here you can see me fumbling around in the client, and eventually punting to the server:

https://github.com/judell/wiki-client/blob/master/client.js#L561

I was working directly in client.max.js because I wanted to understand how it's constructed and I wanted to have access to everything in one place. It was an opportunity to trace through all kinds of things in the client, and I learned a lot about what pieces of machinery exist, but do not yet have an adequate theory of operation. The location.href way of getting at the server-generated page is, I'm sure, wrong.

Here you can see me fumbling around in the server:

https://github.com/judell/wiki-server/blob/master/server.coffee#L539

This feels like a good place to generate an all-plugins page. It won't change after server startup, right? And it kinda sorta works. Except for all kinds of issues, including:

I think your idea will work nicely in the final analysis, and I will learn a lot grinding through the details.

WardCunningham commented 9 years ago

I like your implementation for several reasons. You add more to the factory menu with a small modification that accesses a whole page of plugin descriptions. You also construct that page of descriptions server side so that the enumeration of pages happens close to the content being enumerated. I wish I could see a picture of that page but I do get an idea of what it looks like from reading the code.

I'm also impressed that you're finding your way through the multiple repos and inserting cooperating functions in places that happen at distinctly different times. (Gone are the days when a program has a beginning, middle and end, run in that order.)

Let me suggest a slightly different organization for this same logic. You've created some dependencies that I think we can avoid. I'll give up some of the efficiency you've achieved in order to suggest something that can be built with mechanisms we have in place today. We can restore that efficiency with work we have planned. Until then, set efficiency aside while we localize responsibilities.

Organize this code as the Plugins plugin, known elsewhere as wiki-plugin-plugins. This would be the one plugin that describes other plugins. Plugins would include several pages. One would be About Plugins Plugin which would explain the breadth of what the plugin can do. The other would be All Available Plugins which would be linked to at convenient places under the site administrator's and future author's control. The prototype here is Recent Changes which is served out of the documentation pages of the Activity plugin.

The Plugins plugin running client-side is now free to put whatever it wants into the DOM without depending on the existence of the HTML plugin to do it. Incremental search perhaps? Or some override of the normal drag logic to make using plugins easier?

The server-side responsibility is only to deliver the plugin when asked. The server can be just static pages (with one exception that we will eventually remove.) If the server chooses to implement /system/sitemap.json or /system/plugins.json as dynamic content, that is solely for the convenience of the site operator. This is to the advantage of small read-only servers like Wikiduino.

Adding the more option to the Factory plugin will require a bit of care to make sure it is done in some way that will work for all servers, no matter how they are configured. It could just try to load the Plugins plugin before offering to open its pages. This caution is required because Factory is a core plugin that has to work everywhere.

judell commented 9 years ago

I agree that a client-side solution is best. What you see here was born mainly not of a desire for efficiency but rather of frustration trying to make it work client-side, plus curiosity about the division of responsibility between client and server. The exercise got me to step through interactions that span client and server and learn more about how things fit together.

I think I'm now ready for a focused exercise, during the weekly hangout or some other time, in which you (and others?) can help me make more sense of the client-side machinery. I found a half dozen different ways to construct and show the page in the client (fragments of which I left lying around in a comment as a memory trail) but none succeeded in making it a well-behaved wiki page properly integrated with the lineup, with storage, and with the browser's URL line.

Thanks!