@warkypublic/svelix 0.1.26 → 0.1.28
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/dist/components/BetterMenu/MenuRenderer.svelte +8 -7
- package/dist/components/Boxer/Boxer.svelte +39 -6
- package/dist/components/Boxer/BoxerTarget.svelte +19 -3
- package/dist/components/Boxer/BoxerTarget.svelte.d.ts +4 -0
- package/dist/components/ContentEditor/ContentEditor.svelte +7 -1
- package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +0 -1
- package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +1 -1
- package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +1 -1
- package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +33 -8
- package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +10 -20
- package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +7 -3
- package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +4 -5
- package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +194 -185
- package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +0 -1
- package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +1 -1
- package/dist/components/ContentEditor/types.d.ts +7 -0
- package/dist/components/ContentEditor/utils.d.ts +2 -0
- package/dist/components/ContentEditor/utils.js +71 -0
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +2 -2
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +1 -1
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +3 -3
- package/dist/components/FormerControllers/InlineWrapperPreview.svelte +18 -11
- package/dist/components/Gridler/components/Gridler.svelte +9 -4
- package/dist/components/Gridler/components/GridlerCanvas.svelte +188 -6
- package/dist/components/Gridler/components/GridlerFull.svelte +43 -33
- package/dist/components/Gridler/components/GridlerSearch.svelte +4 -2
- package/dist/components/Gridler/utils/canvasRender.js +4 -2
- package/dist/components/Gridler/utils/helpers.d.ts +16 -0
- package/dist/components/Gridler/utils/helpers.js +29 -0
- package/dist/components/Portal/Portal.svelte +1 -3
- package/dist/components/Svark/Svark.svelte +8 -1
- package/dist/components/Svark/SvarkTopBar.svelte +18 -18
- package/dist/components/SvarkGrid/SvarkGrid.svelte +1 -2
- package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +2 -1
- package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +1 -1
- package/dist/components/SvarkGrid/utils/svarGridMapping.js +0 -1
- package/dist/components/VTree/VTreeResolveSpecAdapter.js +5 -10
- package/dist/css/kinathka.theme.css +205 -0
- package/package.json +26 -26
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import { getContext } from 'svelte';
|
|
3
4
|
import type { BetterMenuInstanceItem, BetterMenuStoreState } from './types';
|
|
4
5
|
import type { BetterMenuStore } from './store';
|
|
@@ -54,9 +55,9 @@
|
|
|
54
55
|
|
|
55
56
|
{#snippet menuItem(item: BetterMenuInstanceItem, onClose: () => void)}
|
|
56
57
|
{#if item.renderer}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
<div class="px-3 py-2">
|
|
59
|
+
{@render (item.renderer as unknown as Snippet<[Record<string, unknown>]>)({ ...item, onClose })}
|
|
60
|
+
</div>
|
|
60
61
|
{:else if item.isDivider}
|
|
61
62
|
<hr class="border-slate-200 dark:border-slate-700 my-1" />
|
|
62
63
|
{:else if item.onClick || item.onClickAsync}
|
|
@@ -100,10 +101,10 @@
|
|
|
100
101
|
style:width={menuState.width ? `${menuState.width}px` : undefined}
|
|
101
102
|
style:z-index={menuZ}
|
|
102
103
|
>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
{#if menu.renderer}
|
|
105
|
+
<div class="py-2">
|
|
106
|
+
{@render (menu.renderer as unknown as Snippet<[Record<string, unknown>]>)({ id: menu.id, onClose: () => hideMenu(menu.id) })}
|
|
107
|
+
</div>
|
|
107
108
|
{:else}
|
|
108
109
|
{#each menu.items ?? [] as item, itemIndex (`${menu.id}_item_${item.id ?? itemIndex}`)}
|
|
109
110
|
{@render menuItem(item, () => hideMenu(menu.id))}
|
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
value = $bindable<any>(undefined),
|
|
38
38
|
}: BoxerProps = $props();
|
|
39
39
|
|
|
40
|
+
const instanceId = `boxer-${Math.random().toString(36).slice(2, 10)}`;
|
|
41
|
+
const inputId = `${instanceId}-input`;
|
|
42
|
+
const listboxId = `${instanceId}-listbox`;
|
|
43
|
+
|
|
40
44
|
// Derive effective dataSource and onAPICall from adapter when not explicitly provided
|
|
41
45
|
const dataSource = dataSourceProp ?? (adapter ? "server" : "local");
|
|
42
46
|
const onAPICall =
|
|
@@ -88,6 +92,7 @@
|
|
|
88
92
|
let popupTop = $state(0);
|
|
89
93
|
let popupLeft = $state(0);
|
|
90
94
|
let popupWidth = $state(0);
|
|
95
|
+
let activeOptionIndex = $state<number | null>(null);
|
|
91
96
|
let backdropZ = $state(1090);
|
|
92
97
|
let dropdownZ = $state(1100);
|
|
93
98
|
let pointerInteractingWithDropdown = $state(false);
|
|
@@ -209,6 +214,8 @@
|
|
|
209
214
|
const option = $store.boxerData?.[index];
|
|
210
215
|
if (!option) return;
|
|
211
216
|
|
|
217
|
+
activeOptionIndex = index;
|
|
218
|
+
|
|
212
219
|
if (multiSelect) {
|
|
213
220
|
const currentValues = Array.isArray(value) ? value : [];
|
|
214
221
|
const isSelected = currentValues.includes(option.value);
|
|
@@ -227,6 +234,7 @@
|
|
|
227
234
|
store.setSearch("");
|
|
228
235
|
store.setInput(option.label);
|
|
229
236
|
store.setOpened(false);
|
|
237
|
+
activeOptionIndex = null;
|
|
230
238
|
}
|
|
231
239
|
}
|
|
232
240
|
|
|
@@ -238,6 +246,7 @@
|
|
|
238
246
|
onChange?.(value);
|
|
239
247
|
store.setSearch("");
|
|
240
248
|
store.setInput("");
|
|
249
|
+
activeOptionIndex = null;
|
|
241
250
|
targetRef?.focus();
|
|
242
251
|
}
|
|
243
252
|
if (openOnClear) {
|
|
@@ -247,6 +256,7 @@
|
|
|
247
256
|
|
|
248
257
|
function focusDropdownItem(index: number) {
|
|
249
258
|
const clamped = Math.max(0, Math.min(index, $store.boxerData.length - 1));
|
|
259
|
+
activeOptionIndex = clamped;
|
|
250
260
|
requestAnimationFrame(() => {
|
|
251
261
|
const el = parentEl?.querySelector<HTMLDivElement>(
|
|
252
262
|
`[data-index="${clamped}"]`,
|
|
@@ -277,17 +287,20 @@
|
|
|
277
287
|
);
|
|
278
288
|
if (selectedIndex >= 0) {
|
|
279
289
|
store.setOpened(false);
|
|
290
|
+
activeOptionIndex = null;
|
|
280
291
|
} else {
|
|
281
292
|
onOptionSubmit(0);
|
|
282
293
|
}
|
|
283
294
|
} else {
|
|
284
295
|
store.setOpened(false);
|
|
296
|
+
activeOptionIndex = null;
|
|
285
297
|
}
|
|
286
298
|
break;
|
|
287
299
|
case "Escape":
|
|
288
300
|
if ($store.opened) {
|
|
289
301
|
e.preventDefault();
|
|
290
302
|
store.setOpened(false);
|
|
303
|
+
activeOptionIndex = null;
|
|
291
304
|
}
|
|
292
305
|
break;
|
|
293
306
|
}
|
|
@@ -302,6 +315,7 @@
|
|
|
302
315
|
case "ArrowUp":
|
|
303
316
|
e.preventDefault();
|
|
304
317
|
if (index === 0) {
|
|
318
|
+
activeOptionIndex = null;
|
|
305
319
|
targetRef?.focus();
|
|
306
320
|
} else {
|
|
307
321
|
focusDropdownItem(index - 1);
|
|
@@ -316,6 +330,7 @@
|
|
|
316
330
|
case "Escape":
|
|
317
331
|
e.preventDefault();
|
|
318
332
|
store.setOpened(false);
|
|
333
|
+
activeOptionIndex = null;
|
|
319
334
|
targetRef?.focus();
|
|
320
335
|
break;
|
|
321
336
|
}
|
|
@@ -326,7 +341,8 @@
|
|
|
326
341
|
const rect = anchorEl.getBoundingClientRect();
|
|
327
342
|
const viewportWidth = typeof window === "undefined" ? rect.width : window.innerWidth;
|
|
328
343
|
const horizontalPadding = 8;
|
|
329
|
-
const
|
|
344
|
+
const maxWidth = Math.max(160, viewportWidth - horizontalPadding * 2);
|
|
345
|
+
const nextWidth = Math.min(Math.max(220, rect.width), maxWidth);
|
|
330
346
|
const maxLeft = viewportWidth - nextWidth - horizontalPadding;
|
|
331
347
|
|
|
332
348
|
popupTop = rect.bottom + 4;
|
|
@@ -368,6 +384,7 @@
|
|
|
368
384
|
}
|
|
369
385
|
export function close() {
|
|
370
386
|
store.setOpened(false);
|
|
387
|
+
activeOptionIndex = null;
|
|
371
388
|
}
|
|
372
389
|
export function focus() {
|
|
373
390
|
targetRef?.focus();
|
|
@@ -377,6 +394,7 @@
|
|
|
377
394
|
}
|
|
378
395
|
export function open() {
|
|
379
396
|
store.setOpened(true);
|
|
397
|
+
activeOptionIndex = null;
|
|
380
398
|
}
|
|
381
399
|
export function setValue(val: unknown) {
|
|
382
400
|
value = val;
|
|
@@ -427,8 +445,8 @@
|
|
|
427
445
|
});
|
|
428
446
|
|
|
429
447
|
$effect(() => {
|
|
430
|
-
anchorEl;
|
|
431
|
-
storeOpened;
|
|
448
|
+
void anchorEl;
|
|
449
|
+
void storeOpened;
|
|
432
450
|
syncInsideDialogFlag();
|
|
433
451
|
});
|
|
434
452
|
|
|
@@ -462,6 +480,7 @@
|
|
|
462
480
|
const relatedTarget = e.relatedTarget as Node | null;
|
|
463
481
|
const insideDropdown = !!(relatedTarget && dropdownEl?.contains(relatedTarget));
|
|
464
482
|
if (!currentTarget.contains(relatedTarget) && !insideDropdown) {
|
|
483
|
+
activeOptionIndex = null;
|
|
465
484
|
if (!value && !multiSelect) {
|
|
466
485
|
store.setSearch("");
|
|
467
486
|
store.setInput("");
|
|
@@ -472,9 +491,13 @@
|
|
|
472
491
|
>
|
|
473
492
|
<BoxerTarget
|
|
474
493
|
bind:this={targetRef}
|
|
494
|
+
activeDescendant={activeOptionIndex !== null ? `${instanceId}-option-${activeOptionIndex}` : undefined}
|
|
475
495
|
{clearable}
|
|
496
|
+
controlsId={listboxId}
|
|
476
497
|
{disabled}
|
|
477
498
|
{error}
|
|
499
|
+
expanded={$store.opened}
|
|
500
|
+
{inputId}
|
|
478
501
|
{label}
|
|
479
502
|
{leftSection}
|
|
480
503
|
{placeholder}
|
|
@@ -486,6 +509,7 @@
|
|
|
486
509
|
onsearch={(v) => {
|
|
487
510
|
store.setSearch(v);
|
|
488
511
|
store.setInput(v);
|
|
512
|
+
activeOptionIndex = null;
|
|
489
513
|
store.setOpened(true);
|
|
490
514
|
}}
|
|
491
515
|
/>
|
|
@@ -493,10 +517,9 @@
|
|
|
493
517
|
<Portal disabled={effectiveDisablePortal}>
|
|
494
518
|
{#if $store.opened}
|
|
495
519
|
{#if !effectiveDisablePortal}
|
|
496
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
497
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
498
520
|
<div
|
|
499
521
|
class="fixed inset-0"
|
|
522
|
+
data-testid="boxer-backdrop"
|
|
500
523
|
onpointerdown={onBackdropPointerDown}
|
|
501
524
|
style:z-index={backdropZ}
|
|
502
525
|
></div>
|
|
@@ -507,6 +530,8 @@
|
|
|
507
530
|
class={`${effectiveDisablePortal
|
|
508
531
|
? "absolute left-0 right-0 top-full mt-1"
|
|
509
532
|
: "fixed"} card bg-surface-50-950 shadow-lg border border-surface-300-700 overflow-hidden`}
|
|
533
|
+
data-testid="boxer-dropdown"
|
|
534
|
+
id={listboxId}
|
|
510
535
|
role="listbox"
|
|
511
536
|
aria-label={label ?? "Options"}
|
|
512
537
|
onpointerdown={markDropdownPointerInteraction}
|
|
@@ -541,6 +566,7 @@
|
|
|
541
566
|
class:bg-primary-100={isSelected}
|
|
542
567
|
class:dark:bg-primary-900={isSelected}
|
|
543
568
|
data-index={vRow.index}
|
|
569
|
+
id={`${instanceId}-option-${vRow.index}`}
|
|
544
570
|
role="option"
|
|
545
571
|
aria-selected={isSelected}
|
|
546
572
|
tabindex="-1"
|
|
@@ -563,7 +589,14 @@
|
|
|
563
589
|
</div>
|
|
564
590
|
</div>
|
|
565
591
|
{:else}
|
|
566
|
-
<div
|
|
592
|
+
<div
|
|
593
|
+
aria-live="polite"
|
|
594
|
+
class="px-3 py-2 text-sm text-surface-500"
|
|
595
|
+
data-testid="boxer-empty"
|
|
596
|
+
role="status"
|
|
597
|
+
>
|
|
598
|
+
no data
|
|
599
|
+
</div>
|
|
567
600
|
{/if}
|
|
568
601
|
</div>
|
|
569
602
|
{/if}
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
|
|
4
4
|
export interface Props {
|
|
5
|
+
activeDescendant?: string;
|
|
6
|
+
controlsId?: string;
|
|
5
7
|
clearable?: boolean;
|
|
6
8
|
disabled?: boolean;
|
|
7
9
|
error?: string;
|
|
10
|
+
expanded?: boolean;
|
|
11
|
+
inputId?: string;
|
|
8
12
|
isFetching?: boolean;
|
|
9
13
|
label?: string;
|
|
10
14
|
leftSection?: Snippet;
|
|
@@ -17,9 +21,13 @@
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
let {
|
|
24
|
+
activeDescendant,
|
|
25
|
+
controlsId,
|
|
20
26
|
clearable = true,
|
|
21
27
|
disabled,
|
|
22
28
|
error,
|
|
29
|
+
expanded = false,
|
|
30
|
+
inputId = 'boxer-input',
|
|
23
31
|
isFetching,
|
|
24
32
|
label,
|
|
25
33
|
leftSection,
|
|
@@ -40,7 +48,7 @@
|
|
|
40
48
|
|
|
41
49
|
<div class="flex flex-col gap-1">
|
|
42
50
|
{#if label}
|
|
43
|
-
<label class="label text-sm font-medium" for=
|
|
51
|
+
<label class="label text-sm font-medium" for={inputId}>{label}</label>
|
|
44
52
|
{/if}
|
|
45
53
|
<div class="flex items-center relative">
|
|
46
54
|
{#if leftSection}
|
|
@@ -49,9 +57,17 @@
|
|
|
49
57
|
</div>
|
|
50
58
|
{/if}
|
|
51
59
|
<input
|
|
52
|
-
id=
|
|
60
|
+
id={inputId}
|
|
53
61
|
bind:this={inputEl}
|
|
54
62
|
bind:value={search}
|
|
63
|
+
aria-activedescendant={activeDescendant}
|
|
64
|
+
aria-autocomplete="list"
|
|
65
|
+
aria-controls={controlsId}
|
|
66
|
+
aria-describedby={error ? `${inputId}-error` : undefined}
|
|
67
|
+
aria-expanded={expanded}
|
|
68
|
+
aria-haspopup="listbox"
|
|
69
|
+
aria-invalid={!!error}
|
|
70
|
+
role="combobox"
|
|
55
71
|
class="input pr-8"
|
|
56
72
|
class:input-error={!!error}
|
|
57
73
|
class:pl-8={!!leftSection}
|
|
@@ -82,6 +98,6 @@
|
|
|
82
98
|
</div>
|
|
83
99
|
</div>
|
|
84
100
|
{#if error}
|
|
85
|
-
<p class="text-error-500 text-xs">{error}</p>
|
|
101
|
+
<p class="text-error-500 text-xs" id={`${inputId}-error`}>{error}</p>
|
|
86
102
|
{/if}
|
|
87
103
|
</div>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
2
|
export interface Props {
|
|
3
|
+
activeDescendant?: string;
|
|
4
|
+
controlsId?: string;
|
|
3
5
|
clearable?: boolean;
|
|
4
6
|
disabled?: boolean;
|
|
5
7
|
error?: string;
|
|
8
|
+
expanded?: boolean;
|
|
9
|
+
inputId?: string;
|
|
6
10
|
isFetching?: boolean;
|
|
7
11
|
label?: string;
|
|
8
12
|
leftSection?: Snippet;
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
filename,
|
|
20
20
|
contentType,
|
|
21
21
|
mode = "value",
|
|
22
|
+
hideHeader = false,
|
|
23
|
+
theme,
|
|
24
|
+
colorScheme = "auto",
|
|
22
25
|
readonly = false,
|
|
23
26
|
onChange,
|
|
24
27
|
onSave,
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
}: ContentEditorProps = $props();
|
|
37
40
|
|
|
38
41
|
const editorType = $derived(getEditorType({ filename, contentType }));
|
|
42
|
+
const forcedColorScheme = $derived(colorScheme === "auto" ? undefined : colorScheme);
|
|
39
43
|
|
|
40
44
|
$effect(() => {
|
|
41
45
|
if (mode === "filepointer") {
|
|
@@ -47,6 +51,8 @@
|
|
|
47
51
|
</script>
|
|
48
52
|
|
|
49
53
|
<div
|
|
54
|
+
data-theme={theme}
|
|
55
|
+
style:color-scheme={forcedColorScheme}
|
|
50
56
|
style="display: flex; flex-direction: column; width: 100%; height: 100%; min-height: 200px; overflow: hidden;"
|
|
51
57
|
>
|
|
52
58
|
{#if editorType === "monaco"}
|
|
@@ -67,7 +73,7 @@
|
|
|
67
73
|
{:else if editorType === "video"}
|
|
68
74
|
<VideoPlayer {value} {filename} />
|
|
69
75
|
{:else if editorType === "text"}
|
|
70
|
-
<TextEditor {value} {filename} {readonly} {onChange} bind:insertText bind:insertHtml />
|
|
76
|
+
<TextEditor {value} {filename} {hideHeader} {readonly} {onChange} bind:insertText bind:insertHtml />
|
|
71
77
|
{:else if editorType === "markdown"}
|
|
72
78
|
<MarkdownViewer {value} {filename} {readonly} {onChange} {onUpload} bind:insertText bind:insertHtml />
|
|
73
79
|
{:else if editorType === "pdf"}
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
import '@cartamd/plugin-attachment/default.css';
|
|
15
15
|
import 'katex/dist/katex.min.css';
|
|
16
16
|
import 'github-markdown-css/github-markdown.css';
|
|
17
|
-
import { blobToString } from '../utils.js';
|
|
17
|
+
import { blobToString, watchSkeletonDarkMode } from '../utils.js';
|
|
18
18
|
import type { MarkdownViewerProps } from '../types.js';
|
|
19
19
|
|
|
20
20
|
let { value, readonly = false, onChange, onUpload, insertText = $bindable(), insertHtml = $bindable() }: MarkdownViewerProps = $props();
|
|
21
21
|
|
|
22
22
|
let carta = $state<Carta | undefined>(undefined);
|
|
23
|
+
let hostEl = $state<HTMLDivElement | undefined>(undefined);
|
|
24
|
+
let isDark = $state(false);
|
|
23
25
|
|
|
24
26
|
onMount(() => {
|
|
25
27
|
carta = new Carta({
|
|
@@ -34,6 +36,13 @@
|
|
|
34
36
|
});
|
|
35
37
|
});
|
|
36
38
|
|
|
39
|
+
onMount(() => {
|
|
40
|
+
const stopWatchingTheme = watchSkeletonDarkMode(hostEl, (nextIsDark) => {
|
|
41
|
+
isDark = nextIsDark;
|
|
42
|
+
});
|
|
43
|
+
return () => stopWatchingTheme();
|
|
44
|
+
});
|
|
45
|
+
|
|
37
46
|
let text = $state('');
|
|
38
47
|
let lastBlobText = $state('');
|
|
39
48
|
|
|
@@ -56,8 +65,18 @@
|
|
|
56
65
|
insertHtml = undefined;
|
|
57
66
|
return;
|
|
58
67
|
}
|
|
68
|
+
type EditorViewLike = {
|
|
69
|
+
state: { selection: { main: { from: number; to: number } } };
|
|
70
|
+
dispatch: (payload: {
|
|
71
|
+
changes: { from: number; to: number; insert: string };
|
|
72
|
+
selection: { anchor: number };
|
|
73
|
+
}) => void;
|
|
74
|
+
focus: () => void;
|
|
75
|
+
};
|
|
76
|
+
type CartaEditorLike = Carta & { editor?: { editorView?: EditorViewLike } };
|
|
77
|
+
|
|
59
78
|
function insertAtCursor(content: string): void {
|
|
60
|
-
const view = carta
|
|
79
|
+
const view = (carta as CartaEditorLike).editor?.editorView;
|
|
61
80
|
if (!view) return;
|
|
62
81
|
const { from, to } = view.state.selection.main;
|
|
63
82
|
view.dispatch({
|
|
@@ -71,7 +90,13 @@
|
|
|
71
90
|
});
|
|
72
91
|
</script>
|
|
73
92
|
|
|
74
|
-
<div
|
|
93
|
+
<div
|
|
94
|
+
bind:this={hostEl}
|
|
95
|
+
class="md-host"
|
|
96
|
+
class:theme-dark={isDark}
|
|
97
|
+
class:theme-light={!isDark}
|
|
98
|
+
style="display: flex; flex-direction: column; width: 100%; height: 100%;"
|
|
99
|
+
>
|
|
75
100
|
{#if carta}
|
|
76
101
|
{#if readonly}
|
|
77
102
|
{#key text}
|
|
@@ -110,8 +135,8 @@
|
|
|
110
135
|
}
|
|
111
136
|
|
|
112
137
|
/* Shiki dual-theme: dark colors only when .dark class is present */
|
|
113
|
-
:global(.dark .shiki),
|
|
114
|
-
:global(.dark .shiki span) {
|
|
138
|
+
:global(.md-host.theme-dark .shiki),
|
|
139
|
+
:global(.md-host.theme-dark .shiki span) {
|
|
115
140
|
color: var(--shiki-dark) !important;
|
|
116
141
|
background-color: var(--shiki-dark-bg) !important;
|
|
117
142
|
font-style: var(--shiki-dark-font-style) !important;
|
|
@@ -120,7 +145,7 @@
|
|
|
120
145
|
}
|
|
121
146
|
|
|
122
147
|
/* carta-md dark mode: remap dark variable aliases when .dark is active */
|
|
123
|
-
:global(.dark .carta-theme__default) {
|
|
148
|
+
:global(.md-host.theme-dark .carta-theme__default) {
|
|
124
149
|
--border-color: var(--border-color-dark);
|
|
125
150
|
--selection-color: var(--selection-color-dark);
|
|
126
151
|
--focus-outline: var(--focus-outline-dark);
|
|
@@ -131,7 +156,7 @@
|
|
|
131
156
|
|
|
132
157
|
/* github-markdown-css dark mode: override CSS vars when .dark class is active.
|
|
133
158
|
github-markdown.css uses @media (prefers-color-scheme) which ignores the .dark class. */
|
|
134
|
-
:global(.dark .markdown-body) {
|
|
159
|
+
:global(.md-host.theme-dark .markdown-body) {
|
|
135
160
|
color-scheme: dark;
|
|
136
161
|
--fgColor-default: #f0f6fc;
|
|
137
162
|
--fgColor-muted: #9198a1;
|
|
@@ -186,7 +211,7 @@
|
|
|
186
211
|
}
|
|
187
212
|
|
|
188
213
|
/* github-markdown-css light mode: force light vars when OS may be dark but app is light */
|
|
189
|
-
:global(
|
|
214
|
+
:global(.md-host.theme-light .markdown-body) {
|
|
190
215
|
color-scheme: light;
|
|
191
216
|
--fgColor-default: #1f2328;
|
|
192
217
|
--fgColor-muted: #59636e;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
3
|
import * as monaco from 'monaco-editor';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getLangFromExt,
|
|
6
|
+
getExtFromFilename,
|
|
7
|
+
blobToString,
|
|
8
|
+
watchSkeletonDarkMode,
|
|
9
|
+
} from '../utils.js';
|
|
5
10
|
import type { MonacoEditorProps } from '../types.js';
|
|
6
11
|
|
|
7
12
|
let {
|
|
@@ -26,23 +31,9 @@
|
|
|
26
31
|
);
|
|
27
32
|
|
|
28
33
|
onMount(() => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function updateDark() {
|
|
34
|
-
const explicit = html.style.colorScheme;
|
|
35
|
-
if (explicit === 'dark' || explicit === 'light') {
|
|
36
|
-
isDark = explicit === 'dark';
|
|
37
|
-
} else {
|
|
38
|
-
isDark = html.classList.contains('dark') || mq.matches;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
updateDark();
|
|
43
|
-
mq.addEventListener('change', updateDark);
|
|
44
|
-
const observer = new MutationObserver(updateDark);
|
|
45
|
-
observer.observe(html, { attributeFilter: ['class', 'style'] });
|
|
34
|
+
const stopWatchingTheme = watchSkeletonDarkMode(container, (nextIsDark) => {
|
|
35
|
+
isDark = nextIsDark;
|
|
36
|
+
});
|
|
46
37
|
|
|
47
38
|
editor = monaco.editor.create(container!, {
|
|
48
39
|
value: '',
|
|
@@ -73,8 +64,7 @@
|
|
|
73
64
|
insertHtml = (html: string) => doInsertText(html.replace(/<[^>]+>/g, ''));
|
|
74
65
|
|
|
75
66
|
return () => {
|
|
76
|
-
|
|
77
|
-
observer.disconnect();
|
|
67
|
+
stopWatchingTheme();
|
|
78
68
|
insertText = undefined;
|
|
79
69
|
insertHtml = undefined;
|
|
80
70
|
editor?.dispose();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { onMount, untrack, tick } from "svelte";
|
|
3
3
|
import type { Office365EditorProps } from "../types.js";
|
|
4
4
|
import { getExtFromFilename } from "../utils.js";
|
|
5
|
+
import { SvelteURLSearchParams } from "svelte/reactivity";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
value,
|
|
@@ -71,7 +72,8 @@
|
|
|
71
72
|
// ── WOPI helpers ─────────────────────────────────────────────────────────────
|
|
72
73
|
|
|
73
74
|
async function fileExistsInWopi(token: string): Promise<boolean> {
|
|
74
|
-
const params = new
|
|
75
|
+
const params = new SvelteURLSearchParams();
|
|
76
|
+
params.append("access_token", token);
|
|
75
77
|
const base = wopiUrl.replace(/\/$/, "");
|
|
76
78
|
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
77
79
|
headers,
|
|
@@ -81,7 +83,8 @@
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
async function uploadToWopi(blob: Blob, token: string): Promise<void> {
|
|
84
|
-
const params = new
|
|
86
|
+
const params = new SvelteURLSearchParams();
|
|
87
|
+
params.append("access_token", token);
|
|
85
88
|
if (filename !== undefined) params.append("filename", filename);
|
|
86
89
|
const base = wopiUrl.replace(/\/$/, "");
|
|
87
90
|
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
@@ -93,7 +96,8 @@
|
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
async function downloadFromWopi(): Promise<Blob | undefined> {
|
|
96
|
-
const params = new
|
|
99
|
+
const params = new SvelteURLSearchParams();
|
|
100
|
+
params.append("access_token", signedToken);
|
|
97
101
|
const base = wopiUrl.replace(/\/$/, "");
|
|
98
102
|
const res = await fetch(`${base}/wopi/files/${fileId}/contents?${params}`, {
|
|
99
103
|
headers,
|
|
@@ -36,14 +36,13 @@
|
|
|
36
36
|
>
|
|
37
37
|
<div class="flex h-full w-full flex-col items-center justify-center gap-3 p-6 text-center text-surface-700-300">
|
|
38
38
|
<p class="text-sm opacity-70">This browser could not display the PDF inline.</p>
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
target="_blank"
|
|
42
|
-
rel="noopener noreferrer"
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
43
41
|
class="btn variant-outline"
|
|
42
|
+
onclick={() => window.open(src, '_blank', 'noopener,noreferrer')}
|
|
44
43
|
>
|
|
45
44
|
Open PDF in new tab
|
|
46
|
-
</
|
|
45
|
+
</button>
|
|
47
46
|
</div>
|
|
48
47
|
</object>
|
|
49
48
|
{:else}
|