@intranefr/superbackend 1.5.2 → 1.6.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/cookies.txt +6 -0
- package/cookies1.txt +6 -0
- package/cookies2.txt +6 -0
- package/cookies3.txt +6 -0
- package/cookies4.txt +5 -0
- package/cookies_old.txt +5 -0
- package/cookies_old_test.txt +6 -0
- package/cookies_super.txt +5 -0
- package/cookies_super_test.txt +6 -0
- package/cookies_test.txt +6 -0
- package/index.js +9 -0
- package/manage.js +745 -0
- package/package.json +6 -2
- package/plugins/core-waiting-list-migration/README.md +118 -0
- package/plugins/core-waiting-list-migration/index.js +438 -0
- package/plugins/global-settings-presets/index.js +20 -0
- package/plugins/hello-cli/index.js +17 -0
- package/plugins/ui-components-seeder/components/suiAlert.js +212 -0
- package/plugins/ui-components-seeder/components/suiToast.js +186 -0
- package/plugins/ui-components-seeder/index.js +31 -0
- package/public/js/admin-ui-components-preview.js +281 -0
- package/public/js/admin-ui-components.js +408 -0
- package/public/js/llm-provider-model-picker.js +193 -0
- package/public/test-iframe-fix.html +63 -0
- package/public/test-iframe.html +14 -0
- package/src/admin/endpointRegistry.js +68 -0
- package/src/controllers/admin.controller.js +36 -10
- package/src/controllers/adminAgents.controller.js +37 -0
- package/src/controllers/adminDataCleanup.controller.js +45 -0
- package/src/controllers/adminLlm.controller.js +19 -8
- package/src/controllers/adminLogin.controller.js +269 -0
- package/src/controllers/adminMarkdowns.controller.js +157 -0
- package/src/controllers/adminPlugins.controller.js +55 -0
- package/src/controllers/adminRegistry.controller.js +106 -0
- package/src/controllers/adminScripts.controller.js +138 -0
- package/src/controllers/adminStats.controller.js +4 -4
- package/src/controllers/adminTelegram.controller.js +72 -0
- package/src/controllers/markdowns.controller.js +42 -0
- package/src/controllers/registry.controller.js +32 -0
- package/src/controllers/waitingList.controller.js +52 -74
- package/src/helpers/mongooseHelper.js +6 -6
- package/src/helpers/scriptBase.js +2 -2
- package/src/middleware/auth.js +71 -1
- package/src/middleware/rbac.js +62 -0
- package/src/middleware.js +584 -176
- package/src/models/Agent.js +105 -0
- package/src/models/AgentMessage.js +82 -0
- package/src/models/GlobalSetting.js +11 -1
- package/src/models/Markdown.js +75 -0
- package/src/models/ScriptRun.js +8 -0
- package/src/models/TelegramBot.js +42 -0
- package/src/models/UiComponent.js +2 -0
- package/src/models/User.js +1 -1
- package/src/routes/admin.routes.js +3 -3
- package/src/routes/adminAgents.routes.js +13 -0
- package/src/routes/adminAssets.routes.js +11 -11
- package/src/routes/adminBlog.routes.js +2 -2
- package/src/routes/adminBlogAi.routes.js +2 -2
- package/src/routes/adminBlogAutomation.routes.js +2 -2
- package/src/routes/adminCache.routes.js +2 -2
- package/src/routes/adminConsoleManager.routes.js +2 -2
- package/src/routes/adminCrons.routes.js +2 -2
- package/src/routes/adminDataCleanup.routes.js +26 -0
- package/src/routes/adminDbBrowser.routes.js +2 -2
- package/src/routes/adminEjsVirtual.routes.js +2 -2
- package/src/routes/adminFeatureFlags.routes.js +6 -6
- package/src/routes/adminHeadless.routes.js +2 -2
- package/src/routes/adminHealthChecks.routes.js +2 -2
- package/src/routes/adminI18n.routes.js +2 -2
- package/src/routes/adminJsonConfigs.routes.js +8 -8
- package/src/routes/adminLlm.routes.js +8 -7
- package/src/routes/adminLogin.routes.js +23 -0
- package/src/routes/adminMarkdowns.routes.js +10 -0
- package/src/routes/adminMigration.routes.js +12 -12
- package/src/routes/adminPages.routes.js +2 -2
- package/src/routes/adminPlugins.routes.js +15 -0
- package/src/routes/adminProxy.routes.js +2 -2
- package/src/routes/adminRateLimits.routes.js +8 -8
- package/src/routes/adminRbac.routes.js +2 -2
- package/src/routes/adminRegistry.routes.js +24 -0
- package/src/routes/adminScripts.routes.js +6 -3
- package/src/routes/adminSeoConfig.routes.js +10 -10
- package/src/routes/adminTelegram.routes.js +14 -0
- package/src/routes/adminTerminals.routes.js +2 -2
- package/src/routes/adminUiComponents.routes.js +2 -2
- package/src/routes/adminUploadNamespaces.routes.js +7 -7
- package/src/routes/blogInternal.routes.js +2 -2
- package/src/routes/experiments.routes.js +2 -2
- package/src/routes/formsAdmin.routes.js +6 -6
- package/src/routes/globalSettings.routes.js +8 -8
- package/src/routes/internalExperiments.routes.js +2 -2
- package/src/routes/markdowns.routes.js +16 -0
- package/src/routes/notificationAdmin.routes.js +7 -7
- package/src/routes/orgAdmin.routes.js +16 -16
- package/src/routes/pages.routes.js +3 -3
- package/src/routes/registry.routes.js +11 -0
- package/src/routes/stripeAdmin.routes.js +12 -12
- package/src/routes/userAdmin.routes.js +7 -7
- package/src/routes/waitingListAdmin.routes.js +2 -2
- package/src/routes/workflows.routes.js +3 -3
- package/src/services/agent.service.js +546 -0
- package/src/services/agentHistory.service.js +345 -0
- package/src/services/agentTools.service.js +578 -0
- package/src/services/dataCleanup.service.js +286 -0
- package/src/services/jsonConfigs.service.js +284 -10
- package/src/services/llm.service.js +219 -6
- package/src/services/markdowns.service.js +522 -0
- package/src/services/plugins.service.js +348 -0
- package/src/services/registry.service.js +452 -0
- package/src/services/scriptsRunner.service.js +328 -37
- package/src/services/telegram.service.js +130 -0
- package/src/services/uiComponents.service.js +180 -0
- package/src/services/waitingListJson.service.js +401 -0
- package/src/utils/rbac/rightsRegistry.js +118 -0
- package/test-access.js +63 -0
- package/test-iframe-fix.html +63 -0
- package/test-iframe.html +14 -0
- package/views/admin-403.ejs +92 -0
- package/views/admin-agents.ejs +273 -0
- package/views/admin-coolify-deploy.ejs +8 -8
- package/views/admin-dashboard-home.ejs +52 -2
- package/views/admin-dashboard.ejs +179 -7
- package/views/admin-data-cleanup.ejs +357 -0
- package/views/admin-experiments.ejs +1 -1
- package/views/admin-login.ejs +286 -0
- package/views/admin-markdowns.ejs +905 -0
- package/views/admin-plugins-system.ejs +223 -0
- package/views/admin-scripts.ejs +221 -4
- package/views/admin-telegram.ejs +269 -0
- package/views/admin-ui-components.ejs +82 -402
- package/views/admin-users.ejs +207 -11
- package/views/partials/dashboard/nav-items.ejs +5 -0
- package/views/partials/llm-provider-model-picker.ejs +0 -161
- package/analysis-only.skill +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
code: 'sui_alert',
|
|
3
|
+
name: 'SUI Alert',
|
|
4
|
+
html: `<div class="sui-alert-overlay" data-sui-alert-overlay>
|
|
5
|
+
<div class="sui-alert" data-sui-alert>
|
|
6
|
+
<div class="sui-alert-header">
|
|
7
|
+
<span class="sui-alert-title" data-sui-alert-title></span>
|
|
8
|
+
<button class="sui-alert-close" data-sui-alert-close aria-label="Close">×</button>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="sui-alert-body" data-sui-alert-body></div>
|
|
11
|
+
<div class="sui-alert-footer">
|
|
12
|
+
<button class="sui-alert-btn sui-alert-btn-primary" data-sui-alert-confirm>OK</button>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>`,
|
|
16
|
+
css: `.sui-alert-overlay {
|
|
17
|
+
position: fixed;
|
|
18
|
+
top: 0;
|
|
19
|
+
left: 0;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
background: rgba(0, 0, 0, 0.5);
|
|
23
|
+
display: none;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
z-index: 9999;
|
|
27
|
+
opacity: 0;
|
|
28
|
+
visibility: hidden;
|
|
29
|
+
transition: opacity 0.3s ease, visibility 0.3s ease;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sui-alert-overlay.sui-alert-show {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
visibility: visible;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.sui-alert {
|
|
38
|
+
background: white;
|
|
39
|
+
border-radius: 8px;
|
|
40
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
41
|
+
max-width: 400px;
|
|
42
|
+
width: 90%;
|
|
43
|
+
transform: scale(0.9);
|
|
44
|
+
transition: transform 0.3s ease;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.sui-alert-overlay.sui-alert-show .sui-alert {
|
|
48
|
+
transform: scale(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.sui-alert-header {
|
|
52
|
+
display: flex;
|
|
53
|
+
justify-content: space-between;
|
|
54
|
+
align-items: center;
|
|
55
|
+
padding: 16px 20px;
|
|
56
|
+
border-bottom: 1px solid #e5e7eb;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.sui-alert-title {
|
|
60
|
+
font-weight: 600;
|
|
61
|
+
font-size: 16px;
|
|
62
|
+
color: #111827;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.sui-alert-close {
|
|
66
|
+
background: none;
|
|
67
|
+
border: none;
|
|
68
|
+
font-size: 20px;
|
|
69
|
+
color: #6b7280;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
padding: 0;
|
|
72
|
+
width: 24px;
|
|
73
|
+
height: 24px;
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
justify-content: center;
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
transition: background-color 0.2s;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.sui-alert-close:hover {
|
|
82
|
+
background-color: #f3f4f6;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.sui-alert-body {
|
|
86
|
+
padding: 20px;
|
|
87
|
+
color: #374151;
|
|
88
|
+
font-size: 14px;
|
|
89
|
+
line-height: 1.5;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.sui-alert-footer {
|
|
93
|
+
padding: 12px 20px 20px;
|
|
94
|
+
display: flex;
|
|
95
|
+
justify-content: flex-end;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.sui-alert-btn {
|
|
99
|
+
padding: 8px 16px;
|
|
100
|
+
border: none;
|
|
101
|
+
border-radius: 6px;
|
|
102
|
+
font-size: 14px;
|
|
103
|
+
font-weight: 500;
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
transition: background-color 0.2s;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.sui-alert-btn-primary {
|
|
109
|
+
background-color: #3b82f6;
|
|
110
|
+
color: white;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.sui-alert-btn-primary:hover {
|
|
114
|
+
background-color: #2563eb;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.sui-alert-overlay.sui-alert-success .sui-alert-title { color: #059669; }
|
|
118
|
+
.sui-alert-overlay.sui-alert-warning .sui-alert-title { color: #d97706; }
|
|
119
|
+
.sui-alert-overlay.sui-alert-error .sui-alert-title { color: #dc2626; }`,
|
|
120
|
+
js: `let currentAlert = null;
|
|
121
|
+
let autoDismissTimer = null;
|
|
122
|
+
|
|
123
|
+
function showAlert(options = {}) {
|
|
124
|
+
const {
|
|
125
|
+
title = 'Alert',
|
|
126
|
+
message = '',
|
|
127
|
+
type = 'info',
|
|
128
|
+
confirmText = 'OK',
|
|
129
|
+
onConfirm = null,
|
|
130
|
+
autoDismiss = 0,
|
|
131
|
+
} = options;
|
|
132
|
+
|
|
133
|
+
if (currentAlert) hideAlert();
|
|
134
|
+
|
|
135
|
+
const overlay = templateRootEl.querySelector('[data-sui-alert-overlay]');
|
|
136
|
+
const titleEl = templateRootEl.querySelector('[data-sui-alert-title]');
|
|
137
|
+
const bodyEl = templateRootEl.querySelector('[data-sui-alert-body]');
|
|
138
|
+
const closeEl = templateRootEl.querySelector('[data-sui-alert-close]');
|
|
139
|
+
const confirmEl = templateRootEl.querySelector('[data-sui-alert-confirm]');
|
|
140
|
+
|
|
141
|
+
titleEl.textContent = title;
|
|
142
|
+
bodyEl.textContent = message;
|
|
143
|
+
confirmEl.textContent = confirmText;
|
|
144
|
+
|
|
145
|
+
overlay.className = 'sui-alert-overlay';
|
|
146
|
+
overlay.classList.add('sui-alert-' + type);
|
|
147
|
+
|
|
148
|
+
function hide() {
|
|
149
|
+
overlay.classList.remove('sui-alert-show');
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
overlay.style.display = 'none';
|
|
152
|
+
if (autoDismissTimer) {
|
|
153
|
+
clearTimeout(autoDismissTimer);
|
|
154
|
+
autoDismissTimer = null;
|
|
155
|
+
}
|
|
156
|
+
currentAlert = null;
|
|
157
|
+
}, 300);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
closeEl.onclick = hide;
|
|
161
|
+
confirmEl.onclick = () => {
|
|
162
|
+
if (typeof onConfirm === 'function') onConfirm();
|
|
163
|
+
hide();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (autoDismiss > 0) autoDismissTimer = setTimeout(hide, autoDismiss);
|
|
167
|
+
|
|
168
|
+
overlay.style.display = 'flex';
|
|
169
|
+
currentAlert = overlay;
|
|
170
|
+
requestAnimationFrame(() => overlay.classList.add('sui-alert-show'));
|
|
171
|
+
return hide;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function hideAlert() {
|
|
175
|
+
if (!currentAlert) return;
|
|
176
|
+
currentAlert.classList.remove('sui-alert-show');
|
|
177
|
+
currentAlert.style.display = 'none';
|
|
178
|
+
currentAlert = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
show: showAlert,
|
|
183
|
+
hide: hideAlert,
|
|
184
|
+
info: (title, message, options) => showAlert({ ...options, title, message, type: 'info' }),
|
|
185
|
+
success: (title, message, options) => showAlert({ ...options, title, message, type: 'success' }),
|
|
186
|
+
warning: (title, message, options) => showAlert({ ...options, title, message, type: 'warning' }),
|
|
187
|
+
error: (title, message, options) => showAlert({ ...options, title, message, type: 'error' }),
|
|
188
|
+
};`,
|
|
189
|
+
usageMarkdown: `# SUI Alert Component
|
|
190
|
+
|
|
191
|
+
Non-blocking customizable alert.
|
|
192
|
+
|
|
193
|
+
## SDK Usage
|
|
194
|
+
|
|
195
|
+
\`\`\`javascript
|
|
196
|
+
await uiCmp.load('sui_alert');
|
|
197
|
+
const alertCmp = await uiCmp.sui_alert.create();
|
|
198
|
+
alertCmp.success('Done', 'Operation completed');
|
|
199
|
+
\`\`\`
|
|
200
|
+
|
|
201
|
+
## Methods
|
|
202
|
+
- \`show(options)\`
|
|
203
|
+
- \`hide()\`
|
|
204
|
+
- \`info(title, message, options)\`
|
|
205
|
+
- \`success(title, message, options)\`
|
|
206
|
+
- \`warning(title, message, options)\`
|
|
207
|
+
- \`error(title, message, options)\``,
|
|
208
|
+
api: 'show(options), hide(), info(title,message,options), success(title,message,options), warning(title,message,options), error(title,message,options)',
|
|
209
|
+
previewExample: `const i = await uiCmp.create({});\ni.warning("ATTENTION");`,
|
|
210
|
+
version: '1.0.0',
|
|
211
|
+
isActive: true,
|
|
212
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
code: 'sui_toast',
|
|
3
|
+
name: 'SUI Toast',
|
|
4
|
+
html: `<div class="sui-toast-container" data-sui-toast-container>
|
|
5
|
+
<div class="sui-toast" data-sui-toast-template>
|
|
6
|
+
<div class="sui-toast-icon" data-sui-toast-icon></div>
|
|
7
|
+
<div class="sui-toast-content">
|
|
8
|
+
<div class="sui-toast-title" data-sui-toast-title></div>
|
|
9
|
+
<div class="sui-toast-message" data-sui-toast-message></div>
|
|
10
|
+
</div>
|
|
11
|
+
<button class="sui-toast-close" data-sui-toast-close aria-label="Close">×</button>
|
|
12
|
+
</div>
|
|
13
|
+
</div>`,
|
|
14
|
+
css: `.sui-toast-container {
|
|
15
|
+
position: fixed;
|
|
16
|
+
top: 20px;
|
|
17
|
+
right: 20px;
|
|
18
|
+
z-index: 10000;
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.sui-toast {
|
|
23
|
+
background: white;
|
|
24
|
+
border-radius: 8px;
|
|
25
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
26
|
+
padding: 16px;
|
|
27
|
+
margin-bottom: 12px;
|
|
28
|
+
min-width: 300px;
|
|
29
|
+
max-width: 400px;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: flex-start;
|
|
32
|
+
gap: 12px;
|
|
33
|
+
pointer-events: auto;
|
|
34
|
+
transform: translateX(100%);
|
|
35
|
+
opacity: 0;
|
|
36
|
+
transition: transform 0.3s ease, opacity 0.3s ease;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.sui-toast.sui-toast-show { transform: translateX(0); opacity: 1; }
|
|
40
|
+
.sui-toast.sui-toast-hide { transform: translateX(100%); opacity: 0; }
|
|
41
|
+
|
|
42
|
+
.sui-toast-icon { width: 20px; height: 20px; flex-shrink: 0; margin-top: 2px; }
|
|
43
|
+
.sui-toast-icon::before { content: ''; display: block; width: 100%; height: 100%; border-radius: 50%; }
|
|
44
|
+
.sui-toast.sui-toast-info .sui-toast-icon::before { background-color: #3b82f6; }
|
|
45
|
+
.sui-toast.sui-toast-success .sui-toast-icon::before { background-color: #059669; }
|
|
46
|
+
.sui-toast.sui-toast-warning .sui-toast-icon::before { background-color: #d97706; }
|
|
47
|
+
.sui-toast.sui-toast-error .sui-toast-icon::before { background-color: #dc2626; }
|
|
48
|
+
|
|
49
|
+
.sui-toast-content { flex: 1; min-width: 0; }
|
|
50
|
+
.sui-toast-title { font-weight: 600; font-size: 14px; color: #111827; margin-bottom: 4px; line-height: 1.4; }
|
|
51
|
+
.sui-toast-message { font-size: 13px; color: #6b7280; line-height: 1.4; }
|
|
52
|
+
|
|
53
|
+
.sui-toast-close {
|
|
54
|
+
background: none;
|
|
55
|
+
border: none;
|
|
56
|
+
font-size: 16px;
|
|
57
|
+
color: #9ca3af;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
padding: 0;
|
|
60
|
+
width: 20px;
|
|
61
|
+
height: 20px;
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
border-radius: 4px;
|
|
66
|
+
transition: background-color 0.2s;
|
|
67
|
+
flex-shrink: 0;
|
|
68
|
+
}
|
|
69
|
+
.sui-toast-close:hover { background-color: #f3f4f6; color: #6b7280; }
|
|
70
|
+
|
|
71
|
+
.sui-toast-container.sui-toast-top-left { top: 20px; left: 20px; right: auto; }
|
|
72
|
+
.sui-toast-container.sui-toast-top-right { top: 20px; right: 20px; }
|
|
73
|
+
.sui-toast-container.sui-toast-bottom-left { bottom: 20px; left: 20px; right: auto; top: auto; }
|
|
74
|
+
.sui-toast-container.sui-toast-bottom-right { bottom: 20px; right: 20px; top: auto; }
|
|
75
|
+
.sui-toast-container.sui-toast-top-center { top: 20px; left: 50%; right: auto; transform: translateX(-50%); }
|
|
76
|
+
.sui-toast-container.sui-toast-bottom-center { bottom: 20px; left: 50%; right: auto; top: auto; transform: translateX(-50%); }`,
|
|
77
|
+
js: `let toastContainer = null;
|
|
78
|
+
let toastQueue = [];
|
|
79
|
+
let activeToasts = new Map();
|
|
80
|
+
|
|
81
|
+
function getContainer() {
|
|
82
|
+
if (!toastContainer) {
|
|
83
|
+
toastContainer = templateRootEl.querySelector('[data-sui-toast-container]');
|
|
84
|
+
}
|
|
85
|
+
return toastContainer;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function createToast(options = {}) {
|
|
89
|
+
const {
|
|
90
|
+
title = '',
|
|
91
|
+
message = '',
|
|
92
|
+
type = 'info',
|
|
93
|
+
duration = 5000,
|
|
94
|
+
position = 'top-right',
|
|
95
|
+
} = options;
|
|
96
|
+
|
|
97
|
+
const container = getContainer();
|
|
98
|
+
const template = container.querySelector('[data-sui-toast-template]');
|
|
99
|
+
const toastEl = template.cloneNode(true);
|
|
100
|
+
toastEl.removeAttribute('data-sui-toast-template');
|
|
101
|
+
|
|
102
|
+
const titleEl = toastEl.querySelector('[data-sui-toast-title]');
|
|
103
|
+
const messageEl = toastEl.querySelector('[data-sui-toast-message]');
|
|
104
|
+
const closeEl = toastEl.querySelector('[data-sui-toast-close]');
|
|
105
|
+
|
|
106
|
+
titleEl.textContent = title;
|
|
107
|
+
messageEl.textContent = message;
|
|
108
|
+
|
|
109
|
+
toastEl.className = 'sui-toast sui-toast-' + type;
|
|
110
|
+
container.className = 'sui-toast-container sui-toast-' + position;
|
|
111
|
+
|
|
112
|
+
function hide() {
|
|
113
|
+
toastEl.classList.add('sui-toast-hide');
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
if (container.contains(toastEl)) container.removeChild(toastEl);
|
|
116
|
+
activeToasts.delete(toastEl);
|
|
117
|
+
if (toastQueue.length > 0) {
|
|
118
|
+
const next = toastQueue.shift();
|
|
119
|
+
showToast(next);
|
|
120
|
+
}
|
|
121
|
+
}, 300);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
closeEl.onclick = hide;
|
|
125
|
+
|
|
126
|
+
let timer = null;
|
|
127
|
+
if (duration > 0) timer = setTimeout(hide, duration);
|
|
128
|
+
|
|
129
|
+
return { element: toastEl, hide, timer };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function showToast(options = {}) {
|
|
133
|
+
if (activeToasts.size >= 5) {
|
|
134
|
+
toastQueue.push(options);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const toast = createToast(options);
|
|
139
|
+
const container = getContainer();
|
|
140
|
+
container.appendChild(toast.element);
|
|
141
|
+
activeToasts.set(toast.element, toast);
|
|
142
|
+
|
|
143
|
+
requestAnimationFrame(() => toast.element.classList.add('sui-toast-show'));
|
|
144
|
+
return toast.hide;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function clearAll() {
|
|
148
|
+
activeToasts.forEach((toast) => {
|
|
149
|
+
if (toast.timer) clearTimeout(toast.timer);
|
|
150
|
+
toast.hide();
|
|
151
|
+
});
|
|
152
|
+
toastQueue = [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
show: showToast,
|
|
157
|
+
clear: clearAll,
|
|
158
|
+
info: (title, message, options) => showToast({ ...options, title, message, type: 'info' }),
|
|
159
|
+
success: (title, message, options) => showToast({ ...options, title, message, type: 'success' }),
|
|
160
|
+
warning: (title, message, options) => showToast({ ...options, title, message, type: 'warning' }),
|
|
161
|
+
error: (title, message, options) => showToast({ ...options, title, message, type: 'error' }),
|
|
162
|
+
};`,
|
|
163
|
+
usageMarkdown: `# SUI Toast Component
|
|
164
|
+
|
|
165
|
+
Non-blocking toast notification system.
|
|
166
|
+
|
|
167
|
+
## SDK Usage
|
|
168
|
+
|
|
169
|
+
\`\`\`javascript
|
|
170
|
+
await uiCmp.load('sui_toast');
|
|
171
|
+
const toastCmp = await uiCmp.sui_toast.create();
|
|
172
|
+
toastCmp.success('Success', 'Toast works', { duration: 2500 });
|
|
173
|
+
\`\`\`
|
|
174
|
+
|
|
175
|
+
## Methods
|
|
176
|
+
- \`show(options)\`
|
|
177
|
+
- \`clear()\`
|
|
178
|
+
- \`info(title, message, options)\`
|
|
179
|
+
- \`success(title, message, options)\`
|
|
180
|
+
- \`warning(title, message, options)\`
|
|
181
|
+
- \`error(title, message, options)\``,
|
|
182
|
+
api: 'show(options), clear(), info(title,message,options), success(title,message,options), warning(title,message,options), error(title,message,options)',
|
|
183
|
+
previewExample: `const i = await uiCmp.create({});\nawait i.success("All good");`,
|
|
184
|
+
version: '1.0.0',
|
|
185
|
+
isActive: true,
|
|
186
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const alertComponent = require('./components/suiAlert');
|
|
2
|
+
const toastComponent = require('./components/suiToast');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
meta: {
|
|
6
|
+
id: 'sui-ui-components',
|
|
7
|
+
name: 'SUI - UI Components',
|
|
8
|
+
version: '1.1.0',
|
|
9
|
+
description: 'Simple UI Components - non-blocking alert and toast components',
|
|
10
|
+
tags: ['ui', 'components', 'alert', 'toast', 'sui'],
|
|
11
|
+
},
|
|
12
|
+
hooks: {
|
|
13
|
+
async install(ctx) {
|
|
14
|
+
const service = ctx?.services?.uiComponents || null;
|
|
15
|
+
if (!service) {
|
|
16
|
+
console.log('[sui-ui-components] uiComponents service not found, skipping seeding');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log('[sui-ui-components] Installing SUI components...');
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
await service.upsertComponent(alertComponent);
|
|
24
|
+
await service.upsertComponent(toastComponent);
|
|
25
|
+
console.log('[sui-ui-components] Successfully installed sui_alert and sui_toast components');
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('[sui-ui-components] Failed to install components:', error);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
function createManager(opts) {
|
|
3
|
+
const {
|
|
4
|
+
nextTick,
|
|
5
|
+
previewMode,
|
|
6
|
+
previewCssIsolation,
|
|
7
|
+
previewPropsJson,
|
|
8
|
+
previewStatus,
|
|
9
|
+
previewLogs,
|
|
10
|
+
previewContainerRef,
|
|
11
|
+
previewTopMountRef,
|
|
12
|
+
previewIframeRef,
|
|
13
|
+
componentEditor,
|
|
14
|
+
showToast,
|
|
15
|
+
} = opts;
|
|
16
|
+
|
|
17
|
+
let runtime = null;
|
|
18
|
+
let topHost = null;
|
|
19
|
+
|
|
20
|
+
function pushLog(...parts) {
|
|
21
|
+
const line = parts.map((p) => (typeof p === 'string' ? p : JSON.stringify(p, null, 2))).join(' ');
|
|
22
|
+
previewLogs.value = [...previewLogs.value, line].slice(-200);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clearLogs() {
|
|
26
|
+
previewLogs.value = [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseProps() {
|
|
30
|
+
const raw = String(previewPropsJson.value || '').trim();
|
|
31
|
+
if (!raw) return {};
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getEditorComponent() {
|
|
36
|
+
const code = String(componentEditor.value.code || '').trim().toLowerCase() || 'preview_component';
|
|
37
|
+
return {
|
|
38
|
+
code,
|
|
39
|
+
html: String(componentEditor.value.html || ''),
|
|
40
|
+
css: String(componentEditor.value.css || ''),
|
|
41
|
+
js: String(componentEditor.value.js || ''),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function buildRuntime(targetWindow, mountEl, cssIsolation, code, html, css, js) {
|
|
46
|
+
let instance = null;
|
|
47
|
+
let styleEl = null;
|
|
48
|
+
let localStyles = [];
|
|
49
|
+
|
|
50
|
+
const created = {
|
|
51
|
+
async create(props, options) {
|
|
52
|
+
created.destroy();
|
|
53
|
+
const doc = targetWindow.document;
|
|
54
|
+
const root = (options && options.mountEl) || mountEl;
|
|
55
|
+
const tpl = doc.createElement('template');
|
|
56
|
+
tpl.innerHTML = String(html || '');
|
|
57
|
+
|
|
58
|
+
const host = doc.createElement('div');
|
|
59
|
+
host.dataset.previewCode = code;
|
|
60
|
+
host.appendChild(tpl.content.cloneNode(true));
|
|
61
|
+
|
|
62
|
+
// Check if we're already inside a shadow DOM (top-frame mode)
|
|
63
|
+
const existingShadowRoot = mountEl.getRootNode?.();
|
|
64
|
+
const isInShadow = existingShadowRoot instanceof targetWindow.ShadowRoot;
|
|
65
|
+
|
|
66
|
+
if (css && ((options && options.cssIsolation) === 'shadow' || cssIsolation === 'shadow')) {
|
|
67
|
+
if (isInShadow) {
|
|
68
|
+
// We're already in a shadow DOM (top-frame mode), inject CSS there
|
|
69
|
+
const style = doc.createElement('style');
|
|
70
|
+
style.textContent = String(css || '');
|
|
71
|
+
existingShadowRoot.appendChild(style);
|
|
72
|
+
localStyles.push(style);
|
|
73
|
+
root.appendChild(host);
|
|
74
|
+
instance = { rootEl: host, templateRootEl: host };
|
|
75
|
+
} else {
|
|
76
|
+
// Create new shadow DOM (iframe mode)
|
|
77
|
+
const shadowHost = doc.createElement('div');
|
|
78
|
+
const shadow = shadowHost.attachShadow({ mode: 'open' });
|
|
79
|
+
const style = doc.createElement('style');
|
|
80
|
+
style.textContent = String(css || '');
|
|
81
|
+
shadow.appendChild(style);
|
|
82
|
+
shadow.appendChild(host);
|
|
83
|
+
root.appendChild(shadowHost);
|
|
84
|
+
instance = { rootEl: shadowHost, templateRootEl: shadow };
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
if (css) {
|
|
88
|
+
if (isInShadow) {
|
|
89
|
+
// Inject CSS into existing shadow DOM for top-frame non-shadow mode
|
|
90
|
+
const style = doc.createElement('style');
|
|
91
|
+
style.textContent = String(css || '');
|
|
92
|
+
existingShadowRoot.appendChild(style);
|
|
93
|
+
localStyles.push(style);
|
|
94
|
+
} else {
|
|
95
|
+
// Inject into document head for iframe mode
|
|
96
|
+
styleEl = doc.createElement('style');
|
|
97
|
+
styleEl.textContent = String(css || '');
|
|
98
|
+
doc.head.appendChild(styleEl);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
root.appendChild(host);
|
|
102
|
+
instance = { rootEl: host, templateRootEl: host };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const api = {
|
|
106
|
+
unmount: created.destroy,
|
|
107
|
+
mountEl: root,
|
|
108
|
+
hostEl: instance.rootEl,
|
|
109
|
+
shadowRoot: instance.templateRootEl instanceof targetWindow.ShadowRoot ? instance.templateRootEl : null,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const fn = new targetWindow.Function('api', 'templateRootEl', 'props', String(js || ''));
|
|
113
|
+
const exported = fn(api, instance.templateRootEl, props || {}) || {};
|
|
114
|
+
instance.api = api;
|
|
115
|
+
instance.exported = exported;
|
|
116
|
+
return Object.assign({ api }, exported);
|
|
117
|
+
},
|
|
118
|
+
destroy() {
|
|
119
|
+
if (instance && instance.rootEl && instance.rootEl.remove) instance.rootEl.remove();
|
|
120
|
+
if (styleEl && styleEl.remove) styleEl.remove();
|
|
121
|
+
localStyles.forEach((s) => {
|
|
122
|
+
if (s && s.remove) s.remove();
|
|
123
|
+
});
|
|
124
|
+
localStyles = [];
|
|
125
|
+
instance = null;
|
|
126
|
+
styleEl = null;
|
|
127
|
+
},
|
|
128
|
+
get instance() {
|
|
129
|
+
return instance;
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return created;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function cleanup() {
|
|
137
|
+
if (runtime) runtime.destroy();
|
|
138
|
+
runtime = null;
|
|
139
|
+
if (previewTopMountRef.value) previewTopMountRef.value.innerHTML = '';
|
|
140
|
+
if (previewIframeRef.value && previewMode.value === 'iframe') {
|
|
141
|
+
const doc = previewIframeRef.value.contentDocument;
|
|
142
|
+
if (doc && doc.body) doc.body.innerHTML = '';
|
|
143
|
+
}
|
|
144
|
+
topHost = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function buildTopRuntime() {
|
|
148
|
+
await nextTick();
|
|
149
|
+
if (!previewTopMountRef.value) throw new Error('Top preview mount not ready');
|
|
150
|
+
|
|
151
|
+
previewTopMountRef.value.innerHTML = '';
|
|
152
|
+
topHost = document.createElement('div');
|
|
153
|
+
topHost.className = 'h-full w-full';
|
|
154
|
+
const shadow = topHost.attachShadow({ mode: 'open' });
|
|
155
|
+
const wrapper = document.createElement('div');
|
|
156
|
+
wrapper.style.height = '100%';
|
|
157
|
+
wrapper.style.padding = '12px';
|
|
158
|
+
wrapper.style.overflow = 'auto';
|
|
159
|
+
const mount = document.createElement('div');
|
|
160
|
+
wrapper.appendChild(mount);
|
|
161
|
+
shadow.appendChild(wrapper);
|
|
162
|
+
previewTopMountRef.value.appendChild(topHost);
|
|
163
|
+
|
|
164
|
+
const c = getEditorComponent();
|
|
165
|
+
runtime = buildRuntime(window, mount, previewCssIsolation.value, c.code, c.html, c.css, c.js);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function buildTopNoIsolationRuntime() {
|
|
169
|
+
await nextTick();
|
|
170
|
+
if (!previewTopMountRef.value) throw new Error('Top preview mount not ready');
|
|
171
|
+
|
|
172
|
+
previewTopMountRef.value.innerHTML = '';
|
|
173
|
+
const mount = document.createElement('div');
|
|
174
|
+
mount.className = 'h-full w-full p-3 overflow-auto';
|
|
175
|
+
previewTopMountRef.value.appendChild(mount);
|
|
176
|
+
|
|
177
|
+
const c = getEditorComponent();
|
|
178
|
+
runtime = buildRuntime(window, mount, previewCssIsolation.value, c.code, c.html, c.css, c.js);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function buildIframeRuntime() {
|
|
182
|
+
await nextTick();
|
|
183
|
+
const iframe = previewIframeRef.value;
|
|
184
|
+
if (!iframe) throw new Error('Iframe preview not ready');
|
|
185
|
+
|
|
186
|
+
iframe.srcdoc = '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body style="margin:0;padding:12px;background:#f9fafb;"><div id="preview-root"></div></body></html>';
|
|
187
|
+
await new Promise((resolve) => { iframe.onload = () => resolve(); });
|
|
188
|
+
|
|
189
|
+
const win = iframe.contentWindow;
|
|
190
|
+
const doc = iframe.contentDocument;
|
|
191
|
+
const mount = doc.getElementById('preview-root');
|
|
192
|
+
const c = getEditorComponent();
|
|
193
|
+
runtime = buildRuntime(win, mount, previewCssIsolation.value, c.code, c.html, c.css, c.js);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function run() {
|
|
197
|
+
try {
|
|
198
|
+
previewStatus.value = 'running';
|
|
199
|
+
cleanup();
|
|
200
|
+
|
|
201
|
+
if (!String(componentEditor.value.html || '').trim() && !String(componentEditor.value.js || '').trim()) {
|
|
202
|
+
previewStatus.value = 'idle';
|
|
203
|
+
pushLog('[preview] editor empty');
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (previewMode.value === 'top') {
|
|
208
|
+
await buildTopRuntime();
|
|
209
|
+
} else if (previewMode.value === 'top-no-isolation') {
|
|
210
|
+
await buildTopNoIsolationRuntime();
|
|
211
|
+
} else {
|
|
212
|
+
await buildIframeRuntime();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const instance = await runtime.create(parseProps(), { cssIsolation: previewCssIsolation.value });
|
|
216
|
+
pushLog('[preview] mounted', { mode: previewMode.value, code: getEditorComponent().code, methods: Object.keys(instance || {}) });
|
|
217
|
+
previewStatus.value = 'ready';
|
|
218
|
+
} catch (e) {
|
|
219
|
+
previewStatus.value = 'error';
|
|
220
|
+
pushLog('[preview:error]', e.message);
|
|
221
|
+
showToast(e.message, 'error');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function reset() {
|
|
226
|
+
cleanup();
|
|
227
|
+
previewStatus.value = 'idle';
|
|
228
|
+
pushLog('[preview] reset');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function apiFacade() {
|
|
232
|
+
return {
|
|
233
|
+
create: async (props, options) => {
|
|
234
|
+
if (!runtime) throw new Error('Preview runtime not initialized. Click Run Preview first.');
|
|
235
|
+
return runtime.create(props || {}, options || { cssIsolation: previewCssIsolation.value });
|
|
236
|
+
},
|
|
237
|
+
destroy: () => {
|
|
238
|
+
if (runtime) runtime.destroy();
|
|
239
|
+
return true;
|
|
240
|
+
},
|
|
241
|
+
getInstance: () => (runtime ? runtime.instance : null),
|
|
242
|
+
mode: previewMode.value,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function runCommand(commandText) {
|
|
247
|
+
if (!runtime) await run();
|
|
248
|
+
const cmd = String(commandText || '').trim();
|
|
249
|
+
if (!cmd) return undefined;
|
|
250
|
+
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
251
|
+
const runner = new AsyncFunction('uiCmp', 'log', 'state', cmd);
|
|
252
|
+
const result = await runner(apiFacade(), (...args) => pushLog('[cmd]', ...args), {
|
|
253
|
+
mode: previewMode.value,
|
|
254
|
+
cssIsolation: previewCssIsolation.value,
|
|
255
|
+
});
|
|
256
|
+
if (result !== undefined) pushLog('[result]', result);
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function toggleFullscreen() {
|
|
261
|
+
const el = previewContainerRef.value;
|
|
262
|
+
if (!el) return;
|
|
263
|
+
if (!document.fullscreenElement) await el.requestFullscreen();
|
|
264
|
+
else await document.exitFullscreen();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
run,
|
|
269
|
+
reset,
|
|
270
|
+
cleanup,
|
|
271
|
+
clearLogs,
|
|
272
|
+
pushLog,
|
|
273
|
+
runCommand,
|
|
274
|
+
toggleFullscreen,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
window.AdminUiComponentsPreview = {
|
|
279
|
+
createManager,
|
|
280
|
+
};
|
|
281
|
+
})();
|