@warkypublic/svelix 0.1.44 → 0.1.47
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/README.md +93 -33
- package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
- package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
- package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
- package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
- package/dist/components/Boxer/Boxer.svelte +488 -602
- package/dist/components/Boxer/BoxerTarget.svelte +6 -46
- package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
- package/dist/components/Boxer/types.d.ts +0 -1
- package/dist/components/Button.svelte +19 -43
- package/dist/components/ButtonPreview.svelte +2 -11
- package/dist/components/CardGrid/CardGrid.svelte +1141 -0
- package/dist/components/CardGrid/CardGrid.svelte.d.ts +61 -0
- package/dist/components/CardGrid/CardGridFilterPanel.svelte +320 -0
- package/dist/components/CardGrid/CardGridFilterPanel.svelte.d.ts +11 -0
- package/dist/components/CardGrid/DefaultCard.svelte +287 -0
- package/dist/components/CardGrid/DefaultCard.svelte.d.ts +10 -0
- package/dist/components/CardGrid/ImageCardStory.svelte +308 -0
- package/dist/components/CardGrid/ImageCardStory.svelte.d.ts +18 -0
- package/dist/components/CardGrid/cardGrid.d.ts +33 -0
- package/dist/components/CardGrid/cardGrid.js +21 -0
- package/dist/components/CardGrid/index.d.ts +4 -0
- package/dist/components/CardGrid/index.js +4 -0
- package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
- package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
- package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
- package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
- package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
- package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
- package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
- package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
- package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
- package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
- package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
- package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
- package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
- package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
- package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
- package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
- package/dist/components/Former/Former.svelte +306 -424
- package/dist/components/Former/FormerButtonArea.svelte +17 -68
- package/dist/components/Former/FormerDrawer.svelte +26 -78
- package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
- package/dist/components/Former/FormerModal.svelte +24 -70
- package/dist/components/Former/FormerModalPreview.svelte +99 -89
- package/dist/components/Former/FormerPreview.svelte +76 -78
- package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
- package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -44
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +280 -445
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
- package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -42
- package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
- package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
- package/dist/components/FormerControllers/NativeSelectCtrl.svelte +3 -29
- package/dist/components/FormerControllers/NumberInputCtrl.svelte +9 -35
- package/dist/components/FormerControllers/PasswordInputCtrl.svelte +4 -23
- package/dist/components/FormerControllers/SwitchCtrl.svelte +3 -20
- package/dist/components/FormerControllers/TextAreaCtrl.svelte +3 -24
- package/dist/components/FormerControllers/TextInputCtrl.svelte +3 -24
- package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
- package/dist/components/Gridler/components/Gridler.svelte +338 -442
- package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
- package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
- package/dist/components/Gridler/components/GridlerFull.svelte +520 -841
- package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
- package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
- package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
- package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
- package/dist/components/Gridler/types.d.ts +1 -1
- package/dist/components/Icons/IconAdd.svelte +1 -3
- package/dist/components/Icons/IconAlertCircle.svelte +1 -3
- package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
- package/dist/components/Icons/IconCamera.svelte +1 -3
- package/dist/components/Icons/IconClose.svelte +1 -3
- package/dist/components/Icons/IconEdit.svelte +1 -3
- package/dist/components/Icons/IconFilter.svelte +1 -3
- package/dist/components/Icons/IconSearch.svelte +1 -3
- package/dist/components/Icons/IconSort.svelte +1 -3
- package/dist/components/Icons/IconTrash.svelte +1 -3
- package/dist/components/Portal/Portal.svelte +14 -38
- package/dist/components/Screenshot/Screenshot.svelte +7 -13
- package/dist/components/Svark/Svark.svelte +680 -852
- package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
- package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
- package/dist/components/Svark/SvarkPager.svelte +2 -28
- package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
- package/dist/components/Svark/SvarkTopBar.svelte +9 -74
- package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
- package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
- package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
- package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
- package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
- package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
- package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
- package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
- package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
- package/dist/components/VTree/VTree.svelte +349 -431
- package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
- package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
- package/dist/components/VTree/VTreeRow.svelte +13 -65
- package/dist/components/VTree/VTreeSearch.svelte +6 -13
- package/dist/components/VTree/VTreeVirtualViewport.svelte +37 -106
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/llm/COMPONENT_GUIDE.md +240 -0
- package/llm/plans/card_grid.plan.md +305 -0
- package/package.json +48 -46
|
@@ -1,312 +1,247 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
);
|
|
246
|
-
frameEl.contentWindow.postMessage(
|
|
247
|
-
JSON.stringify({
|
|
248
|
-
MessageId: "Action_ChangeEditorMode",
|
|
249
|
-
SendTime: Date.now(),
|
|
250
|
-
Values: { Mode: readonly ? "view" : "edit" },
|
|
251
|
-
}),
|
|
252
|
-
"*",
|
|
253
|
-
);
|
|
254
|
-
ready = true;
|
|
255
|
-
loading = false;
|
|
256
|
-
insertText = (text: string) => postPaste("text/plain", text);
|
|
257
|
-
insertHtml = (html: string) => postPaste("text/html", html);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (msgId === "UI_Save") {
|
|
261
|
-
sendSaveAction();
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
if (msgId === "Doc_ModifiedStatus" && msgData?.Modified) {
|
|
265
|
-
handleSaveResponse();
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
if (msgId === "Action_Save_Resp" && msgData?.success) {
|
|
269
|
-
handleSaveResponse();
|
|
270
|
-
}
|
|
271
|
-
} catch {
|
|
272
|
-
// ignore malformed messages from other origins
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
onMount(() => {
|
|
277
|
-
window.addEventListener("message", handlePostMessage);
|
|
278
|
-
return () => window.removeEventListener("message", handlePostMessage);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
// ── Insert actions (bound by parent via bind:insertText / bind:insertHtml) ────
|
|
282
|
-
|
|
283
|
-
function postPaste(mimetype: string, data: string): void {
|
|
284
|
-
if (!frameEl?.contentWindow) return;
|
|
285
|
-
// Restore focus to the iframe so Collabora has an active cursor position
|
|
286
|
-
frameEl.focus();
|
|
287
|
-
frameEl.contentWindow.postMessage(
|
|
288
|
-
JSON.stringify({
|
|
289
|
-
MessageId: "Action_Paste",
|
|
290
|
-
SendTime: Date.now(),
|
|
291
|
-
Values: { Mimetype: mimetype, Data: data },
|
|
292
|
-
}),
|
|
293
|
-
"*",
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// ── Reactive reload ───────────────────────────────────────────────────────────
|
|
298
|
-
|
|
299
|
-
$effect(() => {
|
|
300
|
-
// Track value, filename and WOPI config as dependencies.
|
|
301
|
-
// Update buffer when value prop changes, then reload.
|
|
302
|
-
const v = value;
|
|
303
|
-
const f = filename;
|
|
304
|
-
void wopiUrl;
|
|
305
|
-
void discoverUrl;
|
|
306
|
-
|
|
307
|
-
if (v !== undefined) buffer = v;
|
|
308
|
-
if (f && buffer) void loadEditor();
|
|
309
|
-
});
|
|
1
|
+
<script lang="ts">import { onMount, untrack } from "svelte";
|
|
2
|
+
import { getMimeFromExt, getExtFromFilename } from "../utils.js";
|
|
3
|
+
import { SvelteURLSearchParams } from "svelte/reactivity";
|
|
4
|
+
let { value, filename, readonly = false, wopiUrl, discoverUrl, adminKey = "", userid = 0, username = "user", viewtype: _viewtype = "tabbed", headers, onChange, onSave, insertText = $bindable(), insertHtml = $bindable() } = $props();
|
|
5
|
+
let frameEl = $state(undefined);
|
|
6
|
+
let ready = $state(false);
|
|
7
|
+
let loading = $state(false);
|
|
8
|
+
let errmsg = $state("");
|
|
9
|
+
// Stable IDs for this editor instance — untrack prevents Svelte from treating
|
|
10
|
+
// these as reactive dependencies (they are intentional one-time reads).
|
|
11
|
+
const editorId = `collabora_${crypto.randomUUID().slice(0, 8)}`;
|
|
12
|
+
// const ext = untrack(() => (filename ? getExtFromFilename(filename) : ""));
|
|
13
|
+
const fileId = `${untrack(() => userid)}_${crypto.randomUUID()}`;
|
|
14
|
+
// Mutable buffer — not reactive; mirrors React's useRef<Blob>
|
|
15
|
+
let buffer = untrack(() => value);
|
|
16
|
+
let saveRequestComplete = true;
|
|
17
|
+
let signedToken = "";
|
|
18
|
+
let signedWopiSrc = "";
|
|
19
|
+
const contentType = $derived(getMimeFromExt(filename ? getExtFromFilename(filename) : ""));
|
|
20
|
+
// ── WOPI helpers ────────────────────────────────────────────────────────────
|
|
21
|
+
async function fetchToken(canWrite) {
|
|
22
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
23
|
+
const reqHeaders = {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
...headers
|
|
26
|
+
};
|
|
27
|
+
if (adminKey) reqHeaders["X-API-Key"] = adminKey;
|
|
28
|
+
const res = await fetch(`${base}/api/tokens`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: reqHeaders,
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
resource_type: "file",
|
|
33
|
+
file_id: fileId,
|
|
34
|
+
user_id: String(userid),
|
|
35
|
+
user_name: username,
|
|
36
|
+
can_write: canWrite,
|
|
37
|
+
expires_in_seconds: 3600
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok) throw new Error(`Token fetch failed: ${res.status}`);
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
return {
|
|
43
|
+
token: data.access_token,
|
|
44
|
+
wopiSrc: data.wopi_src
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function fileExistsInWopi(token) {
|
|
48
|
+
const params = new URLSearchParams({ access_token: token });
|
|
49
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
50
|
+
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
51
|
+
headers,
|
|
52
|
+
method: "GET"
|
|
53
|
+
});
|
|
54
|
+
return res.ok;
|
|
55
|
+
}
|
|
56
|
+
async function uploadToWopi(blob, token) {
|
|
57
|
+
const params = new SvelteURLSearchParams();
|
|
58
|
+
params.append("access_token", token);
|
|
59
|
+
if (filename !== undefined) {
|
|
60
|
+
params.append("filename", filename);
|
|
61
|
+
params.append("contenttype", getMimeFromExt(getExtFromFilename(filename)));
|
|
62
|
+
}
|
|
63
|
+
if (userid !== undefined) params.append("userid", String(userid));
|
|
64
|
+
if (username !== undefined) params.append("username", username);
|
|
65
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
66
|
+
const fullurl = `${base}/wopi/files/${fileId}/contents?${params.toString()}`;
|
|
67
|
+
const res = await fetch(fullurl, {
|
|
68
|
+
headers: {
|
|
69
|
+
...headers,
|
|
70
|
+
"X-WOPI-Override": "PUT"
|
|
71
|
+
},
|
|
72
|
+
method: "POST",
|
|
73
|
+
body: blob
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) throw new Error(`WOPI upload failed: ${res.status}`);
|
|
76
|
+
}
|
|
77
|
+
async function downloadFromWopi() {
|
|
78
|
+
const params = new SvelteURLSearchParams();
|
|
79
|
+
params.append("access_token", signedToken);
|
|
80
|
+
if (userid !== undefined) params.append("userid", String(userid));
|
|
81
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
82
|
+
const fullurl = `${base}/wopi/files/${fileId}/contents?${params.toString()}`;
|
|
83
|
+
const res = await fetch(fullurl, {
|
|
84
|
+
headers,
|
|
85
|
+
method: "GET"
|
|
86
|
+
});
|
|
87
|
+
if (res.ok) return res.blob();
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
// ── Discovery ────────────────────────────────────────────────────────────────
|
|
91
|
+
async function findEditorUrl(mimeType) {
|
|
92
|
+
const res = await fetch(discoverUrl);
|
|
93
|
+
if (!res.ok) throw new Error(`Discovery request failed: ${res.status}`);
|
|
94
|
+
const xml = await res.text();
|
|
95
|
+
const doc = new DOMParser().parseFromString(xml, "text/xml");
|
|
96
|
+
const action = doc.querySelector(`app[name="${mimeType}"] action`);
|
|
97
|
+
const urlsrc = action?.getAttribute("urlsrc");
|
|
98
|
+
if (!urlsrc) throw new Error(`No Collabora editor found for MIME type: ${mimeType}`);
|
|
99
|
+
const params = new URLSearchParams({
|
|
100
|
+
WOPISrc: signedWopiSrc,
|
|
101
|
+
access_token: signedToken
|
|
102
|
+
});
|
|
103
|
+
return `${urlsrc}${params}`;
|
|
104
|
+
}
|
|
105
|
+
// ── Load ─────────────────────────────────────────────────────────────────────
|
|
106
|
+
async function loadEditor() {
|
|
107
|
+
if (!filename || !buffer) return;
|
|
108
|
+
errmsg = "";
|
|
109
|
+
loading = true;
|
|
110
|
+
ready = false;
|
|
111
|
+
insertText = undefined;
|
|
112
|
+
insertHtml = undefined;
|
|
113
|
+
try {
|
|
114
|
+
try {
|
|
115
|
+
localStorage.setItem("WSDWelcomeDisabled", "true");
|
|
116
|
+
} catch {}
|
|
117
|
+
// Always use a write token for the initial upload regardless of readonly
|
|
118
|
+
const { token: writeToken } = await fetchToken(true);
|
|
119
|
+
const exists = await fileExistsInWopi(writeToken);
|
|
120
|
+
if (!exists) await uploadToWopi(buffer, writeToken);
|
|
121
|
+
// Editor token respects readonly
|
|
122
|
+
const { token, wopiSrc } = await fetchToken(!readonly);
|
|
123
|
+
signedToken = token;
|
|
124
|
+
signedWopiSrc = wopiSrc;
|
|
125
|
+
const url = await findEditorUrl(contentType);
|
|
126
|
+
// Navigate iframe to Collabora — Collabora will fetch the file from WOPI
|
|
127
|
+
if (frameEl) frameEl.src = url;
|
|
128
|
+
} catch (e) {
|
|
129
|
+
errmsg = String(e);
|
|
130
|
+
loading = false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// ── Save flow ─────────────────────────────────────────────────────────────────
|
|
134
|
+
function sendSaveAction() {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
if (!frameEl?.contentWindow) {
|
|
137
|
+
reject(new Error("No iframe"));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
saveRequestComplete = false;
|
|
141
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
142
|
+
MessageId: "Action_Save",
|
|
143
|
+
SendTime: Date.now(),
|
|
144
|
+
Values: {
|
|
145
|
+
Notify: true,
|
|
146
|
+
DontSaveIfUnmodified: false,
|
|
147
|
+
DontTerminateEdit: false
|
|
148
|
+
}
|
|
149
|
+
}), "*");
|
|
150
|
+
let cnt = 0;
|
|
151
|
+
const itv = setInterval(() => {
|
|
152
|
+
cnt++;
|
|
153
|
+
if (saveRequestComplete) {
|
|
154
|
+
clearInterval(itv);
|
|
155
|
+
resolve();
|
|
156
|
+
}
|
|
157
|
+
if (cnt > 60) {
|
|
158
|
+
clearInterval(itv);
|
|
159
|
+
reject(new Error("Save timeout after 30s"));
|
|
160
|
+
}
|
|
161
|
+
}, 500);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function handleSaveResponse() {
|
|
165
|
+
setTimeout(async () => {
|
|
166
|
+
try {
|
|
167
|
+
const blob = await downloadFromWopi();
|
|
168
|
+
if (blob) {
|
|
169
|
+
buffer = blob;
|
|
170
|
+
onChange?.(blob);
|
|
171
|
+
}
|
|
172
|
+
} catch (e) {
|
|
173
|
+
errmsg = `Could not retrieve saved file: ${e}`;
|
|
174
|
+
} finally {
|
|
175
|
+
saveRequestComplete = true;
|
|
176
|
+
onSave?.(buffer ?? new Blob([]));
|
|
177
|
+
}
|
|
178
|
+
}, 100);
|
|
179
|
+
}
|
|
180
|
+
// ── PostMessage handler ───────────────────────────────────────────────────────
|
|
181
|
+
function handlePostMessage(e) {
|
|
182
|
+
try {
|
|
183
|
+
const msg = typeof e.data === "string" ? JSON.parse(e.data) : typeof e.data === "object" ? e.data : null;
|
|
184
|
+
if (!msg?.MessageId || !frameEl?.contentWindow) return;
|
|
185
|
+
const msgId = msg.MessageId;
|
|
186
|
+
const msgData = msg.Values;
|
|
187
|
+
if (msgId === "App_LoadingStatus" && msgData?.Status === "Frame_Ready") {
|
|
188
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
189
|
+
MessageId: "Host_PostmessageReady",
|
|
190
|
+
SendTime: Date.now()
|
|
191
|
+
}), "*");
|
|
192
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
193
|
+
MessageId: "Action_ChangeEditorMode",
|
|
194
|
+
SendTime: Date.now(),
|
|
195
|
+
Values: { Mode: readonly ? "view" : "edit" }
|
|
196
|
+
}), "*");
|
|
197
|
+
ready = true;
|
|
198
|
+
loading = false;
|
|
199
|
+
insertText = (text) => postPaste("text/plain", text);
|
|
200
|
+
insertHtml = (html) => postPaste("text/html", html);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (msgId === "UI_Save") {
|
|
204
|
+
sendSaveAction();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (msgId === "Doc_ModifiedStatus" && msgData?.Modified) {
|
|
208
|
+
handleSaveResponse();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (msgId === "Action_Save_Resp" && msgData?.success) {
|
|
212
|
+
handleSaveResponse();
|
|
213
|
+
}
|
|
214
|
+
} catch {}
|
|
215
|
+
}
|
|
216
|
+
onMount(() => {
|
|
217
|
+
window.addEventListener("message", handlePostMessage);
|
|
218
|
+
return () => window.removeEventListener("message", handlePostMessage);
|
|
219
|
+
});
|
|
220
|
+
// ── Insert actions (bound by parent via bind:insertText / bind:insertHtml) ────
|
|
221
|
+
function postPaste(mimetype, data) {
|
|
222
|
+
if (!frameEl?.contentWindow) return;
|
|
223
|
+
// Restore focus to the iframe so Collabora has an active cursor position
|
|
224
|
+
frameEl.focus();
|
|
225
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
226
|
+
MessageId: "Action_Paste",
|
|
227
|
+
SendTime: Date.now(),
|
|
228
|
+
Values: {
|
|
229
|
+
Mimetype: mimetype,
|
|
230
|
+
Data: data
|
|
231
|
+
}
|
|
232
|
+
}), "*");
|
|
233
|
+
}
|
|
234
|
+
// ── Reactive reload ───────────────────────────────────────────────────────────
|
|
235
|
+
$effect(() => {
|
|
236
|
+
// Track value, filename and WOPI config as dependencies.
|
|
237
|
+
// Update buffer when value prop changes, then reload.
|
|
238
|
+
const v = value;
|
|
239
|
+
const f = filename;
|
|
240
|
+
void wopiUrl;
|
|
241
|
+
void discoverUrl;
|
|
242
|
+
if (v !== undefined) buffer = v;
|
|
243
|
+
if (f && buffer) void loadEditor();
|
|
244
|
+
});
|
|
310
245
|
</script>
|
|
311
246
|
|
|
312
247
|
<div class="relative flex h-full w-full flex-1 flex-col overflow-hidden">
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
let { value: _value, filename }: ImageViewerProps = $props();
|
|
1
|
+
<script lang="ts">let { value: _value, filename } = $props();
|
|
2
|
+
export {};
|
|
4
3
|
</script>
|
|
5
4
|
|
|
6
5
|
<div class="card p-4 text-surface-700-300">
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
1
|
+
<script lang="ts">import IconAlertTriangle from "../../Icons/IconAlertTriangle.svelte";
|
|
2
|
+
let { value, filename } = $props();
|
|
3
|
+
let src = $state("");
|
|
4
|
+
let err = $state(false);
|
|
5
|
+
$effect(() => {
|
|
6
|
+
if (!value) {
|
|
7
|
+
src = "";
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
err = false;
|
|
11
|
+
const url = URL.createObjectURL(value);
|
|
12
|
+
src = url;
|
|
13
|
+
return () => URL.revokeObjectURL(url);
|
|
14
|
+
});
|
|
16
15
|
</script>
|
|
17
16
|
|
|
18
17
|
<div class="relative flex h-full w-full items-center justify-center overflow-hidden bg-surface-50-950">
|