@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.
@@ -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
- "version": "0.5.5-beta.4",
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.1",
9
- "@vueuse/core": "12.5.0",
9
+ "@vitejs/plugin-vue": "5.2.4",
10
+ "@vueuse/core": "13.3.0",
10
11
  "date-fns": "^4.1.0",
11
- "ky": "1.7.4",
12
- "lodash": "^4.17.21",
13
- "sass": "1.84.0",
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.7.3",
17
- "universal-cookie": "^7.2.2",
18
- "vite": "6.1.0",
19
- "vite-plugin-vuetify": "2.1.0",
20
- "vue": "3.5.13",
21
- "vue-tsc": "2.2.0",
22
- "vuetify": "3.7.11"
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
- "@tailor-cms/eslint-config": "0.0.2"
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
- "build": "vue-tsc --noEmit && vite build",
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 ky from 'ky';
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 apiPrefix = '/tce-server';
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<ContentElement>();
17
+ const element = ref<Element>();
19
18
  const userState = ref({});
20
19
 
21
- onMounted(async () => {
22
- await getElement();
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 getElement(): Promise<any> {
23
+ async function load(id: string): Promise<any> {
45
24
  try {
46
- const res = await api('content-element').json();
47
- const { element: elementVal, userState: userStateVal } = res as any;
48
- element.value = elementVal;
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 getElement();
32
+ return load(id);
55
33
  }
56
34
  }
57
35
 
58
- async function resetElement() {
59
- const id = element.value?.id;
60
- if (!id) return;
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 function resetState() {
67
- const id = element.value?.id;
68
- if (!id) return;
69
- const path = `content-element/${id}/reset-state`;
70
- return api.post(path);
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 v-show="isLoaded">
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="!isLoaded" />
4
- <PreviewPanel id="panelTop" :is-loaded="isLoaded" />
5
- <BottomPanel
6
- id="panelBottom"
7
- :element="props.element"
8
- :is-loaded="isLoaded"
9
- :user-state="props.userState"
10
- class="preview-panel-bottom"
11
- @reset-element="$emit('resetElement')"
12
- @reset-state="$emit('resetState')"
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 isLoaded = ref(false);
31
+ const isMounted = ref(false);
32
+ const isInitialized = computed(() => isMounted.value && props.element?.uid);
31
33
 
32
34
  onMounted(() => {
33
- // Client/server vertical split
34
- Split([`#panelTop`, `#panelBottom`], {
35
- direction: 'vertical',
36
- sizes: [70, 30],
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 v-show="isLoaded" class="d-flex flex-grow-1 preview-panel">
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-${isLoaded ? new Date().getTime() : 0}`"
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-${isLoaded ? new Date().getTime() : 0}`"
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<{ isLoaded: Boolean }>();
35
+ defineProps<{ elementId: string }>();
36
36
 
37
37
  const initPanels = () => {
38
38
  previewPanelSplit.value = Split([`#${PANELS.EDIT}`, `#${PANELS.DISPLAY}`], {
@@ -21,7 +21,7 @@ import logoUrl from '../assets/logo-new.svg';
21
21
  import { version } from './../../package.json';
22
22
 
23
23
  interface Props {
24
- isVisible: boolean;
24
+ isVisible?: boolean;
25
25
  logo?: string;
26
26
  color?: string;
27
27
  backgroundColor?: string;
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/pick';
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.env;
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
- };