retold-content-system 1.0.9 → 1.0.11
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 +8 -5
- package/source/Pict-Application-ContentEditor.js +11 -17
- package/source/cli/ContentSystem-Server-Setup.js +271 -0
- package/source/cli/commands/ContentSystem-Command-Serve.js +22 -1
- package/source/views/PictView-Editor-SettingsPanel.js +15 -28
- package/web-application/js/pict.min.js +2 -2
- package/web-application/js/pict.min.js.map +1 -1
- package/web-application/retold-content-system.compatible.js +71 -78
- package/web-application/retold-content-system.compatible.js.map +1 -1
- package/web-application/retold-content-system.compatible.min.js +6 -6
- package/web-application/retold-content-system.compatible.min.js.map +1 -1
- package/web-application/retold-content-system.js +55 -62
- package/web-application/retold-content-system.js.map +1 -1
- package/web-application/retold-content-system.min.js +2 -2
- package/web-application/retold-content-system.min.js.map +1 -1
- package/source/server/Simple-Server.js +0 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "retold-content-system",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Retold Content System - Markdown content viewer and editor",
|
|
5
5
|
"main": "source/Pict-ContentSystem-Bundle.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"start": "node source/
|
|
20
|
+
"start": "node source/cli/ContentSystem-CLI-Run.js serve",
|
|
21
21
|
"build": "npx quack build && npx quack copy",
|
|
22
22
|
"build-codemirror": "node build/build-codemirror-bundle.js",
|
|
23
23
|
"build-codejar": "node build/build-codejar-bundle.js",
|
|
@@ -27,11 +27,14 @@
|
|
|
27
27
|
},
|
|
28
28
|
"author": "steven velozo <steven@velozo.com>",
|
|
29
29
|
"license": "MIT",
|
|
30
|
+
"optionalDependencies": {
|
|
31
|
+
"ultravisor-beacon": "^0.0.1"
|
|
32
|
+
},
|
|
30
33
|
"dependencies": {
|
|
31
|
-
"fable": "^3.1.
|
|
34
|
+
"fable": "^3.1.66",
|
|
32
35
|
"orator": "^6.0.4",
|
|
33
36
|
"orator-serviceserver-restify": "^2.0.9",
|
|
34
|
-
"pict": "^1.0.
|
|
37
|
+
"pict": "^1.0.359",
|
|
35
38
|
"pict-application": "^1.0.33",
|
|
36
39
|
"pict-docuserve": "^0.0.32",
|
|
37
40
|
"pict-provider": "^1.0.12",
|
|
@@ -50,7 +53,7 @@
|
|
|
50
53
|
"codemirror": "^6.0.2",
|
|
51
54
|
"esbuild": "^0.27.4",
|
|
52
55
|
"highlight.js": "^11.11.1",
|
|
53
|
-
"quackage": "^1.0.
|
|
56
|
+
"quackage": "^1.0.65"
|
|
54
57
|
},
|
|
55
58
|
"copyFilesSettings": {
|
|
56
59
|
"whenFileExists": "overwrite"
|
|
@@ -117,7 +117,7 @@ class ContentEditorApplication extends libPictApplication
|
|
|
117
117
|
// Settings
|
|
118
118
|
AutoSegmentMarkdown: false,
|
|
119
119
|
AutoSegmentDepth: 1,
|
|
120
|
-
|
|
120
|
+
ContentPreviewMode: 'off',
|
|
121
121
|
MarkdownEditingControls: true,
|
|
122
122
|
MarkdownWordWrap: true,
|
|
123
123
|
CodeWordWrap: false,
|
|
@@ -738,19 +738,8 @@ class ContentEditorApplication extends libPictApplication
|
|
|
738
738
|
tmpEditorView.render();
|
|
739
739
|
tmpEditorView.marshalToView();
|
|
740
740
|
|
|
741
|
-
//
|
|
742
|
-
|
|
743
|
-
tmpEditorView.togglePreview(true);
|
|
744
|
-
|
|
745
|
-
// Set per-segment preview visibility based on the
|
|
746
|
-
// Auto Content Preview setting. We must always loop
|
|
747
|
-
// to clear any stale _hiddenPreviewSegments state
|
|
748
|
-
// from previous file loads.
|
|
749
|
-
let tmpShowPreviews = !!tmpSelf.pict.AppData.ContentEditor.AutoContentPreview;
|
|
750
|
-
for (let tmpIdx in tmpEditorView._segmentEditors)
|
|
751
|
-
{
|
|
752
|
-
tmpEditorView.toggleSegmentPreview(parseInt(tmpIdx, 10), tmpShowPreviews);
|
|
753
|
-
}
|
|
741
|
+
// Apply the Content Preview Mode setting
|
|
742
|
+
tmpEditorView.setPreviewMode(tmpSelf.pict.AppData.ContentEditor.ContentPreviewMode || 'off');
|
|
754
743
|
|
|
755
744
|
// Apply the Editing Controls setting (line numbers
|
|
756
745
|
// and right sidebar) via the library's toggleControls.
|
|
@@ -1426,7 +1415,7 @@ class ContentEditorApplication extends libPictApplication
|
|
|
1426
1415
|
{
|
|
1427
1416
|
AutoSegmentMarkdown: tmpSettings.AutoSegmentMarkdown,
|
|
1428
1417
|
AutoSegmentDepth: tmpSettings.AutoSegmentDepth,
|
|
1429
|
-
|
|
1418
|
+
ContentPreviewMode: tmpSettings.ContentPreviewMode,
|
|
1430
1419
|
MarkdownEditingControls: tmpSettings.MarkdownEditingControls,
|
|
1431
1420
|
MarkdownWordWrap: tmpSettings.MarkdownWordWrap,
|
|
1432
1421
|
CodeWordWrap: tmpSettings.CodeWordWrap,
|
|
@@ -1479,9 +1468,14 @@ class ContentEditorApplication extends libPictApplication
|
|
|
1479
1468
|
{
|
|
1480
1469
|
tmpSettings.AutoSegmentDepth = tmpStored.AutoSegmentDepth;
|
|
1481
1470
|
}
|
|
1482
|
-
if (typeof (tmpStored.
|
|
1471
|
+
if (typeof (tmpStored.ContentPreviewMode) === 'string')
|
|
1472
|
+
{
|
|
1473
|
+
tmpSettings.ContentPreviewMode = tmpStored.ContentPreviewMode;
|
|
1474
|
+
}
|
|
1475
|
+
else if (typeof (tmpStored.AutoContentPreview) === 'boolean')
|
|
1483
1476
|
{
|
|
1484
|
-
|
|
1477
|
+
// Backward compat: migrate old boolean setting
|
|
1478
|
+
tmpSettings.ContentPreviewMode = tmpStored.AutoContentPreview ? 'bottom' : 'off';
|
|
1485
1479
|
}
|
|
1486
1480
|
if (typeof (tmpStored.MarkdownEditingControls) === 'boolean')
|
|
1487
1481
|
{
|
|
@@ -402,6 +402,15 @@ function setupContentSystemServer(pOptions, fCallback)
|
|
|
402
402
|
tmpOrator.startService(
|
|
403
403
|
function ()
|
|
404
404
|
{
|
|
405
|
+
// --- Optional Ultravisor Beacon Integration ---
|
|
406
|
+
// When pOptions.Beacon is provided and Enabled is true,
|
|
407
|
+
// register this content system as a beacon worker.
|
|
408
|
+
let tmpBeaconConfig = pOptions.Beacon || {};
|
|
409
|
+
if (tmpBeaconConfig.Enabled)
|
|
410
|
+
{
|
|
411
|
+
_initializeBeacon(tmpFable, tmpContentPath, tmpBeaconConfig);
|
|
412
|
+
}
|
|
413
|
+
|
|
405
414
|
return fCallback(null,
|
|
406
415
|
{
|
|
407
416
|
Fable: tmpFable,
|
|
@@ -412,4 +421,266 @@ function setupContentSystemServer(pOptions, fCallback)
|
|
|
412
421
|
});
|
|
413
422
|
}
|
|
414
423
|
|
|
424
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
425
|
+
// Ultravisor Beacon Integration
|
|
426
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Initialize the Ultravisor beacon service and register ContentSystem
|
|
430
|
+
* capabilities. This is opt-in — only called when Beacon.Enabled is true.
|
|
431
|
+
*
|
|
432
|
+
* @param {object} pFable - Fable instance
|
|
433
|
+
* @param {string} pContentPath - Resolved content directory path
|
|
434
|
+
* @param {object} pBeaconConfig - Beacon configuration object
|
|
435
|
+
*/
|
|
436
|
+
function _initializeBeacon(pFable, pContentPath, pBeaconConfig)
|
|
437
|
+
{
|
|
438
|
+
let libBeaconService;
|
|
439
|
+
try
|
|
440
|
+
{
|
|
441
|
+
libBeaconService = require('ultravisor-beacon');
|
|
442
|
+
}
|
|
443
|
+
catch (pError)
|
|
444
|
+
{
|
|
445
|
+
pFable.log.warn(`Content System Beacon: ultravisor-beacon not installed. Skipping beacon init.`);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
pFable.serviceManager.addAndInstantiateServiceType('UltravisorBeacon', libBeaconService, pBeaconConfig);
|
|
450
|
+
let tmpBeacon = pFable.services.UltravisorBeacon;
|
|
451
|
+
|
|
452
|
+
tmpBeacon.registerCapability({
|
|
453
|
+
Capability: 'ContentSystem',
|
|
454
|
+
Name: 'ContentSystemProvider',
|
|
455
|
+
actions:
|
|
456
|
+
{
|
|
457
|
+
'ReadFile':
|
|
458
|
+
{
|
|
459
|
+
Description: 'Read a content file',
|
|
460
|
+
SettingsSchema: [
|
|
461
|
+
{ Name: 'FilePath', DataType: 'String', Required: true }
|
|
462
|
+
],
|
|
463
|
+
Handler: function (pWorkItem, pContext, fCallback)
|
|
464
|
+
{
|
|
465
|
+
try
|
|
466
|
+
{
|
|
467
|
+
let tmpFilePath = sanitizePath(pWorkItem.Settings.FilePath);
|
|
468
|
+
if (!tmpFilePath)
|
|
469
|
+
{
|
|
470
|
+
return fCallback(null, {
|
|
471
|
+
Outputs: { Content: '', StdOut: 'Invalid file path.' },
|
|
472
|
+
Log: ['ReadFile: invalid or unsafe path rejected.']
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
let tmpFullPath = libPath.join(pContentPath, tmpFilePath);
|
|
477
|
+
let tmpRealContent = libFs.realpathSync(pContentPath);
|
|
478
|
+
if (!tmpFullPath.startsWith(tmpRealContent))
|
|
479
|
+
{
|
|
480
|
+
return fCallback(null, {
|
|
481
|
+
Outputs: { Content: '', StdOut: 'Access denied.' },
|
|
482
|
+
Log: ['ReadFile: path outside content directory.']
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (!libFs.existsSync(tmpFullPath))
|
|
487
|
+
{
|
|
488
|
+
return fCallback(null, {
|
|
489
|
+
Outputs: { Content: '', StdOut: 'File not found.' },
|
|
490
|
+
Log: [`ReadFile: ${tmpFilePath} not found.`]
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
let tmpContent = libFs.readFileSync(tmpFullPath, 'utf8');
|
|
495
|
+
pFable.log.info(`Beacon ReadFile: ${tmpFilePath} (${tmpContent.length} bytes)`);
|
|
496
|
+
return fCallback(null, {
|
|
497
|
+
Outputs: { Content: tmpContent, StdOut: `Read ${tmpContent.length} bytes from ${tmpFilePath}` },
|
|
498
|
+
Log: [`ReadFile: read ${tmpContent.length} bytes from ${tmpFilePath}`]
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
catch (pError)
|
|
502
|
+
{
|
|
503
|
+
return fCallback(pError);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
'SaveFile':
|
|
509
|
+
{
|
|
510
|
+
Description: 'Save content to a file',
|
|
511
|
+
SettingsSchema: [
|
|
512
|
+
{ Name: 'FilePath', DataType: 'String', Required: true },
|
|
513
|
+
{ Name: 'Content', DataType: 'String', Required: true }
|
|
514
|
+
],
|
|
515
|
+
Handler: function (pWorkItem, pContext, fCallback)
|
|
516
|
+
{
|
|
517
|
+
try
|
|
518
|
+
{
|
|
519
|
+
let tmpFilePath = sanitizePath(pWorkItem.Settings.FilePath);
|
|
520
|
+
if (!tmpFilePath)
|
|
521
|
+
{
|
|
522
|
+
return fCallback(null, {
|
|
523
|
+
Outputs: { FilePath: '', StdOut: 'Invalid file path.' },
|
|
524
|
+
Log: ['SaveFile: invalid or unsafe path rejected.']
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
let tmpFullPath = libPath.join(pContentPath, tmpFilePath);
|
|
529
|
+
let tmpRealContent = libFs.realpathSync(pContentPath);
|
|
530
|
+
let tmpTargetDir = libPath.dirname(tmpFullPath);
|
|
531
|
+
if (!tmpTargetDir.startsWith(tmpRealContent))
|
|
532
|
+
{
|
|
533
|
+
return fCallback(null, {
|
|
534
|
+
Outputs: { FilePath: '', StdOut: 'Access denied.' },
|
|
535
|
+
Log: ['SaveFile: path outside content directory.']
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Ensure directory exists
|
|
540
|
+
if (!libFs.existsSync(tmpTargetDir))
|
|
541
|
+
{
|
|
542
|
+
libFs.mkdirSync(tmpTargetDir, { recursive: true });
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
let tmpContent = pWorkItem.Settings.Content || '';
|
|
546
|
+
libFs.writeFileSync(tmpFullPath, tmpContent, 'utf8');
|
|
547
|
+
pFable.log.info(`Beacon SaveFile: ${tmpFilePath} (${tmpContent.length} bytes)`);
|
|
548
|
+
return fCallback(null, {
|
|
549
|
+
Outputs: { FilePath: tmpFilePath, StdOut: `Saved ${tmpContent.length} bytes to ${tmpFilePath}` },
|
|
550
|
+
Log: [`SaveFile: wrote ${tmpContent.length} bytes to ${tmpFilePath}`]
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
catch (pError)
|
|
554
|
+
{
|
|
555
|
+
return fCallback(pError);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
'ListFiles':
|
|
561
|
+
{
|
|
562
|
+
Description: 'List files in a content directory',
|
|
563
|
+
SettingsSchema: [
|
|
564
|
+
{ Name: 'Path', DataType: 'String', Required: false }
|
|
565
|
+
],
|
|
566
|
+
Handler: function (pWorkItem, pContext, fCallback)
|
|
567
|
+
{
|
|
568
|
+
try
|
|
569
|
+
{
|
|
570
|
+
let tmpRelPath = pWorkItem.Settings.Path || '';
|
|
571
|
+
let tmpSafePath = tmpRelPath ? sanitizePath(tmpRelPath) : '';
|
|
572
|
+
let tmpTargetPath = tmpSafePath
|
|
573
|
+
? libPath.join(pContentPath, tmpSafePath)
|
|
574
|
+
: pContentPath;
|
|
575
|
+
|
|
576
|
+
let tmpRealContent = libFs.realpathSync(pContentPath);
|
|
577
|
+
if (!tmpTargetPath.startsWith(tmpRealContent))
|
|
578
|
+
{
|
|
579
|
+
return fCallback(null, {
|
|
580
|
+
Outputs: { Files: '[]', StdOut: 'Access denied.' },
|
|
581
|
+
Log: ['ListFiles: path outside content directory.']
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!libFs.existsSync(tmpTargetPath))
|
|
586
|
+
{
|
|
587
|
+
return fCallback(null, {
|
|
588
|
+
Outputs: { Files: '[]', StdOut: 'Directory not found.' },
|
|
589
|
+
Log: [`ListFiles: ${tmpSafePath || '/'} not found.`]
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
let tmpEntries = libFs.readdirSync(tmpTargetPath, { withFileTypes: true });
|
|
594
|
+
let tmpFiles = tmpEntries.map(function (pEntry)
|
|
595
|
+
{
|
|
596
|
+
return {
|
|
597
|
+
Name: pEntry.name,
|
|
598
|
+
IsDirectory: pEntry.isDirectory()
|
|
599
|
+
};
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
pFable.log.info(`Beacon ListFiles: ${tmpSafePath || '/'} (${tmpFiles.length} entries)`);
|
|
603
|
+
return fCallback(null, {
|
|
604
|
+
Outputs: { Files: JSON.stringify(tmpFiles), StdOut: `Found ${tmpFiles.length} entries in ${tmpSafePath || '/'}` },
|
|
605
|
+
Log: [`ListFiles: ${tmpFiles.length} entries in ${tmpSafePath || '/'}`]
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
catch (pError)
|
|
609
|
+
{
|
|
610
|
+
return fCallback(pError);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
|
|
615
|
+
'CreateFolder':
|
|
616
|
+
{
|
|
617
|
+
Description: 'Create a folder in the content directory',
|
|
618
|
+
SettingsSchema: [
|
|
619
|
+
{ Name: 'Path', DataType: 'String', Required: true }
|
|
620
|
+
],
|
|
621
|
+
Handler: function (pWorkItem, pContext, fCallback)
|
|
622
|
+
{
|
|
623
|
+
try
|
|
624
|
+
{
|
|
625
|
+
let tmpSafePath = sanitizePath(pWorkItem.Settings.Path);
|
|
626
|
+
if (!tmpSafePath)
|
|
627
|
+
{
|
|
628
|
+
return fCallback(null, {
|
|
629
|
+
Outputs: { StdOut: 'Invalid folder path.' },
|
|
630
|
+
Log: ['CreateFolder: invalid or unsafe path rejected.']
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
let tmpFullPath = libPath.join(pContentPath, tmpSafePath);
|
|
635
|
+
let tmpRealContent = libFs.realpathSync(pContentPath);
|
|
636
|
+
let tmpTargetParent = libPath.dirname(tmpFullPath);
|
|
637
|
+
if (libFs.existsSync(tmpTargetParent))
|
|
638
|
+
{
|
|
639
|
+
tmpTargetParent = libFs.realpathSync(tmpTargetParent);
|
|
640
|
+
}
|
|
641
|
+
if (!tmpTargetParent.startsWith(tmpRealContent))
|
|
642
|
+
{
|
|
643
|
+
return fCallback(null, {
|
|
644
|
+
Outputs: { StdOut: 'Access denied.' },
|
|
645
|
+
Log: ['CreateFolder: path outside content directory.']
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (libFs.existsSync(tmpFullPath))
|
|
650
|
+
{
|
|
651
|
+
return fCallback(null, {
|
|
652
|
+
Outputs: { StdOut: 'Folder already exists.' },
|
|
653
|
+
Log: [`CreateFolder: ${tmpSafePath} already exists.`]
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
libFs.mkdirSync(tmpFullPath, { recursive: true });
|
|
658
|
+
pFable.log.info(`Beacon CreateFolder: ${tmpSafePath}`);
|
|
659
|
+
return fCallback(null, {
|
|
660
|
+
Outputs: { StdOut: `Created folder ${tmpSafePath}` },
|
|
661
|
+
Log: [`CreateFolder: created ${tmpSafePath}`]
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
catch (pError)
|
|
665
|
+
{
|
|
666
|
+
return fCallback(pError);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
tmpBeacon.enable(function (pError)
|
|
674
|
+
{
|
|
675
|
+
if (pError)
|
|
676
|
+
{
|
|
677
|
+
pFable.log.warn(`Content System Beacon: enable failed: ${pError.message}`);
|
|
678
|
+
}
|
|
679
|
+
else
|
|
680
|
+
{
|
|
681
|
+
pFable.log.info('Content System Beacon: enabled and connected to Ultravisor.');
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
415
686
|
module.exports = setupContentSystemServer;
|
|
@@ -18,6 +18,15 @@ class ContentSystemCommandServe extends libCommandLineCommand
|
|
|
18
18
|
this.options.CommandOptions.push(
|
|
19
19
|
{ Name: '-p, --port [port]', Description: 'Port to serve on (defaults to random 7000-7999).', Default: '0' });
|
|
20
20
|
|
|
21
|
+
this.options.CommandOptions.push(
|
|
22
|
+
{ Name: '-b, --beacon [url]', Description: 'Enable beacon mode and connect to the Ultravisor server at [url] (e.g. http://localhost:54321).' });
|
|
23
|
+
|
|
24
|
+
this.options.CommandOptions.push(
|
|
25
|
+
{ Name: '--beacon-name [name]', Description: 'Beacon identity name (defaults to "content-system-1").', Default: 'content-system-1' });
|
|
26
|
+
|
|
27
|
+
this.options.CommandOptions.push(
|
|
28
|
+
{ Name: '--beacon-password [password]', Description: 'Beacon authentication password.', Default: '' });
|
|
29
|
+
|
|
21
30
|
this.addCommand();
|
|
22
31
|
}
|
|
23
32
|
|
|
@@ -55,6 +64,17 @@ class ContentSystemCommandServe extends libCommandLineCommand
|
|
|
55
64
|
this.log.info(`Created content directory: ${tmpContentPath}`);
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
let tmpBeaconConfig = {};
|
|
68
|
+
if (this.CommandOptions.beacon)
|
|
69
|
+
{
|
|
70
|
+
tmpBeaconConfig = {
|
|
71
|
+
Enabled: true,
|
|
72
|
+
ServerURL: (typeof this.CommandOptions.beacon === 'string') ? this.CommandOptions.beacon : 'http://localhost:54321',
|
|
73
|
+
Name: this.CommandOptions.beaconName || 'content-system-1',
|
|
74
|
+
Password: this.CommandOptions.beaconPassword || ''
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
58
78
|
let tmpSelf = this;
|
|
59
79
|
let tmpSetupServer = require('../ContentSystem-Server-Setup.js');
|
|
60
80
|
|
|
@@ -62,7 +82,8 @@ class ContentSystemCommandServe extends libCommandLineCommand
|
|
|
62
82
|
{
|
|
63
83
|
ContentPath: tmpContentPath,
|
|
64
84
|
DistPath: tmpDistPath,
|
|
65
|
-
Port: tmpPort
|
|
85
|
+
Port: tmpPort,
|
|
86
|
+
Beacon: tmpBeaconConfig
|
|
66
87
|
},
|
|
67
88
|
function (pError, pServerInfo)
|
|
68
89
|
{
|
|
@@ -274,11 +274,15 @@ const _ViewConfiguration =
|
|
|
274
274
|
onchange="{~P~}.views['ContentEditor-SettingsPanel'].onEditingControlsChanged(this.checked)">
|
|
275
275
|
</div>
|
|
276
276
|
<div class="content-editor-settings-row">
|
|
277
|
-
<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
277
|
+
<span class="content-editor-settings-select-label">Content Preview</span>
|
|
278
|
+
<select class="content-editor-settings-select"
|
|
279
|
+
id="ContentEditor-Setting-ContentPreviewMode"
|
|
280
|
+
onchange="{~P~}.views['ContentEditor-SettingsPanel'].onContentPreviewModeChanged(this.value)">
|
|
281
|
+
<option value="off">Off</option>
|
|
282
|
+
<option value="bottom">Underneath</option>
|
|
283
|
+
<option value="side">Beside</option>
|
|
284
|
+
<option value="tabbed">Tab</option>
|
|
285
|
+
</select>
|
|
282
286
|
</div>
|
|
283
287
|
<div class="content-editor-settings-row">
|
|
284
288
|
<label class="content-editor-settings-checkbox-label"
|
|
@@ -387,10 +391,10 @@ class ContentEditorSettingsPanelView extends libPictView
|
|
|
387
391
|
tmpControlsCheckbox[0].checked = tmpSettings.MarkdownEditingControls;
|
|
388
392
|
}
|
|
389
393
|
|
|
390
|
-
let
|
|
391
|
-
if (
|
|
394
|
+
let tmpPreviewSelect = this.pict.ContentAssignment.getElement('#ContentEditor-Setting-ContentPreviewMode');
|
|
395
|
+
if (tmpPreviewSelect && tmpPreviewSelect[0])
|
|
392
396
|
{
|
|
393
|
-
|
|
397
|
+
tmpPreviewSelect[0].value = tmpSettings.ContentPreviewMode || 'off';
|
|
394
398
|
}
|
|
395
399
|
|
|
396
400
|
let tmpSegmentCheckbox = this.pict.ContentAssignment.getElement('#ContentEditor-Setting-AutoSegment');
|
|
@@ -535,32 +539,15 @@ class ContentEditorSettingsPanelView extends libPictView
|
|
|
535
539
|
}
|
|
536
540
|
}
|
|
537
541
|
|
|
538
|
-
|
|
542
|
+
onContentPreviewModeChanged(pMode)
|
|
539
543
|
{
|
|
540
|
-
this.pict.AppData.ContentEditor.
|
|
544
|
+
this.pict.AppData.ContentEditor.ContentPreviewMode = pMode;
|
|
541
545
|
this.pict.PictApplication.saveSettings();
|
|
542
546
|
|
|
543
547
|
let tmpEditorView = this.pict.views['ContentEditor-MarkdownEditor'];
|
|
544
548
|
if (tmpEditorView && this.pict.AppData.ContentEditor.ActiveEditor === 'markdown')
|
|
545
549
|
{
|
|
546
|
-
|
|
547
|
-
{
|
|
548
|
-
// Turning ON: clear global hidden class and show all previews
|
|
549
|
-
tmpEditorView.togglePreview(true);
|
|
550
|
-
for (let tmpIdx in tmpEditorView._segmentEditors)
|
|
551
|
-
{
|
|
552
|
-
tmpEditorView.toggleSegmentPreview(parseInt(tmpIdx, 10), true);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
else
|
|
556
|
-
{
|
|
557
|
-
// Turning OFF: hide each segment individually so
|
|
558
|
-
// per-segment toggle buttons still work
|
|
559
|
-
for (let tmpIdx in tmpEditorView._segmentEditors)
|
|
560
|
-
{
|
|
561
|
-
tmpEditorView.toggleSegmentPreview(parseInt(tmpIdx, 10), false);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
550
|
+
tmpEditorView.setPreviewMode(pMode);
|
|
564
551
|
}
|
|
565
552
|
}
|
|
566
553
|
|