@tailor-cms/tce-preview-runtime 0.5.5-beta.4 → 1.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.js +19 -0
- package/package.json +19 -18
- package/src/App.vue +33 -51
- package/src/components/BottomPanel.vue +1 -2
- package/src/components/MainLayout.vue +29 -21
- package/src/components/PreviewPanel.vue +6 -6
- package/src/components/SplashLoader.vue +1 -1
- package/src/main.ts +0 -10
- package/vite.config.ts +3 -14
- package/.eslintrc.cjs +0 -13
package/eslint.config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import tailorConfig from '@tailor-cms/eslint-config';
|
|
2
|
+
|
|
3
|
+
export default [
|
|
4
|
+
...tailorConfig,
|
|
5
|
+
{
|
|
6
|
+
ignores: ['**/components.d.ts', '**/shims-vue.d.ts'],
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
languageOptions: {
|
|
10
|
+
parserOptions: {
|
|
11
|
+
projectService: true,
|
|
12
|
+
tsconfigRootDir: import.meta.dirname,
|
|
13
|
+
ecmaVersion: 'latest',
|
|
14
|
+
sourceType: 'module',
|
|
15
|
+
extraFileExtensions: ['.vue'],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
];
|
package/package.json
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailor-cms/tce-preview-runtime",
|
|
3
|
-
"
|
|
3
|
+
"description": "Preview content element client side runtime",
|
|
4
|
+
"author": "Studion <info@gostudion.com>",
|
|
5
|
+
"version": "1.1.0-beta.1",
|
|
4
6
|
"type": "module",
|
|
5
7
|
"dependencies": {
|
|
6
|
-
"@lukeed/uuid": "^2.0.1",
|
|
7
8
|
"@mdi/font": "7.4.47",
|
|
8
|
-
"@vitejs/plugin-vue": "5.2.
|
|
9
|
-
"@vueuse/core": "
|
|
9
|
+
"@vitejs/plugin-vue": "5.2.4",
|
|
10
|
+
"@vueuse/core": "13.3.0",
|
|
10
11
|
"date-fns": "^4.1.0",
|
|
11
|
-
"ky": "1.
|
|
12
|
-
"lodash": "^4.17.21",
|
|
13
|
-
"sass": "1.
|
|
12
|
+
"ky": "1.8.1",
|
|
13
|
+
"lodash-es": "^4.17.21",
|
|
14
|
+
"sass": "1.89.1",
|
|
14
15
|
"split.js": "1.6.5",
|
|
15
16
|
"stringify-object": "^5.0.0",
|
|
16
|
-
"typescript": "5.
|
|
17
|
-
"
|
|
18
|
-
"vite": "
|
|
19
|
-
"
|
|
20
|
-
"vue": "
|
|
21
|
-
"
|
|
22
|
-
"
|
|
17
|
+
"typescript": "5.8.3",
|
|
18
|
+
"vite": "6.3.5",
|
|
19
|
+
"vite-plugin-vuetify": "2.1.1",
|
|
20
|
+
"vue": "3.5.16",
|
|
21
|
+
"vue-tsc": "2.2.10",
|
|
22
|
+
"vuetify": "3.8.7",
|
|
23
|
+
"@tailor-cms/cek-common": "1.1.0-beta.1"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
26
|
+
"@types/lodash-es": "^4.17.12",
|
|
25
27
|
"@types/stringify-object": "^4.0.5",
|
|
26
|
-
"
|
|
28
|
+
"eslint": ">=9.28.0",
|
|
29
|
+
"@tailor-cms/eslint-config": "0.0.3"
|
|
27
30
|
},
|
|
28
31
|
"publishConfig": {
|
|
29
32
|
"access": "public"
|
|
30
33
|
},
|
|
31
34
|
"scripts": {
|
|
32
35
|
"dev": "vite",
|
|
33
|
-
"
|
|
34
|
-
"serve": "vite preview",
|
|
35
|
-
"lint": "eslint --ext .ts,.vue .",
|
|
36
|
+
"lint": "eslint .",
|
|
36
37
|
"lint:fix": "pnpm lint --fix"
|
|
37
38
|
}
|
|
38
39
|
}
|
package/src/App.vue
CHANGED
|
@@ -1,74 +1,56 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
getApiClient,
|
|
4
|
+
initWebSocket,
|
|
5
|
+
resolveElementId,
|
|
6
|
+
} from '@tailor-cms/cek-common';
|
|
2
7
|
import { onMounted, ref } from 'vue';
|
|
3
|
-
import
|
|
8
|
+
import type { Element } from '@tailor-cms/cek-common';
|
|
4
9
|
|
|
5
10
|
import AppBar from './components/AppBar.vue';
|
|
6
11
|
import MainLayout from './components/MainLayout.vue';
|
|
7
12
|
|
|
8
|
-
interface ContentElement {
|
|
9
|
-
id: number;
|
|
10
|
-
uid: string;
|
|
11
|
-
}
|
|
12
13
|
const { VITE_SERVER_RUNTIME_URL } = import.meta.env;
|
|
13
14
|
const serverRuntimeUrl = new URL(VITE_SERVER_RUNTIME_URL);
|
|
14
|
-
const
|
|
15
|
-
const api = ky.create({ prefixUrl: apiPrefix });
|
|
16
|
-
const wsProtocol = serverRuntimeUrl.protocol === 'http:' ? 'ws:' : 'wss:';
|
|
15
|
+
const api = getApiClient(VITE_SERVER_RUNTIME_URL);
|
|
17
16
|
|
|
18
|
-
const element = ref<
|
|
17
|
+
const element = ref<Element>();
|
|
19
18
|
const userState = ref({});
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const ws = new WebSocket(
|
|
24
|
-
`${wsProtocol}//${serverRuntimeUrl.host}${apiPrefix}`,
|
|
25
|
-
);
|
|
26
|
-
ws.addEventListener('message', (message) => {
|
|
27
|
-
const event = JSON.parse(message.data);
|
|
28
|
-
const elementUid = element.value?.uid;
|
|
29
|
-
if (elementUid && elementUid !== event.entityId) return;
|
|
30
|
-
if (event.type === 'userState:update') {
|
|
31
|
-
userState.value = event.payload;
|
|
32
|
-
}
|
|
33
|
-
if (event.type === 'element:update') {
|
|
34
|
-
element.value = event.payload;
|
|
35
|
-
}
|
|
36
|
-
if (event.type === 'userContext:change') getElement();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function timeout(ms: number) {
|
|
41
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42
|
-
}
|
|
20
|
+
const timeout = (ms: number) =>
|
|
21
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
22
|
|
|
44
|
-
async function
|
|
23
|
+
async function load(id: string): Promise<any> {
|
|
45
24
|
try {
|
|
46
|
-
const res = await api
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
userState.value = userStateVal;
|
|
25
|
+
const res = await api.getElement(id);
|
|
26
|
+
element.value = res.element;
|
|
27
|
+
userState.value = res.userState;
|
|
50
28
|
} catch (error) {
|
|
51
29
|
console.log('Error on element get', error);
|
|
52
30
|
await timeout(2000);
|
|
53
31
|
console.log('Retrying element get...');
|
|
54
|
-
return
|
|
32
|
+
return load(id);
|
|
55
33
|
}
|
|
56
34
|
}
|
|
57
35
|
|
|
58
|
-
async function resetElement() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await resetState();
|
|
62
|
-
const path = `content-element/${id}/reset-element`;
|
|
63
|
-
return api.post(path);
|
|
36
|
+
async function resetElement(id: string) {
|
|
37
|
+
await api.resetState(id);
|
|
38
|
+
return api.resetElement(id);
|
|
64
39
|
}
|
|
65
40
|
|
|
66
|
-
async
|
|
67
|
-
const
|
|
68
|
-
if (!
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
41
|
+
onMounted(async () => {
|
|
42
|
+
const elementId = resolveElementId();
|
|
43
|
+
if (!elementId) return;
|
|
44
|
+
await load(elementId);
|
|
45
|
+
const ws = initWebSocket(serverRuntimeUrl, elementId);
|
|
46
|
+
ws.addEventListener('message', (message) => {
|
|
47
|
+
const event = JSON.parse(message.data);
|
|
48
|
+
if (elementId && elementId !== event.entityId) return;
|
|
49
|
+
if (event.type === 'userState:update') userState.value = event.payload;
|
|
50
|
+
if (event.type === 'element:update') element.value = event.payload;
|
|
51
|
+
if (event.type === 'userContext:change') load(elementId);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
72
54
|
</script>
|
|
73
55
|
|
|
74
56
|
<template>
|
|
@@ -78,8 +60,8 @@ async function resetState() {
|
|
|
78
60
|
:element="element"
|
|
79
61
|
:user-state="userState"
|
|
80
62
|
class="mt-14"
|
|
81
|
-
@reset-element="resetElement"
|
|
82
|
-
@reset-state="resetState"
|
|
63
|
+
@reset-element="element && resetElement(element.uid)"
|
|
64
|
+
@reset-state="element && api.resetState(element.uid)"
|
|
83
65
|
/>
|
|
84
66
|
</VApp>
|
|
85
67
|
</template>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div>
|
|
3
3
|
<VTabs v-model="state.selectedTab">
|
|
4
4
|
<VTab :value="1">
|
|
5
5
|
<VChip append-icon="mdi-pen" class="mr-2" color="indigo" size="small">
|
|
@@ -58,7 +58,6 @@ import MutationList from './MutationList.vue';
|
|
|
58
58
|
|
|
59
59
|
defineEmits(['resetElement', 'resetState']);
|
|
60
60
|
const props = defineProps<{
|
|
61
|
-
isLoaded: Boolean;
|
|
62
61
|
element: any;
|
|
63
62
|
userState: any;
|
|
64
63
|
}>();
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<main :class="{ 'dark-theme': isDark }">
|
|
3
|
-
<SplashLoader :is-visible="!
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
<SplashLoader :is-visible="!isInitialized" />
|
|
4
|
+
<template v-if="isInitialized">
|
|
5
|
+
<PreviewPanel id="panelTop" :element-id="element.uid" />
|
|
6
|
+
<BottomPanel
|
|
7
|
+
id="panelBottom"
|
|
8
|
+
:element="element"
|
|
9
|
+
:user-state="userState"
|
|
10
|
+
class="preview-panel-bottom"
|
|
11
|
+
@reset-element="$emit('resetElement')"
|
|
12
|
+
@reset-state="$emit('resetState')"
|
|
13
|
+
/>
|
|
14
|
+
</template>
|
|
14
15
|
</main>
|
|
15
16
|
</template>
|
|
16
17
|
|
|
17
18
|
<script setup lang="ts">
|
|
18
|
-
import { onMounted, ref } from 'vue';
|
|
19
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
19
20
|
import Split from 'split.js';
|
|
20
21
|
|
|
21
22
|
import BottomPanel from './BottomPanel.vue';
|
|
@@ -27,18 +28,25 @@ defineEmits(['resetElement', 'resetState']);
|
|
|
27
28
|
const props = defineProps<{ element: any; userState: any }>();
|
|
28
29
|
|
|
29
30
|
const { isDark } = useGlobalState();
|
|
30
|
-
const
|
|
31
|
+
const isMounted = ref(false);
|
|
32
|
+
const isInitialized = computed(() => isMounted.value && props.element?.uid);
|
|
31
33
|
|
|
32
34
|
onMounted(() => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
isLoaded.value = true;
|
|
40
|
-
}, 2500);
|
|
35
|
+
isMounted.value = true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
watch(isInitialized, (val) => {
|
|
39
|
+
if (val) setupPreview();
|
|
41
40
|
});
|
|
41
|
+
|
|
42
|
+
const setupPreview = () => {
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
Split([`#panelTop`, `#panelBottom`], {
|
|
45
|
+
direction: 'vertical',
|
|
46
|
+
sizes: [70, 30],
|
|
47
|
+
});
|
|
48
|
+
}, 50);
|
|
49
|
+
};
|
|
42
50
|
</script>
|
|
43
51
|
|
|
44
52
|
<style>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div class="d-flex flex-grow-1 preview-panel">
|
|
3
3
|
<div :id="PANELS.EDIT" class="d-flex flex-1-1-50">
|
|
4
4
|
<iframe
|
|
5
|
-
:key="`author-${
|
|
6
|
-
:src="VITE_EDIT_RUNTIME_URL"
|
|
5
|
+
:key="`author-${new Date().getTime()}`"
|
|
6
|
+
:src="`${VITE_EDIT_RUNTIME_URL}?id=${elementId}`"
|
|
7
7
|
class="flex-1-1-100"
|
|
8
8
|
frameBorder="0"
|
|
9
9
|
title="Edit component container"
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
<div :id="PANELS.DISPLAY" class="d-flex flex-1-1-50">
|
|
14
14
|
<iframe
|
|
15
|
-
:key="`display-${
|
|
16
|
-
:src="VITE_DISPLAY_RUNTIME_URL"
|
|
15
|
+
:key="`display-${new Date().getTime()}`"
|
|
16
|
+
:src="`${VITE_DISPLAY_RUNTIME_URL}?id=${elementId}`"
|
|
17
17
|
class="flex-1-1-100"
|
|
18
18
|
frameBorder="0"
|
|
19
19
|
title="Display component container"
|
|
@@ -32,7 +32,7 @@ import { PANELS, useGlobalState } from '../state';
|
|
|
32
32
|
const { VITE_DISPLAY_RUNTIME_URL, VITE_EDIT_RUNTIME_URL } = import.meta.env;
|
|
33
33
|
const { previewPanelSplit } = useGlobalState();
|
|
34
34
|
|
|
35
|
-
defineProps<{
|
|
35
|
+
defineProps<{ elementId: string }>();
|
|
36
36
|
|
|
37
37
|
const initPanels = () => {
|
|
38
38
|
previewPanelSplit.value = Split([`#${PANELS.EDIT}`, `#${PANELS.DISPLAY}`], {
|
package/src/main.ts
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import '@mdi/font/css/materialdesignicons.css';
|
|
2
2
|
import 'vuetify/styles';
|
|
3
3
|
|
|
4
|
-
import Cookies from 'universal-cookie';
|
|
5
4
|
import { createApp } from 'vue';
|
|
6
5
|
import { createVuetify } from 'vuetify';
|
|
7
|
-
import { v4 as uuid } from '@lukeed/uuid/secure';
|
|
8
6
|
|
|
9
7
|
import App from './App.vue';
|
|
10
8
|
|
|
11
|
-
const CLIENT_ID_KEY = 'cekClientId';
|
|
12
|
-
const { VITE_CODESPACES_DOMAIN: codespaceDomain } = import.meta.env;
|
|
13
|
-
const cookieOptions: any = { path: '/' };
|
|
14
|
-
if (codespaceDomain) cookieOptions.domain = codespaceDomain;
|
|
15
|
-
|
|
16
|
-
const cookies = new Cookies(null, cookieOptions);
|
|
17
|
-
if (!cookies.get(CLIENT_ID_KEY)) cookies.set(CLIENT_ID_KEY, uuid());
|
|
18
|
-
|
|
19
9
|
createApp(App)
|
|
20
10
|
.use(createVuetify({ icons: { defaultSet: 'mdi' } }))
|
|
21
11
|
.mount('#app');
|
package/vite.config.ts
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { defineConfig } from 'vite';
|
|
2
|
-
import pick from 'lodash
|
|
2
|
+
import { pick } from 'lodash-es';
|
|
3
3
|
import vue from '@vitejs/plugin-vue';
|
|
4
4
|
import vuetify from 'vite-plugin-vuetify';
|
|
5
5
|
|
|
6
|
-
const env = process
|
|
6
|
+
const { env } = process;
|
|
7
7
|
const config = pick(env, [
|
|
8
8
|
'EDIT_RUNTIME_URL',
|
|
9
9
|
'DISPLAY_RUNTIME_URL',
|
|
10
10
|
'SERVER_RUNTIME_URL',
|
|
11
|
-
'PREVIEW_RUNTIME_URL',
|
|
12
11
|
'PREVIEW_RUNTIME_PORT',
|
|
13
12
|
]);
|
|
14
13
|
|
|
15
14
|
// Expose to Frontend by prefixing with VITE_
|
|
16
|
-
Object.entries(config).forEach(
|
|
17
|
-
([k, v]) => (process.env[`VITE_${k}`] = v as string),
|
|
18
|
-
);
|
|
15
|
+
Object.entries(config).forEach(([k, v]) => (process.env[`VITE_${k}`] = v));
|
|
19
16
|
|
|
20
17
|
// https://vitejs.dev/config/
|
|
21
18
|
export default defineConfig((): any => {
|
|
@@ -25,14 +22,6 @@ export default defineConfig((): any => {
|
|
|
25
22
|
// Accept connections from any host (Docker)
|
|
26
23
|
host: '0.0.0.0',
|
|
27
24
|
port: parseInt(config.PREVIEW_RUNTIME_PORT, 10),
|
|
28
|
-
proxy: {
|
|
29
|
-
'/tce-server': {
|
|
30
|
-
target: config.SERVER_RUNTIME_URL,
|
|
31
|
-
ws: true,
|
|
32
|
-
changeOrigin: true,
|
|
33
|
-
rewrite: (path) => path.replace(/^\/tce-server/, ''),
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
25
|
},
|
|
37
26
|
logLevel: 'warn',
|
|
38
27
|
plugins: [vue(), vuetify({ autoImport: true })],
|
package/.eslintrc.cjs
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
extends: '@tailor-cms/eslint-config',
|
|
4
|
-
parserOptions: {
|
|
5
|
-
tsconfigRootDir: __dirname,
|
|
6
|
-
ecmaVersion: 2022,
|
|
7
|
-
sourceType: 'module',
|
|
8
|
-
parser: '@typescript-eslint/parser',
|
|
9
|
-
project: ['./tsconfig.json', './tsconfig.node.json'],
|
|
10
|
-
extraFileExtensions: ['vue'],
|
|
11
|
-
},
|
|
12
|
-
ignorePatterns: ['**/components.d.ts', '**/shims-vue.d.ts'],
|
|
13
|
-
};
|