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.
Files changed (64) hide show
  1. package/Config.mjs +2 -2
  2. package/README.md +21 -32
  3. package/components/AceLoader.mjs +46 -0
  4. package/components/MarkdownUtils.mjs +5 -1
  5. package/components/vue3-ace-editor.vue +6 -3
  6. package/components/xcode.vue +8 -0
  7. package/components/xinput.vue +1 -1
  8. package/components/xmarkdown.vue +4 -1
  9. package/components/xterminal.vue +7 -4
  10. package/components/xupload.vue +83 -60
  11. package/core/CoreUtils.mjs +22 -9
  12. package/core/GlobalStore.mjs +44 -19
  13. package/core/Island.mjs +63 -25
  14. package/core/IslandDefault.mjs +2 -18
  15. package/core/JsUtils.mjs +48 -4
  16. package/core/PageFooter.vue +44 -0
  17. package/core/PageMain.vue +9 -103
  18. package/core/PageNavigation.vue +95 -0
  19. package/core/VueUtils.mjs +6 -1
  20. package/core/{pages/traffic/WsLogUtils.mjs → WsLogUtils.mjs} +37 -15
  21. package/core/WsUtils.mjs +2 -2
  22. package/core/main-offcanvas.vue +1 -1
  23. package/core/pages/Connection.vue +34 -103
  24. package/core/pages/Connections.vue +107 -58
  25. package/core/pages/Documentation.vue +87 -74
  26. package/core/pages/Notifies.vue +2 -3
  27. package/core/pages/Traffic.vue +19 -62
  28. package/core/pages/connections/ConfigDefaultIsland.vue +59 -0
  29. package/core/pages/connections/ConfigIsland.vue +128 -0
  30. package/core/{sfc → pages/connections}/connection-header.vue +12 -11
  31. package/core/pages/frontend/Bootstrap.vue +4 -18
  32. package/core/pages/frontend/Components.vue +1 -1
  33. package/core/pages/frontend/ComponentsAdv.vue +3 -4
  34. package/core/pages/frontend/ComponentsBasic.vue +2 -2
  35. package/core/pages/traffic/log-ws-exec.vue +51 -40
  36. package/core/ws-client/Connection.mjs +1 -2
  37. package/core/ws-client/WsClient.mjs +4 -7
  38. package/css/bootstrap.min.css +3 -3
  39. package/css/bootstrap.min.css.map +1 -0
  40. package/index-cdn.html +10 -11
  41. package/index.html +9 -9
  42. package/js_ext/Makefile +12 -1
  43. package/js_ext/bootstrap.bundle.min.js +3 -3
  44. package/js_ext/bootstrap.bundle.min.js.map +1 -0
  45. package/js_ext/rxjs-operators.esm.mjs +4 -0
  46. package/js_ext/rxjs-webSocket.esm.mjs +4 -0
  47. package/js_ext/rxjs.esm.mjs +4 -0
  48. package/package.json +4 -3
  49. package/style.css +15 -0
  50. package/c0ckp1t-demo/Config.mjs +0 -71
  51. package/c0ckp1t-demo/components/sidebar.vue +0 -80
  52. package/c0ckp1t-demo/docs/Introduction.md +0 -4
  53. package/c0ckp1t-demo/docs/Issues.md +0 -3
  54. package/c0ckp1t-demo/main.vue +0 -39
  55. package/c0ckp1t-demo/pages/documentation.vue +0 -64
  56. package/c0ckp1t-demo/pages/homepage.vue +0 -73
  57. package/c0ckp1t-demo/store.mjs +0 -94
  58. package/core/DocUtils.mjs +0 -189
  59. package/core/pages/connections/connection-header-details.vue +0 -80
  60. package/core/pages/connections/page-connection-default.vue +0 -91
  61. package/core/pages/connections/page-connection.vue +0 -177
  62. package/core/ws-client/WsLogUtils.mjs +0 -91
  63. package/js_ext/rxjs.umd.min.js +0 -195
  64. 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
- * npm
7
+ * Homepage
8
+ * https://www.c0ckp1t.com/
9
+ * NPM
9
10
  * https://www.npmjs.com/package/c0ckp1t
10
- * cdn
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
- ## Overview
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.2.tgz
39
- tar -zxvf c0ckp1t-1.0.2.tgz
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 package/ folder i.e:
42
- tar -zxvf c0ckp1t-1.0.2.tgz
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
- # to expand to webroot directory
50
- tar -zxvf c0ckp1t-1.0.2.tgz --strip-components=1 -C webroot
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.12 - Beta: fixing cdn index-cdn.html making sure it works with jsfiddle.net
57
- * Had to publish many times to get CDN working with different configurations
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
- if (typeof ace === 'undefined') {
136
- console.error('Ace editor not loaded')
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');
@@ -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'
@@ -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-dark icon-container" @click="isPasswordVisible = !isPasswordVisible" v-if="type.toUpperCase() === 'PASSWORD'">
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>
@@ -13,7 +13,7 @@
13
13
  // IMPORT
14
14
  // ______________________________________________________________________________________
15
15
  import {reactive, ref, markRaw, onMounted, watch, computed, onErrorCaptured, defineAsyncComponent} from 'vue'
16
- // import {store, methods} from 'GlobalStore'
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
  },
@@ -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
- if (typeof ace === 'undefined') {
250
- console.error('Ace editor not loaded');
251
- return;
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');
@@ -1,5 +1,16 @@
1
1
  <script setup>
2
- import {ref, reactive, computed, onMounted, onUnmounted} from 'vue'
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
- async function handleChange() {
32
- files.value = fileInput.value.files;
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
- async function uploadSelectedFiles() {
36
- emit("callback", files.value)
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
- const typeObj = computed(() => {
41
- // if you change type src must be reset to null
42
- local.src = null
43
- if(props.type === 'IMAGE') {
44
- return {
45
- allowUpload: true,
46
- accept: `${props.type.toLowerCase()}/*`,
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
- function handleImagePreview(event) {
67
- local.file = event.target.files[0];
68
- if (!local.file) return;
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
- local.updatedMs = Date.now();
73
- };
74
- reader.readAsDataURL(local.file);
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="handleImagePreview" class="form-control" type="file" :accept="typeObj.accept" multiple>
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 @click="uploadSelectedFiles" :disabled="!files || files.length === 0">Upload</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 v-if="props.type === 'IMAGE'" class="image-preview mt-2">
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="props.type === 'AUDIO'" class="mt-2">
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="props.type === 'VIDEO'" class="mt-2">
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>
@@ -43,14 +43,20 @@ export function validateIslandConfig(config) {
43
43
  // ________________________________________________________________________________
44
44
  export const DEFAULTS = {
45
45
  isDev: true,
46
- // XMLHttpRequest from a different domain cannot set cookie values for their own
47
- // domain unless withCredentials is set to true before making the request.
48
- // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
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: "LOCAL",
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
 
@@ -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
- if(decoratedIslandConfig.type === "LOCAL") {
148
- island = new IslandDefault(api, decoratedIslandConfig)
149
- await api.insertRoutes(`/${island.instanceId}`, config.routes)
150
- } else {
151
- island = new Island(api, decoratedIslandConfig)
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.info(`[routeByEndpoint] - endpoint=${endpoint}, path=${path}`)
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)