@intranefr/superbackend 1.6.6 → 1.6.7
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/.env.example +4 -0
- package/README.md +18 -0
- package/package.json +6 -1
- package/public/js/admin-superdemos.js +396 -0
- package/public/sdk/superdemos.iife.js +614 -0
- package/public/superdemos-qa.html +324 -0
- package/sdk/superdemos/browser/src/index.js +719 -0
- package/src/cli/agent-chat.js +369 -0
- package/src/cli/agent-list.js +42 -0
- package/src/controllers/adminAgentsChat.controller.js +172 -0
- package/src/controllers/adminSuperDemos.controller.js +382 -0
- package/src/controllers/superDemosPublic.controller.js +126 -0
- package/src/middleware.js +102 -19
- package/src/models/BlogAutomationLock.js +4 -4
- package/src/models/BlogPost.js +16 -16
- package/src/models/CacheEntry.js +17 -6
- package/src/models/JsonConfig.js +2 -4
- package/src/models/RateLimitMetricBucket.js +10 -5
- package/src/models/SuperDemo.js +38 -0
- package/src/models/SuperDemoProject.js +32 -0
- package/src/models/SuperDemoStep.js +27 -0
- package/src/routes/adminAgents.routes.js +10 -0
- package/src/routes/adminMarkdowns.routes.js +3 -0
- package/src/routes/adminSuperDemos.routes.js +31 -0
- package/src/routes/superDemos.routes.js +9 -0
- package/src/services/auditLogger.js +75 -37
- package/src/services/email.service.js +18 -3
- package/src/services/superDemosAuthoringSessions.service.js +132 -0
- package/src/services/superDemosWs.service.js +164 -0
- package/src/services/terminalsWs.service.js +35 -3
- package/src/utils/rbac/rightsRegistry.js +2 -0
- package/views/admin-agents.ejs +261 -11
- package/views/admin-dashboard.ejs +78 -8
- package/views/admin-superdemos.ejs +335 -0
- package/views/admin-terminals.ejs +462 -34
- package/views/partials/admin/agents-chat.ejs +80 -0
- package/views/partials/dashboard/nav-items.ejs +1 -0
- package/views/partials/dashboard/tab-bar.ejs +6 -0
- package/cookies.txt +0 -6
- package/cookies1.txt +0 -6
- package/cookies2.txt +0 -6
- package/cookies3.txt +0 -6
- package/cookies4.txt +0 -5
- package/cookies_old.txt +0 -5
- package/cookies_old_test.txt +0 -6
- package/cookies_super.txt +0 -5
- package/cookies_super_test.txt +0 -6
- package/cookies_test.txt +0 -6
- package/test-access.js +0 -63
- package/test-iframe-fix.html +0 -63
- package/test-iframe.html +0 -14
package/.env.example
CHANGED
|
@@ -65,3 +65,7 @@ MAX_FILE_SIZE_HARD_CAP=10485760
|
|
|
65
65
|
# Set to 'false' to disable console manager initialization
|
|
66
66
|
# When disabled, console methods are not overridden and no console entries are tracked
|
|
67
67
|
# CONSOLE_MANAGER_ENABLED=true
|
|
68
|
+
|
|
69
|
+
# Admin Dashboard
|
|
70
|
+
# Maximum number of tabs allowed in the admin dashboard (default: 5)
|
|
71
|
+
ADMIN_MAX_TABS=5
|
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@ Node.js middleware that gives your project backend superpowers. Handles authenti
|
|
|
26
26
|
- **Workflows System**: Node-based automation with LLM integration, conditionals, and HTTP calls
|
|
27
27
|
- **LLM UI Integration**: AI-powered UI components and conversational interfaces
|
|
28
28
|
- **Admin Scripts & Terminals**: Operational tooling for script execution and terminal management
|
|
29
|
+
- **AI Agent System**: Configurable AI agents with tool integration and multi-interface support
|
|
30
|
+
- **CLI Tools**: Terminal-based agent interaction and management commands
|
|
29
31
|
- **Migration System**: Database migration and data transfer between environments
|
|
30
32
|
- **Upload Namespaces**: Advanced file organization with customizable storage rules
|
|
31
33
|
- **UI Components**: Project-scoped reusable UI widgets with browser SDK integration
|
|
@@ -85,6 +87,22 @@ const backend = new SuperBackend({
|
|
|
85
87
|
|
|
86
88
|
---
|
|
87
89
|
|
|
90
|
+
## CLI Tools
|
|
91
|
+
|
|
92
|
+
SuperBackend includes command-line tools for AI agent interaction:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# List available agents
|
|
96
|
+
npx @intranefr/superbackend agent-list
|
|
97
|
+
|
|
98
|
+
# Start interactive chat with an agent
|
|
99
|
+
npx @intranefr/superbackend agent-chat
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See [CLI-README.md](CLI-README.md) for detailed CLI usage.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
88
106
|
## Documentation
|
|
89
107
|
|
|
90
108
|
See the `docs/features/` directory for detailed guides:
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intranefr/superbackend",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"description": "Node.js middleware that gives your project backend superpowers",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-chat": "src/cli/agent-chat.js",
|
|
8
|
+
"agent-list": "src/cli/agent-list.js"
|
|
9
|
+
},
|
|
6
10
|
"scripts": {
|
|
7
11
|
"start": "node server.js",
|
|
8
12
|
"dev": "nodemon --verbose --ignore uploads --ignore stdout.log --ignore '*.log' server.js",
|
|
@@ -10,6 +14,7 @@
|
|
|
10
14
|
"minio:envs": "node -e \"console.log(['S3_ENDPOINT=http://localhost:9000','S3_REGION=us-east-1','S3_ACCESS_KEY_ID=minioadmin','S3_SECRET_ACCESS_KEY=minioadmin','S3_BUCKET=saasbackend','S3_FORCE_PATH_STYLE=true'].join('\\n'))\"",
|
|
11
15
|
"build:sdk:error-tracking:browser": "esbuild sdk/error-tracking/browser/src/embed.js --bundle --format=iife --global-name=saasbackendErrorTrackingEmbed --outfile=sdk/error-tracking/browser/dist/embed.iife.js",
|
|
12
16
|
"build:sdk:ui-components:browser": "esbuild sdk/ui-components/browser/src/index.js --bundle --format=iife --outfile=public/sdk/ui-components.iife.js",
|
|
17
|
+
"build:sdk:superdemos:browser": "esbuild sdk/superdemos/browser/src/index.js --bundle --format=iife --global-name=SuperDemos --outfile=public/sdk/superdemos.iife.js",
|
|
13
18
|
"test": "jest",
|
|
14
19
|
"test:watch": "jest --watch",
|
|
15
20
|
"test:coverage": "jest --coverage"
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const { createApp, ref, computed, onBeforeUnmount } = Vue;
|
|
3
|
+
|
|
4
|
+
function withToast(fn, showToast) {
|
|
5
|
+
return async (...args) => {
|
|
6
|
+
try {
|
|
7
|
+
return await fn(...args);
|
|
8
|
+
} catch (e) {
|
|
9
|
+
showToast(e.message, 'error');
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
createApp({
|
|
16
|
+
setup() {
|
|
17
|
+
const cfg = window.__adminSuperDemosConfig || {};
|
|
18
|
+
const baseUrl = String(cfg.baseUrl || '');
|
|
19
|
+
const API_BASE = window.location.origin + baseUrl;
|
|
20
|
+
|
|
21
|
+
const origin = computed(() => window.location.origin);
|
|
22
|
+
const qaPageUrl = computed(() => `${window.location.origin}${baseUrl}/superdemos-qa.html`);
|
|
23
|
+
|
|
24
|
+
const toast = ref({ show: false, message: '', type: 'success' });
|
|
25
|
+
let toastTimer = null;
|
|
26
|
+
const showToast = (message, type) => {
|
|
27
|
+
toast.value = { show: true, message: String(message || ''), type: type || 'success' };
|
|
28
|
+
if (toastTimer) clearTimeout(toastTimer);
|
|
29
|
+
toastTimer = setTimeout(() => {
|
|
30
|
+
toast.value.show = false;
|
|
31
|
+
}, 2500);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const api = async (path, options) => {
|
|
35
|
+
const res = await fetch(baseUrl + path, {
|
|
36
|
+
method: (options && options.method) || 'GET',
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body: options && options.body ? JSON.stringify(options.body) : undefined,
|
|
39
|
+
});
|
|
40
|
+
const text = await res.text();
|
|
41
|
+
let data = null;
|
|
42
|
+
try {
|
|
43
|
+
data = text ? JSON.parse(text) : null;
|
|
44
|
+
} catch {
|
|
45
|
+
data = null;
|
|
46
|
+
}
|
|
47
|
+
if (!res.ok) throw new Error((data && data.error) || ('Request failed: ' + res.status));
|
|
48
|
+
return data;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const projects = ref([]);
|
|
52
|
+
const selectedProject = ref(null);
|
|
53
|
+
const demos = ref([]);
|
|
54
|
+
const selectedDemo = ref(null);
|
|
55
|
+
const steps = ref([]);
|
|
56
|
+
|
|
57
|
+
const lastGeneratedKey = ref('');
|
|
58
|
+
|
|
59
|
+
const newProject = ref({ name: '', projectId: '', isPublic: true });
|
|
60
|
+
const newDemo = ref({ name: '', startUrlPattern: '' });
|
|
61
|
+
const projectStylePreset = ref('default');
|
|
62
|
+
const projectStyleOverrides = ref('');
|
|
63
|
+
|
|
64
|
+
const lastSelection = ref(null);
|
|
65
|
+
|
|
66
|
+
const authoring = ref({
|
|
67
|
+
targetUrl: '',
|
|
68
|
+
sessionId: '',
|
|
69
|
+
token: '',
|
|
70
|
+
connectUrl: '',
|
|
71
|
+
wsStatus: 'disconnected',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let ws = null;
|
|
75
|
+
|
|
76
|
+
function closeWs() {
|
|
77
|
+
try {
|
|
78
|
+
if (ws) ws.close();
|
|
79
|
+
} catch {}
|
|
80
|
+
ws = null;
|
|
81
|
+
authoring.value.wsStatus = 'disconnected';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function connectWs(sessionId, token) {
|
|
85
|
+
closeWs();
|
|
86
|
+
|
|
87
|
+
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
88
|
+
const wsUrl = `${proto}//${window.location.host}${baseUrl}/api/superdemos/ws?sessionId=${encodeURIComponent(
|
|
89
|
+
sessionId,
|
|
90
|
+
)}&role=admin&token=${encodeURIComponent(token)}`;
|
|
91
|
+
|
|
92
|
+
ws = new WebSocket(wsUrl);
|
|
93
|
+
authoring.value.wsStatus = 'connecting';
|
|
94
|
+
|
|
95
|
+
ws.addEventListener('open', () => {
|
|
96
|
+
authoring.value.wsStatus = 'connected';
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
ws.addEventListener('close', () => {
|
|
100
|
+
authoring.value.wsStatus = 'disconnected';
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
ws.addEventListener('error', () => {
|
|
104
|
+
authoring.value.wsStatus = 'error';
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
ws.addEventListener('message', (evt) => {
|
|
108
|
+
let msg;
|
|
109
|
+
try {
|
|
110
|
+
msg = JSON.parse(String(evt.data || ''));
|
|
111
|
+
} catch {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (msg.type === 'select' && msg.element) {
|
|
116
|
+
lastSelection.value = msg.element;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (msg.type === 'hover' && msg.element && !lastSelection.value) {
|
|
120
|
+
// helpful for first feedback
|
|
121
|
+
lastSelection.value = msg.element;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const refreshProjects = async () => {
|
|
127
|
+
const data = await api('/api/admin/superdemos/projects');
|
|
128
|
+
const items = Array.isArray(data && data.items) ? data.items : [];
|
|
129
|
+
projects.value = items
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.map((p) => ({
|
|
132
|
+
...p,
|
|
133
|
+
projectId: String((p && p.projectId) || ''),
|
|
134
|
+
name: String((p && p.name) || ''),
|
|
135
|
+
isPublic: Boolean(p && p.isPublic),
|
|
136
|
+
}))
|
|
137
|
+
.filter((p) => p.projectId);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const refreshDemos = async (projectId) => {
|
|
141
|
+
const data = await api('/api/admin/superdemos/projects/' + encodeURIComponent(projectId) + '/demos');
|
|
142
|
+
const items = Array.isArray(data && data.items) ? data.items : [];
|
|
143
|
+
demos.value = items
|
|
144
|
+
.filter(Boolean)
|
|
145
|
+
.map((d) => ({
|
|
146
|
+
...d,
|
|
147
|
+
demoId: String((d && d.demoId) || ''),
|
|
148
|
+
name: String((d && d.name) || ''),
|
|
149
|
+
status: String((d && d.status) || 'draft'),
|
|
150
|
+
publishedVersion: Number((d && d.publishedVersion) || 0),
|
|
151
|
+
}))
|
|
152
|
+
.filter((d) => d.demoId);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const refreshSteps = async (demoId) => {
|
|
156
|
+
const data = await api('/api/admin/superdemos/demos/' + encodeURIComponent(demoId) + '/steps');
|
|
157
|
+
const items = (data && data.items) || [];
|
|
158
|
+
steps.value = items.map((s) => ({
|
|
159
|
+
selector: s.selector,
|
|
160
|
+
selectorHints: s.selectorHints || null,
|
|
161
|
+
message: s.message,
|
|
162
|
+
placement: s.placement || 'auto',
|
|
163
|
+
waitFor: s.waitFor || null,
|
|
164
|
+
advance: s.advance || { type: 'manualNext' },
|
|
165
|
+
}));
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const refreshAll = withToast(async () => {
|
|
169
|
+
await refreshProjects();
|
|
170
|
+
if (selectedProject.value) await refreshDemos(selectedProject.value.projectId);
|
|
171
|
+
if (selectedDemo.value) await refreshSteps(selectedDemo.value.demoId);
|
|
172
|
+
showToast('Refreshed', 'success');
|
|
173
|
+
}, showToast);
|
|
174
|
+
|
|
175
|
+
const saveProjectStyleSettings = withToast(async () => {
|
|
176
|
+
if (!selectedProject.value) return;
|
|
177
|
+
const data = await api('/api/admin/superdemos/projects/' + encodeURIComponent(selectedProject.value.projectId), {
|
|
178
|
+
method: 'PUT',
|
|
179
|
+
body: {
|
|
180
|
+
stylePreset: projectStylePreset.value || 'default',
|
|
181
|
+
styleOverrides: projectStyleOverrides.value || '',
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
selectedProject.value = { ...data.item };
|
|
185
|
+
showToast('Project style settings saved', 'success');
|
|
186
|
+
}, showToast);
|
|
187
|
+
|
|
188
|
+
const copyText = async (text, label) => {
|
|
189
|
+
const v = String(text || '');
|
|
190
|
+
if (!v) {
|
|
191
|
+
showToast('Nothing to copy', 'error');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
await navigator.clipboard.writeText(v);
|
|
196
|
+
showToast(`${label || 'Value'} copied`, 'success');
|
|
197
|
+
} catch {
|
|
198
|
+
showToast('Clipboard copy failed', 'error');
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const createProject = withToast(async () => {
|
|
203
|
+
lastGeneratedKey.value = '';
|
|
204
|
+
const data = await api('/api/admin/superdemos/projects', {
|
|
205
|
+
method: 'POST',
|
|
206
|
+
body: {
|
|
207
|
+
name: newProject.value.name,
|
|
208
|
+
projectId: newProject.value.projectId || undefined,
|
|
209
|
+
isPublic: Boolean(newProject.value.isPublic),
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
if (data && data.apiKey) lastGeneratedKey.value = data.apiKey;
|
|
213
|
+
newProject.value = { name: '', projectId: '', isPublic: true };
|
|
214
|
+
await refreshProjects();
|
|
215
|
+
showToast('Project created', 'success');
|
|
216
|
+
}, showToast);
|
|
217
|
+
|
|
218
|
+
const selectProject = withToast(async (p) => {
|
|
219
|
+
selectedProject.value = { ...p };
|
|
220
|
+
projectStylePreset.value = String(p.stylePreset || 'default');
|
|
221
|
+
projectStyleOverrides.value = String(p.styleOverrides || '');
|
|
222
|
+
selectedDemo.value = null;
|
|
223
|
+
demos.value = [];
|
|
224
|
+
steps.value = [];
|
|
225
|
+
closeWs();
|
|
226
|
+
authoring.value = { targetUrl: '', sessionId: '', token: '', connectUrl: '', wsStatus: 'disconnected' };
|
|
227
|
+
lastSelection.value = null;
|
|
228
|
+
await refreshDemos(p.projectId);
|
|
229
|
+
}, showToast);
|
|
230
|
+
|
|
231
|
+
const createDemo = withToast(async () => {
|
|
232
|
+
if (!selectedProject.value) return;
|
|
233
|
+
const data = await api(
|
|
234
|
+
'/api/admin/superdemos/projects/' + encodeURIComponent(selectedProject.value.projectId) + '/demos',
|
|
235
|
+
{
|
|
236
|
+
method: 'POST',
|
|
237
|
+
body: {
|
|
238
|
+
name: newDemo.value.name,
|
|
239
|
+
startUrlPattern: newDemo.value.startUrlPattern || null,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
newDemo.value = { name: '', startUrlPattern: '' };
|
|
244
|
+
await refreshDemos(selectedProject.value.projectId);
|
|
245
|
+
showToast('Demo created', 'success');
|
|
246
|
+
return data;
|
|
247
|
+
}, showToast);
|
|
248
|
+
|
|
249
|
+
const selectDemo = withToast(async (d) => {
|
|
250
|
+
selectedDemo.value = { ...d };
|
|
251
|
+
steps.value = [];
|
|
252
|
+
closeWs();
|
|
253
|
+
authoring.value = { targetUrl: '', sessionId: '', token: '', connectUrl: '', wsStatus: 'disconnected' };
|
|
254
|
+
lastSelection.value = null;
|
|
255
|
+
await refreshSteps(d.demoId);
|
|
256
|
+
}, showToast);
|
|
257
|
+
|
|
258
|
+
const publishDemo = withToast(async () => {
|
|
259
|
+
if (!selectedDemo.value) return;
|
|
260
|
+
const data = await api('/api/admin/superdemos/demos/' + encodeURIComponent(selectedDemo.value.demoId) + '/publish', {
|
|
261
|
+
method: 'POST',
|
|
262
|
+
});
|
|
263
|
+
await refreshDemos(selectedProject.value.projectId);
|
|
264
|
+
selectedDemo.value = data.item;
|
|
265
|
+
showToast('Published', 'success');
|
|
266
|
+
}, showToast);
|
|
267
|
+
|
|
268
|
+
const saveSteps = withToast(async () => {
|
|
269
|
+
if (!selectedDemo.value) return;
|
|
270
|
+
await api('/api/admin/superdemos/demos/' + encodeURIComponent(selectedDemo.value.demoId) + '/steps', {
|
|
271
|
+
method: 'PUT',
|
|
272
|
+
body: {
|
|
273
|
+
steps: steps.value.map((s) => ({
|
|
274
|
+
selector: s.selector,
|
|
275
|
+
selectorHints: s.selectorHints || null,
|
|
276
|
+
message: s.message,
|
|
277
|
+
placement: s.placement || 'auto',
|
|
278
|
+
waitFor: s.waitFor || null,
|
|
279
|
+
advance: s.advance || { type: 'manualNext' },
|
|
280
|
+
})),
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
showToast('Steps saved', 'success');
|
|
284
|
+
}, showToast);
|
|
285
|
+
|
|
286
|
+
const removeStep = (idx) => {
|
|
287
|
+
steps.value = steps.value.filter((_, i) => i !== idx);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const moveStep = (idx, delta) => {
|
|
291
|
+
const next = idx + delta;
|
|
292
|
+
if (next < 0 || next >= steps.value.length) return;
|
|
293
|
+
const copy = [...steps.value];
|
|
294
|
+
const [item] = copy.splice(idx, 1);
|
|
295
|
+
copy.splice(next, 0, item);
|
|
296
|
+
steps.value = copy;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const addStepFromSelection = () => {
|
|
300
|
+
if (!lastSelection.value) {
|
|
301
|
+
showToast('No selection from SDK yet', 'error');
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
steps.value = [
|
|
305
|
+
...steps.value,
|
|
306
|
+
{
|
|
307
|
+
selector: String(lastSelection.value.selector || ''),
|
|
308
|
+
selectorHints: lastSelection.value.hints || null,
|
|
309
|
+
message: '...',
|
|
310
|
+
placement: 'auto',
|
|
311
|
+
waitFor: null,
|
|
312
|
+
advance: { type: 'manualNext' },
|
|
313
|
+
},
|
|
314
|
+
];
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const startAuthoring = withToast(async () => {
|
|
318
|
+
if (!selectedDemo.value) return;
|
|
319
|
+
const data = await api('/api/admin/superdemos/authoring-sessions', {
|
|
320
|
+
method: 'POST',
|
|
321
|
+
body: {
|
|
322
|
+
demoId: selectedDemo.value.demoId,
|
|
323
|
+
targetUrl: authoring.value.targetUrl,
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
authoring.value.sessionId = data.sessionId;
|
|
328
|
+
authoring.value.token = data.token;
|
|
329
|
+
authoring.value.connectUrl = data.connectUrl;
|
|
330
|
+
|
|
331
|
+
connectWs(data.sessionId, data.token);
|
|
332
|
+
showToast('Authoring session created', 'success');
|
|
333
|
+
}, showToast);
|
|
334
|
+
|
|
335
|
+
const setQaTargetUrl = () => {
|
|
336
|
+
authoring.value.targetUrl = qaPageUrl.value;
|
|
337
|
+
showToast('Target URL set to QA page', 'success');
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const previewStep = (step) => {
|
|
341
|
+
if (!ws || ws.readyState !== ws.OPEN) {
|
|
342
|
+
showToast('WS not connected', 'error');
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const s = step || {};
|
|
346
|
+
ws.send(
|
|
347
|
+
JSON.stringify({
|
|
348
|
+
type: 'preview_bubble',
|
|
349
|
+
selector: s.selector,
|
|
350
|
+
message: s.message,
|
|
351
|
+
placement: s.placement || 'auto',
|
|
352
|
+
}),
|
|
353
|
+
);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
refreshAll().catch(() => {});
|
|
357
|
+
|
|
358
|
+
onBeforeUnmount(() => {
|
|
359
|
+
closeWs();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
origin,
|
|
364
|
+
qaPageUrl,
|
|
365
|
+
toast,
|
|
366
|
+
projects,
|
|
367
|
+
selectedProject,
|
|
368
|
+
demos,
|
|
369
|
+
selectedDemo,
|
|
370
|
+
steps,
|
|
371
|
+
newProject,
|
|
372
|
+
newDemo,
|
|
373
|
+
projectStylePreset,
|
|
374
|
+
projectStyleOverrides,
|
|
375
|
+
createProject,
|
|
376
|
+
refreshAll,
|
|
377
|
+
selectProject,
|
|
378
|
+
createDemo,
|
|
379
|
+
selectDemo,
|
|
380
|
+
publishDemo,
|
|
381
|
+
saveSteps,
|
|
382
|
+
removeStep,
|
|
383
|
+
moveStep,
|
|
384
|
+
authoring,
|
|
385
|
+
startAuthoring,
|
|
386
|
+
setQaTargetUrl,
|
|
387
|
+
saveProjectStyleSettings,
|
|
388
|
+
copyText,
|
|
389
|
+
lastGeneratedKey,
|
|
390
|
+
lastSelection,
|
|
391
|
+
addStepFromSelection,
|
|
392
|
+
previewStep,
|
|
393
|
+
};
|
|
394
|
+
},
|
|
395
|
+
}).mount('#app');
|
|
396
|
+
})();
|