Fossil SCM

Ported the various preview mode toggles and clipboard copy from legacy pikchrshow to wasm pikchrshow.

stephan 2022-06-07 11:37 pikchrshow-wasm
Commit e513090c31a0d18867a0dfede50ef54da5a3fce3f163ea6e5366aa509cad681f
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -13,28 +13,35 @@
1313
This is the main entry point for the WASM rendition of fossil's
1414
/pikchrshow app. It sets up the various UI bits, loads a Worker for
1515
the pikchr process, and manages the communication between the UI and
1616
worker.
1717
18
- API dependencies: fossil.dom, fossil.storage
18
+ API dependencies: fossil.dom, fossil.copybutton, fossil.storage
1919
*/
2020
(function(F/*fossil object*/){
2121
'use strict';
2222
2323
/* Recall that the 'self' symbol, except where locally
2424
overwritten, refers to the global window or worker object. */
2525
26
+ const D = F.dom;
2627
/** Name of the stored copy of this app's config. */
2728
const configStorageKey = 'pikchrshow-config';
2829
29
- /**
30
- The PikchrFiddle object is intended to be the primary app-level
31
- object for the main-thread side of the fiddle application. It
32
- uses a worker thread to load the WASM module and communicate
33
- with it.
34
- */
35
- const PS/*local convenience alias*/ = F.PikchrShow/*canonical name*/ = {
30
+ /* querySelectorAll() proxy */
31
+ const EAll = function(/*[element=document,] cssSelector*/){
32
+ return (arguments.length>1 ? arguments[0] : document)
33
+ .querySelectorAll(arguments[arguments.length-1]);
34
+ };
35
+ /* querySelector() proxy */
36
+ const E = function(/*[element=document,] cssSelector*/){
37
+ return (arguments.length>1 ? arguments[0] : document)
38
+ .querySelector(arguments[arguments.length-1]);
39
+ };
40
+
41
+ /** The main application object. */
42
+ const PS = {
3643
/* Config options. */
3744
config: {
3845
/* If true, display input/output areas side-by-side, else stack
3946
them vertically. */
4047
sideBySide: true,
@@ -41,15 +48,27 @@
4148
/* If true, swap positions of the input/output areas. */
4249
swapInOut: false,
4350
/* If true, the SVG is allowed to resize to fit the parent
4451
content area, else the parent is resized to fit the rendered
4552
SVG (as sized by pikchr). */
46
- renderAutoScale: false,
53
+ renderAutofit: false,
4754
/* If true, automatically render while the user is typing. */
4855
renderWhileTyping: false
4956
},
50
- renderMode: 'html'/*one of: 'text','html'*/,
57
+ /* Various DOM elements. */
58
+ e: {
59
+ previewCopyButton: E('#preview-copy-button'),
60
+ previewModeLabel: E('label[for=preview-copy-button]'),
61
+ zoneOutputButtons: E('.zone-wrapper.output > legend > .button-bar'),
62
+ outText: E('#pikchr-output-text'),
63
+ pikOutWrapper: E('#pikchr-output-wrapper'),
64
+ pikOut: E('#pikchr-output')
65
+ },
66
+ renderModes: ['svg'/*SVG must be at index 0*/,'markdown', 'wiki', 'text'],
67
+ renderModeLabels: {
68
+ svg: 'SVG', markdown: 'Markdown', wiki: 'Fossil Wiki', text: 'Text'
69
+ },
5170
_msgMap: {},
5271
/** Adds a worker message handler for messages of the given
5372
type. */
5473
addMsgHandler: function f(type,callback){
5574
if(Array.isArray(type)){
@@ -85,11 +104,11 @@
85104
/** Stores this object's config in the browser's storage. */
86105
storeConfig: function(){
87106
F.storage.setJSON(configStorageKey,this.config);
88107
}
89108
};
90
-
109
+ PS.renderModes.selectedIndex = 0;
91110
PS._config = F.storage.getJSON(configStorageKey);
92111
if(PS._config){
93112
/* Copy all properties to PS.config which are currently in
94113
PS._config. We don't bother copying any other properties: those
95114
would be stale/removed config entries. */
@@ -104,21 +123,10 @@
104123
PS.worker = new Worker('builtin/extsrc/pikchr-worker.js');
105124
PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
106125
PS.addMsgHandler('stdout', console.log.bind(console));
107126
PS.addMsgHandler('stderr', console.error.bind(console));
108127
109
- /* querySelectorAll() proxy */
110
- const EAll = function(/*[element=document,] cssSelector*/){
111
- return (arguments.length>1 ? arguments[0] : document)
112
- .querySelectorAll(arguments[arguments.length-1]);
113
- };
114
- /* querySelector() proxy */
115
- const E = function(/*[element=document,] cssSelector*/){
116
- return (arguments.length>1 ? arguments[0] : document)
117
- .querySelector(arguments[arguments.length-1]);
118
- };
119
-
120128
/** Handles status updates from the Module object. */
121129
PS.addMsgHandler('module', function f(ev){
122130
ev = ev.data;
123131
if('status'!==ev.type){
124132
console.warn("Unexpected module-type message:",ev);
@@ -156,10 +164,13 @@
156164
after the last "load" message has arrived, so
157165
leave f.ui.status and message listener intact. */
158166
}
159167
});
160168
169
+ PS.e.previewModeLabel.innerText =
170
+ PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];
171
+
161172
/**
162173
The 'pikchrshow-ready' event is fired (with no payload) when the
163174
wasm module has finished loading. */
164175
PS.addMsgHandler('pikchrshow-ready', function(){
165176
PS.clearMsgHandlers('pikchrshow-ready');
@@ -287,57 +298,60 @@
287298
pikchr: txt,
288299
darkMode: !!window.fossil.config.skin.isDark
289300
});
290301
};
291302
292
- const eOut = E('#pikchr-output');
293
- const eOutWrapper = E('#pikchr-output-wrapper');
294303
PS.addMsgHandler('pikchr', function(ev){
295304
const m = ev.data;
296
- eOut.classList[m.isError ? 'add' : 'remove']('error');
297
- eOut.dataset.pikchr = m.pikchr;
298
- let content;
299
- let sz;
300
- switch(PS.renderMode){
305
+ PS.e.pikOut.classList[m.isError ? 'add' : 'remove']('error');
306
+ PS.e.pikOut.dataset.pikchr = m.pikchr;
307
+ const mode = PS.renderModes[PS.renderModes.selectedIndex];
308
+ switch(mode){
301309
case 'text':
302
- content = '<textarea>'+m.result+'</textarea>';
303
- eOut.classList.add('text');
304
- eOutWrapper.classList.add('text');
305
- break;
306
- default:
307
- content = m.result;
308
- eOut.classList.remove('text');
309
- eOutWrapper.classList.remove('text');
310
- break;
311
- }
312
- eOut.innerHTML = content;
310
+ case 'markdown':
311
+ case 'wiki': {
312
+ const body = [m.result];
313
+ if('markdown'===mode){
314
+ body.unshift('```pikchr');
315
+ body.push('```');
316
+ }else if('wiki'===mode){
317
+ body.unshift('<verbatim type="pikchr">');
318
+ body.push('</verbatim>');
319
+ }
320
+ PS.e.outText.value = body.join('\n');
321
+ PS.e.outText.classList.remove('hidden');
322
+ PS.e.pikOut.classList.add('hidden');
323
+ PS.e.pikOutWrapper.classList.add('text');
324
+ break;
325
+ }
326
+ case 'svg':
327
+ PS.e.outText.classList.add('hidden');
328
+ PS.e.pikOut.classList.remove('hidden');
329
+ PS.e.pikOutWrapper.classList.remove('text');
330
+ PS.e.pikOut.innerHTML = m.result;
331
+ PS.e.outText.value = m.result/*for clipboard copy*/;
332
+ break;
333
+ default: throw new Error("Unhandled render mode: "+mode);
334
+ }
313335
let vw = null, vh = null;
314
- if(!PS.config.renderAutoScale
315
- && !m.isError && 'html'===PS.renderMode){
316
- const svg = E(eOut,':scope > svg');
317
- const vb = svg ? svg.getAttribute('viewBox').split(' ') : false;
318
- if(vb && 4===vb.length){
319
- vw = (+vb[2] + 10)+'px';
320
- vh = (+vb[3] + 10)+'px';
321
- }else if(svg){
322
- console.warn("SVG element is missing viewBox attribute.");
323
- }
324
- }
325
- eOut.style.width = vw;
326
- eOut.style.height = vh;
336
+ if('svg'===mode && !PS.config.renderAutofit && !m.isError){
337
+ vw = m.width; vh = m.height;
338
+ }
339
+ PS.e.pikOut.style.width = vw ? vw+'px' : null;
340
+ PS.e.pikOut.style.height = vh ? vh+'px' : null;
327341
})/*'pikchr' msg handler*/;
328342
329343
E('#btn-render-mode').addEventListener('click',function(){
330
- let mode = PS.renderMode;
331
- const modes = ['text','html'];
332
- let ndx = modes.indexOf(mode) + 1;
333
- if(ndx>=modes.length) ndx = 0;
334
- PS.renderMode = modes[ndx];
335
- if(eOut.dataset.pikchr){
336
- PS.render(eOut.dataset.pikchr);
344
+ const modes = PS.renderModes;
345
+ modes.selectedIndex = (modes.selectedIndex + 1) % modes.length;
346
+ PS.e.previewModeLabel.innerText = PS.renderModeLabels[modes[modes.selectedIndex]];
347
+ if(PS.e.pikOut.dataset.pikchr){
348
+ PS.render(PS.e.pikOut.dataset.pikchr);
337349
}
338350
});
351
+ F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
352
+ PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false);
339353
340354
PS.addMsgHandler('working',function f(ev){
341355
switch(ev.data){
342356
case 'start': /* See notes in preStartWork(). */; return;
343357
case 'end':
@@ -378,15 +392,15 @@
378392
e.addEventListener('change', function(){
379393
PS.config[this.dataset.config] = this.checked;
380394
PS.storeConfig();
381395
}, false);
382396
});
383
- E('#opt-cb-autoscale').addEventListener('change',function(){
384
- /* PS.config.renderAutoScale was set by the data-config
397
+ E('#opt-cb-autofit').addEventListener('change',function(){
398
+ /* PS.config.renderAutofit was set by the data-config
385399
event handler. */
386
- if('html'==PS.renderMode && eOut.dataset.pikchr){
387
- PS.render(eOut.dataset.pikchr);
400
+ if(0==PS.renderModes.selectedIndex && PS.e.pikOut.dataset.pikchr){
401
+ PS.render(PS.e.pikOut.dataset.pikchr);
388402
}
389403
});
390404
/* For each button with data-cmd=X, map a click handler which
391405
calls PS.render(X). */
392406
const cmdClick = function(){PS.render(this.dataset.cmd);};
393407
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -13,28 +13,35 @@
13 This is the main entry point for the WASM rendition of fossil's
14 /pikchrshow app. It sets up the various UI bits, loads a Worker for
15 the pikchr process, and manages the communication between the UI and
16 worker.
17
18 API dependencies: fossil.dom, fossil.storage
19 */
20 (function(F/*fossil object*/){
21 'use strict';
22
23 /* Recall that the 'self' symbol, except where locally
24 overwritten, refers to the global window or worker object. */
25
 
26 /** Name of the stored copy of this app's config. */
27 const configStorageKey = 'pikchrshow-config';
28
29 /**
30 The PikchrFiddle object is intended to be the primary app-level
31 object for the main-thread side of the fiddle application. It
32 uses a worker thread to load the WASM module and communicate
33 with it.
34 */
35 const PS/*local convenience alias*/ = F.PikchrShow/*canonical name*/ = {
 
 
 
 
 
 
36 /* Config options. */
37 config: {
38 /* If true, display input/output areas side-by-side, else stack
39 them vertically. */
40 sideBySide: true,
@@ -41,15 +48,27 @@
41 /* If true, swap positions of the input/output areas. */
42 swapInOut: false,
43 /* If true, the SVG is allowed to resize to fit the parent
44 content area, else the parent is resized to fit the rendered
45 SVG (as sized by pikchr). */
46 renderAutoScale: false,
47 /* If true, automatically render while the user is typing. */
48 renderWhileTyping: false
49 },
50 renderMode: 'html'/*one of: 'text','html'*/,
 
 
 
 
 
 
 
 
 
 
 
 
51 _msgMap: {},
52 /** Adds a worker message handler for messages of the given
53 type. */
54 addMsgHandler: function f(type,callback){
55 if(Array.isArray(type)){
@@ -85,11 +104,11 @@
85 /** Stores this object's config in the browser's storage. */
86 storeConfig: function(){
87 F.storage.setJSON(configStorageKey,this.config);
88 }
89 };
90
91 PS._config = F.storage.getJSON(configStorageKey);
92 if(PS._config){
93 /* Copy all properties to PS.config which are currently in
94 PS._config. We don't bother copying any other properties: those
95 would be stale/removed config entries. */
@@ -104,21 +123,10 @@
104 PS.worker = new Worker('builtin/extsrc/pikchr-worker.js');
105 PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
106 PS.addMsgHandler('stdout', console.log.bind(console));
107 PS.addMsgHandler('stderr', console.error.bind(console));
108
109 /* querySelectorAll() proxy */
110 const EAll = function(/*[element=document,] cssSelector*/){
111 return (arguments.length>1 ? arguments[0] : document)
112 .querySelectorAll(arguments[arguments.length-1]);
113 };
114 /* querySelector() proxy */
115 const E = function(/*[element=document,] cssSelector*/){
116 return (arguments.length>1 ? arguments[0] : document)
117 .querySelector(arguments[arguments.length-1]);
118 };
119
120 /** Handles status updates from the Module object. */
121 PS.addMsgHandler('module', function f(ev){
122 ev = ev.data;
123 if('status'!==ev.type){
124 console.warn("Unexpected module-type message:",ev);
@@ -156,10 +164,13 @@
156 after the last "load" message has arrived, so
157 leave f.ui.status and message listener intact. */
158 }
159 });
160
 
 
 
161 /**
162 The 'pikchrshow-ready' event is fired (with no payload) when the
163 wasm module has finished loading. */
164 PS.addMsgHandler('pikchrshow-ready', function(){
165 PS.clearMsgHandlers('pikchrshow-ready');
@@ -287,57 +298,60 @@
287 pikchr: txt,
288 darkMode: !!window.fossil.config.skin.isDark
289 });
290 };
291
292 const eOut = E('#pikchr-output');
293 const eOutWrapper = E('#pikchr-output-wrapper');
294 PS.addMsgHandler('pikchr', function(ev){
295 const m = ev.data;
296 eOut.classList[m.isError ? 'add' : 'remove']('error');
297 eOut.dataset.pikchr = m.pikchr;
298 let content;
299 let sz;
300 switch(PS.renderMode){
301 case 'text':
302 content = '<textarea>'+m.result+'</textarea>';
303 eOut.classList.add('text');
304 eOutWrapper.classList.add('text');
305 break;
306 default:
307 content = m.result;
308 eOut.classList.remove('text');
309 eOutWrapper.classList.remove('text');
310 break;
311 }
312 eOut.innerHTML = content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313 let vw = null, vh = null;
314 if(!PS.config.renderAutoScale
315 && !m.isError && 'html'===PS.renderMode){
316 const svg = E(eOut,':scope > svg');
317 const vb = svg ? svg.getAttribute('viewBox').split(' ') : false;
318 if(vb && 4===vb.length){
319 vw = (+vb[2] + 10)+'px';
320 vh = (+vb[3] + 10)+'px';
321 }else if(svg){
322 console.warn("SVG element is missing viewBox attribute.");
323 }
324 }
325 eOut.style.width = vw;
326 eOut.style.height = vh;
327 })/*'pikchr' msg handler*/;
328
329 E('#btn-render-mode').addEventListener('click',function(){
330 let mode = PS.renderMode;
331 const modes = ['text','html'];
332 let ndx = modes.indexOf(mode) + 1;
333 if(ndx>=modes.length) ndx = 0;
334 PS.renderMode = modes[ndx];
335 if(eOut.dataset.pikchr){
336 PS.render(eOut.dataset.pikchr);
337 }
338 });
 
 
339
340 PS.addMsgHandler('working',function f(ev){
341 switch(ev.data){
342 case 'start': /* See notes in preStartWork(). */; return;
343 case 'end':
@@ -378,15 +392,15 @@
378 e.addEventListener('change', function(){
379 PS.config[this.dataset.config] = this.checked;
380 PS.storeConfig();
381 }, false);
382 });
383 E('#opt-cb-autoscale').addEventListener('change',function(){
384 /* PS.config.renderAutoScale was set by the data-config
385 event handler. */
386 if('html'==PS.renderMode && eOut.dataset.pikchr){
387 PS.render(eOut.dataset.pikchr);
388 }
389 });
390 /* For each button with data-cmd=X, map a click handler which
391 calls PS.render(X). */
392 const cmdClick = function(){PS.render(this.dataset.cmd);};
393
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -13,28 +13,35 @@
13 This is the main entry point for the WASM rendition of fossil's
14 /pikchrshow app. It sets up the various UI bits, loads a Worker for
15 the pikchr process, and manages the communication between the UI and
16 worker.
17
18 API dependencies: fossil.dom, fossil.copybutton, fossil.storage
19 */
20 (function(F/*fossil object*/){
21 'use strict';
22
23 /* Recall that the 'self' symbol, except where locally
24 overwritten, refers to the global window or worker object. */
25
26 const D = F.dom;
27 /** Name of the stored copy of this app's config. */
28 const configStorageKey = 'pikchrshow-config';
29
30 /* querySelectorAll() proxy */
31 const EAll = function(/*[element=document,] cssSelector*/){
32 return (arguments.length>1 ? arguments[0] : document)
33 .querySelectorAll(arguments[arguments.length-1]);
34 };
35 /* querySelector() proxy */
36 const E = function(/*[element=document,] cssSelector*/){
37 return (arguments.length>1 ? arguments[0] : document)
38 .querySelector(arguments[arguments.length-1]);
39 };
40
41 /** The main application object. */
42 const PS = {
43 /* Config options. */
44 config: {
45 /* If true, display input/output areas side-by-side, else stack
46 them vertically. */
47 sideBySide: true,
@@ -41,15 +48,27 @@
48 /* If true, swap positions of the input/output areas. */
49 swapInOut: false,
50 /* If true, the SVG is allowed to resize to fit the parent
51 content area, else the parent is resized to fit the rendered
52 SVG (as sized by pikchr). */
53 renderAutofit: false,
54 /* If true, automatically render while the user is typing. */
55 renderWhileTyping: false
56 },
57 /* Various DOM elements. */
58 e: {
59 previewCopyButton: E('#preview-copy-button'),
60 previewModeLabel: E('label[for=preview-copy-button]'),
61 zoneOutputButtons: E('.zone-wrapper.output > legend > .button-bar'),
62 outText: E('#pikchr-output-text'),
63 pikOutWrapper: E('#pikchr-output-wrapper'),
64 pikOut: E('#pikchr-output')
65 },
66 renderModes: ['svg'/*SVG must be at index 0*/,'markdown', 'wiki', 'text'],
67 renderModeLabels: {
68 svg: 'SVG', markdown: 'Markdown', wiki: 'Fossil Wiki', text: 'Text'
69 },
70 _msgMap: {},
71 /** Adds a worker message handler for messages of the given
72 type. */
73 addMsgHandler: function f(type,callback){
74 if(Array.isArray(type)){
@@ -85,11 +104,11 @@
104 /** Stores this object's config in the browser's storage. */
105 storeConfig: function(){
106 F.storage.setJSON(configStorageKey,this.config);
107 }
108 };
109 PS.renderModes.selectedIndex = 0;
110 PS._config = F.storage.getJSON(configStorageKey);
111 if(PS._config){
112 /* Copy all properties to PS.config which are currently in
113 PS._config. We don't bother copying any other properties: those
114 would be stale/removed config entries. */
@@ -104,21 +123,10 @@
123 PS.worker = new Worker('builtin/extsrc/pikchr-worker.js');
124 PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
125 PS.addMsgHandler('stdout', console.log.bind(console));
126 PS.addMsgHandler('stderr', console.error.bind(console));
127
 
 
 
 
 
 
 
 
 
 
 
128 /** Handles status updates from the Module object. */
129 PS.addMsgHandler('module', function f(ev){
130 ev = ev.data;
131 if('status'!==ev.type){
132 console.warn("Unexpected module-type message:",ev);
@@ -156,10 +164,13 @@
164 after the last "load" message has arrived, so
165 leave f.ui.status and message listener intact. */
166 }
167 });
168
169 PS.e.previewModeLabel.innerText =
170 PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];
171
172 /**
173 The 'pikchrshow-ready' event is fired (with no payload) when the
174 wasm module has finished loading. */
175 PS.addMsgHandler('pikchrshow-ready', function(){
176 PS.clearMsgHandlers('pikchrshow-ready');
@@ -287,57 +298,60 @@
298 pikchr: txt,
299 darkMode: !!window.fossil.config.skin.isDark
300 });
301 };
302
 
 
303 PS.addMsgHandler('pikchr', function(ev){
304 const m = ev.data;
305 PS.e.pikOut.classList[m.isError ? 'add' : 'remove']('error');
306 PS.e.pikOut.dataset.pikchr = m.pikchr;
307 const mode = PS.renderModes[PS.renderModes.selectedIndex];
308 switch(mode){
 
309 case 'text':
310 case 'markdown':
311 case 'wiki': {
312 const body = [m.result];
313 if('markdown'===mode){
314 body.unshift('```pikchr');
315 body.push('```');
316 }else if('wiki'===mode){
317 body.unshift('<verbatim type="pikchr">');
318 body.push('</verbatim>');
319 }
320 PS.e.outText.value = body.join('\n');
321 PS.e.outText.classList.remove('hidden');
322 PS.e.pikOut.classList.add('hidden');
323 PS.e.pikOutWrapper.classList.add('text');
324 break;
325 }
326 case 'svg':
327 PS.e.outText.classList.add('hidden');
328 PS.e.pikOut.classList.remove('hidden');
329 PS.e.pikOutWrapper.classList.remove('text');
330 PS.e.pikOut.innerHTML = m.result;
331 PS.e.outText.value = m.result/*for clipboard copy*/;
332 break;
333 default: throw new Error("Unhandled render mode: "+mode);
334 }
335 let vw = null, vh = null;
336 if('svg'===mode && !PS.config.renderAutofit && !m.isError){
337 vw = m.width; vh = m.height;
338 }
339 PS.e.pikOut.style.width = vw ? vw+'px' : null;
340 PS.e.pikOut.style.height = vh ? vh+'px' : null;
 
 
 
 
 
 
 
 
341 })/*'pikchr' msg handler*/;
342
343 E('#btn-render-mode').addEventListener('click',function(){
344 const modes = PS.renderModes;
345 modes.selectedIndex = (modes.selectedIndex + 1) % modes.length;
346 PS.e.previewModeLabel.innerText = PS.renderModeLabels[modes[modes.selectedIndex]];
347 if(PS.e.pikOut.dataset.pikchr){
348 PS.render(PS.e.pikOut.dataset.pikchr);
 
 
349 }
350 });
351 F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
352 PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false);
353
354 PS.addMsgHandler('working',function f(ev){
355 switch(ev.data){
356 case 'start': /* See notes in preStartWork(). */; return;
357 case 'end':
@@ -378,15 +392,15 @@
392 e.addEventListener('change', function(){
393 PS.config[this.dataset.config] = this.checked;
394 PS.storeConfig();
395 }, false);
396 });
397 E('#opt-cb-autofit').addEventListener('change',function(){
398 /* PS.config.renderAutofit was set by the data-config
399 event handler. */
400 if(0==PS.renderModes.selectedIndex && PS.e.pikOut.dataset.pikchr){
401 PS.render(PS.e.pikOut.dataset.pikchr);
402 }
403 });
404 /* For each button with data-cmd=X, map a click handler which
405 calls PS.render(X). */
406 const cmdClick = function(){PS.render(this.dataset.cmd);};
407
+10 -5
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -455,13 +455,13 @@
455455
CX("data-cssclass='swapio' ");
456456
CX("data-config='swapInOut'>");
457457
CX("<label for='opt-cb-swapio'>Swap in/out</label>");
458458
CX("</span>");
459459
CX("<span class='labeled-input'>");
460
- CX("<input type='checkbox' id='opt-cb-autoscale' ");
461
- CX("data-config='renderAutoScale'>");
462
- CX("<label for='opt-cb-autoscale'>Auto-scale SVG</label>");
460
+ CX("<input type='checkbox' id='opt-cb-autofit' ");
461
+ CX("data-config='renderAutofit'>");
462
+ CX("<label for='opt-cb-autofit'>Auto-fit SVG</label>");
463463
CX("</span>");
464464
CX("<span class='labeled-input'>");
465465
CX("<input type='checkbox' id='opt-cb-autorender' ");
466466
CX("data-csstgt='#main-wrapper' ");
467467
CX("data-cssclass='auto-render' ");
@@ -489,19 +489,24 @@
489489
CX("%s</textarea></div>",zContent/*safe-for-%s*/);
490490
} CX("</fieldset><!-- .zone-wrapper.input -->");
491491
/*CX("<div class='splitter-handle hidden'></div>");*/
492492
CX("<fieldset class='zone-wrapper output'>"); {
493493
CX("<legend><div class='button-bar'>");
494
- CX("<button id='btn-render-mode'>Toggle Render Mode</button>");
494
+ CX("<button id='btn-render-mode'>Render Mode</button> ");
495
+ CX("<span id='preview-copy-button' "
496
+ "title='Tap to copy to clipboard.'></span>");
497
+ CX("<label for='preview-copy-button' "
498
+ "title='Tap to copy to clipboard.'></label>");
495499
CX("</div></legend>");
496500
CX("<div id='pikchr-output-wrapper'>");
497501
CX("<div id='pikchr-output'></div>");
502
+ CX("<textarea class='hidden' id='pikchr-output-text'></textarea>");
498503
CX("</div>");
499504
} CX("</fieldset> <!-- .zone-wrapper.output -->");
500505
} CX("</div><!-- #main-wrapper -->");
501506
} CX("</div><!-- #view-split -->");
502
- builtin_fossil_js_bundle_or("dom", "storage", NULL);
507
+ builtin_fossil_js_bundle_or("dom", "storage", "copybutton", NULL);
503508
builtin_request_js("fossil.page.pikchrshowasm.js");
504509
builtin_fulfill_js_requests();
505510
style_finish_page();
506511
}
507512
508513
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -455,13 +455,13 @@
455 CX("data-cssclass='swapio' ");
456 CX("data-config='swapInOut'>");
457 CX("<label for='opt-cb-swapio'>Swap in/out</label>");
458 CX("</span>");
459 CX("<span class='labeled-input'>");
460 CX("<input type='checkbox' id='opt-cb-autoscale' ");
461 CX("data-config='renderAutoScale'>");
462 CX("<label for='opt-cb-autoscale'>Auto-scale SVG</label>");
463 CX("</span>");
464 CX("<span class='labeled-input'>");
465 CX("<input type='checkbox' id='opt-cb-autorender' ");
466 CX("data-csstgt='#main-wrapper' ");
467 CX("data-cssclass='auto-render' ");
@@ -489,19 +489,24 @@
489 CX("%s</textarea></div>",zContent/*safe-for-%s*/);
490 } CX("</fieldset><!-- .zone-wrapper.input -->");
491 /*CX("<div class='splitter-handle hidden'></div>");*/
492 CX("<fieldset class='zone-wrapper output'>"); {
493 CX("<legend><div class='button-bar'>");
494 CX("<button id='btn-render-mode'>Toggle Render Mode</button>");
 
 
 
 
495 CX("</div></legend>");
496 CX("<div id='pikchr-output-wrapper'>");
497 CX("<div id='pikchr-output'></div>");
 
498 CX("</div>");
499 } CX("</fieldset> <!-- .zone-wrapper.output -->");
500 } CX("</div><!-- #main-wrapper -->");
501 } CX("</div><!-- #view-split -->");
502 builtin_fossil_js_bundle_or("dom", "storage", NULL);
503 builtin_request_js("fossil.page.pikchrshowasm.js");
504 builtin_fulfill_js_requests();
505 style_finish_page();
506 }
507
508
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -455,13 +455,13 @@
455 CX("data-cssclass='swapio' ");
456 CX("data-config='swapInOut'>");
457 CX("<label for='opt-cb-swapio'>Swap in/out</label>");
458 CX("</span>");
459 CX("<span class='labeled-input'>");
460 CX("<input type='checkbox' id='opt-cb-autofit' ");
461 CX("data-config='renderAutofit'>");
462 CX("<label for='opt-cb-autofit'>Auto-fit SVG</label>");
463 CX("</span>");
464 CX("<span class='labeled-input'>");
465 CX("<input type='checkbox' id='opt-cb-autorender' ");
466 CX("data-csstgt='#main-wrapper' ");
467 CX("data-cssclass='auto-render' ");
@@ -489,19 +489,24 @@
489 CX("%s</textarea></div>",zContent/*safe-for-%s*/);
490 } CX("</fieldset><!-- .zone-wrapper.input -->");
491 /*CX("<div class='splitter-handle hidden'></div>");*/
492 CX("<fieldset class='zone-wrapper output'>"); {
493 CX("<legend><div class='button-bar'>");
494 CX("<button id='btn-render-mode'>Render Mode</button> ");
495 CX("<span id='preview-copy-button' "
496 "title='Tap to copy to clipboard.'></span>");
497 CX("<label for='preview-copy-button' "
498 "title='Tap to copy to clipboard.'></label>");
499 CX("</div></legend>");
500 CX("<div id='pikchr-output-wrapper'>");
501 CX("<div id='pikchr-output'></div>");
502 CX("<textarea class='hidden' id='pikchr-output-text'></textarea>");
503 CX("</div>");
504 } CX("</fieldset> <!-- .zone-wrapper.output -->");
505 } CX("</div><!-- #main-wrapper -->");
506 } CX("</div><!-- #view-split -->");
507 builtin_fossil_js_bundle_or("dom", "storage", "copybutton", NULL);
508 builtin_request_js("fossil.page.pikchrshowasm.js");
509 builtin_fulfill_js_requests();
510 style_finish_page();
511 }
512
513
--- src/style.pikchrshow.css
+++ src/style.pikchrshow.css
@@ -104,11 +104,11 @@
104104
flex: 1 1 auto;
105105
}
106106
#pikchr-output-wrapper.text > #pikchr-output > textarea {
107107
flex: 1 1 auto;
108108
}
109
-.zone-wrapper > legend {
109
+fieldset > legend {
110110
font-size: 85%;
111111
}
112112
.zone-wrapper textarea {
113113
border-radius: 0.5em;
114114
flex: 1 1 auto;
@@ -131,13 +131,13 @@
131131
overflow: auto;
132132
justify-content: space-between;
133133
}
134134
.button-bar {
135135
display: flex;
136
+ flex-wrap: wrap;
136137
justify-content: center;
137
- flex: 0 1 auto;
138
- flex-wrap: wrap;
138
+ align-items: center;
139139
}
140140
.button-bar button {
141141
margin: 0 0.5em;
142142
}
143143
fieldset.options {
@@ -158,10 +158,11 @@
158158
}
159159
fieldset.options.collapsed > legend > #btn-options-toggle::after {
160160
content: " [show]";
161161
position: relative;
162162
}
163
+
163164
span.labeled-input {
164165
padding: 0.25em;
165166
margin: 0.25em 0.5em;
166167
border-radius: 0.25em;
167168
white-space: nowrap;
168169
--- src/style.pikchrshow.css
+++ src/style.pikchrshow.css
@@ -104,11 +104,11 @@
104 flex: 1 1 auto;
105 }
106 #pikchr-output-wrapper.text > #pikchr-output > textarea {
107 flex: 1 1 auto;
108 }
109 .zone-wrapper > legend {
110 font-size: 85%;
111 }
112 .zone-wrapper textarea {
113 border-radius: 0.5em;
114 flex: 1 1 auto;
@@ -131,13 +131,13 @@
131 overflow: auto;
132 justify-content: space-between;
133 }
134 .button-bar {
135 display: flex;
 
136 justify-content: center;
137 flex: 0 1 auto;
138 flex-wrap: wrap;
139 }
140 .button-bar button {
141 margin: 0 0.5em;
142 }
143 fieldset.options {
@@ -158,10 +158,11 @@
158 }
159 fieldset.options.collapsed > legend > #btn-options-toggle::after {
160 content: " [show]";
161 position: relative;
162 }
 
163 span.labeled-input {
164 padding: 0.25em;
165 margin: 0.25em 0.5em;
166 border-radius: 0.25em;
167 white-space: nowrap;
168
--- src/style.pikchrshow.css
+++ src/style.pikchrshow.css
@@ -104,11 +104,11 @@
104 flex: 1 1 auto;
105 }
106 #pikchr-output-wrapper.text > #pikchr-output > textarea {
107 flex: 1 1 auto;
108 }
109 fieldset > legend {
110 font-size: 85%;
111 }
112 .zone-wrapper textarea {
113 border-radius: 0.5em;
114 flex: 1 1 auto;
@@ -131,13 +131,13 @@
131 overflow: auto;
132 justify-content: space-between;
133 }
134 .button-bar {
135 display: flex;
136 flex-wrap: wrap;
137 justify-content: center;
138 align-items: center;
 
139 }
140 .button-bar button {
141 margin: 0 0.5em;
142 }
143 fieldset.options {
@@ -158,10 +158,11 @@
158 }
159 fieldset.options.collapsed > legend > #btn-options-toggle::after {
160 content: " [show]";
161 position: relative;
162 }
163
164 span.labeled-input {
165 padding: 0.25em;
166 margin: 0.25em 0.5em;
167 border-radius: 0.25em;
168 white-space: nowrap;
169

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button