c0ckp1t 1.0.14 → 1.0.16
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/Config.mjs +2 -2
- package/README.md +21 -32
- package/components/AceLoader.mjs +46 -0
- package/components/MarkdownUtils.mjs +5 -1
- package/components/vue3-ace-editor.vue +6 -3
- package/components/xcode.vue +8 -0
- package/components/xinput.vue +1 -1
- package/components/xmarkdown.vue +4 -1
- package/components/xterminal.vue +7 -4
- package/components/xupload.vue +83 -60
- package/core/CoreUtils.mjs +22 -9
- package/core/GlobalStore.mjs +44 -19
- package/core/Island.mjs +63 -25
- package/core/IslandDefault.mjs +2 -18
- package/core/JsUtils.mjs +48 -4
- package/core/PageFooter.vue +44 -0
- package/core/PageMain.vue +9 -103
- package/core/PageNavigation.vue +95 -0
- package/core/VueUtils.mjs +6 -1
- package/core/{pages/traffic/WsLogUtils.mjs → WsLogUtils.mjs} +37 -15
- package/core/WsUtils.mjs +2 -2
- package/core/main-offcanvas.vue +1 -1
- package/core/pages/Connection.vue +34 -103
- package/core/pages/Connections.vue +107 -58
- package/core/pages/Documentation.vue +87 -74
- package/core/pages/Notifies.vue +2 -3
- package/core/pages/Traffic.vue +19 -62
- package/core/pages/connections/ConfigDefaultIsland.vue +59 -0
- package/core/pages/connections/ConfigIsland.vue +128 -0
- package/core/{sfc → pages/connections}/connection-header.vue +12 -11
- package/core/pages/frontend/Bootstrap.vue +4 -18
- package/core/pages/frontend/Components.vue +1 -1
- package/core/pages/frontend/ComponentsAdv.vue +3 -4
- package/core/pages/frontend/ComponentsBasic.vue +2 -2
- package/core/pages/traffic/log-ws-exec.vue +51 -40
- package/core/ws-client/Connection.mjs +1 -2
- package/core/ws-client/WsClient.mjs +4 -7
- package/css/bootstrap.min.css +3 -3
- package/css/bootstrap.min.css.map +1 -0
- package/index-cdn.html +10 -11
- package/index.html +9 -9
- package/js_ext/Makefile +12 -1
- package/js_ext/bootstrap.bundle.min.js +3 -3
- package/js_ext/bootstrap.bundle.min.js.map +1 -0
- package/js_ext/rxjs-operators.esm.mjs +4 -0
- package/js_ext/rxjs-webSocket.esm.mjs +4 -0
- package/js_ext/rxjs.esm.mjs +4 -0
- package/package.json +4 -3
- package/style.css +15 -0
- package/c0ckp1t-demo/Config.mjs +0 -71
- package/c0ckp1t-demo/components/sidebar.vue +0 -80
- package/c0ckp1t-demo/docs/Introduction.md +0 -4
- package/c0ckp1t-demo/docs/Issues.md +0 -3
- package/c0ckp1t-demo/main.vue +0 -39
- package/c0ckp1t-demo/pages/documentation.vue +0 -64
- package/c0ckp1t-demo/pages/homepage.vue +0 -73
- package/c0ckp1t-demo/store.mjs +0 -94
- package/core/DocUtils.mjs +0 -189
- package/core/pages/connections/connection-header-details.vue +0 -80
- package/core/pages/connections/page-connection-default.vue +0 -91
- package/core/pages/connections/page-connection.vue +0 -177
- package/core/ws-client/WsLogUtils.mjs +0 -91
- package/js_ext/rxjs.umd.min.js +0 -195
- package/js_ext/rxjs.umd.min.js.map +0 -1
package/Config.mjs
CHANGED
|
@@ -23,7 +23,7 @@ import {deepMerge, DEFAULTS} from "CoreUtils";
|
|
|
23
23
|
// ________________________________________________________________________________
|
|
24
24
|
// Default Nav Tree Builder
|
|
25
25
|
// ________________________________________________________________________________
|
|
26
|
-
function buildNavTree(instanceId) {
|
|
26
|
+
export function buildNavTree(instanceId) {
|
|
27
27
|
return {
|
|
28
28
|
icon: "fa-house",
|
|
29
29
|
depth: 0,
|
|
@@ -139,7 +139,7 @@ function buildNavTree(instanceId) {
|
|
|
139
139
|
// ________________________________________________________________________________
|
|
140
140
|
// Routes Builder
|
|
141
141
|
// ________________________________________________________________________________
|
|
142
|
-
function buildRoutes(instanceId = "default", prefix = "") {
|
|
142
|
+
export function buildRoutes(instanceId = "default", prefix = "") {
|
|
143
143
|
return [
|
|
144
144
|
{ path: '/', name: 'root', children: [
|
|
145
145
|
{path: '', redirect: `/${instanceId}/docs/Introduction.md`},
|
package/README.md
CHANGED
|
@@ -2,59 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
This project is a Vue.js 3 zero-build web framework using an Islands architecture. HTTP and WebSocket backends are supported. It can compile .vue SFC files at runtime via **vue3-sfc-loader**, this means no Webpack/Vite build step required. There are many reusable components and bootstrap 5.3 theming.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
## References
|
|
7
6
|
|
|
8
|
-
*
|
|
7
|
+
* Homepage
|
|
8
|
+
* https://www.c0ckp1t.com/
|
|
9
|
+
* NPM
|
|
9
10
|
* https://www.npmjs.com/package/c0ckp1t
|
|
10
|
-
*
|
|
11
|
+
* CDN
|
|
11
12
|
* https://www.jsdelivr.com/package/npm/c0ckp1t
|
|
12
13
|
* https://cdn.jsdelivr.net/npm/c0ckp1t@latest/
|
|
14
|
+
* GITHUB
|
|
15
|
+
* https://github.com/lfmunoz/c0ckp1t-webroot/
|
|
13
16
|
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
The main entry point is `GlobalStore.mjs`. You initialize it with a configuration object, and name of where the C0ckp1t Vue Application should be mounted.
|
|
17
|
-
|
|
18
|
-
```js
|
|
19
|
-
import Constants from 'C0ckp1tAppConfig'
|
|
20
|
-
|
|
21
|
-
import {api as apiMain} from 'GlobalStore'
|
|
22
|
-
apiMain.init("app-default", Constants)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## jsfiddle.net example
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## Use Remotely (jsdelivr CDN)
|
|
17
|
+
## Use CDN
|
|
31
18
|
|
|
19
|
+
* Examples
|
|
20
|
+
* [index-cdn.html](https://github.com/c0ckp1t/c0ckp1t-webroot/blob/main/index-cdn.html)
|
|
32
21
|
|
|
33
22
|
|
|
34
23
|
## Use Locally
|
|
35
24
|
|
|
25
|
+
If you want to use the framework locally, you can download the package from [npm](https://www.npmjs.com/package/c0ckp1t) or do a `git clone` of the repository.
|
|
26
|
+
|
|
27
|
+
**Note:** Check for latest version here: https://registry.npmjs.org/c0ckp1t/
|
|
36
28
|
|
|
37
29
|
```bash
|
|
38
|
-
wget https://registry.npmjs.org/c0ckp1t/-/c0ckp1t-1.0.
|
|
39
|
-
tar -zxvf c0ckp1t-1.0.
|
|
30
|
+
wget https://registry.npmjs.org/c0ckp1t/-/c0ckp1t-1.0.16.tgz
|
|
31
|
+
tar -zxvf c0ckp1t-1.0.16.tgz
|
|
40
32
|
|
|
41
|
-
# Note expands to
|
|
42
|
-
tar -zxvf c0ckp1t-1.0.
|
|
33
|
+
# Note expands to package/ folder
|
|
34
|
+
tar -zxvf c0ckp1t-1.0.16.tgz
|
|
35
|
+
# i.e:
|
|
43
36
|
# package/LICENSE
|
|
44
37
|
# package/css/Makefile
|
|
45
38
|
# package/js_ext/Makefile
|
|
46
39
|
# package/css/bootstrap-c0ckp1t.css
|
|
47
40
|
# ...
|
|
48
41
|
|
|
49
|
-
#
|
|
50
|
-
tar -zxvf c0ckp1t-1.0.
|
|
42
|
+
# To expand to "webroot" directory use this command instead
|
|
43
|
+
tar -zxvf c0ckp1t-1.0.16.tgz --strip-components=1 -C webroot
|
|
51
44
|
```
|
|
52
45
|
|
|
53
|
-
|
|
54
46
|
## Releases
|
|
55
47
|
|
|
56
|
-
* 1.0
|
|
57
|
-
|
|
58
|
-
* 1.0.2 - Beta: fixing cdn index-cdn.html example
|
|
59
|
-
* 1.0.1 - Beta: removing `Constants.mjs` dependencies
|
|
60
|
-
* 1.0.0 - Beta: initial release
|
|
48
|
+
* 1.1.0 - Initial Release
|
|
49
|
+
* 1.0.XX - Anything before 1.1.0 is "pre-release" development and testing.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AceLoader.mjs
|
|
3
|
+
*
|
|
4
|
+
* Lazy-loads the Ace editor by dynamically injecting a <script> tag.
|
|
5
|
+
* Returns a Promise that resolves to window.ace once the script is loaded.
|
|
6
|
+
* Multiple callers share the same promise (no duplicate script tags).
|
|
7
|
+
*/
|
|
8
|
+
import {store as storeMain, api as apiMain} from 'GlobalStore'
|
|
9
|
+
let _promise = null
|
|
10
|
+
|
|
11
|
+
const ACE_SCRIPT_LOCAL = '/js_ext/ace-editor/ace.js'
|
|
12
|
+
const ACE_SCRIPT_CDN = 'https://cdn.jsdelivr.net/npm/c0ckp1t@latest/js_ext/ace-editor/ace.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @returns {Promise<typeof ace>}
|
|
16
|
+
*/
|
|
17
|
+
export function loadAce() {
|
|
18
|
+
// Already on the page (e.g. loaded via static <script> during development)
|
|
19
|
+
if (typeof ace !== 'undefined') return Promise.resolve(ace)
|
|
20
|
+
|
|
21
|
+
// Reuse in-flight / resolved promise
|
|
22
|
+
if (_promise) return _promise
|
|
23
|
+
|
|
24
|
+
const componentPrefix = storeMain.appConfig?.componentPrefix || ""
|
|
25
|
+
const src = `${componentPrefix}${ACE_SCRIPT_LOCAL}`
|
|
26
|
+
|
|
27
|
+
_promise = new Promise((resolve, reject) => {
|
|
28
|
+
const script = document.createElement('script')
|
|
29
|
+
script.src = src
|
|
30
|
+
script.type = 'text/javascript'
|
|
31
|
+
script.onload = () => {
|
|
32
|
+
if (typeof ace !== 'undefined') {
|
|
33
|
+
resolve(ace)
|
|
34
|
+
} else {
|
|
35
|
+
reject(new Error('[AceLoader] ace global not found after script load'))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
script.onerror = () => {
|
|
39
|
+
_promise = null // allow retry
|
|
40
|
+
reject(new Error(`[AceLoader] Failed to load ${src}`))
|
|
41
|
+
}
|
|
42
|
+
document.head.appendChild(script)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return _promise
|
|
46
|
+
}
|
|
@@ -68,7 +68,11 @@ export function replaceHrefLinks(inputString, adjustHrefPath = (path) => path) {
|
|
|
68
68
|
if (match.toLowerCase().includes("http") || match.toLowerCase().includes("class")) {
|
|
69
69
|
return `${match} class="link-primary" target="_blank"`
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
// Check for internal route links (starts with :/)
|
|
72
|
+
if (param.startsWith(':/')) {
|
|
73
|
+
const routePath = param.slice(1) // Remove the leading ':'
|
|
74
|
+
return `a href="${routePath}" @click.prevent="routeEndpoint('${routePath}')"`
|
|
75
|
+
}
|
|
72
76
|
// Relative/absolute document links - use Vue handler
|
|
73
77
|
return `a href="${param}" @click.prevent="href('${param}')"`
|
|
74
78
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {ref, reactive, markRaw, onMounted, onBeforeUnmount, watch} from 'vue';
|
|
8
|
+
import {loadAce} from './AceLoader.mjs';
|
|
8
9
|
|
|
9
10
|
const root = ref("");
|
|
10
11
|
|
|
@@ -131,9 +132,11 @@ watch(() => local.currentRelativeLineNumbers, (val) => {
|
|
|
131
132
|
if (_editor) _editor.setOption('relativeLineNumbers', val);
|
|
132
133
|
});
|
|
133
134
|
|
|
134
|
-
onMounted(() => {
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
onMounted(async () => {
|
|
136
|
+
try {
|
|
137
|
+
await loadAce()
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error('Ace editor failed to load:', err)
|
|
137
140
|
return
|
|
138
141
|
}
|
|
139
142
|
ace.config.set('basePath', '/js_ext/ace-editor');
|
package/components/xcode.vue
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
/*
|
|
3
|
+
This component is used to display code snippets with syntax highlighting.
|
|
4
|
+
It fetches the code from a specified URL and can display either the full code or a specific line range.
|
|
5
|
+
It also includes a button to copy the code to the clipboard.
|
|
6
|
+
|
|
7
|
+
<x-code url="" lang="html" />
|
|
8
|
+
|
|
9
|
+
*/
|
|
2
10
|
import {computed, defineProps, reactive, onMounted} from 'vue'
|
|
3
11
|
|
|
4
12
|
import {store as storeMain, api as apiMain } from 'GlobalStore'
|
package/components/xinput.vue
CHANGED
|
@@ -69,7 +69,7 @@ const typeCheck = computed (() => {
|
|
|
69
69
|
<input :type="type" aria-label="message" class="form-control"
|
|
70
70
|
:style="props.inputStyle" v-model="message" v-else>
|
|
71
71
|
|
|
72
|
-
<div class="input-group-text text-
|
|
72
|
+
<div class="input-group-text text-primary icon-container" @click="isPasswordVisible = !isPasswordVisible" v-if="type.toUpperCase() === 'PASSWORD'">
|
|
73
73
|
<i class="fa-solid fa-eye" v-if="isPasswordVisible"></i>
|
|
74
74
|
<i class="fa-solid fa-eye-slash" v-if="!isPasswordVisible"></i>
|
|
75
75
|
</div>
|
package/components/xmarkdown.vue
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// IMPORT
|
|
14
14
|
// ______________________________________________________________________________________
|
|
15
15
|
import {reactive, ref, markRaw, onMounted, watch, computed, onErrorCaptured, defineAsyncComponent} from 'vue'
|
|
16
|
-
|
|
16
|
+
import {store as storeMain, api as apiMain} from 'GlobalStore'
|
|
17
17
|
import {getLogger} from "Logging"
|
|
18
18
|
import {md} from "./Markdown.mjs"
|
|
19
19
|
|
|
@@ -158,6 +158,9 @@ function renderMarkdown(html) {
|
|
|
158
158
|
}
|
|
159
159
|
},
|
|
160
160
|
methods: {
|
|
161
|
+
async routeEndpoint(endpoint) {
|
|
162
|
+
await apiMain.routeByEndpoint(endpoint)
|
|
163
|
+
},
|
|
161
164
|
href(path) {
|
|
162
165
|
emit('href', path)
|
|
163
166
|
},
|
package/components/xterminal.vue
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import {ref, reactive, markRaw, onMounted, onBeforeUnmount, watch, nextTick} from 'vue';
|
|
3
|
+
import {loadAce} from './AceLoader.mjs';
|
|
3
4
|
|
|
4
5
|
const root = ref(null);
|
|
5
6
|
const inputRef = ref(null);
|
|
@@ -245,10 +246,12 @@ function focusInput(options = {}) {
|
|
|
245
246
|
// LIFECYCLE
|
|
246
247
|
// ________________________________________________________________________________
|
|
247
248
|
|
|
248
|
-
onMounted(() => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
onMounted(async () => {
|
|
250
|
+
try {
|
|
251
|
+
await loadAce()
|
|
252
|
+
} catch (err) {
|
|
253
|
+
console.error('Ace editor failed to load:', err)
|
|
254
|
+
return
|
|
252
255
|
}
|
|
253
256
|
|
|
254
257
|
ace.config.set('basePath', '/js_ext/ace-editor');
|
package/components/xupload.vue
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
|
|
2
|
+
/*
|
|
3
|
+
xupload.vue
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
<x-upload type="text" :callback="handleSelectedFile"/>
|
|
7
|
+
Emits:
|
|
8
|
+
callback(files)
|
|
9
|
+
Props:
|
|
10
|
+
type: "IMAGE", "AUDIO", "VIDEO"
|
|
11
|
+
progress: Number (0-100)
|
|
12
|
+
*/
|
|
13
|
+
import {ref, reactive, computed, watch, onMounted, onUnmounted} from 'vue'
|
|
3
14
|
|
|
4
15
|
const props = defineProps({
|
|
5
16
|
progress: {
|
|
@@ -19,7 +30,6 @@ const props = defineProps({
|
|
|
19
30
|
const local = reactive({
|
|
20
31
|
isLoading: false,
|
|
21
32
|
src: null,
|
|
22
|
-
updatedMs: null,
|
|
23
33
|
file: null
|
|
24
34
|
})
|
|
25
35
|
|
|
@@ -28,50 +38,64 @@ const files = ref();
|
|
|
28
38
|
|
|
29
39
|
const emit = defineEmits(['callback'])
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
const typeObj = computed(() => {
|
|
42
|
+
const type = props.type.trim().toUpperCase()
|
|
43
|
+
switch (type) {
|
|
44
|
+
case 'IMAGE':
|
|
45
|
+
case 'AUDIO':
|
|
46
|
+
case 'VIDEO':
|
|
47
|
+
return { type, allowUpload: true, accept: `${type.toLowerCase()}/*` }
|
|
48
|
+
case 'TEXT':
|
|
49
|
+
return { type, allowUpload: true, accept: '.txt,.csv,.json,.xml,.log,.md,.yml,.yaml,.ini,.cfg,.conf,text/*' }
|
|
50
|
+
case 'ANY':
|
|
51
|
+
return { type, allowUpload: true, accept: '*/*' }
|
|
52
|
+
default:
|
|
53
|
+
return { type, allowUpload: false, accept: '*/*' }
|
|
54
|
+
}
|
|
55
|
+
})
|
|
34
56
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
watch(() => props.type, () => {
|
|
58
|
+
local.src = null
|
|
59
|
+
local.file = files.value = null
|
|
60
|
+
if (fileInput.value) fileInput.value.value = ''
|
|
61
|
+
})
|
|
38
62
|
|
|
39
63
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
} else if(props.type === 'IMAGE') {
|
|
49
|
-
return {
|
|
50
|
-
allowUpload: true,
|
|
51
|
-
accept: `${props.type.toLowerCase()}/*`,
|
|
52
|
-
}
|
|
53
|
-
} else if(props.type === 'VIDEO') {
|
|
54
|
-
return {
|
|
55
|
-
allowUpload: true,
|
|
56
|
-
accept: `${props.type.toLowerCase()}/*`,
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
return {
|
|
60
|
-
allowUpload: false,
|
|
61
|
-
accept: `${props.type.toLowerCase()}/*`,
|
|
62
|
-
}
|
|
64
|
+
function handleFileChange(event) {
|
|
65
|
+
const selectedFiles = event.target.files
|
|
66
|
+
if (!selectedFiles || selectedFiles.length === 0) {
|
|
67
|
+
files.value = null
|
|
68
|
+
local.src = null
|
|
69
|
+
local.file = null
|
|
70
|
+
return
|
|
63
71
|
}
|
|
64
|
-
})
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
local.file =
|
|
68
|
-
|
|
69
|
-
const reader = new FileReader()
|
|
73
|
+
files.value = selectedFiles
|
|
74
|
+
local.file = selectedFiles[0]
|
|
75
|
+
|
|
76
|
+
const reader = new FileReader()
|
|
70
77
|
reader.onload = (e) => {
|
|
71
|
-
local.src = e.target.result
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
local.src = e.target.result
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const type = typeObj.value.type
|
|
82
|
+
if (type === 'TEXT' || type === 'JSON') {
|
|
83
|
+
reader.readAsText(local.file)
|
|
84
|
+
} else {
|
|
85
|
+
reader.readAsDataURL(local.file)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function clearFile() {
|
|
89
|
+
files.value = null
|
|
90
|
+
local.src = null
|
|
91
|
+
local.file = null
|
|
92
|
+
if (fileInput.value) fileInput.value.value = ''
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function uploadSelectedFiles() {
|
|
96
|
+
// Note: getting absolute filepath is not possible in browser for security reasons
|
|
97
|
+
// so we return the file object and content
|
|
98
|
+
emit('callback', { file: local.file, content: local.src })
|
|
75
99
|
}
|
|
76
100
|
|
|
77
101
|
</script>
|
|
@@ -80,37 +104,38 @@ function handleImagePreview(event) {
|
|
|
80
104
|
<template>
|
|
81
105
|
<div class="x-component x-upload">
|
|
82
106
|
|
|
83
|
-
<div class="row">
|
|
107
|
+
<div class="row align-items-center">
|
|
84
108
|
<div class="col">
|
|
85
|
-
<input ref="fileInput" @change="
|
|
109
|
+
<input ref="fileInput" @change="handleFileChange" class="form-control" type="file" :accept="typeObj.accept" multiple>
|
|
86
110
|
</div>
|
|
87
111
|
<div class="col-auto">
|
|
88
|
-
<button
|
|
112
|
+
<button class="btn btn-sm btn-primary" @click="clearFile" >
|
|
113
|
+
Clear
|
|
114
|
+
</button>
|
|
89
115
|
</div>
|
|
90
116
|
</div>
|
|
91
117
|
|
|
118
|
+
<div v-if="files" class="progress mt-2" role="progressbar" aria-label="Success example" :aria-valuenow="props.progress" aria-valuemin="0" aria-valuemax="100">
|
|
119
|
+
<div class="progress-bar bg-success" :style="`width: ${Math.max(props.progress, 1)}%`">{{(props.progress >= 100) ? 'DONE' : props.progress}}</div>
|
|
120
|
+
</div>
|
|
92
121
|
|
|
93
|
-
<div
|
|
122
|
+
<div class="mt-1">
|
|
123
|
+
<button class="btn btn-sm btn-primary w-100" @click="uploadSelectedFiles" :disabled="!files || files.length === 0">
|
|
124
|
+
Upload {{props.type}}
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div v-if="typeObj.type === 'IMAGE' && local.src" class="image-preview mt-2">
|
|
94
129
|
<img :src="local.src" class="img-thumbnail"/>
|
|
95
130
|
</div>
|
|
96
|
-
<div v-if="
|
|
131
|
+
<div v-if="typeObj.type === 'AUDIO' && local.src" class="mt-2">
|
|
97
132
|
<audio :src="local.src" controls class="w-100"></audio>
|
|
98
133
|
</div>
|
|
99
|
-
<div v-if="
|
|
134
|
+
<div v-if="typeObj.type === 'VIDEO' && local.src" class="mt-2" >
|
|
100
135
|
<video :src="local.src" controls class="w-100"></video>
|
|
101
136
|
</div>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<div v-if="files" class="progress mt-2" role="progressbar" aria-label="Success example" :aria-valuenow="props.progress" aria-valuemin="0" aria-valuemax="100">
|
|
106
|
-
<div class="progress-bar bg-success" :style="`width: ${Math.max(props.progress, 1)}%`">{{(props.progress >= 100) ? 'DONE' : props.progress}}</div>
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
<div v-if="local.src" class="image-preview mt-2">
|
|
110
|
-
<img :src="local.src" alt="Preview" class="img-thumbnail"/>
|
|
111
|
-
</div>
|
|
112
|
-
<div v-if="local.src" class="image-preview mt-2">
|
|
113
|
-
<audio :src="local.src" controls class="w-100"></audio>
|
|
137
|
+
<div v-if="typeObj.type === 'TEXT' && local.src" class="mt-2" >
|
|
138
|
+
<textarea type="text" class="form-control" :value="local.src" rows="15" readonly></textarea>
|
|
114
139
|
</div>
|
|
115
140
|
|
|
116
141
|
|
|
@@ -119,11 +144,9 @@ function handleImagePreview(event) {
|
|
|
119
144
|
|
|
120
145
|
<style scoped>
|
|
121
146
|
/* xupload.vue */
|
|
122
|
-
|
|
123
147
|
.x-upload {
|
|
124
148
|
border: 1px solid black;
|
|
125
149
|
padding: 10px;
|
|
126
150
|
}
|
|
127
151
|
|
|
128
|
-
|
|
129
152
|
</style>
|
package/core/CoreUtils.mjs
CHANGED
|
@@ -43,14 +43,20 @@ export function validateIslandConfig(config) {
|
|
|
43
43
|
// ________________________________________________________________________________
|
|
44
44
|
export const DEFAULTS = {
|
|
45
45
|
isDev: true,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
/**
|
|
47
|
+
* XMLHttpRequest from a different domain cannot set cookie values for their own
|
|
48
|
+
* domain unless withCredentials is set to true before making the request.
|
|
49
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredential/
|
|
50
|
+
*
|
|
51
|
+
* `true` tells the browser “this cross-origin request should include credentials”,
|
|
52
|
+
* meaning: - send cookies (and HTTP auth) *with* the request, and
|
|
53
|
+
* allow the browser to store cookies from the response, *if* the server also opts in.
|
|
54
|
+
*/
|
|
49
55
|
WITH_CREDENTIALS: false,
|
|
50
56
|
|
|
51
57
|
defaultInstanceId: "default",
|
|
52
58
|
instanceId: "default",
|
|
53
|
-
type: "
|
|
59
|
+
type: "IslandDefault",
|
|
54
60
|
// Determines GlobalStore.name
|
|
55
61
|
appName: "C0ckp1t Application",
|
|
56
62
|
// Used for default routes prefix (used only in config)
|
|
@@ -61,16 +67,24 @@ export const DEFAULTS = {
|
|
|
61
67
|
appMainComponent: "/core/PageMain.vue",
|
|
62
68
|
// Used for requestion app components and files (GlobalStore.appEndpoint)
|
|
63
69
|
appEndpoint: "",
|
|
64
|
-
|
|
65
70
|
// Nav Configuration
|
|
66
71
|
navCloseLogo: "/core/img/logo_v1.svg",
|
|
67
72
|
navOpenLogo: "/core/img/logo_v2.svg",
|
|
68
73
|
navHasSearch: false,
|
|
69
74
|
navHasThemeSel: true,
|
|
70
|
-
|
|
75
|
+
// Footer Configuration
|
|
76
|
+
showFooter: true,
|
|
77
|
+
// NavBar Configuration
|
|
78
|
+
showTopNavBar: true,
|
|
79
|
+
// Documentation Configuration
|
|
80
|
+
showDocReload: true,
|
|
81
|
+
showDocTrail: false,
|
|
82
|
+
allowDocWrite: true,
|
|
83
|
+
allowDocReload: true,
|
|
84
|
+
// Components Configuration
|
|
85
|
+
componentsDefaultExpand: true,
|
|
71
86
|
// Determine if VueRouter is createWebHashHistory or createWebHistory
|
|
72
87
|
vueRouterModeIsHash: true,
|
|
73
|
-
|
|
74
88
|
// Logger Config (see Logging.mjs)
|
|
75
89
|
defaultLogLevel: "INFO",
|
|
76
90
|
defaultLoggerLevels: {
|
|
@@ -81,8 +95,7 @@ export const DEFAULTS = {
|
|
|
81
95
|
"anonymous": "INFO",
|
|
82
96
|
"demo": "INFO"
|
|
83
97
|
},
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
// Theming
|
|
86
99
|
bootswatchURL: "https://cdn.jsdelivr.net/npm/bootswatch@5.3.8/dist",
|
|
87
100
|
};
|
|
88
101
|
|
package/core/GlobalStore.mjs
CHANGED
|
@@ -12,7 +12,6 @@ import {validateIslandConfig, findHostnamePortProtocol, validateAppConfig} from
|
|
|
12
12
|
import {substrAfterFirstSlash, extractLastPath} from "JsUtils";
|
|
13
13
|
|
|
14
14
|
import IslandDefault from 'IslandDefault'
|
|
15
|
-
import Island from 'Island'
|
|
16
15
|
|
|
17
16
|
// ________________________________________________________________________________
|
|
18
17
|
// LOGGING
|
|
@@ -45,10 +44,22 @@ export const store = reactive({
|
|
|
45
44
|
// Text appears next to logo in pages/navigation.vue
|
|
46
45
|
name: "C0ckp1t",
|
|
47
46
|
id: LOG_HEADER,
|
|
48
|
-
config: null,
|
|
49
47
|
appEndpoint: "",
|
|
50
48
|
serverInfo: findHostnamePortProtocol(),
|
|
51
49
|
|
|
50
|
+
// ________________________________________________________________________________
|
|
51
|
+
// Configuration
|
|
52
|
+
// ________________________________________________________________________________
|
|
53
|
+
config: null,
|
|
54
|
+
// Documentation Configuration
|
|
55
|
+
showDocReload: true,
|
|
56
|
+
showDocTrail: true,
|
|
57
|
+
allowDocWrite: true,
|
|
58
|
+
// FOOTER Configuration
|
|
59
|
+
showFooter: true,
|
|
60
|
+
// TOP NAVBAR Configuration
|
|
61
|
+
showTopNavBar: true,
|
|
62
|
+
|
|
52
63
|
// ________________________________________________________________________________
|
|
53
64
|
// Registries
|
|
54
65
|
// ________________________________________________________________________________
|
|
@@ -56,6 +67,10 @@ export const store = reactive({
|
|
|
56
67
|
defaultInstanceId: "default",
|
|
57
68
|
selectedInstId: null,
|
|
58
69
|
router: null,
|
|
70
|
+
registryType: [
|
|
71
|
+
{k: 'Remote Island', v: 'Island'},
|
|
72
|
+
{k: 'Default Island', v: 'IslandDefault'}
|
|
73
|
+
],
|
|
59
74
|
|
|
60
75
|
// ________________________________________________________________________________
|
|
61
76
|
// Login
|
|
@@ -63,16 +78,6 @@ export const store = reactive({
|
|
|
63
78
|
isAuthenticated: false,
|
|
64
79
|
username: null,
|
|
65
80
|
|
|
66
|
-
// ________________________________________________________________________________
|
|
67
|
-
// TOP NAVBAR
|
|
68
|
-
// ________________________________________________________________________________
|
|
69
|
-
showTopNavBar: true,
|
|
70
|
-
|
|
71
|
-
// ________________________________________________________________________________
|
|
72
|
-
// documentation
|
|
73
|
-
// ________________________________________________________________________________
|
|
74
|
-
showDocReload: true,
|
|
75
|
-
showDocPath: true,
|
|
76
81
|
|
|
77
82
|
// ________________________________________________________________________________
|
|
78
83
|
// Vue App Instance - gets created in App.vue
|
|
@@ -143,12 +148,23 @@ export const api = {
|
|
|
143
148
|
...config,
|
|
144
149
|
SERVER_API_URL: store.serverInfo.serverUrl,
|
|
145
150
|
}
|
|
151
|
+
|
|
146
152
|
let island= null
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
switch (decoratedIslandConfig.type) {
|
|
154
|
+
case "LOCAL":
|
|
155
|
+
case "DEFAULT":
|
|
156
|
+
case "default":
|
|
157
|
+
case "IslandDefault":
|
|
158
|
+
island = new IslandDefault(api, decoratedIslandConfig)
|
|
159
|
+
await api.insertRoutes(`/${island.instanceId}`, config.routes)
|
|
160
|
+
break
|
|
161
|
+
default:
|
|
162
|
+
try {
|
|
163
|
+
const { default: Island } = await import(decoratedIslandConfig.type)
|
|
164
|
+
island = new Island(api, decoratedIslandConfig)
|
|
165
|
+
} catch(e) {
|
|
166
|
+
throw new Error(`Unknown island type: ${decoratedIslandConfig.type}`)
|
|
167
|
+
}
|
|
152
168
|
}
|
|
153
169
|
store.r[island.instanceId] = island
|
|
154
170
|
await island.init()
|
|
@@ -220,7 +236,7 @@ export const api = {
|
|
|
220
236
|
|
|
221
237
|
routeByEndpoint: async (endpoint) => {
|
|
222
238
|
const path = endpoint.split("/")
|
|
223
|
-
logger.
|
|
239
|
+
logger.debug(`[routeByEndpoint] - endpoint=${endpoint}, path=${path}`)
|
|
224
240
|
try {
|
|
225
241
|
if(path.length > 3) {
|
|
226
242
|
extractLastPath(endpoint)
|
|
@@ -306,6 +322,9 @@ export const api = {
|
|
|
306
322
|
app.config.errorHandler = (err, vm, info) => {
|
|
307
323
|
console.error('Global error handler:', err, vm, info);
|
|
308
324
|
};
|
|
325
|
+
app.config.warnHandler = (err, vm, info) => {
|
|
326
|
+
console.warn('Global warn handler:', err, vm, info);
|
|
327
|
+
};
|
|
309
328
|
|
|
310
329
|
//________________________________________________________________________________
|
|
311
330
|
// Create Default Island
|
|
@@ -317,8 +336,13 @@ export const api = {
|
|
|
317
336
|
store.appEndpoint = config.appEndpoint ?? ""
|
|
318
337
|
store.defaultInstanceId = config.instanceId ?? "default"
|
|
319
338
|
store.name = config.appName ?? "C0ckp1t"
|
|
339
|
+
store.showTopNavBar = config.showTopNavBar ?? true
|
|
340
|
+
store.showFooter = config.showFooter ?? true
|
|
341
|
+
store.showDocReload = config.showDocReload ?? true
|
|
342
|
+
store.showDocTrail = config.showDocTrail ?? true
|
|
343
|
+
store.allowDocWrite = config.allowDocWrite ?? false
|
|
344
|
+
store.allowDocReload = config.allowDocReload ?? false
|
|
320
345
|
store.config = config
|
|
321
|
-
const vueRouterModeIsHash = config.vueRouterModeIsHash ?? true
|
|
322
346
|
|
|
323
347
|
// Load default island
|
|
324
348
|
const decoratedIslandConfig = {
|
|
@@ -330,6 +354,7 @@ export const api = {
|
|
|
330
354
|
await islandDefault.init()
|
|
331
355
|
|
|
332
356
|
// Create Vue Router
|
|
357
|
+
const vueRouterModeIsHash = config.vueRouterModeIsHash ?? true
|
|
333
358
|
const router = VueRouter.createRouter({
|
|
334
359
|
history: vueRouterModeIsHash ? VueRouter.createWebHashHistory() : VueRouter.createWebHistory(),
|
|
335
360
|
routes: transformRoutes(api.loadModule, config.routes)
|