Status: | Merged |
---|---|
Approved by: | Paul Hummer |
Approved revision: | 20 |
Merged at revision: | 8 |
Proposed branch: | lp:~rockstar/phazr/editor |
Merge into: | lp:phazr |
Diff against target: |
479 lines (+430/-1) 6 files modified
assets/cancel.svg (+90/-0) assets/close.svg (+90/-0) assets/ok.svg (+90/-0) examples/editableplugin/index.html (+70/-0) src/css/phazr.css (+5/-1) src/js/editableplugin/editableplugin.js (+85/-0) |
To merge this branch: | bzr merge lp:~rockstar/phazr/editor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Deryck Hodge | Pending | ||
Review via email: mp+59869@code.launchpad.net |
Commit message
Add inline editor plugin
Description of the change
This branch creates a Node plugin that makes any text node editable. Try out the example.
One thing it doesn't do currently is provide buttons for save/cancel. On the single line editor, you can hit enter. The multiline editor doesn't provide a way to save just yet. If it did, at least there's a way to handle it.
I'm pushing it now to get opinions on implementing this as a plugin rather than a full blown widget.
Deryck Hodge (deryck) wrote : | # |
Paul Hummer (rockstar) wrote : | # |
Finally got around to fixing this. There are new graphics (and the SVG versions of them as well). I'm trying to get some better graphics out of design. When that happens, then I'll replace them again. The other images were just too horrible. At least these are simple.
As always, test it out, tell me what to fix.
Deryck Hodge (deryck) wrote : | # |
Some thoughts at a quick poke....
The graphics look nice. I see why you want nice ones, but these aren't bad as generic ones.
In terms of the widget itself:
* The text still reflows to input element, rather than preserving the look of the page
* Linebreaks are ignored in multi-line when adding the text back to the DOM
For this last one, we rely on html response back from the server to update the DOM correctly. but that always felt wrong to me. It seems like the widget ought to be responsible for updating the DOM correctly. What do you think?
Not sure what you consider important for release. I think the first point above is very important, but we can talk more offline about that.
Paul Hummer (rockstar) wrote : | # |
I need to figure out what's going on with the line break thing. I don't want to do too many hackery things to it, especially if someone would prefer to bounce it off the server (maybe we should escape HTML, but a XSS against yourself seems a bit silly.
I think the reflow issue needs to be left up to the implementer to fix. I'm not sure if anyone is even going to use this, and if they do, I don't want the CSS to be so specific that it makes their lives harder (and thus eliminating what phazr is really for).
Preview Diff
1 | === added directory 'assets' |
2 | === added file 'assets/cancel.svg' |
3 | --- assets/cancel.svg 1970-01-01 00:00:00 +0000 |
4 | +++ assets/cancel.svg 2011-05-28 21:21:33 +0000 |
5 | @@ -0,0 +1,90 @@ |
6 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
7 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
8 | + |
9 | +<svg |
10 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
11 | + xmlns:cc="http://creativecommons.org/ns#" |
12 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
13 | + xmlns:svg="http://www.w3.org/2000/svg" |
14 | + xmlns="http://www.w3.org/2000/svg" |
15 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
16 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
17 | + width="128" |
18 | + height="128" |
19 | + id="svg2985" |
20 | + version="1.1" |
21 | + inkscape:version="0.48.1 r9760" |
22 | + sodipodi:docname="close.svg"> |
23 | + <defs |
24 | + id="defs2987" /> |
25 | + <sodipodi:namedview |
26 | + id="base" |
27 | + pagecolor="#ffffff" |
28 | + bordercolor="#666666" |
29 | + borderopacity="1.0" |
30 | + inkscape:pageopacity="0.0" |
31 | + inkscape:pageshadow="2" |
32 | + inkscape:zoom="2.8284271" |
33 | + inkscape:cx="61.841334" |
34 | + inkscape:cy="80.280491" |
35 | + inkscape:current-layer="g3788" |
36 | + showgrid="true" |
37 | + inkscape:document-units="px" |
38 | + inkscape:grid-bbox="true" |
39 | + inkscape:window-width="1280" |
40 | + inkscape:window-height="702" |
41 | + inkscape:window-x="0" |
42 | + inkscape:window-y="0" |
43 | + inkscape:window-maximized="0" /> |
44 | + <metadata |
45 | + id="metadata2990"> |
46 | + <rdf:RDF> |
47 | + <cc:Work |
48 | + rdf:about=""> |
49 | + <dc:format>image/svg+xml</dc:format> |
50 | + <dc:type |
51 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
52 | + <dc:title></dc:title> |
53 | + </cc:Work> |
54 | + </rdf:RDF> |
55 | + </metadata> |
56 | + <g |
57 | + id="layer1" |
58 | + inkscape:label="Layer 1" |
59 | + inkscape:groupmode="layer" |
60 | + transform="translate(0,64)"> |
61 | + <path |
62 | + sodipodi:type="arc" |
63 | + style="fill:none;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" |
64 | + id="path2993" |
65 | + sodipodi:cx="63.090908" |
66 | + sodipodi:cy="64" |
67 | + sodipodi:rx="55.454544" |
68 | + sodipodi:ry="54.18182" |
69 | + d="M 118.54545,64 A 55.454544,54.18182 0 1 1 7.636364,64 55.454544,54.18182 0 1 1 118.54545,64 z" |
70 | + transform="matrix(1.0721871,0,0,1.0973726,-3.6452576,-70.231844)" /> |
71 | + <g |
72 | + id="g3788" |
73 | + transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,20.695964,43.304036)"> |
74 | + <rect |
75 | + ry="3" |
76 | + rx="4.5" |
77 | + y="-50" |
78 | + x="54" |
79 | + height="100" |
80 | + width="15" |
81 | + id="rect3763" |
82 | + style="fill:#a40000;fill-opacity:1;stroke:none" /> |
83 | + <rect |
84 | + transform="matrix(0,1,-1,0,0,0)" |
85 | + style="fill:#a40000;fill-opacity:1;stroke:none" |
86 | + id="rect3786" |
87 | + width="15" |
88 | + height="100" |
89 | + x="-7.5" |
90 | + y="-111.5" |
91 | + rx="4.5" |
92 | + ry="3" /> |
93 | + </g> |
94 | + </g> |
95 | +</svg> |
96 | |
97 | === added file 'assets/close.svg' |
98 | --- assets/close.svg 1970-01-01 00:00:00 +0000 |
99 | +++ assets/close.svg 2011-05-28 21:21:33 +0000 |
100 | @@ -0,0 +1,90 @@ |
101 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
102 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
103 | + |
104 | +<svg |
105 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
106 | + xmlns:cc="http://creativecommons.org/ns#" |
107 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
108 | + xmlns:svg="http://www.w3.org/2000/svg" |
109 | + xmlns="http://www.w3.org/2000/svg" |
110 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
111 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
112 | + width="128" |
113 | + height="128" |
114 | + id="svg2985" |
115 | + version="1.1" |
116 | + inkscape:version="0.48.1 r9760" |
117 | + sodipodi:docname="close.svg"> |
118 | + <defs |
119 | + id="defs2987" /> |
120 | + <sodipodi:namedview |
121 | + id="base" |
122 | + pagecolor="#ffffff" |
123 | + bordercolor="#666666" |
124 | + borderopacity="1.0" |
125 | + inkscape:pageopacity="0.0" |
126 | + inkscape:pageshadow="2" |
127 | + inkscape:zoom="2.8284271" |
128 | + inkscape:cx="57.068363" |
129 | + inkscape:cy="80.280491" |
130 | + inkscape:current-layer="g3788" |
131 | + showgrid="true" |
132 | + inkscape:document-units="px" |
133 | + inkscape:grid-bbox="true" |
134 | + inkscape:window-width="1280" |
135 | + inkscape:window-height="702" |
136 | + inkscape:window-x="0" |
137 | + inkscape:window-y="0" |
138 | + inkscape:window-maximized="0" /> |
139 | + <metadata |
140 | + id="metadata2990"> |
141 | + <rdf:RDF> |
142 | + <cc:Work |
143 | + rdf:about=""> |
144 | + <dc:format>image/svg+xml</dc:format> |
145 | + <dc:type |
146 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
147 | + <dc:title></dc:title> |
148 | + </cc:Work> |
149 | + </rdf:RDF> |
150 | + </metadata> |
151 | + <g |
152 | + id="layer1" |
153 | + inkscape:label="Layer 1" |
154 | + inkscape:groupmode="layer" |
155 | + transform="translate(0,64)"> |
156 | + <path |
157 | + sodipodi:type="arc" |
158 | + style="fill:none;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" |
159 | + id="path2993" |
160 | + sodipodi:cx="63.090908" |
161 | + sodipodi:cy="64" |
162 | + sodipodi:rx="55.454544" |
163 | + sodipodi:ry="54.18182" |
164 | + d="M 118.54545,64 A 55.454544,54.18182 0 1 1 7.636364,64 55.454544,54.18182 0 1 1 118.54545,64 z" |
165 | + transform="matrix(1.0721871,0,0,1.0973726,-3.6452576,-70.231844)" /> |
166 | + <g |
167 | + id="g3788" |
168 | + transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,20.695964,43.304036)"> |
169 | + <rect |
170 | + ry="3" |
171 | + rx="4.5" |
172 | + y="-50" |
173 | + x="54" |
174 | + height="100" |
175 | + width="15" |
176 | + id="rect3763" |
177 | + style="fill:#646464;fill-opacity:1;stroke:none" /> |
178 | + <rect |
179 | + transform="matrix(0,1,-1,0,0,0)" |
180 | + style="fill:#646464;fill-opacity:1;stroke:none" |
181 | + id="rect3786" |
182 | + width="15" |
183 | + height="100" |
184 | + x="-7.5" |
185 | + y="-111.5" |
186 | + rx="4.5" |
187 | + ry="3" /> |
188 | + </g> |
189 | + </g> |
190 | +</svg> |
191 | |
192 | === added file 'assets/ok.svg' |
193 | --- assets/ok.svg 1970-01-01 00:00:00 +0000 |
194 | +++ assets/ok.svg 2011-05-28 21:21:33 +0000 |
195 | @@ -0,0 +1,90 @@ |
196 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
197 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
198 | + |
199 | +<svg |
200 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
201 | + xmlns:cc="http://creativecommons.org/ns#" |
202 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
203 | + xmlns:svg="http://www.w3.org/2000/svg" |
204 | + xmlns="http://www.w3.org/2000/svg" |
205 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
206 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
207 | + width="128" |
208 | + height="128" |
209 | + id="svg2985" |
210 | + version="1.1" |
211 | + inkscape:version="0.48.1 r9760" |
212 | + sodipodi:docname="ok.svg"> |
213 | + <defs |
214 | + id="defs2987" /> |
215 | + <sodipodi:namedview |
216 | + id="base" |
217 | + pagecolor="#ffffff" |
218 | + bordercolor="#666666" |
219 | + borderopacity="1.0" |
220 | + inkscape:pageopacity="0.0" |
221 | + inkscape:pageshadow="2" |
222 | + inkscape:zoom="2.8284271" |
223 | + inkscape:cx="68.165161" |
224 | + inkscape:cy="54.161237" |
225 | + inkscape:current-layer="g3788" |
226 | + showgrid="true" |
227 | + inkscape:document-units="px" |
228 | + inkscape:grid-bbox="true" |
229 | + inkscape:window-width="1280" |
230 | + inkscape:window-height="702" |
231 | + inkscape:window-x="0" |
232 | + inkscape:window-y="0" |
233 | + inkscape:window-maximized="0" /> |
234 | + <metadata |
235 | + id="metadata2990"> |
236 | + <rdf:RDF> |
237 | + <cc:Work |
238 | + rdf:about=""> |
239 | + <dc:format>image/svg+xml</dc:format> |
240 | + <dc:type |
241 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
242 | + <dc:title></dc:title> |
243 | + </cc:Work> |
244 | + </rdf:RDF> |
245 | + </metadata> |
246 | + <g |
247 | + id="layer1" |
248 | + inkscape:label="Layer 1" |
249 | + inkscape:groupmode="layer" |
250 | + transform="translate(0,64)"> |
251 | + <path |
252 | + sodipodi:type="arc" |
253 | + style="fill:none;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" |
254 | + id="path2993" |
255 | + sodipodi:cx="63.090908" |
256 | + sodipodi:cy="64" |
257 | + sodipodi:rx="55.454544" |
258 | + sodipodi:ry="54.18182" |
259 | + d="M 118.54545,64 A 55.454544,54.18182 0 1 1 7.636364,64 55.454544,54.18182 0 1 1 118.54545,64 z" |
260 | + transform="matrix(1.0721871,0,0,1.0973726,-3.6452576,-70.231844)" /> |
261 | + <g |
262 | + id="g3788" |
263 | + transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,20.695964,43.304036)"> |
264 | + <rect |
265 | + style="fill:#00a400;fill-opacity:1;stroke:none" |
266 | + id="rect3896" |
267 | + width="15" |
268 | + height="40" |
269 | + x="22.212006" |
270 | + y="-26.36396" |
271 | + rx="3" |
272 | + ry="3" /> |
273 | + <rect |
274 | + ry="2.7" |
275 | + rx="3" |
276 | + transform="matrix(0,1,-1,0,0,0)" |
277 | + y="-112.58459" |
278 | + x="-1.437555" |
279 | + height="90" |
280 | + width="15" |
281 | + id="rect3898" |
282 | + style="fill:#00a400;fill-opacity:1;stroke:none" /> |
283 | + </g> |
284 | + </g> |
285 | +</svg> |
286 | |
287 | === added directory 'examples/editableplugin' |
288 | === added file 'examples/editableplugin/index.html' |
289 | --- examples/editableplugin/index.html 1970-01-01 00:00:00 +0000 |
290 | +++ examples/editableplugin/index.html 2011-05-28 21:21:33 +0000 |
291 | @@ -0,0 +1,70 @@ |
292 | +<!DOCTYPE html> |
293 | +<meta charset="utf-8" /> |
294 | +<html> |
295 | + <head> |
296 | + <title>Editable Plugin</title> |
297 | + <script type="text/javascript" |
298 | + src="http://yui.yahooapis.com/3.3.0/build/yui/yui-min.js"></script> |
299 | + <script type="text/javascript" |
300 | + src="../../src/js/editableplugin/editableplugin.js"></script> |
301 | + <link rel="stylesheet" href="../../src/css/phazr.css" /> |
302 | + </head> |
303 | + <body> |
304 | + <div id="editableplugin-demo"> |
305 | + <h1>Editable Plugin</h1> |
306 | + <p>EditablePlugin allows any text Node to be editable. Select the Node |
307 | + and plug Editable into it.</p> |
308 | + <p> |
309 | + <span id="editable-demo"> |
310 | + This text has a typo, but it's compeletely editable. |
311 | + </span> |
312 | + </p> |
313 | + <script type="text/javascript"> |
314 | + YUI().use('editableplugin', 'node', function(Y) { |
315 | + var demo = Y.one('#editable-demo'); |
316 | + demo.plug(Y.Editable, { |
317 | + saveFn: function(value) { alert(value); } |
318 | + }); |
319 | + }); |
320 | + </script> |
321 | + <pre><code> |
322 | +YUI().use('editableplugin', 'node', function(Y) { |
323 | + var demo = Y.one('#editable-demo'); |
324 | + demo.plug(Y.Editable, { |
325 | + saveFn: function(value) { alert(value); } |
326 | + }); |
327 | +}); |
328 | + </code></pre> |
329 | + |
330 | + <h2>Multi-line editing</h2> |
331 | + <p>By default, the editor only supports a single line. In some cases, |
332 | + you may want to edit paragraphs of text. In this case, you can simply |
333 | + pass <code>multiline: true</code> when you plug the editor into the |
334 | + Node.</p> |
335 | + <p> |
336 | + <p id="multiline-editable-demo"> |
337 | + This is a paragraph of text. There are no typos in this paragraph, but you may want to edit it anyway. Go ahead and try it. |
338 | + </p> |
339 | + </p> |
340 | + <script type="text/javascript"> |
341 | + YUI().use('editableplugin', 'node', function(Y) { |
342 | + var demo = Y.one('#multiline-editable-demo'); |
343 | + demo.plug(Y.Editable, { |
344 | + multiline: true, |
345 | + saveFn: function(value) { alert(value); } |
346 | + }); |
347 | + }); |
348 | + </script> |
349 | + <pre><code> |
350 | +YUI().use('editableplugin', 'node', function(Y) { |
351 | + var demo = Y.one('#multiline-editable-demo'); |
352 | + demo.plug(Y.Editable, { |
353 | + multiline: true, |
354 | + saveFn: function(value) { alert(value); } |
355 | + }); |
356 | +}); |
357 | + </code></pre> |
358 | + |
359 | + </div> |
360 | + </body> |
361 | +</html> |
362 | |
363 | === modified file 'src/css/images/cancel.png' |
364 | Binary files src/css/images/cancel.png 2011-03-23 02:17:50 +0000 and src/css/images/cancel.png 2011-05-28 21:21:33 +0000 differ |
365 | === modified file 'src/css/images/close.png' |
366 | Binary files src/css/images/close.png 2011-03-23 02:17:50 +0000 and src/css/images/close.png 2011-05-28 21:21:33 +0000 differ |
367 | === renamed file 'src/css/images/okay.png' => 'src/css/images/ok.png' |
368 | Binary files src/css/images/okay.png 2011-03-23 02:17:50 +0000 and src/css/images/ok.png 2011-05-28 21:21:33 +0000 differ |
369 | === modified file 'src/css/phazr.css' |
370 | --- src/css/phazr.css 2011-04-07 05:41:04 +0000 |
371 | +++ src/css/phazr.css 2011-05-28 21:21:33 +0000 |
372 | @@ -1,3 +1,7 @@ |
373 | +a.save { background: url('images/ok.png') 0 0 no-repeat; display: inline-block; height: 16px; width: 16px; } |
374 | +a.cancel { background: url('images/cancel.png') 0 0 no-repeat; display: inline-block; height: 16px; width: 16px; } |
375 | + |
376 | + |
377 | div.yui3-widget .clickable { |
378 | cursor: pointer; |
379 | } |
380 | @@ -41,7 +45,7 @@ |
381 | width: 32px; |
382 | } |
383 | div.yui3-overlay div.yui3-overlay-controls a#save-control { |
384 | - background: url('images/okay.png') 0 0 no-repeat; |
385 | + background: url('images/ok.png') 0 0 no-repeat; |
386 | } |
387 | div.yui3-overlay div.yui3-overlay-controls a#cancel-control { |
388 | background: url('images/cancel.png') 0 0 no-repeat; |
389 | |
390 | === added directory 'src/js/editableplugin' |
391 | === added file 'src/js/editableplugin/editableplugin.js' |
392 | --- src/js/editableplugin/editableplugin.js 1970-01-01 00:00:00 +0000 |
393 | +++ src/js/editableplugin/editableplugin.js 2011-05-28 21:21:33 +0000 |
394 | @@ -0,0 +1,85 @@ |
395 | +YUI.add('editableplugin', function(Y) { |
396 | + |
397 | + Y.Editable = Y.Base.create('editable', Y.Plugin.Base, [], { |
398 | + initializer: function(config) { |
399 | + var host = this.get('host'); |
400 | + host.addClass('editable'); |
401 | + host.on('click', Y.bind(this.edit, this)); |
402 | + }, |
403 | + cancel: function() { |
404 | + var editor = this.get('editor'), |
405 | + host = this.get('host'); |
406 | + editor.hide(); |
407 | + host.show(); |
408 | + }, |
409 | + edit: function(e) { |
410 | + e.preventDefault(); |
411 | + var host = this.get('host'); |
412 | + multiline = this.get('multiline'), |
413 | + text = Y.Lang.trim(host.get('text')), |
414 | + editor = this.get('editor'); |
415 | + |
416 | + if (editor === null) { |
417 | + var parentNode = host.get('parentNode'), |
418 | + editor = Y.Node.create('<div></div>'), |
419 | + controls = Y.Node.create('<span></span'), |
420 | + save = Y.Node.create('<a class="clickable save"></a>'), |
421 | + cancel = Y.Node.create('<a class="clickable cancel"></a>'), |
422 | + textbox = null; |
423 | + |
424 | + if (multiline) { |
425 | + textbox = Y.Node.create('<textarea></textarea>'); |
426 | + } else { |
427 | + textbox = Y.Node.create('<input type="text" />'); |
428 | + } |
429 | + |
430 | + save.on('click', this.save, this); |
431 | + cancel.on('click', this.cancel, this); |
432 | + |
433 | + controls.appendChild(save); |
434 | + controls.appendChild(cancel); |
435 | + editor.appendChild(textbox); |
436 | + editor.appendChild(controls); |
437 | + editor.hide(); |
438 | + |
439 | + textbox.on('keyup', this._handleKeyPress, this); |
440 | + parentNode.insert(editor, host); |
441 | + this.set('editor', editor); |
442 | + } |
443 | + |
444 | + editor.get('firstChild').set('value', text); |
445 | + editor.show(); |
446 | + host.hide(); |
447 | + }, |
448 | + save: function() { |
449 | + var editor = this.get('editor'), |
450 | + host = this.get('host'), |
451 | + save = this.get('saveFn'), |
452 | + value = editor.get('firstChild').get('value'); |
453 | + if (save !== null) { save(value); } |
454 | + host.setContent(value); |
455 | + editor.hide(); |
456 | + host.show(); |
457 | + }, |
458 | + _handleKeyPress: function(e) { |
459 | + if (e.keyCode) { |
460 | + if (e.keyCode === 27) { this.cancel(); } |
461 | + if (e.keyCode === 13 && this.get('multiline') === false) { |
462 | + this.save(); |
463 | + } |
464 | + } |
465 | + } |
466 | + }, { |
467 | + ATTRS: { |
468 | + editor: { value: null }, |
469 | + multiline: { value: false }, |
470 | + saveFn: { value: null } |
471 | + }, |
472 | + NS: 'editable' |
473 | + }); |
474 | + |
475 | +}, '1.0', { requires: [ |
476 | + 'base', |
477 | + 'node', |
478 | + 'plugin', |
479 | +]}); |
I like this as a plugin very much. I see no downside to it really. And the code is simple and straightforward.
We absolutely need to add cancel/save icons for the multiline version. Or maybe make this an option on the initializer?
Also, the real neat trick of a text editor is that the underlying form element mimics the layout of the text on the page. I think this is the hard trick and my gut says we need some form of this in phazr. Call sites will override it, I'm sure, but we need some sense of how to do this in the plugin itself.
As a start, this is very good though!