@cloudron/pankow 4.4.1 → 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/SplitLayout.vue +146 -34
- 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',
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
import { ref, computed, watch, onMounted, onUnmounted, useTemplateRef } from 'vue';
|
|
3
3
|
|
|
4
4
|
const props = defineProps({
|
|
5
|
+
orientation: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: 'horizontal'
|
|
8
|
+
},
|
|
9
|
+
// horizontal mode props
|
|
5
10
|
leftWidth: {
|
|
6
11
|
type: Number,
|
|
7
12
|
default: 50
|
|
@@ -13,40 +18,88 @@ const props = defineProps({
|
|
|
13
18
|
minRightWidth: {
|
|
14
19
|
type: Number,
|
|
15
20
|
default: 20
|
|
21
|
+
},
|
|
22
|
+
// vertical mode props
|
|
23
|
+
topHeight: {
|
|
24
|
+
type: Number,
|
|
25
|
+
default: 50
|
|
26
|
+
},
|
|
27
|
+
minTopHeight: {
|
|
28
|
+
type: Number,
|
|
29
|
+
default: 20
|
|
30
|
+
},
|
|
31
|
+
minBottomHeight: {
|
|
32
|
+
type: Number,
|
|
33
|
+
default: 20
|
|
34
|
+
},
|
|
35
|
+
opaque: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: false
|
|
16
38
|
}
|
|
17
39
|
});
|
|
18
40
|
|
|
19
|
-
const emit = defineEmits(['update:leftWidth']);
|
|
41
|
+
const emit = defineEmits(['update:leftWidth', 'update:topHeight']);
|
|
42
|
+
|
|
43
|
+
const isHorizontal = computed(() => props.orientation === 'horizontal');
|
|
20
44
|
|
|
21
45
|
const containerRef = useTemplateRef('container');
|
|
22
46
|
const isDragging = ref(false);
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
47
|
+
const dragStartCoord = ref(0);
|
|
48
|
+
const dragStartSize = ref(0);
|
|
49
|
+
|
|
50
|
+
const currentFirstSize = ref(
|
|
51
|
+
isHorizontal.value ? props.leftWidth : props.topHeight
|
|
52
|
+
);
|
|
26
53
|
|
|
27
54
|
watch(() => props.leftWidth, (val) => {
|
|
55
|
+
if (!isDragging.value && isHorizontal.value) {
|
|
56
|
+
currentFirstSize.value = val;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
watch(() => props.topHeight, (val) => {
|
|
61
|
+
if (!isDragging.value && !isHorizontal.value) {
|
|
62
|
+
currentFirstSize.value = val;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
watch(() => props.orientation, () => {
|
|
28
67
|
if (!isDragging.value) {
|
|
29
|
-
|
|
68
|
+
currentFirstSize.value = isHorizontal.value ? props.leftWidth : props.topHeight;
|
|
30
69
|
}
|
|
31
70
|
});
|
|
32
71
|
|
|
72
|
+
const minFirst = computed(() =>
|
|
73
|
+
isHorizontal.value ? props.minLeftWidth : props.minTopHeight
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const minSecond = computed(() =>
|
|
77
|
+
isHorizontal.value ? props.minRightWidth : props.minBottomHeight
|
|
78
|
+
);
|
|
79
|
+
|
|
33
80
|
function startDrag(e) {
|
|
34
81
|
isDragging.value = true;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
82
|
+
dragStartCoord.value = isHorizontal.value
|
|
83
|
+
? (e.clientX ?? e.touches?.[0]?.clientX ?? 0)
|
|
84
|
+
: (e.clientY ?? e.touches?.[0]?.clientY ?? 0);
|
|
85
|
+
dragStartSize.value = currentFirstSize.value;
|
|
86
|
+
document.body.style.cursor = isHorizontal.value ? 'col-resize' : 'row-resize';
|
|
38
87
|
document.body.style.userSelect = 'none';
|
|
39
88
|
e.preventDefault();
|
|
40
89
|
}
|
|
41
90
|
|
|
42
91
|
function onDrag(e) {
|
|
43
92
|
if (!isDragging.value || !containerRef.value) return;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
93
|
+
const coord = isHorizontal.value
|
|
94
|
+
? (e.clientX ?? e.touches?.[0]?.clientX ?? 0)
|
|
95
|
+
: (e.clientY ?? e.touches?.[0]?.clientY ?? 0);
|
|
96
|
+
const containerSize = isHorizontal.value
|
|
97
|
+
? containerRef.value.getBoundingClientRect().width
|
|
98
|
+
: containerRef.value.getBoundingClientRect().height;
|
|
99
|
+
if (containerSize === 0) return;
|
|
100
|
+
const delta = ((coord - dragStartCoord.value) / containerSize) * 100;
|
|
101
|
+
const newSize = dragStartSize.value + delta;
|
|
102
|
+
currentFirstSize.value = Math.max(minFirst.value, Math.min(100 - minSecond.value, newSize));
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
function stopDrag() {
|
|
@@ -54,7 +107,11 @@ function stopDrag() {
|
|
|
54
107
|
isDragging.value = false;
|
|
55
108
|
document.body.style.cursor = '';
|
|
56
109
|
document.body.style.userSelect = '';
|
|
57
|
-
|
|
110
|
+
if (isHorizontal.value) {
|
|
111
|
+
emit('update:leftWidth', Math.round(currentFirstSize.value));
|
|
112
|
+
} else {
|
|
113
|
+
emit('update:topHeight', Math.round(currentFirstSize.value));
|
|
114
|
+
}
|
|
58
115
|
}
|
|
59
116
|
|
|
60
117
|
onMounted(() => {
|
|
@@ -71,21 +128,35 @@ onUnmounted(() => {
|
|
|
71
128
|
document.removeEventListener('touchend', stopDrag);
|
|
72
129
|
});
|
|
73
130
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
131
|
+
const firstStyle = computed(() => {
|
|
132
|
+
const size = currentFirstSize.value;
|
|
133
|
+
return isHorizontal.value
|
|
134
|
+
? { flex: `0 0 ${size}%`, maxWidth: `${size}%` }
|
|
135
|
+
: { flex: `0 0 ${size}%`, maxHeight: `${size}%` };
|
|
136
|
+
});
|
|
78
137
|
</script>
|
|
79
138
|
|
|
80
139
|
<template>
|
|
81
|
-
<div
|
|
82
|
-
|
|
140
|
+
<div
|
|
141
|
+
ref="container"
|
|
142
|
+
class="pankow-split-layout"
|
|
143
|
+
:class="{
|
|
144
|
+
'pankow-split-layout-dragging': isDragging,
|
|
145
|
+
'pankow-split-layout-vertical': !isHorizontal,
|
|
146
|
+
'pankow-split-layout-divider-opaque': opaque
|
|
147
|
+
}"
|
|
148
|
+
>
|
|
149
|
+
<div class="pankow-split-layout-first" :style="firstStyle">
|
|
83
150
|
<slot name="left" />
|
|
84
151
|
</div>
|
|
85
|
-
<div
|
|
152
|
+
<div
|
|
153
|
+
class="pankow-split-layout-divider"
|
|
154
|
+
@mousedown="startDrag"
|
|
155
|
+
@touchstart.prevent="startDrag"
|
|
156
|
+
>
|
|
86
157
|
<div class="pankow-split-layout-handle"><span></span><span></span></div>
|
|
87
158
|
</div>
|
|
88
|
-
<div class="pankow-split-layout-
|
|
159
|
+
<div class="pankow-split-layout-second">
|
|
89
160
|
<slot name="right" />
|
|
90
161
|
</div>
|
|
91
162
|
</div>
|
|
@@ -100,15 +171,22 @@ const leftStyle = computed(() => ({
|
|
|
100
171
|
overflow: hidden;
|
|
101
172
|
}
|
|
102
173
|
|
|
103
|
-
.pankow-split-layout-
|
|
104
|
-
|
|
174
|
+
.pankow-split-layout-vertical {
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.pankow-split-layout-first {
|
|
105
179
|
min-width: 0;
|
|
106
180
|
overflow: hidden;
|
|
107
181
|
}
|
|
108
182
|
|
|
183
|
+
.pankow-split-layout-vertical .pankow-split-layout-first {
|
|
184
|
+
height: auto;
|
|
185
|
+
min-height: 0;
|
|
186
|
+
min-width: 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
109
189
|
.pankow-split-layout-divider {
|
|
110
|
-
width: 6px;
|
|
111
|
-
cursor: col-resize;
|
|
112
190
|
background: transparent;
|
|
113
191
|
position: relative;
|
|
114
192
|
flex-shrink: 0;
|
|
@@ -117,6 +195,20 @@ const leftStyle = computed(() => ({
|
|
|
117
195
|
justify-content: center;
|
|
118
196
|
}
|
|
119
197
|
|
|
198
|
+
.pankow-split-layout:not(.pankow-split-layout-vertical) .pankow-split-layout-divider {
|
|
199
|
+
width: 6px;
|
|
200
|
+
cursor: col-resize;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.pankow-split-layout-vertical .pankow-split-layout-divider {
|
|
204
|
+
height: 6px;
|
|
205
|
+
cursor: row-resize;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.pankow-split-layout-divider-opaque .pankow-split-layout-divider {
|
|
209
|
+
background: var(--pankow-color-background);
|
|
210
|
+
}
|
|
211
|
+
|
|
120
212
|
.pankow-split-layout-divider:hover,
|
|
121
213
|
.pankow-split-layout-dragging .pankow-split-layout-divider {
|
|
122
214
|
background: var(--pankow-input-border-color);
|
|
@@ -128,13 +220,20 @@ const leftStyle = computed(() => ({
|
|
|
128
220
|
left: 50%;
|
|
129
221
|
transform: translate(-50%, -50%);
|
|
130
222
|
display: flex;
|
|
131
|
-
flex-direction: column;
|
|
132
223
|
gap: 2px;
|
|
133
224
|
opacity: 0;
|
|
134
225
|
transition: opacity 0.15s;
|
|
135
226
|
pointer-events: none;
|
|
136
227
|
}
|
|
137
228
|
|
|
229
|
+
.pankow-split-layout:not(.pankow-split-layout-vertical) .pankow-split-layout-handle {
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.pankow-split-layout-vertical .pankow-split-layout-handle {
|
|
234
|
+
flex-direction: row;
|
|
235
|
+
}
|
|
236
|
+
|
|
138
237
|
.pankow-split-layout-divider:hover .pankow-split-layout-handle,
|
|
139
238
|
.pankow-split-layout-dragging .pankow-split-layout-handle {
|
|
140
239
|
opacity: 0.5;
|
|
@@ -142,21 +241,34 @@ const leftStyle = computed(() => ({
|
|
|
142
241
|
|
|
143
242
|
.pankow-split-layout-handle span {
|
|
144
243
|
display: block;
|
|
145
|
-
width: 2px;
|
|
146
|
-
height: 16px;
|
|
147
244
|
border-radius: 1px;
|
|
148
245
|
background: var(--pankow-color-text);
|
|
149
246
|
}
|
|
150
247
|
|
|
151
|
-
.pankow-split-layout-
|
|
248
|
+
.pankow-split-layout:not(.pankow-split-layout-vertical) .pankow-split-layout-handle span {
|
|
249
|
+
width: 2px;
|
|
250
|
+
height: 16px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.pankow-split-layout-vertical .pankow-split-layout-handle span {
|
|
254
|
+
width: 16px;
|
|
255
|
+
height: 2px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.pankow-split-layout-second {
|
|
152
259
|
flex: 1;
|
|
153
|
-
height: 100%;
|
|
154
260
|
min-width: 0;
|
|
155
261
|
overflow: hidden;
|
|
156
262
|
}
|
|
157
263
|
|
|
158
|
-
.pankow-split-layout-
|
|
159
|
-
|
|
264
|
+
.pankow-split-layout-vertical .pankow-split-layout-second {
|
|
265
|
+
height: auto;
|
|
266
|
+
min-height: 0;
|
|
267
|
+
min-width: 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.pankow-split-layout-dragging .pankow-split-layout-first,
|
|
271
|
+
.pankow-split-layout-dragging .pankow-split-layout-second {
|
|
160
272
|
pointer-events: none;
|
|
161
273
|
}
|
|
162
274
|
</style>
|
|
@@ -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
|
}
|