underpost 3.2.4 → 3.2.8

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.
Files changed (141) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +268 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +58 -2
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +304 -38
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/backup.js +1 -2
  124. package/src/server/client-build-docs.js +45 -46
  125. package/src/server/client-build.js +334 -60
  126. package/src/server/client-formatted.js +47 -16
  127. package/src/server/conf.js +29 -13
  128. package/src/server/cron.js +6 -8
  129. package/src/server/dns.js +2 -1
  130. package/src/server/ipfs-client.js +232 -91
  131. package/src/server/process.js +13 -27
  132. package/src/server/start.js +6 -3
  133. package/src/server/valkey.js +134 -235
  134. package/tsconfig.docs.json +15 -0
  135. package/typedoc.json +20 -0
  136. package/jsdoc.json +0 -52
  137. package/src/client/components/core/ColorPalette.js +0 -5267
  138. package/src/client/components/core/JoyStick.js +0 -80
  139. package/src/client/components/default/RoutesDefault.js +0 -49
  140. package/src/client/sw/default.sw.js +0 -127
  141. package/src/client/sw/template.sw.js +0 -84
@@ -0,0 +1,87 @@
1
+ import { EventBus } from './EventBus.js';
2
+
3
+ const AuthEventType = {
4
+ login: 'auth:login',
5
+ logout: 'auth:logout',
6
+ signup: 'auth:signup',
7
+ };
8
+
9
+ const SessionEventType = {
10
+ updated: 'session:updated',
11
+ cleared: 'session:cleared',
12
+ };
13
+
14
+ const SocketEventType = {
15
+ connect: 'socket:connect',
16
+ connectError: 'socket:connect-error',
17
+ disconnect: 'socket:disconnect',
18
+ channel: (channel) => `socket:channel:${channel}`,
19
+ };
20
+
21
+ const TranslateEventType = {
22
+ changed: 'translate:changed',
23
+ };
24
+
25
+ const ResponsiveEventType = {
26
+ changed: 'responsive:changed',
27
+ settled: 'responsive:settled',
28
+ orientationChanged: 'responsive:orientation:changed',
29
+ orientationSettled: 'responsive:orientation:settled',
30
+ };
31
+
32
+ const RecoverEventType = {
33
+ triggered: 'recover:triggered',
34
+ };
35
+
36
+ const KeyboardEventType = {
37
+ pressed: 'keyboard:pressed',
38
+ };
39
+
40
+ const AccountEventType = {
41
+ updated: 'account:updated',
42
+ };
43
+
44
+ const AppointmentEventType = {
45
+ submitted: 'appointment:submitted',
46
+ };
47
+
48
+ const authLoginEvents = new EventBus();
49
+ const authLogoutEvents = new EventBus();
50
+ const authSignupEvents = new EventBus();
51
+ const sessionEvents = new EventBus();
52
+ const socketEvents = new EventBus();
53
+ const translateEvents = new EventBus();
54
+ const responsiveChangeEvents = new EventBus();
55
+ const responsiveSettledEvents = new EventBus();
56
+ const responsiveOrientationEvents = new EventBus();
57
+ const responsiveOrientationSettledEvents = new EventBus();
58
+ const recoverEvents = new EventBus();
59
+ const keyboardEvents = new EventBus();
60
+ const accountEvents = new EventBus();
61
+ const appointmentEvents = new EventBus();
62
+
63
+ export {
64
+ AuthEventType,
65
+ SessionEventType,
66
+ SocketEventType,
67
+ TranslateEventType,
68
+ ResponsiveEventType,
69
+ RecoverEventType,
70
+ KeyboardEventType,
71
+ AccountEventType,
72
+ AppointmentEventType,
73
+ authLoginEvents,
74
+ authLogoutEvents,
75
+ authSignupEvents,
76
+ sessionEvents,
77
+ socketEvents,
78
+ translateEvents,
79
+ responsiveChangeEvents,
80
+ responsiveSettledEvents,
81
+ responsiveOrientationEvents,
82
+ responsiveOrientationSettledEvents,
83
+ recoverEvents,
84
+ keyboardEvents,
85
+ accountEvents,
86
+ appointmentEvents,
87
+ };
@@ -0,0 +1,309 @@
1
+ const PALETTE_COLUMNS = [
2
+ { name: 'Red', hue: 0 },
3
+ { name: 'Orange', hue: 28 },
4
+ { name: 'Yellow', hue: 52 },
5
+ { name: 'Green', hue: 132 },
6
+ { name: 'Cyan', hue: 182 },
7
+ { name: 'Blue', hue: 224 },
8
+ { name: 'Violet', hue: 278 },
9
+ ];
10
+
11
+ const PALETTE_LIGHTNESS = [92, 82, 72, 60, 48, 36, 24];
12
+ const PALETTE_GRAYSCALE_LIGHTNESS = [100, 84, 68, 52, 36, 18, 0];
13
+ const PALETTE_SATURATION = 82;
14
+
15
+ function clampChannel(value) {
16
+ return Math.max(0, Math.min(255, Math.round(value)));
17
+ }
18
+
19
+ function hslToHex(hue, saturation, lightness) {
20
+ const s = saturation / 100;
21
+ const l = lightness / 100;
22
+ const chroma = (1 - Math.abs(2 * l - 1)) * s;
23
+ const segment = hue / 60;
24
+ const second = chroma * (1 - Math.abs((segment % 2) - 1));
25
+ const match = l - chroma / 2;
26
+
27
+ let red = 0;
28
+ let green = 0;
29
+ let blue = 0;
30
+
31
+ if (segment >= 0 && segment < 1) {
32
+ red = chroma;
33
+ green = second;
34
+ } else if (segment < 2) {
35
+ red = second;
36
+ green = chroma;
37
+ } else if (segment < 3) {
38
+ green = chroma;
39
+ blue = second;
40
+ } else if (segment < 4) {
41
+ green = second;
42
+ blue = chroma;
43
+ } else if (segment < 5) {
44
+ red = second;
45
+ blue = chroma;
46
+ } else {
47
+ red = chroma;
48
+ blue = second;
49
+ }
50
+
51
+ const r = clampChannel((red + match) * 255)
52
+ .toString(16)
53
+ .padStart(2, '0');
54
+ const g = clampChannel((green + match) * 255)
55
+ .toString(16)
56
+ .padStart(2, '0');
57
+ const b = clampChannel((blue + match) * 255)
58
+ .toString(16)
59
+ .padStart(2, '0');
60
+ return `#${r}${g}${b}`.toUpperCase();
61
+ }
62
+
63
+ function normalizeHex(value) {
64
+ const raw = String(value || '')
65
+ .trim()
66
+ .replace('#', '');
67
+ if (/^[\da-fA-F]{3}$/.test(raw)) {
68
+ return `#${raw
69
+ .split('')
70
+ .map((char) => char + char)
71
+ .join('')}`.toUpperCase();
72
+ }
73
+ if (/^[\da-fA-F]{6}$/.test(raw)) {
74
+ return `#${raw}`.toUpperCase();
75
+ }
76
+ return null;
77
+ }
78
+
79
+ function buildPaletteRows() {
80
+ return PALETTE_LIGHTNESS.map((lightness, rowIndex) => {
81
+ const colorSwatches = PALETTE_COLUMNS.map((column) => ({
82
+ name: `${column.name} ${rowIndex + 1}`,
83
+ value: hslToHex(column.hue, PALETTE_SATURATION, lightness),
84
+ }));
85
+ const grayscaleLightness = PALETTE_GRAYSCALE_LIGHTNESS[rowIndex] ?? 0;
86
+ const grayscaleName =
87
+ rowIndex === 0 ? 'White' : rowIndex === PALETTE_LIGHTNESS.length - 1 ? 'Black' : `Gray ${rowIndex}`;
88
+ return [
89
+ ...colorSwatches,
90
+ {
91
+ name: grayscaleName,
92
+ value: hslToHex(0, 0, grayscaleLightness),
93
+ },
94
+ ];
95
+ });
96
+ }
97
+
98
+ const PALETTE_ROWS = buildPaletteRows();
99
+
100
+ class ColorPaletteElement extends HTMLElement {
101
+ constructor() {
102
+ super();
103
+ this.attachShadow({ mode: 'open' });
104
+ this._value = '#FF0000';
105
+ this._disabled = false;
106
+ this._handleClick = this._handleClick.bind(this);
107
+ }
108
+
109
+ static get observedAttributes() {
110
+ return ['value', 'disabled'];
111
+ }
112
+
113
+ attributeChangedCallback(name, _oldValue, newValue) {
114
+ if (name === 'value') {
115
+ this._setValue(newValue, { reflect: false, emit: false });
116
+ return;
117
+ }
118
+ if (name === 'disabled') {
119
+ this._disabled = newValue !== null;
120
+ this._updateSelection();
121
+ }
122
+ }
123
+
124
+ connectedCallback() {
125
+ this.render();
126
+ this.shadowRoot.addEventListener('click', this._handleClick);
127
+ this._setValue(this.getAttribute('value') || this._value, { reflect: false, emit: false });
128
+ }
129
+
130
+ disconnectedCallback() {
131
+ this.shadowRoot.removeEventListener('click', this._handleClick);
132
+ }
133
+
134
+ get value() {
135
+ return this._value;
136
+ }
137
+
138
+ set value(value) {
139
+ this._setValue(value, { reflect: true, emit: false });
140
+ }
141
+
142
+ _setValue(value, { reflect, emit } = { reflect: true, emit: false }) {
143
+ const normalized = normalizeHex(value);
144
+ if (!normalized) {
145
+ return;
146
+ }
147
+ const changed = normalized !== this._value;
148
+ this._value = normalized;
149
+ if (reflect && this.getAttribute('value') !== normalized) {
150
+ this.setAttribute('value', normalized);
151
+ }
152
+ this._updateSelection();
153
+ if (emit && changed) {
154
+ const detail = { value: normalized, hex: normalized };
155
+ this.dispatchEvent(new CustomEvent('colorchange', { detail }));
156
+ this.dispatchEvent(new CustomEvent('change', { detail }));
157
+ }
158
+ }
159
+
160
+ _handleClick(event) {
161
+ const swatch = event.target.closest('button[data-color]');
162
+ if (!swatch || this._disabled) {
163
+ return;
164
+ }
165
+ this._setValue(swatch.dataset.color, { reflect: true, emit: true });
166
+ }
167
+
168
+ _updateSelection() {
169
+ if (!this.shadowRoot) {
170
+ return;
171
+ }
172
+ const swatches = this.shadowRoot.querySelectorAll('button[data-color]');
173
+ swatches.forEach((swatch) => {
174
+ const selected = swatch.dataset.color === this._value;
175
+ swatch.toggleAttribute('data-selected', selected);
176
+ swatch.setAttribute('aria-pressed', selected ? 'true' : 'false');
177
+ swatch.disabled = this._disabled;
178
+ });
179
+
180
+ const currentValue = this.shadowRoot.querySelector('.current-value');
181
+ if (currentValue) {
182
+ currentValue.textContent = this._value;
183
+ }
184
+ const currentSwatch = this.shadowRoot.querySelector('.current-swatch');
185
+ if (currentSwatch) {
186
+ currentSwatch.style.background = this._value;
187
+ }
188
+ }
189
+
190
+ render() {
191
+ const gridMarkup = PALETTE_ROWS.map(
192
+ (row) => `
193
+ <div class="swatch-row">
194
+ ${row
195
+ .map(
196
+ (swatch) => `
197
+ <button
198
+ type="button"
199
+ class="swatch"
200
+ data-color="${swatch.value}"
201
+ title="${swatch.name} ${swatch.value}"
202
+ aria-label="${swatch.name} ${swatch.value}"
203
+ style="background:${swatch.value};"
204
+ ></button>
205
+ `,
206
+ )
207
+ .join('')}
208
+ </div>
209
+ `,
210
+ ).join('');
211
+
212
+ this.shadowRoot.innerHTML = `
213
+ <style>
214
+ :host {
215
+ display: block;
216
+ width: 100%;
217
+ }
218
+
219
+ .palette {
220
+ display: grid;
221
+ gap: 10px;
222
+ font-family: monospace;
223
+ color: #d7d7d7;
224
+ }
225
+
226
+ .grid {
227
+ display: grid;
228
+ gap: 6px;
229
+ }
230
+
231
+ .swatch-row {
232
+ display: grid;
233
+ grid-template-columns: repeat(8, minmax(0, 1fr));
234
+ gap: 6px;
235
+ }
236
+
237
+ .swatch {
238
+ appearance: none;
239
+ border: 2px solid rgba(255, 255, 255, 0.16);
240
+ border-radius: 8px;
241
+ cursor: pointer;
242
+ display: block;
243
+ min-height: 24px;
244
+ padding: 0;
245
+ position: relative;
246
+ transition: transform 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
247
+ width: 100%;
248
+ }
249
+
250
+ .swatch:hover {
251
+ transform: translateY(-1px);
252
+ border-color: rgba(255, 255, 255, 0.5);
253
+ }
254
+
255
+ .swatch[data-selected] {
256
+ border-color: #ffffff;
257
+ box-shadow: 0 0 0 2px rgba(28, 28, 28, 0.7), 0 0 0 4px rgba(255, 255, 255, 0.45);
258
+ }
259
+
260
+ .swatch[data-selected]::after {
261
+ content: '';
262
+ position: absolute;
263
+ inset: 6px;
264
+ border: 1px solid rgba(0, 0, 0, 0.45);
265
+ border-radius: 5px;
266
+ }
267
+
268
+ .current {
269
+ align-items: center;
270
+ display: flex;
271
+ gap: 8px;
272
+ font-size: 12px;
273
+ }
274
+
275
+ .current-swatch {
276
+ border: 1px solid rgba(255, 255, 255, 0.24);
277
+ border-radius: 6px;
278
+ height: 18px;
279
+ width: 18px;
280
+ }
281
+
282
+ .current-label {
283
+ color: #9f9f9f;
284
+ text-transform: uppercase;
285
+ }
286
+
287
+ .swatch:disabled {
288
+ cursor: default;
289
+ opacity: 0.55;
290
+ transform: none;
291
+ }
292
+ </style>
293
+ <div class="palette">
294
+ <div class="grid">${gridMarkup}</div>
295
+ <div class="current">
296
+ <div class="current-swatch"></div>
297
+ <div><span class="current-label">Selected</span> <span class="current-value"></span></div>
298
+ </div>
299
+ </div>
300
+ `;
301
+ this._updateSelection();
302
+ }
303
+ }
304
+
305
+ if (!customElements.get('color-palette')) {
306
+ customElements.define('color-palette', ColorPaletteElement);
307
+ }
308
+
309
+ export { ColorPaletteElement };
@@ -32,7 +32,7 @@ const attachMarkdownLinkHandlers = (containerSelector) => {
32
32
  id: `external-link-${s4()}`,
33
33
  html: async () => html`
34
34
  <div class="in section-mp" style="text-align: center; padding: 20px;">
35
- <p>${Translate.Render('external-link-warning')}</p>
35
+ <p>${Translate.instance('external-link-warning')}</p>
36
36
  <p style="word-break: break-all; margin-top: 10px;"><strong>${href}</strong></p>
37
37
  </div>
38
38
  `,
@@ -55,8 +55,8 @@ const attachMarkdownLinkHandlers = (containerSelector) => {
55
55
  });
56
56
  };
57
57
 
58
- const Content = {
59
- Render: async function (options = { idModal: '', titleIcon: '' }) {
58
+ class Content {
59
+ static async instance(options = { idModal: '', titleIcon: '' }) {
60
60
  const { idModal } = options;
61
61
  setTimeout(async () => {
62
62
  try {
@@ -140,12 +140,15 @@ const Content = {
140
140
  htmls(`.content-render-${idModal}`, ``);
141
141
 
142
142
  // Pass file IDs to RenderFile - it will fetch blobs as needed
143
- if (md) await this.RenderFile({ idModal, file: md, id: md._id });
144
- if (file) await this.RenderFile({ idModal, file, id: file._id });
143
+ if (md) await Content.RenderFile({ idModal, file: md, id: md._id });
144
+ if (file) await Content.RenderFile({ idModal, file, id: file._id });
145
145
  Modal.Data[idModal].onObserverListener[`main-content-observer`]();
146
146
  } catch (error) {
147
147
  htmls(`.content-render-${idModal}`, '');
148
- htmls(`.error-${idModal}`, html`<i class="fas fa-exclamation-circle"></i> ${Translate.Render(error.message)}`);
148
+ htmls(
149
+ `.error-${idModal}`,
150
+ html`<i class="fas fa-exclamation-circle"></i> ${Translate.instance(error.message)}`,
151
+ );
149
152
  s(`.error-${idModal}`).classList.remove('hide');
150
153
  }
151
154
  s(`.ssr-shimmer-content-${idModal}`).classList.add('hide');
@@ -164,14 +167,14 @@ const Content = {
164
167
  ${imageShimmer()}
165
168
  </div>
166
169
  <div class="abs center error-${idModal} hide"></div>`;
167
- },
170
+ }
168
171
 
169
172
  /**
170
173
  * Helper function to get file content
171
174
  * Supports both legacy format (with buffer data) and new format (metadata only)
172
175
  * For new format, fetches content from blob endpoint
173
176
  */
174
- getFileContent: async function (file, options = {}) {
177
+ static async getFileContent(file, options = {}) {
175
178
  // If custom URL provided, use it
176
179
  if (options.url) {
177
180
  return await CoreService.getRaw({ url: options.url });
@@ -197,9 +200,9 @@ const Content = {
197
200
  }
198
201
 
199
202
  throw new Error('No file content available');
200
- },
203
+ }
201
204
 
202
- RenderFile: async function (
205
+ static async RenderFile(
203
206
  options = {
204
207
  file: {
205
208
  _id: '',
@@ -308,13 +311,13 @@ ${JSON.stringify(JSON.parse(content), null, 4)}</pre
308
311
  if (ext === 'md') {
309
312
  attachMarkdownLinkHandlers(container);
310
313
  }
311
- },
314
+ }
312
315
 
313
316
  /**
314
317
  * Generate appropriate URL for file display
315
318
  * Prefers blob endpoint for new metadata-only format
316
319
  */
317
- urlFactory: async function (options) {
320
+ static async urlFactory(options) {
318
321
  // If custom URL provided, use it
319
322
  if (options.url) {
320
323
  return options.url;
@@ -340,7 +343,7 @@ ${JSON.stringify(JSON.parse(content), null, 4)}</pre
340
343
  }
341
344
 
342
345
  return null;
343
- },
344
- };
346
+ }
347
+ }
345
348
 
346
349
  export { Content, attachMarkdownLinkHandlers };