pict-docuserve 1.2.0 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-docuserve",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Pict Documentation Server - A single-page documentation viewer built on Pict",
5
5
  "main": "source/Pict-Application-Docuserve.js",
6
6
  "bin": {
@@ -35,13 +35,14 @@
35
35
  "pict-provider": "^1.0.13",
36
36
  "pict-section-code": "^1.0.11",
37
37
  "pict-section-content": "^1.0.2",
38
+ "pict-section-histogram": "^1.0.1",
38
39
  "pict-section-modal": "^1.1.1",
39
40
  "pict-section-theme": "^1.0.2",
40
41
  "pict-service-commandlineutility": "^1.0.19",
41
42
  "pict-view": "^1.0.68"
42
43
  },
43
44
  "devDependencies": {
44
- "pict-docuserve": "^1.1.0",
45
+ "pict-docuserve": "^1.3.0",
45
46
  "quackage": "^1.2.3"
46
47
  },
47
48
  "copyFilesSettings": {
@@ -117,7 +117,7 @@ class DocuserveApplication extends libPictApplication
117
117
  {
118
118
  this.pict.addProvider('Theme-Section',
119
119
  {
120
- ApplyDefault: 'retold-default',
120
+ ApplyDefault: 'pict-default',
121
121
  DefaultMode: 'system',
122
122
  DefaultScale: 1.0,
123
123
  Brand: pBrand,
@@ -447,6 +447,14 @@ class DocuserveApplication extends libPictApplication
447
447
  this.pict.AppData.Docuserve.Playground = {};
448
448
  }
449
449
  this.pict.AppData.Docuserve.Playground.Config = pConfig || null;
450
+ // Config controls DOM-sandbox pane visibility; let the
451
+ // playground view re-evaluate the moment it lands. No-op
452
+ // if the view hasn't been mounted yet.
453
+ let tmpPlaygroundView = this.pict.views['Docuserve-Fable-Playground'];
454
+ if (tmpPlaygroundView && typeof tmpPlaygroundView._applySandboxMode === 'function')
455
+ {
456
+ tmpPlaygroundView._applySandboxMode();
457
+ }
450
458
  });
451
459
  }
452
460
  else
@@ -455,6 +463,11 @@ class DocuserveApplication extends libPictApplication
455
463
  {
456
464
  this.pict.AppData.Docuserve.Playground.Config = null;
457
465
  }
466
+ let tmpPlaygroundView = this.pict.views['Docuserve-Fable-Playground'];
467
+ if (tmpPlaygroundView && typeof tmpPlaygroundView._applySandboxMode === 'function')
468
+ {
469
+ tmpPlaygroundView._applySandboxMode();
470
+ }
458
471
  }
459
472
  }
460
473
 
@@ -1,6 +1,8 @@
1
1
  const libPictView = require('pict-view');
2
2
  const libPictSectionCode = require('pict-section-code');
3
3
  const libFableServiceProviderBase = require('fable-serviceproviderbase');
4
+ const libPict = require('pict');
5
+ const libPictSectionHistogram = require('pict-section-histogram');
4
6
 
5
7
  // Note: we deliberately do NOT `require('fable')` here. Fable is already
6
8
  // bundled into the host page's pict.min.js (via pict's own dep tree);
@@ -146,6 +148,12 @@ const _ViewConfiguration =
146
148
  }
147
149
  .docuserve-playground-pane-code { border-right: 1px solid var(--theme-color-border-default, #DDD6CA); }
148
150
  .docuserve-playground-pane-log { background: var(--theme-color-background-secondary, #F6F3EE); }
151
+ /* DOM sandbox pane — third column, opt-in via _playground.json's
152
+ "Sandbox": "dom" field. Hidden by default so fable-family docs
153
+ keep their familiar two-pane layout. */
154
+ .docuserve-playground-pane-dom { display: none; border-left: 1px solid var(--theme-color-border-default, #DDD6CA); background: var(--theme-color-background-panel, #FFFFFF); }
155
+ .docuserve-playground-mode-dom .docuserve-playground-pane-dom { display: flex; }
156
+ .docuserve-playground-sandbox { flex: 1 1 0; min-height: 0; overflow: auto; padding: 12px; }
149
157
 
150
158
  /* Per-pane header — small uppercase label on the left, controls
151
159
  on the right. Same row height as the doc page section
@@ -407,6 +415,17 @@ const _ViewConfiguration =
407
415
  </div>
408
416
  <pre class="docuserve-playground-log" id="Docuserve-Fable-Playground-Log">{~T:Docuserve-Fable-Playground-Log-Body-Template:AppData~}</pre>
409
417
  </div>
418
+ <div class="docuserve-playground-pane docuserve-playground-pane-dom">
419
+ <div class="docuserve-playground-pane-header">
420
+ <span class="docuserve-playground-pane-title">DOM sandbox</span>
421
+ <div class="docuserve-playground-pane-controls">
422
+ <button type="button" class="docuserve-playground-iconbtn" title="Clear sandbox" aria-label="Clear sandbox" onclick="{~P~}.views['Docuserve-Fable-Playground'].clearSandbox()">
423
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>
424
+ </button>
425
+ </div>
426
+ </div>
427
+ <div class="docuserve-playground-sandbox" id="Docuserve-Fable-Playground-Sandbox"></div>
428
+ </div>
410
429
  </div>
411
430
  </div>
412
431
  `
@@ -505,6 +524,42 @@ class DocuserveFablePlaygroundView extends libPictView
505
524
  }
506
525
  this._wireHelpTooltip();
507
526
  this._mountCodeEditor();
527
+ // Toggle the DOM-sandbox pane visibility based on the loaded
528
+ // _playground.json config. Safe to call repeatedly — it just
529
+ // adds/removes the mode class on the playground root.
530
+ this._applySandboxMode();
531
+ }
532
+
533
+ /**
534
+ * Read the current `_playground.json` (already cached into AppData
535
+ * by the application's route handler) and toggle the
536
+ * `docuserve-playground-mode-dom` class on the playground root
537
+ * accordingly. Idempotent. Called from `mountInto()` and on
538
+ * every `run()` so a config that loads asynchronously still gets
539
+ * picked up the moment the user interacts with the playground.
540
+ */
541
+ _applySandboxMode()
542
+ {
543
+ let tmpRoot = document.querySelector('.docuserve-playground');
544
+ if (!tmpRoot) { return; }
545
+ let tmpConfig = this.pict.AppData.Docuserve
546
+ && this.pict.AppData.Docuserve.Playground
547
+ && this.pict.AppData.Docuserve.Playground.Config;
548
+ let tmpSandboxMode = (tmpConfig && tmpConfig.Sandbox === 'dom');
549
+ tmpRoot.classList.toggle('docuserve-playground-mode-dom', tmpSandboxMode);
550
+ }
551
+
552
+ /**
553
+ * Empty the DOM sandbox. Wired to the trash-icon button in the
554
+ * sandbox pane header. Doesn't touch the log pane.
555
+ */
556
+ clearSandbox()
557
+ {
558
+ let tmpSandbox = document.getElementById('Docuserve-Fable-Playground-Sandbox');
559
+ if (tmpSandbox)
560
+ {
561
+ tmpSandbox.innerHTML = '';
562
+ }
508
563
  }
509
564
 
510
565
  /**
@@ -716,6 +771,17 @@ class DocuserveFablePlaygroundView extends libPictView
716
771
  this._cancelAsyncRender();
717
772
  this._cancelFinishBanner();
718
773
 
774
+ // Re-evaluate sandbox-pane visibility every run — covers the
775
+ // case where _playground.json finishes loading after the
776
+ // playground has already been mounted.
777
+ this._applySandboxMode();
778
+
779
+ // Reset the DOM sandbox so each Run starts on a clean canvas.
780
+ // Doing this BEFORE the user code runs (not after) means async
781
+ // renders that arrive on a later tick aren't wiped out. Snippets
782
+ // in console-mode docs leave the sandbox empty and pay nothing.
783
+ this.clearSandbox();
784
+
719
785
  let tmpCode = this._getCurrentCode();
720
786
  this.pict.AppData.Docuserve.Playground.Code = tmpCode;
721
787
 
@@ -742,7 +808,17 @@ class DocuserveFablePlaygroundView extends libPictView
742
808
  // (firing template-render traces) keep using the global
743
809
  // console untouched — avoids the obvious feedback loop.
744
810
 
811
+ // Sandbox-mode flag determines whether we hand the user a fresh
812
+ // Pict (DOM-mode docs need view registrations isolated per run)
813
+ // or the docuserve app's pict (console-mode docs preserve today's
814
+ // behaviour exactly).
815
+ let tmpConfig = this.pict.AppData.Docuserve
816
+ && this.pict.AppData.Docuserve.Playground
817
+ && this.pict.AppData.Docuserve.Playground.Config;
818
+ let tmpSandboxMode = (tmpConfig && tmpConfig.Sandbox === 'dom');
819
+
745
820
  let tmpFable;
821
+ let tmpUserPict = this.pict;
746
822
  try
747
823
  {
748
824
  let tmpFableClass = this.fable && this.fable.constructor;
@@ -750,8 +826,25 @@ class DocuserveFablePlaygroundView extends libPictView
750
826
  {
751
827
  throw new Error('Fable constructor unavailable on this.fable');
752
828
  }
753
- tmpFable = new tmpFableClass({ Product: 'FablePlayground', LogStreams: [] });
754
- tmpFable.log.addLogger(this._buildCaptureLogger(tmpRecords, tmpStart), 'trace');
829
+ if (tmpSandboxMode)
830
+ {
831
+ // DOM mode: fresh Pict (extends Fable) so user code's
832
+ // `pict.addView`, `pict.TemplateProvider`, etc. don't
833
+ // pollute the docuserve UI. The Pict instance IS a
834
+ // Fable, so `fable === pict` here — same capture
835
+ // logger, single object.
836
+ tmpUserPict = new libPict({ Product: 'FablePlayground', LogStreams: [] });
837
+ tmpUserPict.log.addLogger(this._buildCaptureLogger(tmpRecords, tmpStart), 'trace');
838
+ tmpFable = tmpUserPict;
839
+ }
840
+ else
841
+ {
842
+ tmpFable = new tmpFableClass({ Product: 'FablePlayground', LogStreams: [] });
843
+ tmpFable.log.addLogger(this._buildCaptureLogger(tmpRecords, tmpStart), 'trace');
844
+ // Console-mode: `pict` param stays docuserve's pict
845
+ // for backwards compat with the fable-family docs.
846
+ tmpUserPict = this.pict;
847
+ }
755
848
  }
756
849
  catch (pError)
757
850
  {
@@ -790,9 +883,10 @@ class DocuserveFablePlaygroundView extends libPictView
790
883
  // into error-level records in the log pane.
791
884
  let tmpRequire = this._buildRequireShim(tmpFable, tmpRecords, tmpStart);
792
885
  let tmpConsole = this._buildCapturingConsole(tmpRecords, tmpStart);
793
- let tmpFn = new Function('fable', 'pict', 'require', 'console',
886
+ let tmpSandboxEl = document.getElementById('Docuserve-Fable-Playground-Sandbox');
887
+ let tmpFn = new Function('fable', 'pict', 'require', 'console', 'sandbox',
794
888
  'return (async () => { ' + tmpCode + '\n})();');
795
- let tmpPromise = tmpFn(tmpFable, this.pict, tmpRequire, tmpConsole);
889
+ let tmpPromise = tmpFn(tmpFable, tmpUserPict, tmpRequire, tmpConsole, tmpSandboxEl);
796
890
  if (tmpPromise && typeof tmpPromise.then === 'function')
797
891
  {
798
892
  let tmpSelf = this;
@@ -1137,16 +1231,54 @@ class DocuserveFablePlaygroundView extends libPictView
1137
1231
  return tmpInstance;
1138
1232
  }
1139
1233
 
1234
+ // Wrapper around the FableLog class. fable-log's default
1235
+ // console stream calls globalThis.console.log directly — which
1236
+ // the playground's per-run console shim (scoped through the
1237
+ // IIFE parameter) can't see. By attaching our capture logger
1238
+ // to every new FableLog instance, log.* calls land in the panel
1239
+ // regardless of which streams the user configured. Mirrors
1240
+ // `PlaygroundFableWrapper` above. Static FableLog properties
1241
+ // (LogProviderBase, LogProviderConsole, etc.) are forwarded so
1242
+ // `FableLog.LogProviderBase` still works in user code.
1243
+ let tmpFableLogClass = pFableInstance.Logging && pFableInstance.Logging.constructor;
1244
+ function PlaygroundFableLogWrapper(pSettings, pServiceHash)
1245
+ {
1246
+ let tmpInstance = new tmpFableLogClass(pSettings, pServiceHash);
1247
+ tmpInstance.addLogger(tmpSelf._buildCaptureLogger(pRecords, pStart), 'trace');
1248
+ return tmpInstance;
1249
+ }
1250
+ if (tmpFableLogClass)
1251
+ {
1252
+ // Forward static / class-level properties the user might
1253
+ // reach for (LogProviderBase etc.). Done by iterating own
1254
+ // enumerable keys plus the well-known statics, so any
1255
+ // future additions to FableLog flow through automatically.
1256
+ let tmpKeys = Object.getOwnPropertyNames(tmpFableLogClass);
1257
+ for (let i = 0; i < tmpKeys.length; i++)
1258
+ {
1259
+ let tmpKey = tmpKeys[i];
1260
+ if (tmpKey === 'length' || tmpKey === 'name' || tmpKey === 'prototype') { continue; }
1261
+ try { PlaygroundFableLogWrapper[tmpKey] = tmpFableLogClass[tmpKey]; }
1262
+ catch (pError) { /* non-forwardable property — skip */ }
1263
+ }
1264
+ // Legacy factory: `FableLog.new(settings)` should also work.
1265
+ PlaygroundFableLogWrapper.new = function (pSettings)
1266
+ {
1267
+ return PlaygroundFableLogWrapper(pSettings);
1268
+ };
1269
+ }
1270
+
1140
1271
  // Built-in resolvers — every "bundled" module in the schema
1141
1272
  // goes through one of these. Future "cdn" / "dynamic" sources
1142
1273
  // would be wired in alongside as lazy promises.
1143
1274
  let tmpResolvers =
1144
1275
  {
1145
1276
  'fable': () => tmpAutoCapture ? PlaygroundFableWrapper : tmpFableClass,
1146
- 'pict': () => tmpPict.constructor,
1277
+ 'pict': () => libPict,
1278
+ 'pict-section-histogram': () => libPictSectionHistogram,
1147
1279
  'fable-uuid': () => pFableInstance.UUID && pFableInstance.UUID.constructor,
1148
1280
  'fable-settings': () => pFableInstance.SettingsManager && pFableInstance.SettingsManager.constructor,
1149
- 'fable-log': () => pFableInstance.Logging && pFableInstance.Logging.constructor,
1281
+ 'fable-log': () => tmpFableLogClass ? PlaygroundFableLogWrapper : null,
1150
1282
  'fable-serviceproviderbase': () => libFableServiceProviderBase
1151
1283
  };
1152
1284