retold-content-system 1.0.4 → 1.0.6

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.
@@ -4,6 +4,8 @@ The content editor is a browser-based application for editing markdown files and
4
4
 
5
5
  ## Layout
6
6
 
7
+ ![Editor-Overview](/content/1772209863522-Editor-Overview.png)
8
+
7
9
  The editor has three main areas:
8
10
 
9
11
  - **Top Bar** -- Shows the application name, current file name, save status, word/character stats, and action buttons (Save, Close).
@@ -2,6 +2,7 @@
2
2
 
3
3
  The Retold Content System is a local markdown editor and documentation viewer. Install it once with npm and point it at any folder of markdown files.
4
4
 
5
+
5
6
  ## Installation
6
7
 
7
8
  Install globally from npm:
@@ -12,6 +13,8 @@ npm install -g retold-content-system
12
13
 
13
14
  This gives you two equivalent CLI commands: `retold-content-system` and `rcs`.
14
15
 
16
+ ![Editor Overview](1772209863522-Editor-Overview.png)
17
+
15
18
  ## Quick Start
16
19
 
17
20
  Create a folder with some markdown files and serve it:
@@ -33,6 +36,7 @@ The server starts on a random port between 7000 and 7999 and prints the URLs for
33
36
 
34
37
  Open the **Reader** URL for a pict-docuserve documentation viewer, or the **Editor** URL for the full editing environment.
35
38
 
39
+
36
40
  ## Choosing a Port
37
41
 
38
42
  Pass `-p` to pin the server to a specific port:
@@ -41,6 +45,7 @@ Pass `-p` to pin the server to a specific port:
41
45
  rcs serve -p 8080
42
46
  ```
43
47
 
48
+
44
49
  ## Pointing at a Different Folder
45
50
 
46
51
  Provide a content path as the first argument:
@@ -51,6 +56,7 @@ rcs serve ~/projects/my-wiki
51
56
 
52
57
  If the target directory has a `content/` subfolder, the server uses that automatically. This means running `rcs serve` from a project root that has a `content/` directory does the right thing without extra arguments.
53
58
 
59
+
54
60
  ## What Gets Served
55
61
 
56
62
  The content system sets up three static routes and several API endpoints:
@@ -66,6 +72,7 @@ The content system sets up three static routes and several API endpoints:
66
72
  | `/api/content/save/*` | File content save API |
67
73
  | `/api/content/upload-image` | Image upload endpoint |
68
74
 
75
+
69
76
  ## Next Steps
70
77
 
71
78
  - Read the [Editor Guide](editor-guide.md) for a walkthrough of the editing UI
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retold-content-system",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Retold Content System - Markdown content viewer and editor",
5
5
  "main": "source/Pict-ContentSystem-Bundle.js",
6
6
  "bin": {
@@ -43,7 +43,7 @@
43
43
  "pict-section-code": "^1.0.3",
44
44
  "pict-section-content": "^0.0.8",
45
45
  "pict-section-filebrowser": "^0.0.2",
46
- "pict-section-markdowneditor": "^1.0.1",
46
+ "pict-section-markdowneditor": "^1.0.3",
47
47
  "pict-service-commandlineutility": "^1.0.19",
48
48
  "pict-view": "^1.0.67"
49
49
  },
package/server.js CHANGED
@@ -21,7 +21,6 @@ let tmpPort = parseInt(process.env.PORT, 10) || 8086;
21
21
  libSetupServer(
22
22
  {
23
23
  ContentPath: libPath.join(__dirname, 'content'),
24
- UploadPath: libPath.join(__dirname, 'uploads'),
25
24
  DistPath: libPath.join(__dirname, 'web-application'),
26
25
  Port: tmpPort
27
26
  },
@@ -36,7 +35,6 @@ libSetupServer(
36
35
  pServerInfo.Fable.log.info(` Retold Content System running on http://localhost:${pServerInfo.Port}`);
37
36
  pServerInfo.Fable.log.info('==========================================================');
38
37
  pServerInfo.Fable.log.info(` Content path: ${libPath.join(__dirname, 'content')}`);
39
- pServerInfo.Fable.log.info(` Upload path: ${libPath.join(__dirname, 'uploads')}`);
40
38
  pServerInfo.Fable.log.info(` Reader: http://localhost:${pServerInfo.Port}/`);
41
39
  pServerInfo.Fable.log.info(` Editor: http://localhost:${pServerInfo.Port}/edit.html`);
42
40
  pServerInfo.Fable.log.info('==========================================================');
@@ -46,9 +46,45 @@ class ContentEditorApplication extends libPictApplication
46
46
  tmpFileBrowserConfig.DefaultState.Layout = 'list-only';
47
47
  this.pict.addView('Pict-FileBrowser', tmpFileBrowserConfig, libPictSectionFileBrowser);
48
48
 
49
- // Register the list detail sub-view for the file list pane
50
- this.pict.addView('Pict-FileBrowser-ListDetail',
51
- libPictSectionFileBrowser.PictViewListDetail.default_configuration,
49
+ // Register the list detail sub-view for the file list pane.
50
+ // Override templates to:
51
+ // - Add a "create folder" button to the breadcrumb bar
52
+ // - Add a hover-visible "+" insert button on each file row
53
+ let tmpListDetailConfig = JSON.parse(JSON.stringify(
54
+ libPictSectionFileBrowser.PictViewListDetail.default_configuration));
55
+ for (let i = 0; i < tmpListDetailConfig.Templates.length; i++)
56
+ {
57
+ if (tmpListDetailConfig.Templates[i].Hash === 'FileBrowser-ListDetail-Container-Template')
58
+ {
59
+ tmpListDetailConfig.Templates[i].Template = /*html*/`
60
+ <div class="pict-fb-detail" id="Pict-FileBrowser-DetailList">
61
+ <div class="pict-fb-breadcrumb-bar">
62
+ <div class="pict-fb-breadcrumb" id="Pict-FileBrowser-Breadcrumb"></div>
63
+ <button class="pict-fb-breadcrumb-addfolder" onclick="pict.PictApplication.promptNewFolder()" title="New folder">+</button>
64
+ </div>
65
+ <div class="pict-fb-detail-header">
66
+ <div class="pict-fb-detail-header-cell pict-fb-detail-col-name" onclick="pict.views['{~D:Record.ViewHash~}'].sortBy('Name')">Name</div>
67
+ <div class="pict-fb-detail-header-cell pict-fb-detail-col-size" onclick="pict.views['{~D:Record.ViewHash~}'].sortBy('Size')">Size</div>
68
+ <div class="pict-fb-detail-header-cell pict-fb-detail-col-modified" onclick="pict.views['{~D:Record.ViewHash~}'].sortBy('Modified')">Modified</div>
69
+ </div>
70
+ <div id="Pict-FileBrowser-DetailRows"></div>
71
+ </div>
72
+ `;
73
+ }
74
+ if (tmpListDetailConfig.Templates[i].Hash === 'FileBrowser-ListDetail-Row-Template')
75
+ {
76
+ tmpListDetailConfig.Templates[i].Template = /*html*/`
77
+ <div class="pict-fb-detail-row{~D:Record.SelectedClass~}" data-index="{~D:Record.Index~}" data-name="{~D:Record.Name~}" onclick="{~D:Record.ClickHandler~}" ondblclick="{~D:Record.DblClickHandler~}">
78
+ <span class="pict-fb-detail-icon">{~D:Record.Icon~}</span>
79
+ <span class="pict-fb-detail-name">{~D:Record.Name~}</span>
80
+ <span class="pict-fb-detail-size">{~D:Record.SizeFormatted~}</span>
81
+ <span class="pict-fb-detail-modified">{~D:Record.ModifiedFormatted~}</span>
82
+ <button class="pict-fb-insert-btn" onclick="event.stopPropagation(); pict.PictApplication.insertFileReference(this.parentElement.getAttribute('data-name'))" title="Insert into editor">+</button>
83
+ </div>
84
+ `;
85
+ }
86
+ }
87
+ this.pict.addView('Pict-FileBrowser-ListDetail', tmpListDetailConfig,
52
88
  libPictSectionFileBrowser.PictViewListDetail);
53
89
  }
54
90
 
@@ -81,7 +117,7 @@ class ContentEditorApplication extends libPictApplication
81
117
  // Settings
82
118
  AutoSegmentMarkdown: false,
83
119
  AutoSegmentDepth: 1,
84
- AutoContentPreview: false,
120
+ AutoContentPreview: true,
85
121
  MarkdownEditingControls: true,
86
122
  MarkdownWordWrap: true,
87
123
  CodeWordWrap: false,
@@ -689,13 +725,32 @@ class ContentEditorApplication extends libPictApplication
689
725
  let tmpEditorView = tmpSelf.pict.views['ContentEditor-MarkdownEditor'];
690
726
  if (tmpEditorView)
691
727
  {
728
+ // Set the image base URL so relative image references
729
+ // resolve through the /content/ static route.
730
+ let tmpImageBase = '/content/';
731
+ let tmpLastSlash = pFilePath.lastIndexOf('/');
732
+ if (tmpLastSlash > 0)
733
+ {
734
+ tmpImageBase = '/content/' + pFilePath.substring(0, tmpLastSlash) + '/';
735
+ }
736
+ tmpEditorView.options.ImageBaseURL = tmpImageBase;
737
+
692
738
  tmpEditorView.render();
693
739
  tmpEditorView.marshalToView();
694
740
 
695
- // Apply the Auto Content Preview setting via the
696
- // library's own toggle so the per-segment button
697
- // continues to work.
698
- tmpEditorView.togglePreview(tmpSelf.pict.AppData.ContentEditor.AutoContentPreview);
741
+ // Always ensure the global preview class is clear so
742
+ // per-segment toggles work.
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
+ }
699
754
 
700
755
  // Apply the Editing Controls setting (line numbers
701
756
  // and right sidebar) via the library's toggleControls.
@@ -1007,6 +1062,126 @@ class ContentEditorApplication extends libPictApplication
1007
1062
  }
1008
1063
  }
1009
1064
 
1065
+ /**
1066
+ * Prompt the user for a folder name and create it in the current
1067
+ * browse location via the server API.
1068
+ */
1069
+ promptNewFolder()
1070
+ {
1071
+ let tmpFolderName = prompt('Enter a name for the new folder:');
1072
+ if (!tmpFolderName || !tmpFolderName.trim())
1073
+ {
1074
+ return;
1075
+ }
1076
+ tmpFolderName = tmpFolderName.trim();
1077
+
1078
+ // Build the full relative path inside the current browse location
1079
+ let tmpCurrentLocation = '';
1080
+ if (this.pict.AppData.PictFileBrowser && this.pict.AppData.PictFileBrowser.CurrentLocation)
1081
+ {
1082
+ tmpCurrentLocation = this.pict.AppData.PictFileBrowser.CurrentLocation;
1083
+ }
1084
+ let tmpPath = tmpCurrentLocation
1085
+ ? (tmpCurrentLocation + '/' + tmpFolderName)
1086
+ : tmpFolderName;
1087
+
1088
+ let tmpSelf = this;
1089
+
1090
+ fetch('/api/content/mkdir',
1091
+ {
1092
+ method: 'POST',
1093
+ headers: { 'Content-Type': 'application/json' },
1094
+ body: JSON.stringify({ Path: tmpPath })
1095
+ })
1096
+ .then((pResponse) => pResponse.json())
1097
+ .then((pData) =>
1098
+ {
1099
+ if (pData && pData.Success)
1100
+ {
1101
+ tmpSelf.log.info(`Folder created: ${tmpPath}`);
1102
+ // Refresh the file list to show the new folder
1103
+ tmpSelf.loadFileList();
1104
+ }
1105
+ else
1106
+ {
1107
+ alert('Could not create folder: ' + (pData ? pData.Error : 'Unknown error'));
1108
+ }
1109
+ })
1110
+ .catch((pError) =>
1111
+ {
1112
+ alert('Error creating folder: ' + pError.message);
1113
+ });
1114
+ }
1115
+
1116
+ /**
1117
+ * Insert a file reference into the active markdown editor segment.
1118
+ *
1119
+ * Called from the "+" button on file browser rows. For image files
1120
+ * this inserts markdown image syntax; for other files a markdown link.
1121
+ *
1122
+ * @param {string} pFilename - The filename to insert
1123
+ */
1124
+ insertFileReference(pFilename)
1125
+ {
1126
+ if (!pFilename)
1127
+ {
1128
+ return;
1129
+ }
1130
+
1131
+ let tmpEditorView = this.pict.views['ContentEditor-MarkdownEditor'];
1132
+ if (!tmpEditorView || this.pict.AppData.ContentEditor.ActiveEditor !== 'markdown')
1133
+ {
1134
+ return;
1135
+ }
1136
+
1137
+ // Determine the active segment (the one with focus, or the last one)
1138
+ let tmpSegmentIndex = tmpEditorView._activeSegmentIndex;
1139
+ if (tmpSegmentIndex < 0)
1140
+ {
1141
+ // Fall back to the first segment
1142
+ let tmpIndices = tmpEditorView._getOrderedSegmentIndices();
1143
+ if (tmpIndices.length > 0)
1144
+ {
1145
+ tmpSegmentIndex = tmpIndices[0];
1146
+ }
1147
+ }
1148
+ if (tmpSegmentIndex < 0)
1149
+ {
1150
+ return;
1151
+ }
1152
+
1153
+ // Build alt text from the filename (strip extension and timestamp prefix)
1154
+ let tmpAltText = pFilename.replace(/\.[^.]+$/, '');
1155
+ tmpAltText = tmpAltText.replace(/^\d{10,}-/, '');
1156
+ tmpAltText = tmpAltText.replace(/[-_]+/g, ' ').trim() || 'image';
1157
+
1158
+ // Check if this is an image file
1159
+ let tmpExt = pFilename.substring(pFilename.lastIndexOf('.')).toLowerCase();
1160
+ let tmpImageExts = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.avif', '.apng', '.ico', '.tiff', '.tif', '.jfif'];
1161
+
1162
+ if (tmpImageExts.indexOf(tmpExt) >= 0)
1163
+ {
1164
+ // Insert markdown image syntax — relative filename resolved by ImageBaseURL
1165
+ tmpEditorView._insertImageMarkdown(tmpSegmentIndex, pFilename, tmpAltText);
1166
+ }
1167
+ else
1168
+ {
1169
+ // Insert a markdown link for non-image files
1170
+ let tmpEditor = tmpEditorView._segmentEditors[tmpSegmentIndex];
1171
+ if (tmpEditor)
1172
+ {
1173
+ let tmpInsert = '[' + tmpAltText + '](' + pFilename + ')';
1174
+ let tmpCursorPos = tmpEditor.state.selection.main.head;
1175
+ tmpEditor.dispatch(
1176
+ {
1177
+ changes: { from: tmpCursorPos, insert: tmpInsert },
1178
+ selection: { anchor: tmpCursorPos + tmpInsert.length }
1179
+ });
1180
+ tmpEditor.focus();
1181
+ }
1182
+ }
1183
+ }
1184
+
1010
1185
  /**
1011
1186
  * Handle F4 / Cmd+Shift+T: context-aware topic creation.
1012
1187
  *
@@ -6,7 +6,6 @@
6
6
  *
7
7
  * @param {object} pOptions
8
8
  * @param {string} pOptions.ContentPath - Absolute path to the markdown content folder
9
- * @param {string} pOptions.UploadPath - Absolute path to the uploads folder
10
9
  * @param {string} pOptions.DistPath - Absolute path to the built web-application folder
11
10
  * @param {number} pOptions.Port - HTTP port to listen on
12
11
  * @param {Function} fCallback - Callback(pError, { Fable, Orator, Port })
@@ -84,7 +83,6 @@ function sanitizeFilename(pName)
84
83
  function setupContentSystemServer(pOptions, fCallback)
85
84
  {
86
85
  let tmpContentPath = pOptions.ContentPath;
87
- let tmpUploadPath = pOptions.UploadPath;
88
86
  let tmpDistFolder = pOptions.DistPath;
89
87
  let tmpPort = pOptions.Port;
90
88
 
@@ -93,18 +91,11 @@ function setupContentSystemServer(pOptions, fCallback)
93
91
  Product: 'Retold-Content-System',
94
92
  ProductVersion: require('../../package.json').version,
95
93
  APIServerPort: tmpPort,
96
- ContentPath: tmpContentPath,
97
- UploadPath: tmpUploadPath
94
+ ContentPath: tmpContentPath
98
95
  };
99
96
 
100
97
  let tmpFable = new libFable(tmpSettings);
101
98
 
102
- // Ensure the uploads directory exists
103
- if (!libFs.existsSync(tmpUploadPath))
104
- {
105
- libFs.mkdirSync(tmpUploadPath, { recursive: true });
106
- }
107
-
108
99
  // Ensure the content directory exists
109
100
  if (!libFs.existsSync(tmpContentPath))
110
101
  {
@@ -156,6 +147,57 @@ function setupContentSystemServer(pOptions, fCallback)
156
147
  return fNext();
157
148
  });
158
149
 
150
+ // --- POST /api/content/mkdir ---
151
+ // Create a new folder inside the content directory.
152
+ // Body: { Path: "relative/path/to/new-folder" }
153
+ tmpServiceServer.post('/api/content/mkdir',
154
+ (pRequest, pResponse, fNext) =>
155
+ {
156
+ try
157
+ {
158
+ let tmpRawPath = (pRequest.body && pRequest.body.Path) ? pRequest.body.Path : null;
159
+ let tmpSafePath = sanitizePath(tmpRawPath);
160
+
161
+ if (!tmpSafePath)
162
+ {
163
+ pResponse.send(400, { Success: false, Error: 'Invalid folder path.' });
164
+ return fNext();
165
+ }
166
+
167
+ let tmpFullPath = libPath.join(tmpContentPath, tmpSafePath);
168
+
169
+ // Ensure the resolved path is within the content directory
170
+ let tmpRealContent = libFs.realpathSync(tmpContentPath);
171
+ let tmpTargetParent = libPath.dirname(tmpFullPath);
172
+ if (libFs.existsSync(tmpTargetParent))
173
+ {
174
+ tmpTargetParent = libFs.realpathSync(tmpTargetParent);
175
+ }
176
+ if (!tmpTargetParent.startsWith(tmpRealContent))
177
+ {
178
+ pResponse.send(403, { Success: false, Error: 'Access denied.' });
179
+ return fNext();
180
+ }
181
+
182
+ if (libFs.existsSync(tmpFullPath))
183
+ {
184
+ pResponse.send(409, { Success: false, Error: 'Folder already exists.' });
185
+ return fNext();
186
+ }
187
+
188
+ libFs.mkdirSync(tmpFullPath, { recursive: true });
189
+
190
+ tmpFable.log.info(`Folder created: ${tmpSafePath}`);
191
+ pResponse.send({ Success: true, Path: tmpSafePath });
192
+ }
193
+ catch (pError)
194
+ {
195
+ tmpFable.log.error(`Folder creation failed: ${pError.message}`);
196
+ pResponse.send(500, { Success: false, Error: pError.message });
197
+ }
198
+ return fNext();
199
+ });
200
+
159
201
  // --- GET /api/content/read/* ---
160
202
  // Read the raw markdown content of a file
161
203
  tmpServiceServer.get('/api/content/read/*',
@@ -350,39 +392,7 @@ function setupContentSystemServer(pOptions, fCallback)
350
392
  return fNext();
351
393
  });
352
394
 
353
- // --- GET /api/content/uploads ---
354
- // List uploaded images
355
- tmpServiceServer.get('/api/content/uploads',
356
- (pRequest, pResponse, fNext) =>
357
- {
358
- try
359
- {
360
- let tmpFiles = libFs.readdirSync(tmpUploadPath);
361
- let tmpFileList = tmpFiles.map(
362
- (pFilename) =>
363
- {
364
- let tmpStat = libFs.statSync(libPath.join(tmpUploadPath, pFilename));
365
- return (
366
- {
367
- Filename: pFilename,
368
- URL: `/uploads/${pFilename}`,
369
- Size: tmpStat.size,
370
- Modified: tmpStat.mtime
371
- });
372
- });
373
- pResponse.send({ Success: true, Files: tmpFileList });
374
- }
375
- catch (pError)
376
- {
377
- pResponse.send(500, { Success: false, Error: pError.message });
378
- }
379
- return fNext();
380
- });
381
-
382
- // Serve uploaded images at /uploads/
383
- tmpOrator.addStaticRoute(`${tmpUploadPath}/`, 'index.html', '/uploads/*', '/uploads/');
384
-
385
- // Serve content markdown files at /content/ (for the reader)
395
+ // Serve content files (markdown, images, etc.) at /content/
386
396
  tmpOrator.addStaticRoute(`${tmpContentPath}/`, 'index.html', '/content/*', '/content/');
387
397
 
388
398
  // Serve the built application from dist/ (main static route)
@@ -38,7 +38,6 @@ class ContentSystemCommandServe extends libCommandLineCommand
38
38
  }
39
39
 
40
40
  let tmpDistPath = libPath.resolve(__dirname, '..', '..', '..', 'web-application');
41
- let tmpUploadPath = libPath.join(tmpContentPath, 'uploads');
42
41
  let tmpPortOption = parseInt(this.CommandOptions.port, 10);
43
42
  let tmpPort = (tmpPortOption > 0) ? tmpPortOption : (7000 + Math.floor(Math.random() * 1000));
44
43
 
@@ -56,19 +55,12 @@ class ContentSystemCommandServe extends libCommandLineCommand
56
55
  this.log.info(`Created content directory: ${tmpContentPath}`);
57
56
  }
58
57
 
59
- // Ensure uploads directory exists
60
- if (!libFs.existsSync(tmpUploadPath))
61
- {
62
- libFs.mkdirSync(tmpUploadPath, { recursive: true });
63
- }
64
-
65
58
  let tmpSelf = this;
66
59
  let tmpSetupServer = require('../ContentSystem-Server-Setup.js');
67
60
 
68
61
  tmpSetupServer(
69
62
  {
70
63
  ContentPath: tmpContentPath,
71
- UploadPath: tmpUploadPath,
72
64
  DistPath: tmpDistPath,
73
65
  Port: tmpPort
74
66
  },
@@ -85,10 +77,6 @@ class ContentSystemCommandServe extends libCommandLineCommand
85
77
  tmpSelf.log.info(` Retold Content System running on http://localhost:${pServerInfo.Port}`);
86
78
  tmpSelf.log.info('==========================================================');
87
79
  tmpSelf.log.info(` Content: ${tmpContentPath}`);
88
- tmpSelf.log.info(` Uploads: ${tmpUploadPath}`);
89
- tmpSelf.log.info(` Assets: ${tmpDistPath}`);
90
- tmpSelf.log.info(` Reader: http://localhost:${pServerInfo.Port}/`);
91
- tmpSelf.log.info(` Editor: http://localhost:${pServerInfo.Port}/edit.html`);
92
80
  tmpSelf.log.info('==========================================================');
93
81
  tmpSelf.log.info('');
94
82
  tmpSelf.log.info(' Press Ctrl+C to stop.');
@@ -160,31 +160,6 @@ class ContentEditorProvider extends libPictProvider
160
160
  });
161
161
  }
162
162
 
163
- /**
164
- * List uploaded images.
165
- *
166
- * @param {Function} fCallback - Callback receiving (error, filesArray)
167
- */
168
- listUploads(fCallback)
169
- {
170
- let tmpCallback = (typeof (fCallback) === 'function') ? fCallback : () => {};
171
-
172
- fetch('/api/content/uploads')
173
- .then((pResponse) => pResponse.json())
174
- .then((pData) =>
175
- {
176
- if (pData && pData.Success)
177
- {
178
- return tmpCallback(null, pData.Files || []);
179
- }
180
- return tmpCallback(pData ? pData.Error : 'Unknown error', []);
181
- })
182
- .catch((pError) =>
183
- {
184
- this.log.warn(`ContentEditor: Error listing uploads: ${pError}`);
185
- return tmpCallback(pError.message, []);
186
- });
187
- }
188
163
  }
189
164
 
190
165
  module.exports = ContentEditorProvider;