@underpostnet/underpost 2.98.0 → 2.98.3
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/.vscode/settings.json +7 -8
- package/README.md +2 -2
- package/bin/build.js +21 -5
- package/bin/deploy.js +10 -0
- package/bin/file.js +2 -1
- package/bin/util.js +0 -17
- package/cli.md +2 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +2 -4
- package/scripts/rocky-setup.sh +12 -39
- package/src/api/document/document.service.js +1 -1
- package/src/cli/cluster.js +5 -9
- package/src/cli/repository.js +9 -9
- package/src/cli/run.js +108 -106
- package/src/client/components/core/Content.js +54 -4
- package/src/client/components/core/FullScreen.js +202 -9
- package/src/client/components/core/Panel.js +91 -22
- package/src/client/components/core/PanelForm.js +5 -2
- package/src/client/components/core/Translate.js +8 -0
- package/src/client/components/core/VanillaJs.js +80 -29
- package/src/client/services/default/default.management.js +324 -136
- package/src/index.js +58 -20
- package/src/client/components/core/ObjectLayerEngine.js +0 -1520
- package/src/client/components/core/ObjectLayerEngineModal.js +0 -1245
- package/src/client/components/core/ObjectLayerEngineViewer.js +0 -880
- package/src/server/object-layer.js +0 -335
|
@@ -13,7 +13,7 @@ import { ToggleSwitch } from './ToggleSwitch.js';
|
|
|
13
13
|
import { RichText } from './RichText.js';
|
|
14
14
|
import { loggerFactory } from './Logger.js';
|
|
15
15
|
import { Badge } from './Badge.js';
|
|
16
|
-
import { Content } from './Content.js';
|
|
16
|
+
import { Content, attachMarkdownLinkHandlers } from './Content.js';
|
|
17
17
|
import { DocumentService } from '../../services/document/document.service.js';
|
|
18
18
|
import { NotificationManager } from './NotificationManager.js';
|
|
19
19
|
import { getApiBaseUrl } from '../../services/core/core.service.js';
|
|
@@ -37,6 +37,7 @@ const Panel = {
|
|
|
37
37
|
onClick: () => {},
|
|
38
38
|
share: {
|
|
39
39
|
copyLink: false,
|
|
40
|
+
copySourceMd: false,
|
|
40
41
|
},
|
|
41
42
|
showCreatorProfile: false,
|
|
42
43
|
},
|
|
@@ -152,6 +153,51 @@ const Panel = {
|
|
|
152
153
|
}
|
|
153
154
|
});
|
|
154
155
|
}
|
|
156
|
+
if (options.share && options.share.copySourceMd) {
|
|
157
|
+
EventsUI.onClick(
|
|
158
|
+
`.${idPanel}-btn-copy-source-md-${id}`,
|
|
159
|
+
async (e) => {
|
|
160
|
+
try {
|
|
161
|
+
const filesData = options.filesData();
|
|
162
|
+
const foundFiles = filesData.find((d) => String(d._id || d.id) === String(obj._id || obj.id));
|
|
163
|
+
|
|
164
|
+
if (foundFiles && foundFiles.mdFileId && foundFiles.mdFileId.mdPlain) {
|
|
165
|
+
await copyData(foundFiles.mdFileId.mdPlain);
|
|
166
|
+
await NotificationManager.Push({
|
|
167
|
+
status: 'success',
|
|
168
|
+
html: html`<div>${Translate.Render('markdown-source-copied')}</div>`,
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
await NotificationManager.Push({
|
|
172
|
+
status: 'warning',
|
|
173
|
+
html: html`<div>No markdown source available</div>`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
logger.error('Error copying markdown source:', error);
|
|
178
|
+
await NotificationManager.Push({
|
|
179
|
+
status: 'error',
|
|
180
|
+
html: html`<div>${Translate.Render('error-copying-markdown')}</div>`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{ context: 'modal' },
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Add tooltip hover effect
|
|
188
|
+
setTimeout(() => {
|
|
189
|
+
const btn = s(`.${idPanel}-btn-copy-source-md-${id}`);
|
|
190
|
+
const tooltip = s(`.${idPanel}-source-md-tooltip-${id}`);
|
|
191
|
+
if (btn && tooltip) {
|
|
192
|
+
btn.addEventListener('mouseenter', () => {
|
|
193
|
+
tooltip.style.opacity = '1';
|
|
194
|
+
});
|
|
195
|
+
btn.addEventListener('mouseleave', () => {
|
|
196
|
+
tooltip.style.opacity = '0';
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
155
201
|
EventsUI.onClick(
|
|
156
202
|
`.${idPanel}-btn-delete-${id}`,
|
|
157
203
|
async (e) => {
|
|
@@ -514,30 +560,50 @@ const Panel = {
|
|
|
514
560
|
</div>
|
|
515
561
|
</div>
|
|
516
562
|
</div>
|
|
517
|
-
${options.share && options.share.copyLink
|
|
563
|
+
${options.share && (options.share.copyLink || options.share.copySourceMd)
|
|
518
564
|
? html`<div
|
|
519
565
|
class="${idPanel}-share-btn-container ${idPanel}-share-btn-container-${id}"
|
|
520
|
-
style="position: absolute; bottom: 8px; right: 8px; z-index: 2;"
|
|
566
|
+
style="position: absolute; bottom: 8px; right: 8px; z-index: 2; display: flex; gap: 8px;"
|
|
521
567
|
>
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
568
|
+
${options.share.copyLink
|
|
569
|
+
? html`<div style="position: relative;">
|
|
570
|
+
<button
|
|
571
|
+
class="btn-icon ${idPanel}-btn-copy-share-${id}"
|
|
572
|
+
style="background: transparent; color: #888; border: none; border-radius: 50%; width: 40px; height: 40px; cursor: pointer; display: flex; align-items: center; justify-content: center; position: relative; transition: all 0.3s ease;"
|
|
573
|
+
>
|
|
574
|
+
<i class="fas fa-link" style="font-size: 20px;"></i>
|
|
575
|
+
${obj.totalCopyShareLinkCount && obj.totalCopyShareLinkCount > 0
|
|
576
|
+
? html`<span
|
|
577
|
+
class="${idPanel}-share-count-${id}"
|
|
578
|
+
style="position: absolute; top: -4px; right: -4px; background: #666; color: white; border-radius: 10px; padding: 1px 5px; font-size: 10px; font-weight: bold; min-width: 16px; text-align: center;"
|
|
579
|
+
>${obj.totalCopyShareLinkCount}</span
|
|
580
|
+
>`
|
|
581
|
+
: ''}
|
|
582
|
+
</button>
|
|
583
|
+
<div
|
|
584
|
+
class="${idPanel}-share-tooltip-${id}"
|
|
585
|
+
style="position: absolute; bottom: 50px; right: 0; background: rgba(0,0,0,0.8); color: white; padding: 6px 10px; border-radius: 4px; font-size: 12px; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.3s ease;"
|
|
586
|
+
>
|
|
587
|
+
${Translate.Render('copy-share-link')}
|
|
588
|
+
</div>
|
|
589
|
+
</div>`
|
|
590
|
+
: ''}
|
|
591
|
+
${options.share.copySourceMd
|
|
592
|
+
? html`<div style="position: relative;">
|
|
593
|
+
<button
|
|
594
|
+
class="btn-icon ${idPanel}-btn-copy-source-md-${id}"
|
|
595
|
+
style="background: transparent; color: #888; border: none; border-radius: 50%; width: 40px; height: 40px; cursor: pointer; display: flex; align-items: center; justify-content: center; position: relative; transition: all 0.3s ease;"
|
|
596
|
+
>
|
|
597
|
+
<i class="fas fa-code" style="font-size: 20px;"></i>
|
|
598
|
+
</button>
|
|
599
|
+
<div
|
|
600
|
+
class="${idPanel}-source-md-tooltip-${id}"
|
|
601
|
+
style="position: absolute; bottom: 50px; right: 0; background: rgba(0,0,0,0.8); color: white; padding: 6px 10px; border-radius: 4px; font-size: 12px; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.3s ease;"
|
|
602
|
+
>
|
|
603
|
+
Copy Source MD
|
|
604
|
+
</div>
|
|
605
|
+
</div>`
|
|
606
|
+
: ''}
|
|
541
607
|
</div>`
|
|
542
608
|
: ''}
|
|
543
609
|
</div>`;
|
|
@@ -748,6 +814,8 @@ const Panel = {
|
|
|
748
814
|
append(`.${idPanel}-render`, await renderPanel(doc));
|
|
749
815
|
}
|
|
750
816
|
} else htmls(`.${idPanel}-render`, await renderPanel({ ...obj, ...documents }));
|
|
817
|
+
|
|
818
|
+
attachMarkdownLinkHandlers(`.${idPanel}-render`);
|
|
751
819
|
Input.cleanValues(formData);
|
|
752
820
|
s(`.btn-${idPanel}-close`).click();
|
|
753
821
|
s(`.${scrollClassContainer}`).scrollTop = 0;
|
|
@@ -810,6 +878,7 @@ const Panel = {
|
|
|
810
878
|
if (options.on.initAdd) await options.on.initAdd();
|
|
811
879
|
};
|
|
812
880
|
if (s(`.${scrollClassContainer}`)) s(`.${scrollClassContainer}`).style.overflow = 'auto';
|
|
881
|
+
attachMarkdownLinkHandlers(`.${idPanel}-render`);
|
|
813
882
|
});
|
|
814
883
|
|
|
815
884
|
if (data.length > 0) for (const obj of data) render += await renderPanel(obj);
|
|
@@ -92,6 +92,7 @@ const PanelForm = {
|
|
|
92
92
|
firsUpdateEvent: async () => {},
|
|
93
93
|
share: {
|
|
94
94
|
copyLink: false,
|
|
95
|
+
copySourceMd: false,
|
|
95
96
|
},
|
|
96
97
|
showCreatorProfile: false,
|
|
97
98
|
},
|
|
@@ -454,7 +455,9 @@ const PanelForm = {
|
|
|
454
455
|
|
|
455
456
|
const baseNewDoc = newInstance(data);
|
|
456
457
|
baseNewDoc.tags = tags.filter((t) => !prefixTags.includes(t));
|
|
457
|
-
baseNewDoc.mdFileId = hasMdContent
|
|
458
|
+
baseNewDoc.mdFileId = hasMdContent
|
|
459
|
+
? `<div class="markdown-content">${marked.parse(data.mdFileId)}</div>`
|
|
460
|
+
: null;
|
|
458
461
|
baseNewDoc.userId = Elements.Data.user?.main?.model?.user?._id;
|
|
459
462
|
|
|
460
463
|
// Ensure profileImageId is properly formatted as object with _id property
|
|
@@ -676,7 +679,7 @@ const PanelForm = {
|
|
|
676
679
|
mdPlain = await blobArray[0].text();
|
|
677
680
|
// Parse markdown with proper error handling
|
|
678
681
|
try {
|
|
679
|
-
parsedMarkdown = mdPlain ? marked.parse(mdPlain) : '';
|
|
682
|
+
parsedMarkdown = mdPlain ? `<div class="markdown-content">${marked.parse(mdPlain)}</div>` : '';
|
|
680
683
|
} catch (parseError) {
|
|
681
684
|
logger.error('Error parsing markdown for document:', documentObject._id, parseError);
|
|
682
685
|
parsedMarkdown = `<p><strong>Error rendering markdown:</strong> ${parseError.message}</p>`;
|
|
@@ -679,6 +679,14 @@ const TranslateCore = {
|
|
|
679
679
|
en: 'Public Profile',
|
|
680
680
|
es: 'Perfil Público',
|
|
681
681
|
};
|
|
682
|
+
Translate.Data['external-link-warning'] = {
|
|
683
|
+
en: 'You are about to open an external link. Do you want to continue?',
|
|
684
|
+
es: 'Está a punto de abrir un enlace externo. ¿Desea continuar?',
|
|
685
|
+
};
|
|
686
|
+
Translate.Data['markdown-source-copied'] = {
|
|
687
|
+
en: 'Markdown source copied to clipboard',
|
|
688
|
+
es: 'Fuente de Markdown copiada al portapapeles',
|
|
689
|
+
};
|
|
682
690
|
},
|
|
683
691
|
};
|
|
684
692
|
|
|
@@ -174,52 +174,103 @@ const disableOptionsClick = (element, types) => {
|
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* The function `checkFullScreen` checks if the document is in full screen mode and returns a boolean
|
|
177
|
-
* value accordingly.
|
|
178
|
-
* @returns The function `checkFullScreen` is returning `true` if
|
|
179
|
-
*
|
|
177
|
+
* value accordingly. Checks all vendor-prefixed APIs for maximum compatibility.
|
|
178
|
+
* @returns The function `checkFullScreen` is returning `true` if any fullscreen API indicates
|
|
179
|
+
* fullscreen mode is active, otherwise it returns `false`.
|
|
180
180
|
* @memberof VanillaJS
|
|
181
181
|
*/
|
|
182
182
|
const checkFullScreen = () => {
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
return !!(
|
|
184
|
+
document.fullscreenElement ||
|
|
185
|
+
document.webkitFullscreenElement ||
|
|
186
|
+
document.mozFullScreenElement ||
|
|
187
|
+
document.msFullscreenElement ||
|
|
188
|
+
document.fullscreen ||
|
|
189
|
+
document.webkitIsFullScreen ||
|
|
190
|
+
document.mozFullScreen
|
|
191
|
+
);
|
|
185
192
|
};
|
|
186
193
|
|
|
187
194
|
/**
|
|
188
195
|
* The function `fullScreenOut` is used to exit full screen mode in a web browser.
|
|
196
|
+
* Handles all vendor-prefixed APIs and returns a promise for better control flow.
|
|
197
|
+
* @returns {Promise<boolean>} Promise that resolves to true if exit was attempted
|
|
189
198
|
* @memberof VanillaJS
|
|
190
199
|
*/
|
|
191
200
|
const fullScreenOut = () => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
+
return new Promise((resolve) => {
|
|
202
|
+
if (!checkFullScreen()) {
|
|
203
|
+
resolve(true);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
if (document.exitFullscreen) {
|
|
209
|
+
document
|
|
210
|
+
.exitFullscreen()
|
|
211
|
+
.then(() => resolve(true))
|
|
212
|
+
.catch(() => resolve(false));
|
|
213
|
+
} else if (document.webkitExitFullscreen) {
|
|
214
|
+
document.webkitExitFullscreen();
|
|
215
|
+
setTimeout(() => resolve(true), 100);
|
|
216
|
+
} else if (document.mozCancelFullScreen) {
|
|
217
|
+
document.mozCancelFullScreen();
|
|
218
|
+
setTimeout(() => resolve(true), 100);
|
|
219
|
+
} else if (document.msExitFullscreen) {
|
|
220
|
+
document.msExitFullscreen();
|
|
221
|
+
setTimeout(() => resolve(true), 100);
|
|
222
|
+
} else {
|
|
223
|
+
resolve(false);
|
|
224
|
+
}
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.warn('Fullscreen exit error:', err);
|
|
227
|
+
resolve(false);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
201
230
|
};
|
|
202
231
|
|
|
203
232
|
/**
|
|
204
233
|
* The `fullScreenIn` function is used to request full screen mode in a web browser using different
|
|
205
|
-
* vendor-specific methods.
|
|
234
|
+
* vendor-specific methods. Returns a promise for better control flow.
|
|
235
|
+
* @returns {Promise<boolean>} Promise that resolves to true if fullscreen was requested
|
|
206
236
|
* @memberof VanillaJS
|
|
207
237
|
*/
|
|
208
238
|
const fullScreenIn = () => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
239
|
+
return new Promise((resolve) => {
|
|
240
|
+
if (checkFullScreen()) {
|
|
241
|
+
resolve(true);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const elem = document.documentElement;
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
if (elem.requestFullscreen) {
|
|
249
|
+
elem
|
|
250
|
+
.requestFullscreen()
|
|
251
|
+
.then(() => resolve(true))
|
|
252
|
+
.catch(() => resolve(false));
|
|
253
|
+
} else if (elem.webkitRequestFullscreen) {
|
|
254
|
+
/* Chrome, Safari & Opera */
|
|
255
|
+
elem.webkitRequestFullscreen();
|
|
256
|
+
setTimeout(() => resolve(true), 100);
|
|
257
|
+
} else if (elem.mozRequestFullScreen) {
|
|
258
|
+
/* Firefox */
|
|
259
|
+
elem.mozRequestFullScreen();
|
|
260
|
+
setTimeout(() => resolve(true), 100);
|
|
261
|
+
} else if (elem.msRequestFullscreen) {
|
|
262
|
+
/* IE/Edge */
|
|
263
|
+
const msElem = window.top.document.body;
|
|
264
|
+
msElem.msRequestFullscreen();
|
|
265
|
+
setTimeout(() => resolve(true), 100);
|
|
266
|
+
} else {
|
|
267
|
+
resolve(false);
|
|
268
|
+
}
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.warn('Fullscreen request error:', err);
|
|
271
|
+
resolve(false);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
223
274
|
};
|
|
224
275
|
|
|
225
276
|
/**
|