@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,300 +1,237 @@
|
|
|
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
|
-
const msgData = msg.Values;
|
|
236
|
-
|
|
237
|
-
if (msgId === "App_LoadingStatus" && msgData?.Status === "Frame_Ready") {
|
|
238
|
-
frameEl.contentWindow.postMessage(
|
|
239
|
-
JSON.stringify({
|
|
240
|
-
MessageId: "Host_PostmessageReady",
|
|
241
|
-
SendTime: Date.now(),
|
|
242
|
-
}),
|
|
243
|
-
"*",
|
|
244
|
-
);
|
|
245
|
-
ready = true;
|
|
246
|
-
loading = false;
|
|
247
|
-
insertText = (text: string) => postPaste("text/plain", text);
|
|
248
|
-
insertHtml = (html: string) => postPaste("text/html", html);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
if (msgId === "UI_Save") {
|
|
252
|
-
sendSaveAction();
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
if (msgId === "Doc_ModifiedStatus" && msgData?.Modified) {
|
|
256
|
-
handleSaveResponse();
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
if (msgId === "Action_Save_Resp" && msgData?.success) {
|
|
260
|
-
handleSaveResponse();
|
|
261
|
-
}
|
|
262
|
-
} catch {
|
|
263
|
-
// ignore malformed messages from other origins
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
onMount(() => {
|
|
268
|
-
window.addEventListener("message", handlePostMessage);
|
|
269
|
-
return () => window.removeEventListener("message", handlePostMessage);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// ── Insert actions ────────────────────────────────────────────────────────────
|
|
273
|
-
|
|
274
|
-
function postPaste(mimetype: string, data: string): void {
|
|
275
|
-
if (!frameEl?.contentWindow) return;
|
|
276
|
-
frameEl.focus();
|
|
277
|
-
frameEl.contentWindow.postMessage(
|
|
278
|
-
JSON.stringify({
|
|
279
|
-
MessageId: "Action_Paste",
|
|
280
|
-
SendTime: Date.now(),
|
|
281
|
-
Values: { Mimetype: mimetype, Data: data },
|
|
282
|
-
}),
|
|
283
|
-
"*",
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// ── Reactive reload ───────────────────────────────────────────────────────────
|
|
288
|
-
|
|
289
|
-
$effect(() => {
|
|
290
|
-
const v = value;
|
|
291
|
-
const f = filename;
|
|
292
|
-
void wopiUrl;
|
|
293
|
-
void officeOnlineUrl;
|
|
294
|
-
|
|
295
|
-
if (v !== undefined) buffer = v;
|
|
296
|
-
if (f && buffer) void loadEditor();
|
|
297
|
-
});
|
|
1
|
+
<script lang="ts">import { onMount, untrack, tick } from "svelte";
|
|
2
|
+
import { getExtFromFilename } from "../utils.js";
|
|
3
|
+
import { SvelteURLSearchParams } from "svelte/reactivity";
|
|
4
|
+
let { value, filename, readonly = false, wopiUrl, officeOnlineUrl = "https://onenote.officeapps.live.com", adminKey = "", userid = 0, username = "user", headers, onChange, onSave, insertText = $bindable(), insertHtml = $bindable() } = $props();
|
|
5
|
+
let frameEl = $state(undefined);
|
|
6
|
+
let formEl = $state(undefined);
|
|
7
|
+
let ready = $state(false);
|
|
8
|
+
let loading = $state(false);
|
|
9
|
+
let errmsg = $state("");
|
|
10
|
+
let editorActionUrl = $state("");
|
|
11
|
+
const editorId = `office365_${crypto.randomUUID().slice(0, 8)}`;
|
|
12
|
+
const fileId = `${untrack(() => userid)}_${crypto.randomUUID()}`;
|
|
13
|
+
// Mutable buffer — not reactive
|
|
14
|
+
let buffer = untrack(() => value);
|
|
15
|
+
let saveRequestComplete = true;
|
|
16
|
+
let signedToken = $state("");
|
|
17
|
+
let signedTokenTtl = $state(0);
|
|
18
|
+
let signedWopiSrc = "";
|
|
19
|
+
// ── Token ─────────────────────────────────────────────────────────────────────
|
|
20
|
+
async function fetchToken(canWrite) {
|
|
21
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
22
|
+
const reqHeaders = {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
...headers
|
|
25
|
+
};
|
|
26
|
+
if (adminKey) reqHeaders["X-API-Key"] = adminKey;
|
|
27
|
+
const res = await fetch(`${base}/api/tokens`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: reqHeaders,
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
resource_type: "file",
|
|
32
|
+
file_id: fileId,
|
|
33
|
+
user_id: String(userid),
|
|
34
|
+
user_name: username,
|
|
35
|
+
can_write: canWrite,
|
|
36
|
+
expires_in_seconds: 3600
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) throw new Error(`Token fetch failed: ${res.status}`);
|
|
40
|
+
const data = await res.json();
|
|
41
|
+
return {
|
|
42
|
+
token: data.access_token,
|
|
43
|
+
wopiSrc: data.wopi_src,
|
|
44
|
+
ttl: Date.now() + (data.expires_in_seconds ?? 3600) * 1e3
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ── WOPI helpers ─────────────────────────────────────────────────────────────
|
|
48
|
+
async function fileExistsInWopi(token) {
|
|
49
|
+
const params = new SvelteURLSearchParams();
|
|
50
|
+
params.append("access_token", token);
|
|
51
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
52
|
+
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
53
|
+
headers,
|
|
54
|
+
method: "GET"
|
|
55
|
+
});
|
|
56
|
+
return res.ok;
|
|
57
|
+
}
|
|
58
|
+
async function uploadToWopi(blob, token) {
|
|
59
|
+
const params = new SvelteURLSearchParams();
|
|
60
|
+
params.append("access_token", token);
|
|
61
|
+
if (filename !== undefined) params.append("filename", filename);
|
|
62
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
63
|
+
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
64
|
+
headers: {
|
|
65
|
+
...headers,
|
|
66
|
+
"X-WOPI-Override": "PUT"
|
|
67
|
+
},
|
|
68
|
+
method: "POST",
|
|
69
|
+
body: blob
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) throw new Error(`WOPI upload failed: ${res.status}`);
|
|
72
|
+
}
|
|
73
|
+
async function downloadFromWopi() {
|
|
74
|
+
const params = new SvelteURLSearchParams();
|
|
75
|
+
params.append("access_token", signedToken);
|
|
76
|
+
const base = wopiUrl.replace(/\/$/, "");
|
|
77
|
+
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
78
|
+
headers,
|
|
79
|
+
method: "GET"
|
|
80
|
+
});
|
|
81
|
+
if (res.ok) return res.blob();
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
// ── Discovery ────────────────────────────────────────────────────────────────
|
|
85
|
+
// Office Online uses extension-based action lookup (not MIME-type app names).
|
|
86
|
+
// urlsrc may contain optional placeholder blocks like <rs=xx> — strip them.
|
|
87
|
+
async function findEditorUrl(ext) {
|
|
88
|
+
const discoveryUrl = `${officeOnlineUrl.replace(/\/$/, "")}/hosting/discovery`;
|
|
89
|
+
const res = await fetch(discoveryUrl);
|
|
90
|
+
if (!res.ok) throw new Error(`Discovery failed: ${res.status}`);
|
|
91
|
+
const xml = await res.text();
|
|
92
|
+
const doc = new DOMParser().parseFromString(xml, "text/xml");
|
|
93
|
+
const actionName = readonly ? "view" : "edit";
|
|
94
|
+
const actionEl = doc.querySelector(`action[name="${actionName}"][ext="${ext.toLowerCase()}"]`);
|
|
95
|
+
const urlsrc = actionEl?.getAttribute("urlsrc");
|
|
96
|
+
if (!urlsrc) throw new Error(`No Office Online editor for extension: ${ext} (action: ${actionName})`);
|
|
97
|
+
// Strip optional placeholder blocks, e.g. <rs=xx&dc=xx>
|
|
98
|
+
return urlsrc.replace(/<[^>]+>/g, "");
|
|
99
|
+
}
|
|
100
|
+
// ── Load ─────────────────────────────────────────────────────────────────────
|
|
101
|
+
// Office Online requires a form POST into the iframe (not iframe src assignment).
|
|
102
|
+
async function loadEditor() {
|
|
103
|
+
if (!filename || !buffer) return;
|
|
104
|
+
errmsg = "";
|
|
105
|
+
loading = true;
|
|
106
|
+
ready = false;
|
|
107
|
+
insertText = undefined;
|
|
108
|
+
insertHtml = undefined;
|
|
109
|
+
try {
|
|
110
|
+
// Always use a write token for the initial upload regardless of readonly
|
|
111
|
+
const { token: writeToken } = await fetchToken(true);
|
|
112
|
+
const exists = await fileExistsInWopi(writeToken);
|
|
113
|
+
if (!exists) await uploadToWopi(buffer, writeToken);
|
|
114
|
+
// Editor token respects readonly
|
|
115
|
+
const { token, wopiSrc, ttl } = await fetchToken(!readonly);
|
|
116
|
+
signedToken = token;
|
|
117
|
+
signedWopiSrc = wopiSrc;
|
|
118
|
+
signedTokenTtl = ttl;
|
|
119
|
+
const ext = getExtFromFilename(filename);
|
|
120
|
+
const actionUrl = await findEditorUrl(ext);
|
|
121
|
+
const sep = actionUrl.includes("?") ? "&" : "?";
|
|
122
|
+
editorActionUrl = `${actionUrl}${sep}WOPISrc=${encodeURIComponent(signedWopiSrc)}`;
|
|
123
|
+
// Flush reactive state so the form action attribute is updated before submit
|
|
124
|
+
await tick();
|
|
125
|
+
formEl?.submit();
|
|
126
|
+
} catch (e) {
|
|
127
|
+
errmsg = String(e);
|
|
128
|
+
loading = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// ── Save flow ─────────────────────────────────────────────────────────────────
|
|
132
|
+
function sendSaveAction() {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
if (!frameEl?.contentWindow) {
|
|
135
|
+
reject(new Error("No iframe"));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
saveRequestComplete = false;
|
|
139
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
140
|
+
MessageId: "Action_Save",
|
|
141
|
+
SendTime: Date.now(),
|
|
142
|
+
Values: {
|
|
143
|
+
Notify: true,
|
|
144
|
+
DontSaveIfUnmodified: false,
|
|
145
|
+
DontTerminateEdit: false
|
|
146
|
+
}
|
|
147
|
+
}), "*");
|
|
148
|
+
let cnt = 0;
|
|
149
|
+
const itv = setInterval(() => {
|
|
150
|
+
cnt++;
|
|
151
|
+
if (saveRequestComplete) {
|
|
152
|
+
clearInterval(itv);
|
|
153
|
+
resolve();
|
|
154
|
+
}
|
|
155
|
+
if (cnt > 60) {
|
|
156
|
+
clearInterval(itv);
|
|
157
|
+
reject(new Error("Save timeout after 30s"));
|
|
158
|
+
}
|
|
159
|
+
}, 500);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function handleSaveResponse() {
|
|
163
|
+
setTimeout(async () => {
|
|
164
|
+
try {
|
|
165
|
+
const blob = await downloadFromWopi();
|
|
166
|
+
if (blob) {
|
|
167
|
+
buffer = blob;
|
|
168
|
+
onChange?.(blob);
|
|
169
|
+
}
|
|
170
|
+
} catch (e) {
|
|
171
|
+
errmsg = `Could not retrieve saved file: ${e}`;
|
|
172
|
+
} finally {
|
|
173
|
+
saveRequestComplete = true;
|
|
174
|
+
onSave?.(buffer ?? new Blob([]));
|
|
175
|
+
}
|
|
176
|
+
}, 100);
|
|
177
|
+
}
|
|
178
|
+
// ── PostMessage handler ───────────────────────────────────────────────────────
|
|
179
|
+
function handlePostMessage(e) {
|
|
180
|
+
try {
|
|
181
|
+
const msg = typeof e.data === "string" ? JSON.parse(e.data) : typeof e.data === "object" ? e.data : null;
|
|
182
|
+
if (!msg?.MessageId || !frameEl?.contentWindow) return;
|
|
183
|
+
const msgId = msg.MessageId;
|
|
184
|
+
const msgData = msg.Values;
|
|
185
|
+
if (msgId === "App_LoadingStatus" && msgData?.Status === "Frame_Ready") {
|
|
186
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
187
|
+
MessageId: "Host_PostmessageReady",
|
|
188
|
+
SendTime: Date.now()
|
|
189
|
+
}), "*");
|
|
190
|
+
ready = true;
|
|
191
|
+
loading = false;
|
|
192
|
+
insertText = (text) => postPaste("text/plain", text);
|
|
193
|
+
insertHtml = (html) => postPaste("text/html", html);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (msgId === "UI_Save") {
|
|
197
|
+
sendSaveAction();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (msgId === "Doc_ModifiedStatus" && msgData?.Modified) {
|
|
201
|
+
handleSaveResponse();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (msgId === "Action_Save_Resp" && msgData?.success) {
|
|
205
|
+
handleSaveResponse();
|
|
206
|
+
}
|
|
207
|
+
} catch {}
|
|
208
|
+
}
|
|
209
|
+
onMount(() => {
|
|
210
|
+
window.addEventListener("message", handlePostMessage);
|
|
211
|
+
return () => window.removeEventListener("message", handlePostMessage);
|
|
212
|
+
});
|
|
213
|
+
// ── Insert actions ────────────────────────────────────────────────────────────
|
|
214
|
+
function postPaste(mimetype, data) {
|
|
215
|
+
if (!frameEl?.contentWindow) return;
|
|
216
|
+
frameEl.focus();
|
|
217
|
+
frameEl.contentWindow.postMessage(JSON.stringify({
|
|
218
|
+
MessageId: "Action_Paste",
|
|
219
|
+
SendTime: Date.now(),
|
|
220
|
+
Values: {
|
|
221
|
+
Mimetype: mimetype,
|
|
222
|
+
Data: data
|
|
223
|
+
}
|
|
224
|
+
}), "*");
|
|
225
|
+
}
|
|
226
|
+
// ── Reactive reload ───────────────────────────────────────────────────────────
|
|
227
|
+
$effect(() => {
|
|
228
|
+
const v = value;
|
|
229
|
+
const f = filename;
|
|
230
|
+
void wopiUrl;
|
|
231
|
+
void officeOnlineUrl;
|
|
232
|
+
if (v !== undefined) buffer = v;
|
|
233
|
+
if (f && buffer) void loadEditor();
|
|
234
|
+
});
|
|
298
235
|
</script>
|
|
299
236
|
|
|
300
237
|
<div class="relative flex h-full w-full flex-1 flex-col overflow-hidden">
|
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
src = `${url}#toolbar=1&navpanes=0&scrollbar=1`;
|
|
16
|
-
|
|
17
|
-
return () => {
|
|
18
|
-
URL.revokeObjectURL(url);
|
|
19
|
-
};
|
|
20
|
-
});
|
|
1
|
+
<script lang="ts">let { value, filename } = $props();
|
|
2
|
+
let src = $state("");
|
|
3
|
+
$effect(() => {
|
|
4
|
+
if (!value) {
|
|
5
|
+
src = "";
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const url = URL.createObjectURL(value);
|
|
9
|
+
src = `${url}#toolbar=1&navpanes=0&scrollbar=1`;
|
|
10
|
+
return () => {
|
|
11
|
+
URL.revokeObjectURL(url);
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
export {};
|
|
21
15
|
</script>
|
|
22
16
|
|
|
23
17
|
<div class="flex h-full w-full flex-col overflow-hidden bg-surface-50-950">
|