@vue-skuilder/cli 0.1.7 → 0.1.8-1
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/README.md +122 -8
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +34 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pack.d.ts +10 -0
- package/dist/commands/pack.d.ts.map +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/pack.js.map +1 -1
- package/dist/commands/studio.d.ts.map +1 -1
- package/dist/commands/studio.js +641 -106
- package/dist/commands/studio.js.map +1 -1
- package/dist/commands/unpack.d.ts +12 -0
- package/dist/commands/unpack.d.ts.map +1 -1
- package/dist/commands/unpack.js +1 -1
- package/dist/commands/unpack.js.map +1 -1
- package/dist/express-assets/app.d.ts +6 -0
- package/dist/express-assets/app.d.ts.map +1 -0
- package/dist/express-assets/app.js +209 -0
- package/dist/express-assets/app.js.map +1 -0
- package/dist/express-assets/assets/classroomDesignDoc.js +24 -0
- package/dist/express-assets/assets/courseValidateDocUpdate.js +56 -0
- package/dist/express-assets/assets/get-tagsDesignDoc.json +9 -0
- package/dist/express-assets/attachment-preprocessing/index.d.ts +11 -0
- package/dist/express-assets/attachment-preprocessing/index.d.ts.map +1 -0
- package/dist/express-assets/attachment-preprocessing/index.js +204 -0
- package/dist/express-assets/attachment-preprocessing/index.js.map +1 -0
- package/dist/express-assets/attachment-preprocessing/normalize.d.ts +7 -0
- package/dist/express-assets/attachment-preprocessing/normalize.d.ts.map +1 -0
- package/dist/express-assets/attachment-preprocessing/normalize.js +90 -0
- package/dist/express-assets/attachment-preprocessing/normalize.js.map +1 -0
- package/dist/express-assets/client-requests/classroom-requests.d.ts +26 -0
- package/dist/express-assets/client-requests/classroom-requests.d.ts.map +1 -0
- package/dist/express-assets/client-requests/classroom-requests.js +171 -0
- package/dist/express-assets/client-requests/classroom-requests.js.map +1 -0
- package/dist/express-assets/client-requests/course-requests.d.ts +10 -0
- package/dist/express-assets/client-requests/course-requests.d.ts.map +1 -0
- package/dist/express-assets/client-requests/course-requests.js +135 -0
- package/dist/express-assets/client-requests/course-requests.js.map +1 -0
- package/dist/express-assets/client-requests/pack-requests.d.ts +19 -0
- package/dist/express-assets/client-requests/pack-requests.d.ts.map +1 -0
- package/dist/express-assets/client-requests/pack-requests.js +130 -0
- package/dist/express-assets/client-requests/pack-requests.js.map +1 -0
- package/dist/express-assets/client.d.ts +31 -0
- package/dist/express-assets/client.d.ts.map +1 -0
- package/dist/express-assets/client.js +70 -0
- package/dist/express-assets/client.js.map +1 -0
- package/dist/express-assets/couchdb/authentication.d.ts +4 -0
- package/dist/express-assets/couchdb/authentication.d.ts.map +1 -0
- package/dist/express-assets/couchdb/authentication.js +69 -0
- package/dist/express-assets/couchdb/authentication.js.map +1 -0
- package/dist/express-assets/couchdb/index.d.ts +18 -0
- package/dist/express-assets/couchdb/index.d.ts.map +1 -0
- package/dist/express-assets/couchdb/index.js +52 -0
- package/dist/express-assets/couchdb/index.js.map +1 -0
- package/dist/express-assets/design-docs.d.ts +63 -0
- package/dist/express-assets/design-docs.d.ts.map +1 -0
- package/dist/express-assets/design-docs.js +90 -0
- package/dist/express-assets/design-docs.js.map +1 -0
- package/dist/express-assets/logger.d.ts +3 -0
- package/dist/express-assets/logger.d.ts.map +1 -0
- package/dist/express-assets/logger.js +62 -0
- package/dist/express-assets/logger.js.map +1 -0
- package/dist/express-assets/routes/logs.d.ts +3 -0
- package/dist/express-assets/routes/logs.d.ts.map +1 -0
- package/dist/express-assets/routes/logs.js +274 -0
- package/dist/express-assets/routes/logs.js.map +1 -0
- package/dist/express-assets/utils/env.d.ts +11 -0
- package/dist/express-assets/utils/env.d.ts.map +1 -0
- package/dist/express-assets/utils/env.js +39 -0
- package/dist/express-assets/utils/env.js.map +1 -0
- package/dist/express-assets/utils/processQueue.d.ts +39 -0
- package/dist/express-assets/utils/processQueue.d.ts.map +1 -0
- package/dist/express-assets/utils/processQueue.js +175 -0
- package/dist/express-assets/utils/processQueue.js.map +1 -0
- package/dist/studio-ui-src/App.vue +132 -0
- package/dist/studio-ui-src/api/index.ts +30 -0
- package/dist/studio-ui-src/components/StudioFlush.vue +108 -0
- package/dist/studio-ui-src/config/development.ts +98 -0
- package/dist/studio-ui-src/index.html +13 -0
- package/dist/studio-ui-src/main.ts +186 -0
- package/dist/studio-ui-src/package.json +35 -0
- package/dist/studio-ui-src/router/index.ts +32 -0
- package/dist/studio-ui-src/stores/useAuthStore.ts +3 -0
- package/dist/studio-ui-src/tsconfig.json +28 -0
- package/dist/studio-ui-src/utils/courseConfigRegistration.ts +297 -0
- package/dist/studio-ui-src/views/BrowseView.vue +82 -0
- package/dist/studio-ui-src/views/BulkImportView.vue +89 -0
- package/dist/studio-ui-src/views/CourseEditorView.vue +62 -0
- package/dist/studio-ui-src/views/CreateCardView.vue +144 -0
- package/dist/studio-ui-src/vite.config.base.js +103 -0
- package/dist/studio-ui-src/vite.config.ts +26 -0
- package/dist/templates/.skuilder/README.md +29 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/ExpressManager.d.ts +28 -0
- package/dist/utils/ExpressManager.d.ts.map +1 -0
- package/dist/utils/ExpressManager.js +166 -0
- package/dist/utils/ExpressManager.js.map +1 -0
- package/dist/utils/NodeFileSystemAdapter.d.ts +6 -0
- package/dist/utils/NodeFileSystemAdapter.d.ts.map +1 -1
- package/dist/utils/NodeFileSystemAdapter.js +29 -1
- package/dist/utils/NodeFileSystemAdapter.js.map +1 -1
- package/dist/utils/error-reporting.d.ts +54 -0
- package/dist/utils/error-reporting.d.ts.map +1 -0
- package/dist/utils/error-reporting.js +143 -0
- package/dist/utils/error-reporting.js.map +1 -0
- package/dist/utils/pack-courses.d.ts.map +1 -1
- package/dist/utils/pack-courses.js +10 -27
- package/dist/utils/pack-courses.js.map +1 -1
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +24 -0
- package/dist/utils/prompts.js.map +1 -1
- package/dist/utils/questions-hash.d.ts +22 -0
- package/dist/utils/questions-hash.d.ts.map +1 -0
- package/dist/utils/questions-hash.js +96 -0
- package/dist/utils/questions-hash.js.map +1 -0
- package/dist/utils/template.d.ts +1 -1
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +209 -27
- package/dist/utils/template.js.map +1 -1
- package/eslint.config.mjs +1 -1
- package/package.json +30 -11
- package/src/cli.ts +3 -1
- package/src/commands/init.ts +48 -3
- package/src/commands/pack.ts +1 -1
- package/src/commands/studio.ts +851 -122
- package/src/commands/unpack.ts +1 -1
- package/src/types.ts +5 -0
- package/src/utils/ExpressManager.ts +210 -0
- package/src/utils/NodeFileSystemAdapter.ts +46 -2
- package/src/utils/error-reporting.ts +192 -0
- package/src/utils/pack-courses.ts +11 -36
- package/src/utils/prompts.ts +34 -0
- package/src/utils/questions-hash.ts +109 -0
- package/src/utils/template.ts +243 -29
- package/templates/.skuilder/README.md +29 -0
- package/dist/studio-ui-assets/assets/BrowseView-BJbixGOU.js +0 -2
- package/dist/studio-ui-assets/assets/BrowseView-BJbixGOU.js.map +0 -1
- package/dist/studio-ui-assets/assets/BrowseView-CM4HBO4j.css +0 -1
- package/dist/studio-ui-assets/assets/BulkImportView-DB6DYDJU.js +0 -2
- package/dist/studio-ui-assets/assets/BulkImportView-DB6DYDJU.js.map +0 -1
- package/dist/studio-ui-assets/assets/BulkImportView-g4wQUfPA.css +0 -1
- package/dist/studio-ui-assets/assets/CourseEditorView-BIlhlhw1.js +0 -2
- package/dist/studio-ui-assets/assets/CourseEditorView-BIlhlhw1.js.map +0 -1
- package/dist/studio-ui-assets/assets/CourseEditorView-WuPNLVKp.css +0 -1
- package/dist/studio-ui-assets/assets/CreateCardView-CyNOKCkm.css +0 -1
- package/dist/studio-ui-assets/assets/CreateCardView-DPjPvzzt.js +0 -2
- package/dist/studio-ui-assets/assets/CreateCardView-DPjPvzzt.js.map +0 -1
- package/dist/studio-ui-assets/assets/edit-ui.es-DiUxqbgF.js +0 -330
- package/dist/studio-ui-assets/assets/edit-ui.es-DiUxqbgF.js.map +0 -1
- package/dist/studio-ui-assets/assets/index--zY88pg6.css +0 -14
- package/dist/studio-ui-assets/assets/index-BnAv1C72.js +0 -287
- package/dist/studio-ui-assets/assets/index-BnAv1C72.js.map +0 -1
- package/dist/studio-ui-assets/assets/index-DHMXQY3-.js +0 -192
- package/dist/studio-ui-assets/assets/index-DHMXQY3-.js.map +0 -1
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/dist/studio-ui-assets/assets/vue-DZcMATiC.js +0 -28
- package/dist/studio-ui-assets/assets/vue-DZcMATiC.js.map +0 -1
- package/dist/studio-ui-assets/assets/vuetify-qg7mRxy_.js +0 -6
- package/dist/studio-ui-assets/assets/vuetify-qg7mRxy_.js.map +0 -1
- package/dist/studio-ui-assets/index.html +0 -16
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import logger from '../logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* This queue executes async prcesses sequentially, waiting
|
|
4
|
+
* for each to complete before launching the next.
|
|
5
|
+
*/
|
|
6
|
+
export default class AsyncProcessQueue {
|
|
7
|
+
processRequest;
|
|
8
|
+
queue = [];
|
|
9
|
+
errors = [];
|
|
10
|
+
completed = [];
|
|
11
|
+
processing = false;
|
|
12
|
+
nextID = 0;
|
|
13
|
+
/**
|
|
14
|
+
* Returns 'complete' if the job is complete, 'error' if the
|
|
15
|
+
* job failed, and the job's position in queue if not yet
|
|
16
|
+
* completed.
|
|
17
|
+
*
|
|
18
|
+
* @param jobID The jobID returned by addRequest
|
|
19
|
+
*/
|
|
20
|
+
jobStatus(jobID) {
|
|
21
|
+
let ret = -1; // Default to -1 if job not found
|
|
22
|
+
this.queue.forEach((req) => {
|
|
23
|
+
if (req.id === jobID) {
|
|
24
|
+
ret = this.queue.indexOf(req);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
this.completed.forEach((req) => {
|
|
28
|
+
if (req.id === jobID) {
|
|
29
|
+
ret = 'complete';
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
this.errors.forEach((req) => {
|
|
33
|
+
if (req.id === jobID) {
|
|
34
|
+
ret = 'error';
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return ret;
|
|
38
|
+
}
|
|
39
|
+
async recurseGetResult(jobID, depth) {
|
|
40
|
+
// polling intervals in milliseconds
|
|
41
|
+
logger.info(`Checking job status of job ${jobID}...`);
|
|
42
|
+
const intervals = [100, 200, 400, 800, 1000, 2000, 3000, 5000];
|
|
43
|
+
depth = Math.min(depth, intervals.length - 1);
|
|
44
|
+
let status;
|
|
45
|
+
const p = new Promise((resolve, reject) => {
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
status = this.jobStatus(jobID);
|
|
48
|
+
if (status === 'complete' || status === 'error') {
|
|
49
|
+
resolve(null);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
reject();
|
|
53
|
+
}
|
|
54
|
+
}, intervals[depth]);
|
|
55
|
+
});
|
|
56
|
+
return p
|
|
57
|
+
.then(() => {
|
|
58
|
+
return this.getResult(jobID);
|
|
59
|
+
})
|
|
60
|
+
.catch(() => {
|
|
61
|
+
return this.recurseGetResult(jobID, depth + 1);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async getResult(jobID, _depth = 0) {
|
|
65
|
+
const status = this.jobStatus(jobID);
|
|
66
|
+
if (status === 'complete') {
|
|
67
|
+
const res = this.completed.find((val) => {
|
|
68
|
+
return val.id === jobID;
|
|
69
|
+
});
|
|
70
|
+
if (res) {
|
|
71
|
+
return res.result;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return {
|
|
75
|
+
error: 'No result found',
|
|
76
|
+
ok: false,
|
|
77
|
+
status: 'error',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (status === 'error') {
|
|
82
|
+
const res = this.errors.find((val) => {
|
|
83
|
+
return val.id === jobID;
|
|
84
|
+
});
|
|
85
|
+
if (!res) {
|
|
86
|
+
return {
|
|
87
|
+
error: 'Job failed - no error log found',
|
|
88
|
+
ok: false,
|
|
89
|
+
status: 'error',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (res.result) {
|
|
93
|
+
return res.result;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// result is null b/c of an uncaugth exception
|
|
97
|
+
return {
|
|
98
|
+
error: res.error,
|
|
99
|
+
ok: false,
|
|
100
|
+
status: 'error',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return this.recurseGetResult(jobID, 0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
addRequest(req) {
|
|
109
|
+
const id = this.nextID++;
|
|
110
|
+
this.queue.push({
|
|
111
|
+
id: id,
|
|
112
|
+
request: req,
|
|
113
|
+
});
|
|
114
|
+
if (!this.processing) {
|
|
115
|
+
void this.process();
|
|
116
|
+
}
|
|
117
|
+
return id;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
122
|
+
constructor(processingFcn) {
|
|
123
|
+
this.processRequest = processingFcn;
|
|
124
|
+
}
|
|
125
|
+
async process() {
|
|
126
|
+
this.processing = true;
|
|
127
|
+
while (this.queue.length > 0) {
|
|
128
|
+
const req = this.queue[0];
|
|
129
|
+
logger.info(`Processing ${req.id}`);
|
|
130
|
+
try {
|
|
131
|
+
const result = await this.processRequest(req.request);
|
|
132
|
+
if (result.ok) {
|
|
133
|
+
this.completed.push({
|
|
134
|
+
id: req.id,
|
|
135
|
+
request: req.request,
|
|
136
|
+
result: result,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
if (result) {
|
|
141
|
+
this.errors.push({
|
|
142
|
+
id: req.id,
|
|
143
|
+
request: req.request,
|
|
144
|
+
result: result,
|
|
145
|
+
error: result.error,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.errors.push({
|
|
150
|
+
id: req.id,
|
|
151
|
+
request: req.request,
|
|
152
|
+
error: 'error',
|
|
153
|
+
result: null,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
this.errors.push({
|
|
160
|
+
id: req.id,
|
|
161
|
+
request: req.request,
|
|
162
|
+
result: null,
|
|
163
|
+
error: e,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
// remove the completed (or errored)
|
|
168
|
+
// request from the queue
|
|
169
|
+
this.queue.shift();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
this.processing = false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=processQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processQueue.js","sourceRoot":"","sources":["../../src/utils/processQueue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-app>
|
|
3
|
+
<v-app-bar color="primary">
|
|
4
|
+
<v-toolbar-title>
|
|
5
|
+
<v-icon start>mdi-palette</v-icon>
|
|
6
|
+
Skuilder Studio
|
|
7
|
+
</v-toolbar-title>
|
|
8
|
+
<v-spacer />
|
|
9
|
+
|
|
10
|
+
<!-- Navigation buttons -->
|
|
11
|
+
<v-btn-group v-if="courseId" class="mr-4">
|
|
12
|
+
<v-btn :color="$route.name === 'browse' ? 'secondary' : 'transparent'" @click="$router.push('/')">
|
|
13
|
+
<v-icon start>mdi-magnify</v-icon>
|
|
14
|
+
Browse Course
|
|
15
|
+
</v-btn>
|
|
16
|
+
<v-btn
|
|
17
|
+
:color="$route.name === 'create-card' ? 'secondary' : 'transparent'"
|
|
18
|
+
@click="$router.push('/create-card')"
|
|
19
|
+
>
|
|
20
|
+
<v-icon start>mdi-card-plus</v-icon>
|
|
21
|
+
Create Card
|
|
22
|
+
</v-btn>
|
|
23
|
+
<v-btn
|
|
24
|
+
:color="$route.name === 'bulk-import' ? 'secondary' : 'transparent'"
|
|
25
|
+
@click="$router.push('/bulk-import')"
|
|
26
|
+
>
|
|
27
|
+
<v-icon start>mdi-file-import</v-icon>
|
|
28
|
+
Bulk Import
|
|
29
|
+
</v-btn>
|
|
30
|
+
</v-btn-group>
|
|
31
|
+
|
|
32
|
+
<studio-flush v-if="courseId" :course-id="courseId" />
|
|
33
|
+
</v-app-bar>
|
|
34
|
+
|
|
35
|
+
<v-main>
|
|
36
|
+
<v-container fluid>
|
|
37
|
+
<div v-if="loading" class="text-center pa-4">
|
|
38
|
+
<v-progress-circular indeterminate />
|
|
39
|
+
<p class="mt-2">Loading studio environment...</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div v-else-if="error" class="text-center pa-4">
|
|
43
|
+
<v-icon color="error" size="48">mdi-alert-circle</v-icon>
|
|
44
|
+
<h2 class="mt-2">Studio Error</h2>
|
|
45
|
+
<p>{{ error }}</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div v-else-if="courseId">
|
|
49
|
+
<!-- Course information header -->
|
|
50
|
+
<v-card class="mb-4" flat>
|
|
51
|
+
<v-card-text>
|
|
52
|
+
<div class="studio-header">
|
|
53
|
+
<h1>Course Editor</h1>
|
|
54
|
+
<p class="text-subtitle-1">
|
|
55
|
+
Editing course: <strong>{{ courseId }}</strong>
|
|
56
|
+
</p>
|
|
57
|
+
</div>
|
|
58
|
+
</v-card-text>
|
|
59
|
+
</v-card>
|
|
60
|
+
|
|
61
|
+
<!-- Router view for different editing modes -->
|
|
62
|
+
<router-view />
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div v-else class="text-center pa-4">
|
|
66
|
+
<v-icon size="48">mdi-school</v-icon>
|
|
67
|
+
<h2 class="mt-2">No Course Loaded</h2>
|
|
68
|
+
<p>Studio is waiting for course data...</p>
|
|
69
|
+
</div>
|
|
70
|
+
</v-container>
|
|
71
|
+
</v-main>
|
|
72
|
+
|
|
73
|
+
<!-- Global services -->
|
|
74
|
+
<snackbar-service id="SnackbarService" />
|
|
75
|
+
</v-app>
|
|
76
|
+
</template>
|
|
77
|
+
|
|
78
|
+
<script setup lang="ts">
|
|
79
|
+
import { ref, onMounted } from 'vue';
|
|
80
|
+
import { SnackbarService } from '@vue-skuilder/common-ui';
|
|
81
|
+
import StudioFlush from './components/StudioFlush.vue';
|
|
82
|
+
import { getStudioConfig, getConfigErrorMessage } from './config/development';
|
|
83
|
+
|
|
84
|
+
// Studio state
|
|
85
|
+
const loading = ref(true);
|
|
86
|
+
const error = ref<string | null>(null);
|
|
87
|
+
const courseId = ref<string | null>(null);
|
|
88
|
+
|
|
89
|
+
// Initialize studio environment
|
|
90
|
+
onMounted(async () => {
|
|
91
|
+
try {
|
|
92
|
+
// Get studio configuration (CLI-injected or environment variables)
|
|
93
|
+
const studioConfig = getStudioConfig();
|
|
94
|
+
|
|
95
|
+
if (!studioConfig) {
|
|
96
|
+
throw new Error(getConfigErrorMessage());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Use the actual course ID from the configuration
|
|
100
|
+
courseId.value = studioConfig.database.name;
|
|
101
|
+
|
|
102
|
+
// Debug: Check if course database is accessible
|
|
103
|
+
console.log('Studio: Course ID set to', courseId.value);
|
|
104
|
+
console.log('Studio: Data layer initialized, checking course access...');
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const dataLayer = (await import('@vue-skuilder/db')).getDataLayer();
|
|
108
|
+
const courseDB = dataLayer.getCourseDB(studioConfig.database.name);
|
|
109
|
+
console.log('Studio: CourseDB obtained:', courseDB);
|
|
110
|
+
|
|
111
|
+
const courseConfig = await courseDB.getCourseConfig();
|
|
112
|
+
console.log('Studio: Course config loaded:', courseConfig);
|
|
113
|
+
} catch (dbError) {
|
|
114
|
+
console.error('Studio: Course database error:', dbError);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
loading.value = false;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error('Studio: Initialization error:', err);
|
|
120
|
+
error.value = err instanceof Error ? err.message : 'Unknown error';
|
|
121
|
+
loading.value = false;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<style scoped>
|
|
127
|
+
.studio-header {
|
|
128
|
+
padding: 16px 0;
|
|
129
|
+
border-bottom: 1px solid #e0e0e0;
|
|
130
|
+
margin-bottom: 16px;
|
|
131
|
+
}
|
|
132
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ServerRequest, ServerRequestType, PackCourse } from '@vue-skuilder/common';
|
|
2
|
+
|
|
3
|
+
async function postWithResult<T extends ServerRequest>(request: Omit<T, 'response' | 'user'>): Promise<T['response']> {
|
|
4
|
+
const response = await fetch('http://localhost:3000/', {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
headers: {
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
},
|
|
9
|
+
body: JSON.stringify(request),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return await response.json();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function flushCourse(courseId: string, outputPath?: string) {
|
|
20
|
+
// Get the original course ID for the output path
|
|
21
|
+
// courseId here is the decorated database name, we need the original ID for the path
|
|
22
|
+
const studioConfig = (window as any).STUDIO_CONFIG;
|
|
23
|
+
const originalCourseId = studioConfig?.database?.originalCourseId || courseId;
|
|
24
|
+
|
|
25
|
+
return await postWithResult<PackCourse>({
|
|
26
|
+
type: ServerRequestType.PACK_COURSE,
|
|
27
|
+
courseId,
|
|
28
|
+
outputPath: outputPath ? outputPath : `./public/static-courses/${originalCourseId}`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-btn
|
|
3
|
+
color="success"
|
|
4
|
+
:loading="flushing"
|
|
5
|
+
:disabled="flushing"
|
|
6
|
+
@click="handleFlush"
|
|
7
|
+
>
|
|
8
|
+
<v-icon start>mdi-content-save</v-icon>
|
|
9
|
+
Flush to Static
|
|
10
|
+
</v-btn>
|
|
11
|
+
|
|
12
|
+
<!-- Flush status dialog -->
|
|
13
|
+
<v-dialog v-model="showDialog" max-width="600" persistent>
|
|
14
|
+
<v-card>
|
|
15
|
+
<v-card-title>
|
|
16
|
+
<v-icon :color="dialogIcon.color" class="me-2">{{ dialogIcon.icon }}</v-icon>
|
|
17
|
+
{{ dialogTitle }}
|
|
18
|
+
</v-card-title>
|
|
19
|
+
|
|
20
|
+
<v-card-text>
|
|
21
|
+
<div v-if="flushing">
|
|
22
|
+
<v-progress-linear indeterminate class="mb-4" />
|
|
23
|
+
<p>{{ flushStatus }}</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div v-else-if="flushError">
|
|
27
|
+
<v-alert type="error" class="mb-4">
|
|
28
|
+
{{ flushError }}
|
|
29
|
+
</v-alert>
|
|
30
|
+
<p>The flush operation failed. Please check the console for more details.</p>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div v-else>
|
|
34
|
+
<v-alert type="success" class="mb-4">
|
|
35
|
+
Course successfully saved to static files!
|
|
36
|
+
</v-alert>
|
|
37
|
+
<p>Your changes have been packed and saved to the course directory.</p>
|
|
38
|
+
</div>
|
|
39
|
+
</v-card-text>
|
|
40
|
+
|
|
41
|
+
<v-card-actions>
|
|
42
|
+
<v-spacer />
|
|
43
|
+
<v-btn
|
|
44
|
+
v-if="!flushing"
|
|
45
|
+
color="primary"
|
|
46
|
+
@click="showDialog = false"
|
|
47
|
+
>
|
|
48
|
+
Close
|
|
49
|
+
</v-btn>
|
|
50
|
+
</v-card-actions>
|
|
51
|
+
</v-card>
|
|
52
|
+
</v-dialog>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
import { ref, computed } from 'vue';
|
|
57
|
+
import { flushCourse } from '../api';
|
|
58
|
+
|
|
59
|
+
interface Props {
|
|
60
|
+
courseId: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const props = defineProps<Props>();
|
|
64
|
+
|
|
65
|
+
// Flush state
|
|
66
|
+
const flushing = ref(false);
|
|
67
|
+
const flushError = ref<string | null>(null);
|
|
68
|
+
const flushStatus = ref('');
|
|
69
|
+
const showDialog = ref(false);
|
|
70
|
+
|
|
71
|
+
// Computed properties for dialog display
|
|
72
|
+
const dialogTitle = computed(() => {
|
|
73
|
+
if (flushing.value) return 'Saving Course...';
|
|
74
|
+
if (flushError.value) return 'Save Failed';
|
|
75
|
+
return 'Course Saved';
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const dialogIcon = computed(() => {
|
|
79
|
+
if (flushing.value) return { icon: 'mdi-loading', color: 'primary' };
|
|
80
|
+
if (flushError.value) return { icon: 'mdi-alert-circle', color: 'error' };
|
|
81
|
+
return { icon: 'mdi-check-circle', color: 'success' };
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Flush operation
|
|
85
|
+
async function handleFlush() {
|
|
86
|
+
flushing.value = true;
|
|
87
|
+
flushError.value = null;
|
|
88
|
+
showDialog.value = true;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
flushStatus.value = 'Connecting to CLI...';
|
|
92
|
+
|
|
93
|
+
const result = await flushCourse(props.courseId);
|
|
94
|
+
|
|
95
|
+
if (!result.ok) {
|
|
96
|
+
throw new Error(result.errorText ?? 'Unknown error');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Flush failed:', error);
|
|
101
|
+
flushError.value = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
102
|
+
} finally {
|
|
103
|
+
flushing.value = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
</script>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development configuration utilities for studio-ui
|
|
3
|
+
* Handles both CLI-injected config and environment variable fallbacks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface StudioConfig {
|
|
7
|
+
couchdb: {
|
|
8
|
+
url: string;
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
};
|
|
12
|
+
database: {
|
|
13
|
+
name: string;
|
|
14
|
+
courseId: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get studio configuration from either CLI injection or environment variables
|
|
20
|
+
*/
|
|
21
|
+
export function getStudioConfig(): StudioConfig | null {
|
|
22
|
+
// First try CLI-injected configuration (production studio mode)
|
|
23
|
+
const cliConfig = (window as unknown as { STUDIO_CONFIG: StudioConfig | undefined }).STUDIO_CONFIG;
|
|
24
|
+
if (cliConfig?.couchdb) {
|
|
25
|
+
return cliConfig as StudioConfig;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Fall back to environment variables (development mode)
|
|
29
|
+
const envConfig = getEnvironmentConfig();
|
|
30
|
+
if (envConfig) {
|
|
31
|
+
console.log('🔧 Studio Dev Mode: Using environment configuration');
|
|
32
|
+
return envConfig;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse studio configuration from environment variables
|
|
40
|
+
*/
|
|
41
|
+
function getEnvironmentConfig(): StudioConfig | null {
|
|
42
|
+
// Check if we're in development mode with studio environment variables
|
|
43
|
+
const couchUrl = import.meta.env.VITE_STUDIO_COUCH_URL;
|
|
44
|
+
const couchUser = import.meta.env.VITE_STUDIO_COUCH_USER;
|
|
45
|
+
const couchPass = import.meta.env.VITE_STUDIO_COUCH_PASS;
|
|
46
|
+
const courseId = import.meta.env.VITE_STUDIO_COURSE_ID;
|
|
47
|
+
|
|
48
|
+
if (!couchUrl || !couchUser || !couchPass || !courseId) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// For development, we'll use the courseId as the database name
|
|
53
|
+
// This assumes the course has been unpacked to CouchDB already
|
|
54
|
+
const databaseName = courseId;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
couchdb: {
|
|
58
|
+
url: couchUrl,
|
|
59
|
+
username: couchUser,
|
|
60
|
+
password: couchPass,
|
|
61
|
+
},
|
|
62
|
+
database: {
|
|
63
|
+
name: databaseName,
|
|
64
|
+
courseId: courseId,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if we're running in development studio mode
|
|
71
|
+
*/
|
|
72
|
+
export function isDevelopmentStudioMode(): boolean {
|
|
73
|
+
return !!import.meta.env.VITE_STUDIO_COUCH_URL;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get helpful error message for missing configuration
|
|
78
|
+
*/
|
|
79
|
+
export function getConfigErrorMessage(): string {
|
|
80
|
+
if (isDevelopmentStudioMode()) {
|
|
81
|
+
return `
|
|
82
|
+
Studio development mode configuration incomplete. Please provide:
|
|
83
|
+
- VITE_STUDIO_COUCH_URL (e.g., http://localhost:5985)
|
|
84
|
+
- VITE_STUDIO_COUCH_USER (e.g., admin)
|
|
85
|
+
- VITE_STUDIO_COUCH_PASS (e.g., admin)
|
|
86
|
+
- VITE_STUDIO_COURSE_ID (e.g., 2aeb8315ef78f3e89ca386992d00825b)
|
|
87
|
+
|
|
88
|
+
Run with:
|
|
89
|
+
VITE_STUDIO_COUCH_URL=http://localhost:5985 \\
|
|
90
|
+
VITE_STUDIO_COUCH_USER=admin \\
|
|
91
|
+
VITE_STUDIO_COUCH_PASS=admin \\
|
|
92
|
+
VITE_STUDIO_COURSE_ID=your-course-id \\
|
|
93
|
+
yarn dev:studio
|
|
94
|
+
`.trim();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return 'Studio configuration not found. Please run via skuilder CLI studio command.';
|
|
98
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Skuilder Studio</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="./main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|