c0ckp1t 1.0.11 → 1.0.13
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 +248 -0
- package/README.md +4 -1
- package/c0ckp1t-demo/{C0ckp1tConfig.mjs → Config.mjs} +6 -3
- package/c0ckp1t-demo/components/sidebar.vue +1 -2
- package/c0ckp1t-demo/docs/Introduction.md +1 -0
- package/c0ckp1t-demo/docs/Issues.md +3 -0
- package/c0ckp1t-demo/pages/documentation.vue +64 -0
- package/c0ckp1t-demo/store.mjs +2 -2
- package/components/code-mirror.vue +23 -8
- package/components/tools.sh +9 -0
- package/components/xsound.vue +1 -2
- package/core/CoreUtils.mjs +103 -35
- package/core/GlobalStore.mjs +18 -10
- package/core/Island.mjs +2 -2
- package/core/IslandDefault.mjs +1 -1
- package/core/PageMain.vue +15 -10
- package/core/VueUtils.mjs +23 -13
- package/core/img/logo_v2.svg +3 -3
- package/core/notify/toast.vue +1 -1
- package/core/pages/frontend/ComponentsAdv.vue +0 -2
- package/core/pages/frontend/ComponentsBasic.vue +4 -0
- package/index-cdn.html +18 -8
- package/index.html +10 -7
- package/package.json +3 -5
- package/CdnConfig.mjs +0 -177
- package/DefaultConfig.mjs +0 -177
- package/c0ckp1t-demo/CdnConfig.mjs +0 -68
- package/docs/Introduction.md +0 -3
package/Config.mjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {deepMerge, DEFAULTS} from "CoreUtils";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration Factory for C0ckp1t Applications
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { createConfig } from './Config.mjs';
|
|
7
|
+
*
|
|
8
|
+
* // Local mode (all defaults)
|
|
9
|
+
* const config = createConfig();
|
|
10
|
+
*
|
|
11
|
+
* // CDN mode
|
|
12
|
+
* const config = createConfig({
|
|
13
|
+
* appEndpoint: "https://cdn.jsdelivr.net/npm/c0ckp1t@latest"
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // Custom instance
|
|
17
|
+
* const config = createConfig({
|
|
18
|
+
* instanceId: "myapp",
|
|
19
|
+
* appName: "My App",
|
|
20
|
+
* isDev: false,
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
// ________________________________________________________________________________
|
|
24
|
+
// Default Nav Tree Builder
|
|
25
|
+
// ________________________________________________________________________________
|
|
26
|
+
function buildNavTree(instanceId) {
|
|
27
|
+
return {
|
|
28
|
+
icon: "fa-house",
|
|
29
|
+
depth: 0,
|
|
30
|
+
endpoint: "/",
|
|
31
|
+
isLeaf: false,
|
|
32
|
+
isRoot: true,
|
|
33
|
+
name: "",
|
|
34
|
+
path: [],
|
|
35
|
+
children: [
|
|
36
|
+
{
|
|
37
|
+
depth: 1,
|
|
38
|
+
endpoint: `/${instanceId}/connections`,
|
|
39
|
+
isLeaf: true,
|
|
40
|
+
isRoot: false,
|
|
41
|
+
path: ["connections"],
|
|
42
|
+
name: "Connections",
|
|
43
|
+
children: []
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
depth: 1,
|
|
47
|
+
endpoint: `/${instanceId}/cache`,
|
|
48
|
+
isLeaf: true,
|
|
49
|
+
isRoot: false,
|
|
50
|
+
path: ["cache"],
|
|
51
|
+
name: "Cache",
|
|
52
|
+
children: []
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
icon: "fa-network-wired",
|
|
56
|
+
depth: 1,
|
|
57
|
+
endpoint: `/${instanceId}/traffic`,
|
|
58
|
+
isLeaf: true,
|
|
59
|
+
isRoot: false,
|
|
60
|
+
path: ["traffic"],
|
|
61
|
+
name: "Traffic",
|
|
62
|
+
children: []
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
icon: "fa-bell",
|
|
66
|
+
depth: 1,
|
|
67
|
+
endpoint: `/${instanceId}/notifies`,
|
|
68
|
+
isLeaf: true,
|
|
69
|
+
isRoot: false,
|
|
70
|
+
path: ["notifies"],
|
|
71
|
+
name: "Notifies",
|
|
72
|
+
children: []
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
icon: "fa-info",
|
|
76
|
+
depth: 1,
|
|
77
|
+
endpoint: `/${instanceId}/docs`,
|
|
78
|
+
isLeaf: true,
|
|
79
|
+
isRoot: false,
|
|
80
|
+
path: ["docs"],
|
|
81
|
+
name: "Documentation",
|
|
82
|
+
children: []
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
icon: "fa-info",
|
|
86
|
+
depth: 1,
|
|
87
|
+
endpoint: `/${instanceId}/components`,
|
|
88
|
+
isLeaf: true,
|
|
89
|
+
isRoot: false,
|
|
90
|
+
path: ["components"],
|
|
91
|
+
name: "Components",
|
|
92
|
+
children: [
|
|
93
|
+
{
|
|
94
|
+
icon: "fa-info",
|
|
95
|
+
depth: 2,
|
|
96
|
+
endpoint: `/${instanceId}/components/bootstrap`,
|
|
97
|
+
isLeaf: true,
|
|
98
|
+
isRoot: false,
|
|
99
|
+
path: ["bootstrap"],
|
|
100
|
+
name: "Bootstrap",
|
|
101
|
+
children: []
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
icon: "fa-info",
|
|
105
|
+
depth: 2,
|
|
106
|
+
endpoint: `/${instanceId}/components/basic`,
|
|
107
|
+
isLeaf: true,
|
|
108
|
+
isRoot: false,
|
|
109
|
+
path: ["basic"],
|
|
110
|
+
name: "Basic",
|
|
111
|
+
children: []
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
icon: "fa-info",
|
|
115
|
+
depth: 2,
|
|
116
|
+
endpoint: `/${instanceId}/components/advanced`,
|
|
117
|
+
isLeaf: true,
|
|
118
|
+
isRoot: false,
|
|
119
|
+
path: ["advanced"],
|
|
120
|
+
name: "Advanced",
|
|
121
|
+
children: []
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
icon: "fa-info",
|
|
125
|
+
depth: 2,
|
|
126
|
+
endpoint: `/${instanceId}/components/theme`,
|
|
127
|
+
isLeaf: true,
|
|
128
|
+
isRoot: false,
|
|
129
|
+
path: ["theme"],
|
|
130
|
+
name: "Theme",
|
|
131
|
+
children: []
|
|
132
|
+
},
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ________________________________________________________________________________
|
|
140
|
+
// Routes Builder
|
|
141
|
+
// ________________________________________________________________________________
|
|
142
|
+
function buildRoutes(instanceId = "default", prefix = "") {
|
|
143
|
+
return [
|
|
144
|
+
{ path: '/', name: 'root', children: [
|
|
145
|
+
{path: '', redirect: `/${instanceId}/docs/Introduction.md`},
|
|
146
|
+
{path: `${instanceId}`, children: [
|
|
147
|
+
{path: 'docs', redirect: `/${instanceId}/docs/Introduction.md`},
|
|
148
|
+
{path: 'docs/:pathMatch(.*)*', location: `${prefix}/core/pages/Documentation.vue`},
|
|
149
|
+
{path: 'connections', location: `${prefix}/core/pages/Connections.vue`},
|
|
150
|
+
{path: 'connections/:id', location: `${prefix}/core/pages/Connection.vue`},
|
|
151
|
+
{path: 'cache', location: `${prefix}/core/pages/Cache.vue`},
|
|
152
|
+
{path: 'traffic', location: `${prefix}/core/pages/Traffic.vue`},
|
|
153
|
+
{path: 'notifies', location: `${prefix}/core/pages/Notifies.vue`},
|
|
154
|
+
{path: 'components', location: `${prefix}/core/pages/frontend/Components.vue`, children: [
|
|
155
|
+
{path: 'basic', location: `${prefix}/core/pages/frontend/ComponentsBasic.vue`},
|
|
156
|
+
{path: 'advanced', location: `${prefix}/core/pages/frontend/ComponentsAdv.vue`},
|
|
157
|
+
{path: 'theme', location: `${prefix}/core/pages/frontend/Theme.vue`},
|
|
158
|
+
{path: 'bootstrap', location: `${prefix}/core/pages/frontend/Bootstrap.vue`},
|
|
159
|
+
]},
|
|
160
|
+
]}
|
|
161
|
+
] },
|
|
162
|
+
{ path: '/:pathMatch(.*)*', name: '404', location: `${prefix}/core/Page404.vue` }
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ________________________________________________________________________________
|
|
167
|
+
// Components
|
|
168
|
+
// ________________________________________________________________________________
|
|
169
|
+
/**
|
|
170
|
+
* Return the default Vue components
|
|
171
|
+
* using sha1 hashes
|
|
172
|
+
* @returns {Object}
|
|
173
|
+
*/
|
|
174
|
+
export function defaultVueComponents(prefix = "") {
|
|
175
|
+
return {
|
|
176
|
+
ExecButton: { path: `${prefix}/components/ExecButton.vue`, hash: `97e3d2ce89808c5a69f41404e1337f743015f0cc` },
|
|
177
|
+
XInput: { path: `${prefix}/components/xinput.vue`, hash: `cd47d7d038316367df6fd7e265aa3625b72a7777` },
|
|
178
|
+
XInput2: { path: `${prefix}/components/xinput2.vue`, hash: `320e52ac991baebded7c2a9f52a4cfc2cde47b55` },
|
|
179
|
+
XLabel: { path: `${prefix}/components/xlabel.vue`, hash: `90a9837aa8e06f1d3a5b7601337afd78a872d181` },
|
|
180
|
+
XDropdown: { path: `${prefix}/components/xdropdown.vue`, hash: `52b29b72fd6512eefccd9caa76e26dd91d9f9f9e` },
|
|
181
|
+
XDropdown2: { path: `${prefix}/components/xdropdown2.vue`, hash: `e7b163eda42fb7e1a6f07b011c423f8f275eb65d` },
|
|
182
|
+
XSection: { path: `${prefix}/components/xsection.vue`, hash: `27285d606f57d13f80156bb11cd34449f88df950` },
|
|
183
|
+
XTableOpen: { path: `${prefix}/components/xtable-open.vue`, hash: `a1b9a4f670817978022a4c596e2e2f89dd4568c2` },
|
|
184
|
+
XCollapse: { path: `${prefix}/components/xcollapse.vue`, hash: `ba1479bd1080a4fa5abdf6c91c7328ae679f78e6` },
|
|
185
|
+
XToggle: { path: `${prefix}/components/xtoggle.vue`, hash: `2f6871d2d3069ac8f35b6bf76de6c2109f42d9d1` },
|
|
186
|
+
XToggle3: { path: `${prefix}/components/xtoggle3.vue`, hash: `61d0464e6ed9983e817eb0cde928dceb7c0fdc75` },
|
|
187
|
+
XCheck: { path: `${prefix}/components/xcheck.vue`, hash: `ee0d6b30600fb41589123f6d66f2791f1332d4f7` },
|
|
188
|
+
XCheckbox: { path: `${prefix}/components/xcheckbox.vue`, hash: `a290c0cbb7bffce83c235dd5a1c98ed9c441a5b0` },
|
|
189
|
+
XTextarea: { path: `${prefix}/components/xtextarea.vue`, hash: `f8bb08419082aa5443630ab07172674b50c7a248` },
|
|
190
|
+
XHidden: { path: `${prefix}/components/xhidden.vue`, hash: `ecb396e12dd894040e715c0854275e4d5016fcb9` },
|
|
191
|
+
XCode: { path: `${prefix}/components/xcode.vue`, hash: `4d9d9165fea0539c9a983fcdffae8dedcfd537ae` },
|
|
192
|
+
XButton: { path: `${prefix}/components/xbutton.vue`, hash: `2e956caa47e46377ea5a809f7438d0fc38be73b9` },
|
|
193
|
+
XTabs: { path: `${prefix}/components/xtabs.vue`, hash: `83dc219106bdc86ae86dcd16cf95ebd7f11bc952` },
|
|
194
|
+
XKv: { path: `${prefix}/components/xkv.vue`, hash: `8951d3a5e3786cfc9c705b13c1f71e3f90dd2552` },
|
|
195
|
+
XNav: { path: `${prefix}/components/xnav.vue`, hash: `8d51c73e5716deed3577652e362c50526ddbe4e1` },
|
|
196
|
+
XMap: { path: `${prefix}/components/xmap.vue`, hash: `daee357d9e2ef96df0166dd7add0339d46a1cc01` },
|
|
197
|
+
XList: { path: `${prefix}/components/xlist.vue`, hash: `217ced04a333238d169c300a721b75f0ddd5e95b` },
|
|
198
|
+
XJson: { path: `${prefix}/components/xjson.vue`, hash: `0a3ef6265b4070b0f002d659776c02756cc1da5a` },
|
|
199
|
+
XCard: { path: `${prefix}/components/xcard.vue`, hash: `de3fbb23ae7b00d4c90a717dd361cb9315e9ded6` },
|
|
200
|
+
XCardH: { path: `${prefix}/components/xcard-h.vue`, hash: `de4d42f1056c5d2b8431f15e6b1180d9f9898ac2` },
|
|
201
|
+
XColor: { path: `${prefix}/components/xcolor.vue`, hash: `9bf9497ff66e213277f17af290c21c0a35752510` },
|
|
202
|
+
"v-ace-editor": { path: `${prefix}/components/vue3-ace-editor.vue`, hash: `70ce4a39152af5cf0f7cb6b1d4fdafc8b6225edc` },
|
|
203
|
+
XMarkdown: { path: `${prefix}/components/xmarkdown.vue`, hash: `15f835547fab8a8c8aad47d72640c8e918a7b9da` },
|
|
204
|
+
XSound: { path: `${prefix}/components/xsound.vue`, hash: `3e8ad4aa3c767f757dd49b99aa6f961547caf970` },
|
|
205
|
+
XUpload: { path: `${prefix}/components/xupload.vue`, hash: `7a872277e0047fca11e950efe08f2bffa670abdb` },
|
|
206
|
+
XTree: { path: `${prefix}/components/xtree.vue`, hash: `3b6534e86996c48ab05072a9b793ecc78d83a0eb` },
|
|
207
|
+
CodeMirror: { path: `${prefix}/components/code-mirror.vue`, hash: `3ce1028adb75831e01c4264d5764c14f60a1bd00` },
|
|
208
|
+
XTerminal: { path: `${prefix}/components/xterminal.vue`, hash: `1c01f92c0a08bd4937f9768a1d49f12a9a84feea` },
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
// ________________________________________________________________________________
|
|
214
|
+
// Factory
|
|
215
|
+
// ________________________________________________________________________________
|
|
216
|
+
/**
|
|
217
|
+
* Create a C0ckp1t configuration object.
|
|
218
|
+
*
|
|
219
|
+
* @param {Object} overrides - Properties to override.
|
|
220
|
+
* @returns {Object} The configuration object.
|
|
221
|
+
*/
|
|
222
|
+
export function createConfig(overrides = {}) {
|
|
223
|
+
// 1. Separate root/routes from scalar overrides so deepMerge handles scalars
|
|
224
|
+
const { root: rootOverride, components: componentsOverride, routes: routesOverride, ...scalarOverrides } = overrides;
|
|
225
|
+
|
|
226
|
+
// 2. Merge scalar defaults with overrides
|
|
227
|
+
const merged = deepMerge(DEFAULTS, scalarOverrides);
|
|
228
|
+
|
|
229
|
+
// 3. Resolve the key variables that nav/routes depend on
|
|
230
|
+
const { instanceId, routePrefix, componentPrefix } = merged;
|
|
231
|
+
|
|
232
|
+
// 4. Build or use provided root nav tree
|
|
233
|
+
const root = rootOverride !== undefined ? rootOverride : buildNavTree(instanceId);
|
|
234
|
+
|
|
235
|
+
// 5. Build or use provided routes
|
|
236
|
+
const routes = routesOverride !== undefined ? routesOverride : buildRoutes(instanceId, routePrefix);
|
|
237
|
+
|
|
238
|
+
const components = componentsOverride !== undefined ? componentsOverride : defaultVueComponents(componentPrefix);
|
|
239
|
+
// 6. Assemble final config
|
|
240
|
+
return {
|
|
241
|
+
...merged,
|
|
242
|
+
root,
|
|
243
|
+
routes,
|
|
244
|
+
components
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export default createConfig;
|
package/README.md
CHANGED
|
@@ -45,12 +45,15 @@ tar -zxvf c0ckp1t-1.0.2.tgz
|
|
|
45
45
|
# package/js_ext/Makefile
|
|
46
46
|
# package/css/bootstrap-c0ckp1t.css
|
|
47
47
|
# ...
|
|
48
|
+
|
|
49
|
+
# to expand to webroot directory
|
|
50
|
+
tar -zxvf c0ckp1t-1.0.2.tgz --strip-components=1 -C webroot
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
|
|
51
54
|
## Releases
|
|
52
55
|
|
|
53
|
-
* 1.0.
|
|
56
|
+
* 1.0.12 - Beta: fixing cdn index-cdn.html making sure it works with jsfiddle.net
|
|
54
57
|
* Had to publish many times to get CDN working with different configurations
|
|
55
58
|
* 1.0.2 - Beta: fixing cdn index-cdn.html example
|
|
56
59
|
* 1.0.1 - Beta: removing `Constants.mjs` dependencies
|
|
@@ -10,6 +10,8 @@ const instanceId = "demo";
|
|
|
10
10
|
// Used for requestion app components and files
|
|
11
11
|
const appEndpoint = "";
|
|
12
12
|
|
|
13
|
+
const islandDir = "c0ckp1t-demo"
|
|
14
|
+
|
|
13
15
|
// ________________________________________________________________________________
|
|
14
16
|
// GLOBAL CONSTANTS
|
|
15
17
|
// ________________________________________________________________________________
|
|
@@ -21,6 +23,7 @@ export default {
|
|
|
21
23
|
type: "LOCAL",
|
|
22
24
|
appName: "C0ckp1t Demo",
|
|
23
25
|
appEndpoint: appEndpoint,
|
|
26
|
+
islandDir: islandDir,
|
|
24
27
|
|
|
25
28
|
// This creates the navigation tree
|
|
26
29
|
root: {
|
|
@@ -55,11 +58,11 @@ export default {
|
|
|
55
58
|
|
|
56
59
|
// This is used to create routes for the vue router
|
|
57
60
|
routes: [
|
|
58
|
-
{path: instanceId, location: `${appEndpoint}/
|
|
61
|
+
{path: instanceId, location: `${appEndpoint}/${islandDir}/main.vue`, children: [
|
|
59
62
|
{path: '', redirect: `/${instanceId}/homepage`},
|
|
60
|
-
{path: 'homepage', location: `${appEndpoint}/
|
|
63
|
+
{path: 'homepage', location: `${appEndpoint}/${islandDir}/pages/homepage.vue`},
|
|
61
64
|
{path: 'docs', redirect: `/${instanceId}/docs/Introduction.md`},
|
|
62
|
-
{path: 'docs/:pathMatch(.*)*', location: `${appEndpoint}/
|
|
65
|
+
{path: 'docs/:pathMatch(.*)*', location: `${appEndpoint}/${islandDir}/pages/documentation.vue`},
|
|
63
66
|
] },
|
|
64
67
|
]
|
|
65
68
|
|
|
@@ -39,8 +39,7 @@ logger.debug("[INIT]")
|
|
|
39
39
|
|
|
40
40
|
<ul class="nav flex-column mb-auto">
|
|
41
41
|
<RouterLink class="nav-link d-flex align-items-center gap-2" :to="`${routerEndpoint}/homepage`">Homepage</RouterLink>
|
|
42
|
-
<RouterLink class="nav-link d-flex align-items-center gap-2" :class="{ 'router-link-active': isDocsActive }" :to="`${routerEndpoint}/docs`"
|
|
43
|
-
<RouterLink class="nav-link d-flex align-items-center gap-2" :to="`${routerEndpoint}/admin`" v-if="registry?.store?.context?.accessLevel <= 500">Admin</RouterLink>
|
|
42
|
+
<RouterLink class="nav-link d-flex align-items-center gap-2" :class="{ 'router-link-active': isDocsActive }" :to="`${routerEndpoint}/docs`" >Documentation</RouterLink>
|
|
44
43
|
</ul>
|
|
45
44
|
|
|
46
45
|
<hr class="my-2">
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/*
|
|
3
|
+
[VARS] - init, imports, template, script, style, name
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
const documentation = defineAsyncComponent(() => import("/v3/actions/auth/www/pages/documentation.vue"))
|
|
7
|
+
<documentation/>
|
|
8
|
+
*/
|
|
9
|
+
// ________________________________________________________________________________
|
|
10
|
+
// IMPORTS
|
|
11
|
+
// ________________________________________________________________________________
|
|
12
|
+
import {reactive, computed, ref, onMounted, onUnmounted, defineAsyncComponent, watch} from 'vue'
|
|
13
|
+
import {getLogger} from "Logging";
|
|
14
|
+
import {store as storeLocal, api as apiLocal, registry, instanceId} from '../store.mjs'
|
|
15
|
+
import Documentation from "/core/pages/Documentation.vue"
|
|
16
|
+
|
|
17
|
+
// !# C0CKP1T_START imports
|
|
18
|
+
|
|
19
|
+
// !# C0CKP1T_END imports
|
|
20
|
+
|
|
21
|
+
// ________________________________________________________________________________
|
|
22
|
+
// LOGGING
|
|
23
|
+
// ________________________________________________________________________________
|
|
24
|
+
const LOG_HEADER = "pages/homepage.vue"
|
|
25
|
+
const logger = getLogger(LOG_HEADER)
|
|
26
|
+
logger.debug("[INIT]")
|
|
27
|
+
|
|
28
|
+
// !# C0CKP1T_START script
|
|
29
|
+
const local = reactive({
|
|
30
|
+
id: LOG_HEADER,
|
|
31
|
+
instanceId: instanceId
|
|
32
|
+
})
|
|
33
|
+
// !# C0CKP1T_END script
|
|
34
|
+
|
|
35
|
+
// ________________________________________________________________________________
|
|
36
|
+
// INIT
|
|
37
|
+
// ________________________________________________________________________________
|
|
38
|
+
async function init() {
|
|
39
|
+
if (registry.state.isReady) {
|
|
40
|
+
// !# C0CKP1T_START init
|
|
41
|
+
|
|
42
|
+
// !# C0CKP1T_END init
|
|
43
|
+
} else {
|
|
44
|
+
setTimeout(async () => { await init() }, 1000)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
onMounted(async () => { init() })
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<!-- !# C0CKP1T_START template -->
|
|
52
|
+
<x-section extra="fs-3" k="Documentation" :visible="true">
|
|
53
|
+
<Documentation :remotePathMapping="`/${storeLocal.islandDir}/docs`"></Documentation>
|
|
54
|
+
</x-section>
|
|
55
|
+
<!-- !# C0CKP1T_END template -->
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style scoped>
|
|
59
|
+
/* !# C0CKP1T_START style */
|
|
60
|
+
|
|
61
|
+
/* !# C0CKP1T_END style */
|
|
62
|
+
</style>
|
|
63
|
+
|
|
64
|
+
|
package/c0ckp1t-demo/store.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import {getLogger} from "Logging";
|
|
|
12
12
|
import {store as storeMain, api as apiMain} from 'GlobalStore'
|
|
13
13
|
|
|
14
14
|
// !# C0CKP1T_START import
|
|
15
|
-
import C0ckp1tConfig from "./
|
|
15
|
+
import C0ckp1tConfig from "./Config.mjs";
|
|
16
16
|
// !# C0CKP1T_END import
|
|
17
17
|
|
|
18
18
|
export const instanceId = C0ckp1tConfig.instanceId
|
|
@@ -38,7 +38,7 @@ export const store = reactive({
|
|
|
38
38
|
endpoint: routerEndpoint,
|
|
39
39
|
|
|
40
40
|
// !# C0CKP1T_START store
|
|
41
|
-
|
|
41
|
+
islandDir: C0ckp1tConfig.islandDir,
|
|
42
42
|
// !# C0CKP1T_END store
|
|
43
43
|
})
|
|
44
44
|
|
|
@@ -73,6 +73,9 @@ let _ro = null;
|
|
|
73
73
|
let _contentBackup = '';
|
|
74
74
|
let _isSettingContent = false;
|
|
75
75
|
|
|
76
|
+
let _mounted = false;
|
|
77
|
+
let _initAborted = false;
|
|
78
|
+
|
|
76
79
|
// CodeMirror modules - loaded dynamically
|
|
77
80
|
let EditorView = null;
|
|
78
81
|
let EditorState = null;
|
|
@@ -215,6 +218,12 @@ watch(() => local.currentReadonly, (val) => {
|
|
|
215
218
|
});
|
|
216
219
|
|
|
217
220
|
onMounted(async () => {
|
|
221
|
+
_mounted = true;
|
|
222
|
+
// --- guard helper ---
|
|
223
|
+
const alive = () => _mounted && root.value != null;
|
|
224
|
+
if (!alive()) return;
|
|
225
|
+
|
|
226
|
+
|
|
218
227
|
// Load core CodeMirror modules
|
|
219
228
|
const [stateModule, viewModule, basicSetupModule] = await Promise.all([
|
|
220
229
|
import('https://esm.sh/@codemirror/state'),
|
|
@@ -222,6 +231,9 @@ onMounted(async () => {
|
|
|
222
231
|
import('https://esm.sh/codemirror'),
|
|
223
232
|
]);
|
|
224
233
|
|
|
234
|
+
// Bail if component unmounted during async load
|
|
235
|
+
if (!alive()) return;
|
|
236
|
+
|
|
225
237
|
EditorState = stateModule.EditorState;
|
|
226
238
|
Compartment = stateModule.Compartment;
|
|
227
239
|
EditorView = viewModule.EditorView;
|
|
@@ -239,6 +251,9 @@ onMounted(async () => {
|
|
|
239
251
|
getThemeExtension(props.theme),
|
|
240
252
|
]);
|
|
241
253
|
|
|
254
|
+
// Bail again after second async gap
|
|
255
|
+
if (!alive()) return;
|
|
256
|
+
|
|
242
257
|
const extensions = [
|
|
243
258
|
basicSetup,
|
|
244
259
|
languageCompartment.of(langExt),
|
|
@@ -301,14 +316,14 @@ onMounted(async () => {
|
|
|
301
316
|
});
|
|
302
317
|
|
|
303
318
|
onBeforeUnmount(() => {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
319
|
+
_mounted = false;
|
|
320
|
+
|
|
321
|
+
_ro?.disconnect();
|
|
322
|
+
_ro = null;
|
|
323
|
+
|
|
324
|
+
_editor?.destroy();
|
|
325
|
+
_editor = null;
|
|
326
|
+
|
|
312
327
|
});
|
|
313
328
|
|
|
314
329
|
// Public methods
|
package/components/xsound.vue
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { reactive, computed, markRaw, onMounted, onBeforeUnmount } from 'vue'
|
|
3
|
-
import XDropdown2 from '/components/xdropdown2.vue'
|
|
4
3
|
|
|
5
4
|
// https://wavesurfer-js.org/docs/
|
|
6
5
|
// NOTE: streaming not supported?
|
|
@@ -197,7 +196,7 @@ onBeforeUnmount(() => {
|
|
|
197
196
|
</div>
|
|
198
197
|
|
|
199
198
|
<!-- Playback Rate -->
|
|
200
|
-
<
|
|
199
|
+
<x-dropdown2
|
|
201
200
|
v-model="local.playbackRate"
|
|
202
201
|
:items="playbackRateOptions"
|
|
203
202
|
:on-change="onPlaybackRateChange"
|
package/core/CoreUtils.mjs
CHANGED
|
@@ -11,6 +11,9 @@ export function findHostnamePortProtocol() {
|
|
|
11
11
|
return {hostname, port, protocol, isSecure, serverUrl}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// ________________________________________________________________________________
|
|
15
|
+
// Island Configuration
|
|
16
|
+
// ________________________________________________________________________________
|
|
14
17
|
/**
|
|
15
18
|
* Validate and set defaults for the island config object.
|
|
16
19
|
* @param config
|
|
@@ -35,43 +38,108 @@ export function validateIslandConfig(config) {
|
|
|
35
38
|
return config
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
// ________________________________________________________________________________
|
|
42
|
+
// Application Configuration
|
|
43
|
+
// ________________________________________________________________________________
|
|
44
|
+
export const DEFAULTS = {
|
|
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
|
|
49
|
+
WITH_CREDENTIALS: false,
|
|
50
|
+
|
|
51
|
+
defaultInstanceId: "default",
|
|
52
|
+
instanceId: "default",
|
|
53
|
+
type: "LOCAL",
|
|
54
|
+
// Determines GlobalStore.name
|
|
55
|
+
appName: "C0ckp1t Application",
|
|
56
|
+
// Used for default routes prefix (used only in config)
|
|
57
|
+
routePrefix: "",
|
|
58
|
+
// Used for default components prefix (used only in config)
|
|
59
|
+
componentPrefix: "",
|
|
60
|
+
// Main entry point
|
|
61
|
+
appMainComponent: "/core/PageMain.vue",
|
|
62
|
+
// Used for requestion app components and files (GlobalStore.appEndpoint)
|
|
63
|
+
appEndpoint: "",
|
|
64
|
+
|
|
65
|
+
// Nav Configuration
|
|
66
|
+
navCloseLogo: "/core/img/logo_v1.svg",
|
|
67
|
+
navOpenLogo: "/core/img/logo_v2.svg",
|
|
68
|
+
navHasSearch: false,
|
|
69
|
+
navHasThemeSel: true,
|
|
70
|
+
|
|
71
|
+
// Determine if VueRouter is createWebHashHistory or createWebHistory
|
|
72
|
+
vueRouterModeIsHash: true,
|
|
73
|
+
|
|
74
|
+
// Logger Config (see Logging.mjs)
|
|
75
|
+
defaultLogLevel: "INFO",
|
|
76
|
+
defaultLoggerLevels: {
|
|
77
|
+
"GlobalStore.mjs": "INFO",
|
|
78
|
+
"VueUtils.mjs": "INFO",
|
|
79
|
+
"Connection.mjs": "INFO",
|
|
80
|
+
"default": "INFO",
|
|
81
|
+
"anonymous": "INFO",
|
|
82
|
+
"demo": "INFO"
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
|
|
38
87
|
/**
|
|
39
|
-
*
|
|
40
|
-
* @
|
|
88
|
+
* Validate and set defaults for the config object.
|
|
89
|
+
* @param config
|
|
90
|
+
* @returns {*}
|
|
41
91
|
*/
|
|
42
|
-
export function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
XCheckbox: `/components/xcheckbox.vue`,
|
|
56
|
-
XTextarea: `/components/xtextarea.vue`,
|
|
57
|
-
XHidden: `/components/xhidden.vue`,
|
|
58
|
-
|
|
59
|
-
XTabs: `/components/xtabs.vue`,
|
|
60
|
-
XKv: `/components/xkv.vue`,
|
|
61
|
-
XNav: `/components/xnav.vue`,
|
|
62
|
-
XMap: `/components/xmap.vue`,
|
|
63
|
-
XList: `/components/xlist.vue`,
|
|
64
|
-
XJson: `/components/xjson.vue`,
|
|
65
|
-
XCard: `/components/xcard.vue`,
|
|
66
|
-
XCardH: `/components/xcard-h.vue`,
|
|
67
|
-
XColor: `/components/xcolor.vue`,
|
|
92
|
+
export function validateAppConfig(config) {
|
|
93
|
+
if (!config) {
|
|
94
|
+
throw new Error("config is required")
|
|
95
|
+
}
|
|
96
|
+
if (typeof config !== 'object') {
|
|
97
|
+
throw new Error("Config must be an object must was `" + typeof config + "`")
|
|
98
|
+
}
|
|
99
|
+
if (!Array.isArray(config?.routes) || config.routes.length === 0) {
|
|
100
|
+
config.routes = []
|
|
101
|
+
}
|
|
102
|
+
if (typeof config.components !== 'object' || config.components === null) {
|
|
103
|
+
config.components = {}
|
|
104
|
+
}
|
|
68
105
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
CodeMirror: `/components/CodeMirror.vue`,
|
|
75
|
-
XTerminal: `/components/XTerminal.vue`,
|
|
106
|
+
if (typeof config.instanceId !== `string` || config.instanceId.trim() === ``) {
|
|
107
|
+
config.instanceId = DEFAULTS.instanceId
|
|
108
|
+
}
|
|
109
|
+
if (typeof config.type !== `string` || config.type.trim() === ``) {
|
|
110
|
+
config.type = DEFAULTS.type
|
|
76
111
|
}
|
|
112
|
+
|
|
113
|
+
return config
|
|
77
114
|
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// ________________________________________________________________________________
|
|
118
|
+
// Deep Merge Utility
|
|
119
|
+
// ________________________________________________________________________________
|
|
120
|
+
/**
|
|
121
|
+
* Deep merge source into target. Returns a new object.
|
|
122
|
+
* - Objects are recursively merged (source keys override target keys)
|
|
123
|
+
* - Arrays are replaced entirely (source array wins)
|
|
124
|
+
* - Scalars are replaced (source wins)
|
|
125
|
+
*/
|
|
126
|
+
export function deepMerge(target, source) {
|
|
127
|
+
const result = { ...target };
|
|
128
|
+
for (const key of Object.keys(source)) {
|
|
129
|
+
const sourceVal = source[key];
|
|
130
|
+
const targetVal = target[key];
|
|
131
|
+
if (
|
|
132
|
+
sourceVal !== null &&
|
|
133
|
+
typeof sourceVal === 'object' &&
|
|
134
|
+
!Array.isArray(sourceVal) &&
|
|
135
|
+
targetVal !== null &&
|
|
136
|
+
typeof targetVal === 'object' &&
|
|
137
|
+
!Array.isArray(targetVal)
|
|
138
|
+
) {
|
|
139
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
140
|
+
} else {
|
|
141
|
+
result[key] = sourceVal;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|