@autumnsgrove/groveengine 0.6.1 → 0.6.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/dist/auth/jwt.d.ts +10 -4
- package/dist/auth/jwt.js +18 -4
- package/dist/auth/session.d.ts +22 -15
- package/dist/auth/session.js +35 -16
- package/dist/components/admin/GutterManager.svelte +81 -139
- package/dist/components/admin/GutterManager.svelte.d.ts +6 -6
- package/dist/components/admin/MarkdownEditor.svelte +80 -23
- package/dist/components/admin/MarkdownEditor.svelte.d.ts +14 -8
- package/dist/components/admin/composables/useAmbientSounds.svelte.d.ts +52 -2
- package/dist/components/admin/composables/useAmbientSounds.svelte.js +38 -4
- package/dist/components/admin/composables/useCommandPalette.svelte.d.ts +80 -10
- package/dist/components/admin/composables/useCommandPalette.svelte.js +45 -5
- package/dist/components/admin/composables/useDraftManager.svelte.d.ts +76 -14
- package/dist/components/admin/composables/useDraftManager.svelte.js +44 -10
- package/dist/components/admin/composables/useEditorTheme.svelte.d.ts +168 -2
- package/dist/components/admin/composables/useEditorTheme.svelte.js +40 -7
- package/dist/components/admin/composables/useSlashCommands.svelte.d.ts +94 -22
- package/dist/components/admin/composables/useSlashCommands.svelte.js +58 -9
- package/dist/components/admin/composables/useSnippets.svelte.d.ts +51 -2
- package/dist/components/admin/composables/useSnippets.svelte.js +35 -3
- package/dist/components/admin/composables/useWritingSession.svelte.d.ts +64 -6
- package/dist/components/admin/composables/useWritingSession.svelte.js +42 -5
- package/dist/components/custom/ContentWithGutter.svelte +53 -23
- package/dist/components/custom/ContentWithGutter.svelte.d.ts +6 -14
- package/dist/components/custom/GutterItem.svelte +1 -1
- package/dist/components/custom/LeftGutter.svelte +43 -13
- package/dist/components/custom/LeftGutter.svelte.d.ts +6 -6
- package/dist/config/ai-models.js +1 -1
- package/dist/groveauth/client.js +11 -11
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -2
- package/dist/server/logger.d.ts +74 -26
- package/dist/server/logger.js +133 -184
- package/dist/server/services/cache.js +1 -10
- package/dist/ui/components/charts/ActivityOverview.svelte +14 -3
- package/dist/ui/components/charts/ActivityOverview.svelte.d.ts +10 -7
- package/dist/ui/components/charts/RepoBreakdown.svelte +9 -3
- package/dist/ui/components/charts/RepoBreakdown.svelte.d.ts +12 -11
- package/dist/ui/components/charts/Sparkline.svelte +18 -7
- package/dist/ui/components/charts/Sparkline.svelte.d.ts +21 -2
- package/dist/ui/components/gallery/ImageGallery.svelte +12 -8
- package/dist/ui/components/gallery/ImageGallery.svelte.d.ts +2 -2
- package/dist/ui/components/gallery/Lightbox.svelte +5 -2
- package/dist/ui/components/gallery/ZoomableImage.svelte +8 -5
- package/dist/ui/components/primitives/accordion/index.d.ts +1 -1
- package/dist/ui/components/primitives/input/input.svelte.d.ts +1 -1
- package/dist/ui/components/primitives/tabs/index.d.ts +1 -1
- package/dist/ui/components/primitives/textarea/textarea.svelte.d.ts +1 -1
- package/dist/ui/components/ui/Button.svelte +5 -0
- package/dist/ui/components/ui/Button.svelte.d.ts +4 -1
- package/dist/ui/components/ui/Input.svelte +4 -0
- package/dist/ui/components/ui/Input.svelte.d.ts +3 -1
- package/dist/ui/components/ui/Logo.svelte +86 -0
- package/dist/ui/components/ui/Logo.svelte.d.ts +25 -0
- package/dist/ui/components/ui/LogoLoader.svelte +71 -0
- package/dist/ui/components/ui/LogoLoader.svelte.d.ts +9 -0
- package/dist/ui/components/ui/index.d.ts +2 -0
- package/dist/ui/components/ui/index.js +2 -0
- package/dist/ui/tailwind.preset.js +8 -8
- package/dist/utils/api.js +2 -1
- package/dist/utils/debounce.d.ts +4 -3
- package/dist/utils/debounce.js +10 -6
- package/dist/utils/gallery.d.ts +58 -32
- package/dist/utils/gallery.js +111 -129
- package/dist/utils/gutter.d.ts +47 -26
- package/dist/utils/gutter.js +116 -124
- package/dist/utils/imageProcessor.d.ts +66 -19
- package/dist/utils/imageProcessor.js +31 -10
- package/dist/utils/index.d.ts +11 -11
- package/dist/utils/index.js +4 -3
- package/dist/utils/json.js +1 -1
- package/dist/utils/markdown.d.ts +183 -103
- package/dist/utils/markdown.js +517 -678
- package/dist/utils/sanitize.d.ts +22 -12
- package/dist/utils/sanitize.js +268 -282
- package/dist/utils/validation.js +4 -3
- package/package.json +4 -3
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { marked } from "marked";
|
|
3
3
|
import { onMount, tick } from "svelte";
|
|
4
|
-
import { sanitizeMarkdown } from "../../utils/sanitize
|
|
4
|
+
import { sanitizeMarkdown } from "../../utils/sanitize";
|
|
5
5
|
import "../../styles/content.css";
|
|
6
|
-
import { Button, Input } from '../../ui';
|
|
6
|
+
import { Button, Input, Logo } from '../../ui';
|
|
7
7
|
import Dialog from "../../ui/components/ui/Dialog.svelte";
|
|
8
8
|
|
|
9
9
|
// Import composables
|
|
@@ -19,22 +19,32 @@
|
|
|
19
19
|
useCommandPalette,
|
|
20
20
|
} from "./composables/index.js";
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Object} StoredDraft
|
|
24
|
+
* @property {string} content
|
|
25
|
+
* @property {number} savedAt
|
|
26
|
+
* @property {number} [wordCount]
|
|
27
|
+
*/
|
|
28
|
+
|
|
22
29
|
// Props
|
|
23
30
|
let {
|
|
24
31
|
content = $bindable(""),
|
|
25
32
|
onSave = () => {},
|
|
26
33
|
saving = false,
|
|
27
34
|
readonly = false,
|
|
28
|
-
draftKey = null,
|
|
29
|
-
onDraftRestored = () => {},
|
|
35
|
+
draftKey = /** @type {string | null} */ (null),
|
|
36
|
+
onDraftRestored = /** @type {(draft: StoredDraft) => void} */ (() => {}),
|
|
30
37
|
previewTitle = "",
|
|
31
38
|
previewDate = "",
|
|
32
|
-
previewTags = [],
|
|
39
|
+
previewTags = /** @type {string[]} */ ([]),
|
|
33
40
|
} = $props();
|
|
34
41
|
|
|
35
42
|
// Core refs and state
|
|
43
|
+
/** @type {HTMLTextAreaElement | null} */
|
|
36
44
|
let textareaRef = $state(null);
|
|
45
|
+
/** @type {HTMLElement | null} */
|
|
37
46
|
let previewRef = $state(null);
|
|
47
|
+
/** @type {HTMLElement | null} */
|
|
38
48
|
let lineNumbersRef = $state(null);
|
|
39
49
|
let showPreview = $state(true);
|
|
40
50
|
let cursorLine = $state(1);
|
|
@@ -46,6 +56,7 @@
|
|
|
46
56
|
let isDragging = $state(false);
|
|
47
57
|
let isUploading = $state(false);
|
|
48
58
|
let uploadProgress = $state("");
|
|
59
|
+
/** @type {string | null} */
|
|
49
60
|
let uploadError = $state(null);
|
|
50
61
|
|
|
51
62
|
// Full preview mode
|
|
@@ -71,10 +82,11 @@
|
|
|
71
82
|
getWordCount: () => wordCount,
|
|
72
83
|
});
|
|
73
84
|
|
|
85
|
+
// svelte-ignore state_referenced_locally - draftKey, readonly, onDraftRestored don't change during lifecycle
|
|
74
86
|
const draftManager = useDraftManager({
|
|
75
87
|
draftKey,
|
|
76
88
|
getContent: () => content,
|
|
77
|
-
setContent: (c) => (content = c),
|
|
89
|
+
setContent: (/** @type {string} */ c) => (content = c),
|
|
78
90
|
onDraftRestored,
|
|
79
91
|
readonly,
|
|
80
92
|
});
|
|
@@ -82,7 +94,7 @@
|
|
|
82
94
|
const slashCommands = useSlashCommands({
|
|
83
95
|
getTextareaRef: () => textareaRef,
|
|
84
96
|
getContent: () => content,
|
|
85
|
-
setContent: (c) => (content = c),
|
|
97
|
+
setContent: (/** @type {string} */ c) => (content = c),
|
|
86
98
|
getSnippets: () => snippetsManager.snippets,
|
|
87
99
|
onOpenSnippetsModal: () => snippetsManager.openModal(),
|
|
88
100
|
});
|
|
@@ -116,7 +128,7 @@
|
|
|
116
128
|
let wordCount = $derived(content.trim() ? content.trim().split(/\s+/).length : 0);
|
|
117
129
|
let charCount = $derived(content.length);
|
|
118
130
|
let lineCount = $derived(content.split("\n").length);
|
|
119
|
-
let previewHtml = $derived(content ? sanitizeMarkdown(marked.parse(content)) : "");
|
|
131
|
+
let previewHtml = $derived(content ? sanitizeMarkdown(/** @type {string} */ (marked.parse(content, { async: false }))) : "");
|
|
120
132
|
|
|
121
133
|
let readingTime = $derived.by(() => {
|
|
122
134
|
const minutes = Math.ceil(wordCount / 200);
|
|
@@ -172,6 +184,7 @@
|
|
|
172
184
|
return availableAnchors;
|
|
173
185
|
}
|
|
174
186
|
|
|
187
|
+
/** @param {string} name */
|
|
175
188
|
export function insertAnchor(name) {
|
|
176
189
|
insertAtCursor(`<!-- anchor:${name} -->\n`);
|
|
177
190
|
}
|
|
@@ -195,6 +208,7 @@
|
|
|
195
208
|
}
|
|
196
209
|
|
|
197
210
|
// Keyboard handlers
|
|
211
|
+
/** @param {KeyboardEvent} e */
|
|
198
212
|
function handleKeydown(e) {
|
|
199
213
|
// Escape key handling
|
|
200
214
|
if (e.key === "Escape") {
|
|
@@ -213,9 +227,10 @@
|
|
|
213
227
|
}
|
|
214
228
|
|
|
215
229
|
// Slash commands trigger
|
|
216
|
-
if (e.key === "/" && !slashCommands.isOpen) {
|
|
230
|
+
if (e.key === "/" && !slashCommands.isOpen && textareaRef) {
|
|
217
231
|
const pos = textareaRef.selectionStart;
|
|
218
|
-
|
|
232
|
+
// Only trigger at start of line or after whitespace
|
|
233
|
+
if (pos === 0 || /\s$/.test(content.substring(0, pos))) {
|
|
219
234
|
setTimeout(() => slashCommands.open(), 0);
|
|
220
235
|
}
|
|
221
236
|
}
|
|
@@ -254,13 +269,15 @@
|
|
|
254
269
|
}
|
|
255
270
|
|
|
256
271
|
// Tab for indentation
|
|
257
|
-
if (e.key === "Tab") {
|
|
272
|
+
if (e.key === "Tab" && textareaRef) {
|
|
258
273
|
e.preventDefault();
|
|
259
274
|
const start = textareaRef.selectionStart;
|
|
260
275
|
const end = textareaRef.selectionEnd;
|
|
261
276
|
content = content.substring(0, start) + " " + content.substring(end);
|
|
262
277
|
setTimeout(() => {
|
|
263
|
-
|
|
278
|
+
if (textareaRef) {
|
|
279
|
+
textareaRef.selectionStart = textareaRef.selectionEnd = start + 2;
|
|
280
|
+
}
|
|
264
281
|
}, 0);
|
|
265
282
|
}
|
|
266
283
|
|
|
@@ -283,6 +300,7 @@
|
|
|
283
300
|
}
|
|
284
301
|
}
|
|
285
302
|
|
|
303
|
+
/** @param {KeyboardEvent} e */
|
|
286
304
|
function handleGlobalKeydown(e) {
|
|
287
305
|
if (e.key === "Escape") {
|
|
288
306
|
if (ambientSounds.showPanel) {
|
|
@@ -327,6 +345,10 @@
|
|
|
327
345
|
}
|
|
328
346
|
|
|
329
347
|
// Text manipulation helpers
|
|
348
|
+
/**
|
|
349
|
+
* @param {string} before
|
|
350
|
+
* @param {string} after
|
|
351
|
+
*/
|
|
330
352
|
async function wrapSelection(before, after) {
|
|
331
353
|
if (!textareaRef || isUpdating) return;
|
|
332
354
|
isUpdating = true;
|
|
@@ -347,6 +369,7 @@
|
|
|
347
369
|
isUpdating = false;
|
|
348
370
|
}
|
|
349
371
|
|
|
372
|
+
/** @param {string} text */
|
|
350
373
|
async function insertAtCursor(text) {
|
|
351
374
|
if (!textareaRef || isUpdating) return;
|
|
352
375
|
isUpdating = true;
|
|
@@ -365,6 +388,7 @@
|
|
|
365
388
|
}
|
|
366
389
|
|
|
367
390
|
// Toolbar actions
|
|
391
|
+
/** @param {number} level */
|
|
368
392
|
function insertHeading(level) {
|
|
369
393
|
insertAtCursor("#".repeat(level) + " ");
|
|
370
394
|
}
|
|
@@ -429,6 +453,7 @@
|
|
|
429
453
|
});
|
|
430
454
|
|
|
431
455
|
// Drag and drop handlers
|
|
456
|
+
/** @param {DragEvent} e */
|
|
432
457
|
function handleDragEnter(e) {
|
|
433
458
|
e.preventDefault();
|
|
434
459
|
if (readonly) return;
|
|
@@ -437,6 +462,7 @@
|
|
|
437
462
|
}
|
|
438
463
|
}
|
|
439
464
|
|
|
465
|
+
/** @param {DragEvent} e */
|
|
440
466
|
function handleDragOver(e) {
|
|
441
467
|
e.preventDefault();
|
|
442
468
|
if (readonly) return;
|
|
@@ -446,13 +472,16 @@
|
|
|
446
472
|
}
|
|
447
473
|
}
|
|
448
474
|
|
|
475
|
+
/** @param {DragEvent} e */
|
|
449
476
|
function handleDragLeave(e) {
|
|
450
477
|
e.preventDefault();
|
|
451
|
-
|
|
478
|
+
const target = /** @type {HTMLElement} */ (e.currentTarget);
|
|
479
|
+
if (!target.contains(/** @type {Node | null} */ (e.relatedTarget))) {
|
|
452
480
|
isDragging = false;
|
|
453
481
|
}
|
|
454
482
|
}
|
|
455
483
|
|
|
484
|
+
/** @param {DragEvent} e */
|
|
456
485
|
async function handleDrop(e) {
|
|
457
486
|
e.preventDefault();
|
|
458
487
|
isDragging = false;
|
|
@@ -472,6 +501,7 @@
|
|
|
472
501
|
}
|
|
473
502
|
}
|
|
474
503
|
|
|
504
|
+
/** @param {File} file */
|
|
475
505
|
async function uploadImage(file) {
|
|
476
506
|
isUploading = true;
|
|
477
507
|
uploadProgress = `Uploading ${file.name}...`;
|
|
@@ -499,7 +529,7 @@
|
|
|
499
529
|
|
|
500
530
|
uploadProgress = "";
|
|
501
531
|
} catch (err) {
|
|
502
|
-
uploadError = err.message;
|
|
532
|
+
uploadError = err instanceof Error ? err.message : String(err);
|
|
503
533
|
setTimeout(() => (uploadError = null), 5000);
|
|
504
534
|
} finally {
|
|
505
535
|
isUploading = false;
|
|
@@ -507,6 +537,7 @@
|
|
|
507
537
|
}
|
|
508
538
|
}
|
|
509
539
|
|
|
540
|
+
/** @param {ClipboardEvent} e */
|
|
510
541
|
function handlePaste(e) {
|
|
511
542
|
if (readonly) return;
|
|
512
543
|
|
|
@@ -528,6 +559,7 @@
|
|
|
528
559
|
}
|
|
529
560
|
|
|
530
561
|
// Command palette execution
|
|
562
|
+
/** @param {number} index */
|
|
531
563
|
function executePaletteCommand(index) {
|
|
532
564
|
const cmd = filteredPaletteCommands[index];
|
|
533
565
|
if (cmd && cmd.action) {
|
|
@@ -558,6 +590,7 @@
|
|
|
558
590
|
class:zen-mode={isZenMode}
|
|
559
591
|
class:campfire-mode={writingSession.isCampfireActive}
|
|
560
592
|
aria-label="Markdown editor with live preview"
|
|
593
|
+
role="application"
|
|
561
594
|
ondragenter={handleDragEnter}
|
|
562
595
|
ondragover={handleDragOver}
|
|
563
596
|
ondragleave={handleDragLeave}
|
|
@@ -717,6 +750,7 @@
|
|
|
717
750
|
<div class="preview-panel">
|
|
718
751
|
<div class="preview-header">
|
|
719
752
|
<span class="preview-label">:: preview</span>
|
|
753
|
+
<Logo class="preview-logo" />
|
|
720
754
|
</div>
|
|
721
755
|
<div class="preview-content" bind:this={previewRef}>
|
|
722
756
|
{#if previewHtml}
|
|
@@ -760,7 +794,7 @@
|
|
|
760
794
|
onclick={() => ambientSounds.togglePanel()}
|
|
761
795
|
title="Ambient sounds"
|
|
762
796
|
>
|
|
763
|
-
[{soundLibrary[ambientSounds.currentSound]?.name || "snd"}]{#if ambientSounds.enabled}<span class="sound-wave">~</span>{/if}
|
|
797
|
+
[{soundLibrary[/** @type {keyof typeof soundLibrary} */ (ambientSounds.currentSound)]?.name || "snd"}]{#if ambientSounds.enabled}<span class="sound-wave">~</span>{/if}
|
|
764
798
|
</button>
|
|
765
799
|
<span class="status-divider">|</span>
|
|
766
800
|
{#if editorSettings.typewriterMode}
|
|
@@ -800,14 +834,16 @@
|
|
|
800
834
|
|
|
801
835
|
<!-- Command Palette -->
|
|
802
836
|
{#if commandPalette.isOpen}
|
|
803
|
-
|
|
837
|
+
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
|
|
838
|
+
<div class="command-palette-overlay" onclick={() => commandPalette.close()} role="dialog" aria-modal="true" aria-label="Command palette">
|
|
839
|
+
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
|
|
804
840
|
<div class="command-palette" onclick={(e) => e.stopPropagation()}>
|
|
805
841
|
<input
|
|
806
842
|
type="text"
|
|
807
843
|
class="command-palette-input"
|
|
808
844
|
placeholder="> type a command..."
|
|
809
845
|
value={commandPalette.query}
|
|
810
|
-
oninput={(e) => commandPalette.setQuery(e.target.value)}
|
|
846
|
+
oninput={(e) => commandPalette.setQuery(/** @type {HTMLInputElement} */ (e.target).value)}
|
|
811
847
|
onkeydown={(e) => {
|
|
812
848
|
if (e.key === "ArrowDown") {
|
|
813
849
|
e.preventDefault();
|
|
@@ -860,9 +896,10 @@
|
|
|
860
896
|
{/if}
|
|
861
897
|
|
|
862
898
|
<!-- Snippets Modal -->
|
|
863
|
-
<Dialog
|
|
864
|
-
|
|
865
|
-
|
|
899
|
+
<Dialog
|
|
900
|
+
bind:open={snippetsManager.modal.open}
|
|
901
|
+
title={`:: ${snippetsManager.modal.editingId ? "edit snippet" : "new snippet"}`}
|
|
902
|
+
>
|
|
866
903
|
<div class="snippets-modal-body">
|
|
867
904
|
<div class="snippets-form">
|
|
868
905
|
<div class="snippet-field">
|
|
@@ -898,7 +935,10 @@
|
|
|
898
935
|
|
|
899
936
|
<div class="snippet-actions">
|
|
900
937
|
{#if snippetsManager.modal.editingId}
|
|
901
|
-
<Button variant="danger" onclick={() =>
|
|
938
|
+
<Button variant="danger" onclick={() => {
|
|
939
|
+
const id = snippetsManager.modal.editingId;
|
|
940
|
+
if (id) snippetsManager.deleteSnippet(id);
|
|
941
|
+
}}>
|
|
902
942
|
[<span class="key">d</span>elete]
|
|
903
943
|
</Button>
|
|
904
944
|
{/if}
|
|
@@ -971,7 +1011,10 @@
|
|
|
971
1011
|
max="1"
|
|
972
1012
|
step="0.05"
|
|
973
1013
|
value={ambientSounds.volume}
|
|
974
|
-
oninput={(e) =>
|
|
1014
|
+
oninput={(e) => {
|
|
1015
|
+
const target = /** @type {HTMLInputElement} */ (e.target);
|
|
1016
|
+
ambientSounds.setVolume(parseFloat(target.value));
|
|
1017
|
+
}}
|
|
975
1018
|
class="volume-slider"
|
|
976
1019
|
/>
|
|
977
1020
|
</label>
|
|
@@ -994,7 +1037,8 @@
|
|
|
994
1037
|
|
|
995
1038
|
<!-- Full Preview Modal -->
|
|
996
1039
|
{#if showFullPreview}
|
|
997
|
-
<div class="full-preview-modal" role="dialog" aria-modal="true">
|
|
1040
|
+
<div class="full-preview-modal" role="dialog" aria-modal="true" aria-label="Full article preview">
|
|
1041
|
+
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
|
|
998
1042
|
<div class="full-preview-backdrop" onclick={() => (showFullPreview = false)}></div>
|
|
999
1043
|
<div class="full-preview-container">
|
|
1000
1044
|
<header class="full-preview-header">
|
|
@@ -1357,6 +1401,9 @@
|
|
|
1357
1401
|
min-height: 0;
|
|
1358
1402
|
}
|
|
1359
1403
|
.preview-header {
|
|
1404
|
+
display: flex;
|
|
1405
|
+
align-items: center;
|
|
1406
|
+
justify-content: space-between;
|
|
1360
1407
|
padding: 0.5rem 1rem;
|
|
1361
1408
|
background: #2d2d2d;
|
|
1362
1409
|
border-bottom: 1px solid var(--light-border-primary);
|
|
@@ -1366,6 +1413,16 @@
|
|
|
1366
1413
|
font-size: 0.85rem;
|
|
1367
1414
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
|
1368
1415
|
}
|
|
1416
|
+
:global(.preview-logo) {
|
|
1417
|
+
width: 18px;
|
|
1418
|
+
height: 18px;
|
|
1419
|
+
color: var(--editor-accent, #8bc48b);
|
|
1420
|
+
opacity: 0.6;
|
|
1421
|
+
transition: opacity 0.2s ease;
|
|
1422
|
+
}
|
|
1423
|
+
:global(.preview-logo:hover) {
|
|
1424
|
+
opacity: 1;
|
|
1425
|
+
}
|
|
1369
1426
|
.preview-content {
|
|
1370
1427
|
flex: 1;
|
|
1371
1428
|
padding: 1rem;
|
|
@@ -4,9 +4,12 @@ type MarkdownEditor = {
|
|
|
4
4
|
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
5
|
} & {
|
|
6
6
|
getAvailableAnchors: () => string[];
|
|
7
|
-
insertAnchor: (name:
|
|
7
|
+
insertAnchor: (name: string) => void;
|
|
8
8
|
clearDraft: () => void;
|
|
9
|
-
getDraftStatus: () =>
|
|
9
|
+
getDraftStatus: () => {
|
|
10
|
+
hasDraft: boolean;
|
|
11
|
+
storedDraft: StoredDraft | null;
|
|
12
|
+
};
|
|
10
13
|
};
|
|
11
14
|
declare const MarkdownEditor: import("svelte").Component<{
|
|
12
15
|
content?: string;
|
|
@@ -14,15 +17,18 @@ declare const MarkdownEditor: import("svelte").Component<{
|
|
|
14
17
|
saving?: boolean;
|
|
15
18
|
readonly?: boolean;
|
|
16
19
|
draftKey?: any;
|
|
17
|
-
onDraftRestored?:
|
|
20
|
+
onDraftRestored?: any;
|
|
18
21
|
previewTitle?: string;
|
|
19
22
|
previewDate?: string;
|
|
20
|
-
previewTags?: any
|
|
23
|
+
previewTags?: any;
|
|
21
24
|
}, {
|
|
22
25
|
getAvailableAnchors: () => string[];
|
|
23
|
-
insertAnchor: (name:
|
|
26
|
+
insertAnchor: (name: string) => void;
|
|
24
27
|
clearDraft: () => void;
|
|
25
|
-
getDraftStatus: () =>
|
|
28
|
+
getDraftStatus: () => {
|
|
29
|
+
hasDraft: boolean;
|
|
30
|
+
storedDraft: import("./composables/useDraftManager.svelte.js").StoredDraft | null;
|
|
31
|
+
};
|
|
26
32
|
}, "content">;
|
|
27
33
|
type $$ComponentProps = {
|
|
28
34
|
content?: string;
|
|
@@ -30,8 +36,8 @@ type $$ComponentProps = {
|
|
|
30
36
|
saving?: boolean;
|
|
31
37
|
readonly?: boolean;
|
|
32
38
|
draftKey?: any;
|
|
33
|
-
onDraftRestored?:
|
|
39
|
+
onDraftRestored?: any;
|
|
34
40
|
previewTitle?: string;
|
|
35
41
|
previewDate?: string;
|
|
36
|
-
previewTags?: any
|
|
42
|
+
previewTags?: any;
|
|
37
43
|
};
|
|
@@ -1,8 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} AmbientSoundsState
|
|
3
|
+
* @property {boolean} enabled
|
|
4
|
+
* @property {string} currentSound
|
|
5
|
+
* @property {number} volume
|
|
6
|
+
* @property {boolean} showPanel
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} AmbientSoundsManager
|
|
10
|
+
* @property {AmbientSoundsState} state
|
|
11
|
+
* @property {boolean} enabled
|
|
12
|
+
* @property {string} currentSound
|
|
13
|
+
* @property {number} volume
|
|
14
|
+
* @property {boolean} showPanel
|
|
15
|
+
* @property {() => void} loadSettings
|
|
16
|
+
* @property {() => void} toggle
|
|
17
|
+
* @property {(soundKey: string) => void} play
|
|
18
|
+
* @property {() => void} stop
|
|
19
|
+
* @property {(volume: number) => void} setVolume
|
|
20
|
+
* @property {(soundKey: string) => void} selectSound
|
|
21
|
+
* @property {() => void} togglePanel
|
|
22
|
+
* @property {() => void} closePanel
|
|
23
|
+
* @property {() => void} cleanup
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {keyof typeof soundLibrary} SoundKey
|
|
27
|
+
*/
|
|
1
28
|
/**
|
|
2
29
|
* Creates an ambient sounds manager with Svelte 5 runes
|
|
3
|
-
* @returns {
|
|
30
|
+
* @returns {AmbientSoundsManager} Ambient sounds state and controls
|
|
4
31
|
*/
|
|
5
|
-
export function useAmbientSounds():
|
|
32
|
+
export function useAmbientSounds(): AmbientSoundsManager;
|
|
6
33
|
export namespace soundLibrary {
|
|
7
34
|
namespace forest {
|
|
8
35
|
let name: string;
|
|
@@ -51,3 +78,26 @@ export namespace soundLibrary {
|
|
|
51
78
|
export { description_4 as description };
|
|
52
79
|
}
|
|
53
80
|
}
|
|
81
|
+
export type AmbientSoundsState = {
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
currentSound: string;
|
|
84
|
+
volume: number;
|
|
85
|
+
showPanel: boolean;
|
|
86
|
+
};
|
|
87
|
+
export type AmbientSoundsManager = {
|
|
88
|
+
state: AmbientSoundsState;
|
|
89
|
+
enabled: boolean;
|
|
90
|
+
currentSound: string;
|
|
91
|
+
volume: number;
|
|
92
|
+
showPanel: boolean;
|
|
93
|
+
loadSettings: () => void;
|
|
94
|
+
toggle: () => void;
|
|
95
|
+
play: (soundKey: string) => void;
|
|
96
|
+
stop: () => void;
|
|
97
|
+
setVolume: (volume: number) => void;
|
|
98
|
+
selectSound: (soundKey: string) => void;
|
|
99
|
+
togglePanel: () => void;
|
|
100
|
+
closePanel: () => void;
|
|
101
|
+
cleanup: () => void;
|
|
102
|
+
};
|
|
103
|
+
export type SoundKey = keyof typeof soundLibrary;
|
|
@@ -39,18 +39,49 @@ export const soundLibrary = {
|
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {Object} AmbientSoundsState
|
|
44
|
+
* @property {boolean} enabled
|
|
45
|
+
* @property {string} currentSound
|
|
46
|
+
* @property {number} volume
|
|
47
|
+
* @property {boolean} showPanel
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {Object} AmbientSoundsManager
|
|
52
|
+
* @property {AmbientSoundsState} state
|
|
53
|
+
* @property {boolean} enabled
|
|
54
|
+
* @property {string} currentSound
|
|
55
|
+
* @property {number} volume
|
|
56
|
+
* @property {boolean} showPanel
|
|
57
|
+
* @property {() => void} loadSettings
|
|
58
|
+
* @property {() => void} toggle
|
|
59
|
+
* @property {(soundKey: string) => void} play
|
|
60
|
+
* @property {() => void} stop
|
|
61
|
+
* @property {(volume: number) => void} setVolume
|
|
62
|
+
* @property {(soundKey: string) => void} selectSound
|
|
63
|
+
* @property {() => void} togglePanel
|
|
64
|
+
* @property {() => void} closePanel
|
|
65
|
+
* @property {() => void} cleanup
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {keyof typeof soundLibrary} SoundKey
|
|
70
|
+
*/
|
|
71
|
+
|
|
42
72
|
/**
|
|
43
73
|
* Creates an ambient sounds manager with Svelte 5 runes
|
|
44
|
-
* @returns {
|
|
74
|
+
* @returns {AmbientSoundsManager} Ambient sounds state and controls
|
|
45
75
|
*/
|
|
46
76
|
export function useAmbientSounds() {
|
|
47
77
|
let state = $state({
|
|
48
78
|
enabled: false,
|
|
49
|
-
currentSound: "forest",
|
|
79
|
+
currentSound: /** @type {string} */ ("forest"),
|
|
50
80
|
volume: 0.3,
|
|
51
81
|
showPanel: false,
|
|
52
82
|
});
|
|
53
83
|
|
|
84
|
+
/** @type {HTMLAudioElement | null} */
|
|
54
85
|
let audioElement = $state(null);
|
|
55
86
|
|
|
56
87
|
function loadSettings() {
|
|
@@ -89,8 +120,9 @@ export function useAmbientSounds() {
|
|
|
89
120
|
}
|
|
90
121
|
}
|
|
91
122
|
|
|
123
|
+
/** @param {string} soundKey */
|
|
92
124
|
function play(soundKey) {
|
|
93
|
-
const sound = soundLibrary[soundKey];
|
|
125
|
+
const sound = soundLibrary[/** @type {keyof typeof soundLibrary} */ (soundKey)];
|
|
94
126
|
if (!sound) return;
|
|
95
127
|
|
|
96
128
|
// Stop current sound if playing
|
|
@@ -117,7 +149,7 @@ export function useAmbientSounds() {
|
|
|
117
149
|
state.currentSound = soundKey;
|
|
118
150
|
saveSettings();
|
|
119
151
|
})
|
|
120
|
-
.catch((e) => {
|
|
152
|
+
.catch((/** @type {Error} */ e) => {
|
|
121
153
|
console.warn("Failed to play sound:", e);
|
|
122
154
|
state.enabled = false;
|
|
123
155
|
});
|
|
@@ -131,6 +163,7 @@ export function useAmbientSounds() {
|
|
|
131
163
|
state.enabled = false;
|
|
132
164
|
}
|
|
133
165
|
|
|
166
|
+
/** @param {number} newVolume */
|
|
134
167
|
function setVolume(newVolume) {
|
|
135
168
|
state.volume = newVolume;
|
|
136
169
|
if (audioElement) {
|
|
@@ -139,6 +172,7 @@ export function useAmbientSounds() {
|
|
|
139
172
|
saveSettings();
|
|
140
173
|
}
|
|
141
174
|
|
|
175
|
+
/** @param {string} soundKey */
|
|
142
176
|
function selectSound(soundKey) {
|
|
143
177
|
if (state.enabled) {
|
|
144
178
|
play(soundKey);
|
|
@@ -2,16 +2,86 @@
|
|
|
2
2
|
* Command Palette Composable
|
|
3
3
|
* Manages the command palette (Cmd+K) functionality
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} PaletteAction
|
|
7
|
+
* @property {string} id
|
|
8
|
+
* @property {string} label
|
|
9
|
+
* @property {string} shortcut
|
|
10
|
+
* @property {() => void} action
|
|
11
|
+
* @property {string} [themeKey]
|
|
12
|
+
* @property {boolean} [isTheme]
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} CommandPaletteState
|
|
16
|
+
* @property {boolean} open
|
|
17
|
+
* @property {string} query
|
|
18
|
+
* @property {number} selectedIndex
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {Object} CommandPaletteOptions
|
|
22
|
+
* @property {() => PaletteAction[]} [getActions] - Function to get available actions
|
|
23
|
+
* @property {() => Record<string, any>} [getThemes] - Function to get available themes
|
|
24
|
+
* @property {() => string} [getCurrentTheme] - Function to get current theme
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} CommandPaletteManager
|
|
28
|
+
* @property {CommandPaletteState} state
|
|
29
|
+
* @property {boolean} isOpen
|
|
30
|
+
* @property {string} query
|
|
31
|
+
* @property {number} selectedIndex
|
|
32
|
+
* @property {() => PaletteAction[]} getAllCommands
|
|
33
|
+
* @property {() => PaletteAction[]} getFilteredCommands
|
|
34
|
+
* @property {() => void} open
|
|
35
|
+
* @property {() => void} close
|
|
36
|
+
* @property {() => void} toggle
|
|
37
|
+
* @property {(direction: 'up' | 'down') => void} navigate
|
|
38
|
+
* @property {(index: number) => PaletteAction | undefined} execute
|
|
39
|
+
* @property {(query: string) => void} setQuery
|
|
40
|
+
*/
|
|
5
41
|
/**
|
|
6
42
|
* Creates a command palette manager with Svelte 5 runes
|
|
7
|
-
* @param {
|
|
8
|
-
* @
|
|
9
|
-
* @param {Function} options.getThemes - Function to get available themes
|
|
10
|
-
* @param {Function} options.getCurrentTheme - Function to get current theme
|
|
11
|
-
* @returns {object} Command palette state and controls
|
|
43
|
+
* @param {CommandPaletteOptions} options - Configuration options
|
|
44
|
+
* @returns {CommandPaletteManager} Command palette state and controls
|
|
12
45
|
*/
|
|
13
|
-
export function useCommandPalette(options?:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
46
|
+
export function useCommandPalette(options?: CommandPaletteOptions): CommandPaletteManager;
|
|
47
|
+
export type PaletteAction = {
|
|
48
|
+
id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
shortcut: string;
|
|
51
|
+
action: () => void;
|
|
52
|
+
themeKey?: string | undefined;
|
|
53
|
+
isTheme?: boolean | undefined;
|
|
54
|
+
};
|
|
55
|
+
export type CommandPaletteState = {
|
|
56
|
+
open: boolean;
|
|
57
|
+
query: string;
|
|
58
|
+
selectedIndex: number;
|
|
59
|
+
};
|
|
60
|
+
export type CommandPaletteOptions = {
|
|
61
|
+
/**
|
|
62
|
+
* - Function to get available actions
|
|
63
|
+
*/
|
|
64
|
+
getActions?: (() => PaletteAction[]) | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* - Function to get available themes
|
|
67
|
+
*/
|
|
68
|
+
getThemes?: (() => Record<string, any>) | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* - Function to get current theme
|
|
71
|
+
*/
|
|
72
|
+
getCurrentTheme?: (() => string) | undefined;
|
|
73
|
+
};
|
|
74
|
+
export type CommandPaletteManager = {
|
|
75
|
+
state: CommandPaletteState;
|
|
76
|
+
isOpen: boolean;
|
|
77
|
+
query: string;
|
|
78
|
+
selectedIndex: number;
|
|
79
|
+
getAllCommands: () => PaletteAction[];
|
|
80
|
+
getFilteredCommands: () => PaletteAction[];
|
|
81
|
+
open: () => void;
|
|
82
|
+
close: () => void;
|
|
83
|
+
toggle: () => void;
|
|
84
|
+
navigate: (direction: "up" | "down") => void;
|
|
85
|
+
execute: (index: number) => PaletteAction | undefined;
|
|
86
|
+
setQuery: (query: string) => void;
|
|
87
|
+
};
|