c0ckp1t 1.0.18 → 1.0.20

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 (57) hide show
  1. package/components/MarkdownUtils.mjs +24 -18
  2. package/components/vue3-ace-editor.vue +12 -7
  3. package/components/xinput.vue +11 -3
  4. package/components/xjson.vue +28 -3
  5. package/components/xmarkdown.vue +11 -75
  6. package/components/xmd.vue +75 -0
  7. package/components/xsection.vue +6 -3
  8. package/components/xterminal.vue +293 -94
  9. package/config.default.mjs +327 -0
  10. package/config.mjs +312 -0
  11. package/core/ConfigUtils.mjs +197 -328
  12. package/core/Content.mjs +1 -0
  13. package/core/GlobalStore.mjs +69 -30
  14. package/core/Island.mjs +97 -39
  15. package/core/IslandDefault.mjs +10 -27
  16. package/core/JsUtils.mjs +28 -0
  17. package/core/PageMain.vue +2 -0
  18. package/core/PageNavigation.vue +31 -5
  19. package/core/Stash.mjs +92 -0
  20. package/core/VueUtils.mjs +2 -0
  21. package/core/WsLogUtils.mjs +19 -22
  22. package/core/alert-offcanvas.vue +137 -0
  23. package/core/img/200by200.png +0 -0
  24. package/core/main-offcanvas.vue +3 -11
  25. package/core/notify/NotifyUtils.mjs +59 -27
  26. package/core/pages/Connection.vue +22 -14
  27. package/core/pages/Connections.vue +85 -35
  28. package/core/pages/Documentation.vue +82 -63
  29. package/core/pages/Notifies.vue +3 -14
  30. package/core/pages/Traffic.vue +102 -51
  31. package/core/pages/connections/ConfigIsland.vue +41 -37
  32. package/core/pages/connections/connection-header.vue +1 -1
  33. package/core/pages/frontend/Bootstrap.vue +0 -1
  34. package/core/pages/frontend/ComponentsAdv.vue +7 -0
  35. package/core/pages/traffic/log-ws-event.vue +81 -0
  36. package/core/sfc/code-item2.vue +171 -0
  37. package/core/sfc/p1t-event-.vue +98 -0
  38. package/core/ws-client/AuthNState.mjs +109 -55
  39. package/core/ws-client/Connection.mjs +374 -395
  40. package/core/ws-client/WsClient.mjs +14 -1
  41. package/index-cdn.html +74 -7
  42. package/index.html +8 -11
  43. package/js_ext/Makefile +17 -0
  44. package/js_ext/README.md +16 -0
  45. package/js_ext/chart.umd.min.js +14 -0
  46. package/js_ext/chartjs-plugin-zoom.min.js +7 -0
  47. package/js_ext/hammer.min.js +7 -0
  48. package/js_ext/moment-timezone.min.js +1 -0
  49. package/js_ext/xstate-5.29.0.esm.mjs +8 -0
  50. package/package.json +3 -2
  51. package/style.css +9 -0
  52. package/Config.mjs +0 -58
  53. package/components/FsTree.vue +0 -550
  54. package/components/Tree.mjs +0 -175
  55. package/core/pages/connections/ws-connection.vue +0 -145
  56. package/core/ws-client/Session.mjs +0 -72
  57. package/core/ws-client/ws-connection.vue +0 -140
@@ -10,8 +10,6 @@ const LOG_HEADER = 'MarkdownUtils.mjs'
10
10
  const logger = getLogger(LOG_HEADER)
11
11
  logger.debug("[INIT]")
12
12
 
13
- const remotePathPrefix = '/docs/';
14
-
15
13
  // ________________________________________________________________________________
16
14
  // EXPORTED UTILITY FUNCTIONS
17
15
  // ________________________________________________________________________________
@@ -100,26 +98,34 @@ export function extractSizeFromAlt(alt) {
100
98
  * Converts relative paths to absolute paths for documentation
101
99
  *
102
100
  * @param {string} inputString - HTML string with img tags
101
+ * @param {Object} images
103
102
  * @returns {string} Modified HTML string
104
103
  */
105
- export function replaceImgLinks(inputString) {
106
- return inputString.replace(
107
- /img src="([^"]*)" alt="([^"]*)"/g,
108
- (match, src, alt) => {
109
- logger.debug(`[REGEX_IMG] match=${match} src=${src} alt=${alt}`)
110
- const size = extractSizeFromAlt(alt);
111
- let sizeAttrs = '';
112
- if (size) {
113
- sizeAttrs = `width="${size.width}" `;
114
- alt = alt.replace(/=(\d+)x(\d+)$/, ''); // Remove size from alt
115
- }
116
- const adjustedSrc = "" + src.replace(/^\.\//, remotePathPrefix);
117
- logger.debug(`[IMG] - ${src} -> ${adjustedSrc}`)
118
- return `img src="${adjustedSrc}" class="mk-img" ${sizeAttrs} alt="${alt.trim()}"`
119
- }
120
- );
104
+ export function replaceImgLinks(inputString, images = {}) {
105
+ return inputString.replace(
106
+ /img src="([^"]*)" alt="([^"]*)"/g,
107
+ (match, src, alt) => {
108
+ logger.debug(`[REGEX_IMG] match=${match} src=${src} alt=${alt}`)
109
+ const size = extractSizeFromAlt(alt);
110
+ let sizeAttrs = '';
111
+ if (size) {
112
+ sizeAttrs = `width="${size.width}" `;
113
+ // sizeAttrs = `width="${size.width}" height="${size.height}"`;
114
+ alt = alt.replace(/=(\d+)x(\d+)$/, ''); // Remove size from alt
115
+ }
116
+ if (match.toLowerCase().includes("http") || match.toLowerCase().includes("class")) {
117
+ // do nothing
118
+ return `${match} class="mk-img"`
119
+ } else {
120
+ images[src] = null
121
+ // return `${match} class="mk-img"`
122
+ return `img :src="images['${src}']" alt="${alt.trim()}" class="mk-img" ${sizeAttrs}`
123
+ }
124
+ }
125
+ );
121
126
  }
122
127
 
128
+
123
129
  /**
124
130
  * Replace Vue template syntax to prevent interpretation
125
131
  * Escapes {{ }} so they display literally in markdown
@@ -6,6 +6,7 @@
6
6
 
7
7
  import {ref, reactive, markRaw, onMounted, onBeforeUnmount, watch} from 'vue';
8
8
  import {loadAce} from './AceLoader.mjs';
9
+ import {store as storeMain, api as apiMain} from 'GlobalStore'
9
10
 
10
11
  const root = ref("");
11
12
 
@@ -101,7 +102,8 @@ let _editor = undefined
101
102
  let _ro = undefined
102
103
  let _contentBackup = undefined;
103
104
  let _isSettingContent = false;
104
-
105
+ const currentHeight = ref(props.height);
106
+ const container = ref(null)
105
107
 
106
108
  watch(() => props.modelValue, (first, second) => {
107
109
  value(first)
@@ -139,7 +141,7 @@ onMounted(async () => {
139
141
  console.error('Ace editor failed to load:', err)
140
142
  return
141
143
  }
142
- ace.config.set('basePath', '/js_ext/ace-editor');
144
+ ace.config.set('basePath', `${storeMain.appEndpoint}/js_ext/ace-editor`);
143
145
  _editor = markRaw(ace.edit(root.value, {
144
146
  mode: 'ace/mode/' + props.lang,
145
147
  theme: 'ace/theme/' + props.theme,
@@ -171,8 +173,12 @@ onMounted(async () => {
171
173
  local.currentRelativeLineNumbers = false;
172
174
 
173
175
  value(props.modelValue)
174
- _ro = new ResizeObserver(() => _editor.resize());
175
- _ro.observe(root.value);
176
+ _ro = new ResizeObserver(() => {
177
+ if (container.value) {
178
+ currentHeight.value = container.value.style.height || container.value.offsetHeight + 'px';
179
+ }
180
+ _editor.resize();
181
+ });
176
182
  emit('init', _editor);
177
183
  })
178
184
 
@@ -246,7 +252,7 @@ function maxLines(val) {
246
252
  </script>
247
253
 
248
254
  <template>
249
- <div class="ace-editor" style="display: flex; flex-direction: column;" :style="{ height: props.height }">
255
+ <div ref="container" class="ace-editor" style="display: flex; flex-direction: column;" :style="{ height: currentHeight }">
250
256
  <div class="debug-toolbar" v-if="props.debug">
251
257
  <label>
252
258
  Theme:
@@ -286,8 +292,7 @@ function maxLines(val) {
286
292
 
287
293
  .ace-editor {
288
294
  resize: vertical;
289
- overflow-y: auto;
290
- overflow-x: hidden;
295
+ overflow: hidden;
291
296
  }
292
297
 
293
298
  .debug-toolbar {
@@ -21,6 +21,10 @@ const props = defineProps({
21
21
  type: String,
22
22
  default: 'text'
23
23
  },
24
+ step: {
25
+ type: Number,
26
+ default: 1
27
+ },
24
28
  showLabel: {
25
29
  type: Boolean,
26
30
  default: true
@@ -34,6 +38,10 @@ const props = defineProps({
34
38
  inputStyle: {
35
39
  type: String,
36
40
  default: ""
41
+ },
42
+ readonly: {
43
+ type: Boolean,
44
+ default: false
37
45
  }
38
46
  })
39
47
 
@@ -61,13 +69,13 @@ const typeCheck = computed (() => {
61
69
  <span v-if="showLabel" :style="props.styleObject" class=" input-group-text label fw-bold" > {{ k }} </span>
62
70
 
63
71
  <input :type="type" aria-label="message" class="form-control"
64
- :style="props.inputStyle" v-model.number="message" v-if="type.toUpperCase() === 'NUMBER'">
72
+ :style="props.inputStyle" v-model.number="message" :step="step" :readonly="props.readonly" v-if="type.toUpperCase() === 'NUMBER'">
65
73
 
66
74
  <input :type="isPasswordVisible ? 'text' : 'password'" aria-label="message" class="form-control"
67
- :style="props.inputStyle" v-model="message" v-else-if="type.toUpperCase() === 'PASSWORD'">
75
+ :style="props.inputStyle" v-model="message" :readonly="props.readonly" v-else-if="type.toUpperCase() === 'PASSWORD'">
68
76
 
69
77
  <input :type="type" aria-label="message" class="form-control"
70
- :style="props.inputStyle" v-model="message" v-else>
78
+ :style="props.inputStyle" v-model="message" :readonly="props.readonly" v-else>
71
79
 
72
80
  <div class="input-group-text text-primary icon-container" @click="isPasswordVisible = !isPasswordVisible" v-if="type.toUpperCase() === 'PASSWORD'">
73
81
  <i class="fa-solid fa-eye" v-if="isPasswordVisible"></i>
@@ -14,6 +14,10 @@ const props = defineProps({
14
14
  expanded: {
15
15
  type: Boolean,
16
16
  default: false
17
+ },
18
+ copyButton: {
19
+ type: Boolean,
20
+ default: true
17
21
  }
18
22
  })
19
23
  const emit = defineEmits(['select'])
@@ -30,7 +34,9 @@ function sanitize(obj, seen = new WeakSet()) {
30
34
  if (typeof obj === 'function') return `[Function: ${obj.name || 'anonymous'}]`
31
35
  if (typeof obj === 'symbol') return obj.toString()
32
36
  if (typeof obj !== 'object') return obj
33
-
37
+ if (obj instanceof Set) {
38
+ return Array.from(obj).map(item => sanitize(item, seen))
39
+ }
34
40
  // Handle circular references
35
41
  if (seen.has(obj)) return '[Circular]'
36
42
  seen.add(obj)
@@ -53,6 +59,7 @@ const local = reactive({
53
59
  id: id,
54
60
  el: null,
55
61
  isExpanded: props.expanded,
62
+ copied: false,
56
63
  })
57
64
 
58
65
  watch(
@@ -75,6 +82,14 @@ function collapseAll() {
75
82
  local.el.collapse('**');
76
83
  }
77
84
 
85
+ function copyToClipboard() {
86
+ const text = JSON.stringify(sanitize(props.obj), null, 2)
87
+ navigator.clipboard.writeText(text).then(() => {
88
+ local.copied = true
89
+ setTimeout(() => { local.copied = false }, 1500)
90
+ })
91
+ }
92
+
78
93
  function toggleExpand() {
79
94
  if(local.isExpanded) {
80
95
  local.isExpanded = false
@@ -110,11 +125,21 @@ onMounted(async () => { init() })
110
125
 
111
126
  <template>
112
127
  <div class="x-json">
113
- <!-- <button @click="toggleExpand">Toggle Expand</button>-->
128
+ <button v-if="props.copyButton" class="btn btn-sm btn-outline-secondary copy-btn" @click="copyToClipboard">
129
+ <i class="fa-solid" :class="local.copied ? 'fa-check' : 'fa-copy'"></i>
130
+ </button>
114
131
  <json-viewer class="p-1" :id="local.id"> </json-viewer>
115
132
  </div>
116
133
  </template>
117
134
 
118
135
  <style scoped>
119
-
136
+ .x-json {
137
+ position: relative;
138
+ }
139
+ .copy-btn {
140
+ position: absolute;
141
+ top: 4px;
142
+ right: 4px;
143
+ z-index: 1;
144
+ }
120
145
  </style>
@@ -18,6 +18,7 @@ import {getLogger} from "Logging"
18
18
  import {md} from "./Markdown.mjs"
19
19
 
20
20
  import CodeItem from "../core/sfc/code-item.vue"
21
+ import CodeItem2 from "../core/sfc/code-item2.vue"
21
22
 
22
23
  import {
23
24
  replaceHrefLinks,
@@ -25,7 +26,7 @@ import {
25
26
  replaceVueSpecial,
26
27
  removeMarkdownHeader,
27
28
  slugify,
28
- extractSizeFromAlt, decorateTableHTML
29
+ decorateTableHTML
29
30
  } from "./MarkdownUtils.mjs";
30
31
 
31
32
  const props = defineProps({
@@ -48,91 +49,25 @@ const emit = defineEmits(['href'])
48
49
  // ________________________________________________________________________________
49
50
  const LOG_HEADER = 'xmarkdown.vue'
50
51
  const logger = getLogger(LOG_HEADER)
51
- logger.debug("init")
52
+ logger.debug("INIT")
52
53
 
53
54
  // ________________________________________________________________________________
54
55
  // LOCAL STATE
55
56
  // ________________________________________________________________________________
56
57
  const local = reactive({
58
+ id: LOG_HEADER,
57
59
  isLoading: false,
58
-
59
60
  markdownComponent: null,
60
-
61
61
  images: null,
62
- text: null,
63
62
  })
64
63
 
65
64
  // ______________________________________________________________________________________
66
65
  // RENDING VUE COMPONENT
67
66
  // ______________________________________________________________________________________
68
- // Note: Helper functions have been extracted to MarkdownUtils.mjs for testability
69
-
70
-
71
- // We do not support recursive injection
72
- async function injectText(text) {
73
- logger.debug(`Scanning for markdown injection`)
74
-
75
- // Fetch all
76
- const promises = [];
77
- text.replace(/{%\s*include\s+(.*?)\s*%}/g, (match, param) => {
78
- logger.debug(`[REGEX_INJECT] match=${match} param=${param}`)
79
- const promise = props.fetchText(param)
80
- promises.push(promise);
81
- });
82
-
83
- // Replace all
84
- const results = await Promise.all(promises)
85
-
86
- let currentIndex = 0;
87
- return text.replace(/{%\s*include\s+(.*?)\s*%}/g, () => results[currentIndex++]);
88
- }
89
-
90
- async function injectTextV2(text) {
91
- logger.debug(`[INJECT_TEXT] - Version 2`)
92
-
93
- // Fetch all
94
- const promises = [];
95
- text.replace(/<div data-ref="([^"]+)".*?><\/div>/g, (match, param) => {
96
- logger.debug(`match=${match} param=${param}`)
97
-
98
- const refRegex = /data-(id|lines)="([^"]+)"/g;
99
- const refMatch = refRegex.exec(match)
100
- if(refMatch !== null) {
101
- logger.debug(`type=${refMatch[1]} refId=${refMatch[2]}`)
102
- const promise = props.fetchText(param, refMatch[2])
103
- promises.push(promise);
104
- } else {
105
- const promise = props.fetchText(param)
106
- promises.push(promise);
107
- }
108
-
109
- });
110
-
111
- // Replace all
112
- const results = await Promise.all(promises);
113
- logger.trace("received injection text:")
114
- logger.trace(results)
115
- let currentIndex = 0;
116
- return text.replace(/<div data-ref="([^"]+)".*?><\/div>/g, () => results[currentIndex++]);
117
- }
118
-
119
-
120
- async function fetchAllText() {
121
- const fetchPromises = Object.keys(local.text).map(relativeURL => {
122
- logger.debug(`fetchText - ${relativeURL}`)
123
- return props.fetchText(relativeURL).then(text => {
124
- logger.debug(text)
125
- local.text[relativeURL] = text
126
- });
127
- });
128
- await Promise.all(fetchPromises);
129
- }
130
-
131
67
  async function fetchAllImages() {
132
68
  const fetchPromises = Object.keys(local.images).map(relativeURL => {
133
69
  logger.debug(`fetchImage - ${relativeURL}`)
134
70
  return props.fetchImage(relativeURL).then(blobUrl => {
135
- console.log(blobUrl)
136
71
  local.images[relativeURL] = blobUrl
137
72
  });
138
73
  });
@@ -150,11 +85,16 @@ function renderMarkdown(html) {
150
85
  template: html,
151
86
  components: {
152
87
  CodeItem,
88
+ CodeItem2,
89
+ },
90
+ provide() {
91
+ return {
92
+ fetchText: props.fetchText,
93
+ }
153
94
  },
154
95
  data: () => {
155
96
  return {
156
97
  images: local.images,
157
- text: local.text
158
98
  }
159
99
  },
160
100
  methods: {
@@ -217,8 +157,6 @@ async function buildComponent(markdownText) {
217
157
  logger.trace(markdownText)
218
158
 
219
159
  let markdown = removeMarkdownHeader(markdownText)
220
- // markdown = await injectText(markdown)
221
- markdown = await injectTextV2(markdown)
222
160
  logger.trace("markdown after injection")
223
161
  logger.trace(markdown)
224
162
 
@@ -227,10 +165,8 @@ async function buildComponent(markdownText) {
227
165
  html = replaceVueSpecial(html)
228
166
  html = replaceHrefLinks(html, props.adjustHrefPath)
229
167
  html = decorateTableHTML(html)
230
-
231
- // Initialize images object before replacing img links
232
168
  local.images = {}
233
- html = replaceImgLinks(html)
169
+ html = replaceImgLinks(html, local.images)
234
170
 
235
171
  local.markdownComponent = markRaw(renderMarkdown(html))
236
172
  }
@@ -0,0 +1,75 @@
1
+ <script setup>
2
+ /*
3
+ <XMd url="/docs/Introduction.md" />
4
+
5
+ */
6
+
7
+ import {computed, defineProps, reactive, onMounted} from 'vue'
8
+
9
+ import {store as storeMain, api as apiMain } from 'GlobalStore'
10
+ import {getLogger} from "Logging";
11
+
12
+ const props = defineProps({
13
+ url: {type: String, required: true},
14
+ instanceId: {type: String, required: false, default: "default"}
15
+ })
16
+
17
+ // ________________________________________________________________________________
18
+ // LOGGING
19
+ // ________________________________________________________________________________
20
+ const LOG_HEADER = 'xmd.vue'
21
+ const logger = getLogger(LOG_HEADER)
22
+ logger.debug("[INIT]")
23
+
24
+ // ________________________________________________________________________________
25
+ // STATE
26
+ // ________________________________________________________________________________
27
+ const local = reactive({
28
+ id: LOG_HEADER,
29
+ isLoading: false,
30
+ // !# C0CKP1T_START local
31
+ hasError: false,
32
+ fullCode: "N/A",
33
+ // !# C0CKP1T_END local
34
+ })
35
+
36
+
37
+ async function findCode() {
38
+ const registry = storeMain.r[props.instanceId]
39
+
40
+ if (typeof props.url !== 'string' || props.url.length < 3) {
41
+ local.fullCode = `[INVALID_URL] - problem with props.url\n\n`
42
+ local.hasError = true
43
+ return
44
+ }
45
+ logger.debug(`[findCode] - ${props.url}`)
46
+ const resp = await registry.getText( props.url)
47
+ logger.debug(resp)
48
+ if (!resp.isOk) {
49
+ local.fullCode = `[API_ERROR]\n${resp.result}\n\n`
50
+ local.hasError = true
51
+ return
52
+ }
53
+ // Get the full code
54
+ local.fullCode = resp.result
55
+ local.hasError = false
56
+ }
57
+
58
+ // ________________________________________________________________________________
59
+ // INIT
60
+ // ________________________________________________________________________________
61
+ onMounted(() => {
62
+ findCode()
63
+ })
64
+
65
+ </script>
66
+
67
+ <template>
68
+ <div>
69
+ <XMarkdown :v="local.fullCode" />
70
+ </div>
71
+ </template>
72
+
73
+ <style scoped>
74
+
75
+ </style>
@@ -124,19 +124,22 @@ function titleClick() {
124
124
  <template>
125
125
  <div class="x-section">
126
126
 
127
- <div class="row no-select" :class="bgClass">
128
- <div class="col" @click="titleClick">
127
+ <div class="d-flex justify-content-between align-items-center no-select py-1" :class="bgClass">
128
+
129
+ <div class="ms-2 col" @click="titleClick">
129
130
  <span :class="[fontSizeClass, textClass]" class="title"> {{ props.k }} </span>
130
131
  </div>
132
+
131
133
  <div class="col-auto d-flex align-items-center">
132
134
  <slot name="header"></slot>
133
- <button class="btn is-small ms-2" :class="btnClass" @click="titleClick">
135
+ <button class="btn btn-sm ms-2 me-2" :class="btnClass" @click="titleClick">
134
136
  <span class="icon is-small">
135
137
  <i class="fa-solid fa-eye" v-if="local.visible"></i>
136
138
  <i class="fa-solid fa-eye-slash" v-else></i>
137
139
  </span>
138
140
  </button>
139
141
  </div>
142
+
140
143
  </div>
141
144
 
142
145
  <div class="mt-2" v-if="local.visible">