@cloudron/pankow 4.4.2 → 4.4.3
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/components/ClipboardAction.vue +5 -2
- package/components/ClipboardButton.vue +5 -1
- package/components/VersionCheckBanner.vue +99 -0
- package/package.json +1 -1
- package/plugin.js +20 -7
- package/types/utils.d.ts +5 -2
- package/utils.js +5 -3
- package/vite-plugin.js +48 -2
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
|
|
3
3
|
import { copyToClipboard } from '../utils';
|
|
4
|
-
import Button from './Button.vue';
|
|
5
4
|
|
|
6
5
|
const props = defineProps({
|
|
7
6
|
value: String,
|
|
8
7
|
doneHint: String,
|
|
8
|
+
multiline: {
|
|
9
|
+
type: Boolean,
|
|
10
|
+
default: false
|
|
11
|
+
},
|
|
9
12
|
});
|
|
10
13
|
|
|
11
14
|
function onClipboard() {
|
|
12
|
-
copyToClipboard(props.value);
|
|
15
|
+
copyToClipboard(props.value, { multiline: props.multiline });
|
|
13
16
|
window.pankow.notify({
|
|
14
17
|
text: props.doneHint || 'Copied',
|
|
15
18
|
type: 'success',
|
|
@@ -6,10 +6,14 @@ import Button from './Button.vue';
|
|
|
6
6
|
const props = defineProps({
|
|
7
7
|
value: String,
|
|
8
8
|
doneHint: String,
|
|
9
|
+
multiline: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false
|
|
12
|
+
},
|
|
9
13
|
});
|
|
10
14
|
|
|
11
15
|
function onClipboard() {
|
|
12
|
-
copyToClipboard(props.value);
|
|
16
|
+
copyToClipboard(props.value, { multiline: props.multiline });
|
|
13
17
|
window.pankow.notify({
|
|
14
18
|
text: props.doneHint || 'Copied',
|
|
15
19
|
type: 'success',
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Transition name="pankow-version-banner">
|
|
3
|
+
<div v-if="mismatch" class="pankow-version-banner">
|
|
4
|
+
<span>A new version is available.</span>
|
|
5
|
+
<button class="pankow-version-banner-reload" @click="reload">Reload</button>
|
|
6
|
+
<button class="pankow-version-banner-close" @click="dismiss" aria-label="Dismiss">✕</button>
|
|
7
|
+
</div>
|
|
8
|
+
</Transition>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted, onUnmounted } from 'vue';
|
|
13
|
+
|
|
14
|
+
const mismatch = ref(false);
|
|
15
|
+
|
|
16
|
+
function setMismatch() {
|
|
17
|
+
mismatch.value = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function reload() {
|
|
21
|
+
window.location.reload(true);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function dismiss() {
|
|
25
|
+
mismatch.value = false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function checkFlag() {
|
|
29
|
+
if (window.__pankow_version_mismatch) setMismatch();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onMounted(() => {
|
|
33
|
+
if (window.__pankow_version_mismatch) setMismatch();
|
|
34
|
+
window.addEventListener('pankow:version-mismatch', checkFlag);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
onUnmounted(() => {
|
|
38
|
+
window.removeEventListener('pankow:version-mismatch', checkFlag);
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.pankow-version-banner {
|
|
44
|
+
position: fixed;
|
|
45
|
+
top: 0;
|
|
46
|
+
left: 0;
|
|
47
|
+
right: 0;
|
|
48
|
+
z-index: 30002;
|
|
49
|
+
display: flex;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: 16px;
|
|
53
|
+
padding: 10px 24px;
|
|
54
|
+
background: var(--pankow-color-primary);
|
|
55
|
+
color: white;
|
|
56
|
+
font-family: var(--pankow-font-family, Inter, sans-serif);
|
|
57
|
+
font-size: 13px;
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.pankow-version-banner-reload {
|
|
63
|
+
background: rgba(255, 255, 255, 0.2);
|
|
64
|
+
border: 1px solid rgba(255, 255, 255, 0.4);
|
|
65
|
+
color: white;
|
|
66
|
+
padding: 4px 12px;
|
|
67
|
+
border-radius: var(--pankow-border-radius, 4px);
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
font: inherit;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.pankow-version-banner-reload:hover {
|
|
73
|
+
background: rgba(255, 255, 255, 0.3);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.pankow-version-banner-close {
|
|
77
|
+
background: none;
|
|
78
|
+
border: none;
|
|
79
|
+
color: rgba(255, 255, 255, 0.7);
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
font-size: 14px;
|
|
82
|
+
padding: 0 4px;
|
|
83
|
+
line-height: 1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.pankow-version-banner-close:hover {
|
|
87
|
+
color: white;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.pankow-version-banner-enter-active,
|
|
91
|
+
.pankow-version-banner-leave-active {
|
|
92
|
+
transition: transform 0.3s ease;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.pankow-version-banner-enter-from,
|
|
96
|
+
.pankow-version-banner-leave-to {
|
|
97
|
+
transform: translateY(-100%);
|
|
98
|
+
}
|
|
99
|
+
</style>
|
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -5,8 +5,10 @@ import textOverflow from './text-overflow.js';
|
|
|
5
5
|
import fetcher from './fetcher.js';
|
|
6
6
|
import { setNotifyPosition } from './notifyState.js';
|
|
7
7
|
import Notification from './components/Notification.vue';
|
|
8
|
+
import VersionCheckBanner from './components/VersionCheckBanner.vue';
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const NOTIFICATION_CONTAINER_ID = 'pankow-notification-root';
|
|
11
|
+
const VERSION_BANNER_CONTAINER_ID = 'pankow-version-banner-root';
|
|
10
12
|
|
|
11
13
|
export default {
|
|
12
14
|
install(app, options = {}) {
|
|
@@ -28,14 +30,25 @@ export default {
|
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
if (typeof document !== 'undefined' && !import.meta.env.SSR) {
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
+
const existingNotification = document.getElementById(NOTIFICATION_CONTAINER_ID);
|
|
34
|
+
if (existingNotification) existingNotification.remove();
|
|
33
35
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
document.body.appendChild(
|
|
36
|
+
const notificationContainer = document.createElement('div');
|
|
37
|
+
notificationContainer.id = NOTIFICATION_CONTAINER_ID;
|
|
38
|
+
document.body.appendChild(notificationContainer);
|
|
37
39
|
|
|
38
|
-
createApp(Notification).mount(
|
|
40
|
+
createApp(Notification).mount(notificationContainer);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof document !== 'undefined' && !import.meta.env.SSR) {
|
|
44
|
+
const existingBanner = document.getElementById(VERSION_BANNER_CONTAINER_ID);
|
|
45
|
+
if (existingBanner) existingBanner.remove();
|
|
46
|
+
|
|
47
|
+
const bannerContainer = document.createElement('div');
|
|
48
|
+
bannerContainer.id = VERSION_BANNER_CONTAINER_ID;
|
|
49
|
+
document.body.appendChild(bannerContainer);
|
|
50
|
+
|
|
51
|
+
createApp(VersionCheckBanner).mount(bannerContainer);
|
|
39
52
|
}
|
|
40
53
|
}
|
|
41
54
|
};
|
package/types/utils.d.ts
CHANGED
|
@@ -127,12 +127,15 @@ declare function getExtension(entry: {
|
|
|
127
127
|
fileName: string;
|
|
128
128
|
}): string;
|
|
129
129
|
/**
|
|
130
|
-
* Copies a string to the system clipboard using a temporary
|
|
130
|
+
* Copies a string to the system clipboard using a temporary form element.
|
|
131
131
|
* @param {string} value
|
|
132
|
+
* @param {{ multiline?: boolean }} [options] - When multiline is true, uses a textarea so newlines are preserved
|
|
132
133
|
* @returns {void}
|
|
133
134
|
* @deprecated Prefer the native `navigator.clipboard.writeText()` API.
|
|
134
135
|
*/
|
|
135
|
-
declare function copyToClipboard(value: string
|
|
136
|
+
declare function copyToClipboard(value: string, options?: {
|
|
137
|
+
multiline?: boolean;
|
|
138
|
+
}): void;
|
|
136
139
|
/**
|
|
137
140
|
* Parses the current page's URL query string into a key/value object.
|
|
138
141
|
* @returns {Object<string, string>}
|
package/utils.js
CHANGED
|
@@ -332,13 +332,15 @@ function getExtension(entry) {
|
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
|
-
* Copies a string to the system clipboard using a temporary
|
|
335
|
+
* Copies a string to the system clipboard using a temporary form element.
|
|
336
336
|
* @param {string} value
|
|
337
|
+
* @param {{ multiline?: boolean }} [options] - When multiline is true, uses a textarea so newlines are preserved
|
|
337
338
|
* @returns {void}
|
|
338
339
|
* @deprecated Prefer the native `navigator.clipboard.writeText()` API.
|
|
339
340
|
*/
|
|
340
|
-
function copyToClipboard(value) {
|
|
341
|
-
var
|
|
341
|
+
function copyToClipboard(value, options) {
|
|
342
|
+
var multiline = options && options.multiline;
|
|
343
|
+
var elem = document.createElement(multiline ? 'textarea' : 'input');
|
|
342
344
|
elem.value = value;
|
|
343
345
|
document.body.append(elem);
|
|
344
346
|
elem.select();
|
package/vite-plugin.js
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
import { dirname, resolve } from 'node:path';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
3
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
4
4
|
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
|
|
7
7
|
const PANKOW_PACKAGE = '@cloudron/pankow';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const versionCheckScript = `<script>
|
|
10
|
+
(function () {
|
|
11
|
+
var meta = document.querySelector('meta[name="build-id"]');
|
|
12
|
+
if (!meta) return;
|
|
13
|
+
|
|
14
|
+
var id = meta.content;
|
|
15
|
+
|
|
16
|
+
function check() {
|
|
17
|
+
fetch('/build.json')
|
|
18
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
19
|
+
.then(function (d) {
|
|
20
|
+
if (d && d.buildId && d.buildId !== id) {
|
|
21
|
+
window.__pankow_version_mismatch = true;
|
|
22
|
+
window.dispatchEvent(new CustomEvent('pankow:version-mismatch'));
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
.catch(function () {});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setInterval(check, 30000);
|
|
29
|
+
|
|
30
|
+
document.addEventListener('visibilitychange', function () {
|
|
31
|
+
if (document.visibilityState === 'visible') check();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
setTimeout(check, 5000);
|
|
35
|
+
})();
|
|
36
|
+
</script>`;
|
|
37
|
+
|
|
38
|
+
export default function pankowPlugin({ versionCheck = false } = {}) {
|
|
39
|
+
let buildId = null;
|
|
40
|
+
let config = null;
|
|
41
|
+
|
|
10
42
|
return {
|
|
11
43
|
name: 'vite-plugin-pankow',
|
|
12
44
|
config(config) {
|
|
@@ -21,6 +53,9 @@ export default function pankowPlugin() {
|
|
|
21
53
|
},
|
|
22
54
|
};
|
|
23
55
|
},
|
|
56
|
+
configResolved(resolvedConfig) {
|
|
57
|
+
config = resolvedConfig;
|
|
58
|
+
},
|
|
24
59
|
configureServer(server) {
|
|
25
60
|
const allow = server.config.server.fs.allow;
|
|
26
61
|
if (Array.isArray(allow) && !allow.includes(__dirname)) {
|
|
@@ -36,5 +71,16 @@ export default function pankowPlugin() {
|
|
|
36
71
|
}
|
|
37
72
|
}
|
|
38
73
|
},
|
|
74
|
+
transformIndexHtml(html) {
|
|
75
|
+
if (!versionCheck) return html;
|
|
76
|
+
buildId = Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8);
|
|
77
|
+
return html.replace('</head>', `<meta name="build-id" content="${buildId}">\n${versionCheckScript}\n</head>`);
|
|
78
|
+
},
|
|
79
|
+
closeBundle() {
|
|
80
|
+
if (!versionCheck || !buildId || !config) return;
|
|
81
|
+
const outDir = config.build.outDir || 'dist';
|
|
82
|
+
const outPath = resolve(config.root, outDir, 'build.json');
|
|
83
|
+
writeFileSync(outPath, JSON.stringify({ buildId }));
|
|
84
|
+
},
|
|
39
85
|
};
|
|
40
86
|
}
|