@pixerate/editor-svelte 0.18.0 → 0.20.0

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.
Files changed (81) hide show
  1. package/README.md +26 -2
  2. package/dist/EditableTextNodeEditor.svelte +19 -3
  3. package/dist/EditableTextNodeEditor.svelte.d.ts +5 -1
  4. package/dist/EditableTextNodeEditor.svelte.d.ts.map +1 -1
  5. package/dist/OutlinePanel.svelte +85 -0
  6. package/dist/OutlinePanel.svelte.d.ts +25 -0
  7. package/dist/OutlinePanel.svelte.d.ts.map +1 -0
  8. package/dist/TemplateRenderer.svelte +54 -7
  9. package/dist/TemplateRenderer.svelte.d.ts +8 -1
  10. package/dist/TemplateRenderer.svelte.d.ts.map +1 -1
  11. package/dist/VariableForm.svelte +86 -0
  12. package/dist/VariableForm.svelte.d.ts +21 -0
  13. package/dist/VariableForm.svelte.d.ts.map +1 -0
  14. package/dist/agentTools.svelte.d.ts +12 -0
  15. package/dist/agentTools.svelte.d.ts.map +1 -0
  16. package/dist/agentTools.svelte.js +17 -0
  17. package/dist/canvas/CanvasGuidesLayer.svelte +52 -0
  18. package/dist/canvas/CanvasGuidesLayer.svelte.d.ts +16 -0
  19. package/dist/canvas/CanvasGuidesLayer.svelte.d.ts.map +1 -0
  20. package/dist/canvas/createCanvasArrange.svelte.d.ts +63 -0
  21. package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -0
  22. package/dist/canvas/createCanvasArrange.svelte.js +60 -0
  23. package/dist/canvas/createCanvasScenes.svelte.d.ts +51 -0
  24. package/dist/canvas/createCanvasScenes.svelte.d.ts.map +1 -0
  25. package/dist/canvas/createCanvasScenes.svelte.js +62 -0
  26. package/dist/canvas/index.d.ts +4 -0
  27. package/dist/canvas/index.d.ts.map +1 -1
  28. package/dist/canvas/index.js +5 -0
  29. package/dist/fileImport.svelte.d.ts +25 -0
  30. package/dist/fileImport.svelte.d.ts.map +1 -0
  31. package/dist/fileImport.svelte.js +40 -0
  32. package/dist/history/createHistoryShortcuts.d.ts +1 -1
  33. package/dist/history/createHistoryShortcuts.d.ts.map +1 -1
  34. package/dist/history/history.svelte.d.ts +1 -1
  35. package/dist/history/history.svelte.d.ts.map +1 -1
  36. package/dist/history/history.svelte.js +1 -1
  37. package/dist/index.d.ts +11 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +6 -0
  40. package/dist/outline.svelte.d.ts +18 -0
  41. package/dist/outline.svelte.d.ts.map +1 -0
  42. package/dist/outline.svelte.js +27 -0
  43. package/dist/review/CommentButton.svelte +32 -0
  44. package/dist/review/CommentButton.svelte.d.ts +10 -0
  45. package/dist/review/CommentButton.svelte.d.ts.map +1 -0
  46. package/dist/review/CommentsPanel.svelte +226 -0
  47. package/dist/review/CommentsPanel.svelte.d.ts +18 -0
  48. package/dist/review/CommentsPanel.svelte.d.ts.map +1 -0
  49. package/dist/review/ReviewToolbar.svelte +86 -0
  50. package/dist/review/ReviewToolbar.svelte.d.ts +9 -0
  51. package/dist/review/ReviewToolbar.svelte.d.ts.map +1 -0
  52. package/dist/review/index.d.ts +12 -0
  53. package/dist/review/index.d.ts.map +1 -0
  54. package/dist/review/index.js +4 -0
  55. package/dist/review/review.svelte.d.ts +40 -0
  56. package/dist/review/review.svelte.d.ts.map +1 -0
  57. package/dist/review/review.svelte.js +104 -0
  58. package/dist/spreadsheet/FormulaBar.svelte +14 -6
  59. package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -1
  60. package/dist/spreadsheet/FormulaHints.svelte +149 -0
  61. package/dist/spreadsheet/FormulaHints.svelte.d.ts +14 -0
  62. package/dist/spreadsheet/FormulaHints.svelte.d.ts.map +1 -0
  63. package/dist/spreadsheet/SpreadsheetContextMenu.svelte +60 -0
  64. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts +11 -0
  65. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -0
  66. package/dist/spreadsheet/SpreadsheetEditor.svelte +172 -19
  67. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +1 -1
  68. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
  69. package/dist/spreadsheet/SpreadsheetGroupRow.svelte +55 -0
  70. package/dist/spreadsheet/SpreadsheetGroupRow.svelte.d.ts +14 -0
  71. package/dist/spreadsheet/SpreadsheetGroupRow.svelte.d.ts.map +1 -0
  72. package/dist/spreadsheet/index.d.ts +6 -0
  73. package/dist/spreadsheet/index.d.ts.map +1 -1
  74. package/dist/spreadsheet/index.js +4 -0
  75. package/dist/spreadsheet/spreadsheet.svelte.d.ts +43 -5
  76. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
  77. package/dist/spreadsheet/spreadsheet.svelte.js +16 -2
  78. package/dist/spreadsheet/tableInteractions.d.ts +28 -0
  79. package/dist/spreadsheet/tableInteractions.d.ts.map +1 -0
  80. package/dist/spreadsheet/tableInteractions.js +61 -0
  81. package/package.json +15 -3
@@ -0,0 +1,226 @@
1
+ <script lang="ts">
2
+ import type { Editor } from "@tiptap/core";
3
+ import {
4
+ formatReviewTime,
5
+ getCommentAuthor,
6
+ type CommentStore,
7
+ type CommentThread,
8
+ type ReviewAuthor,
9
+ } from "@pixerate/editor";
10
+ import { createComments, type SvelteComments } from "./review.svelte.js";
11
+
12
+ interface Props {
13
+ editor?: Editor | null;
14
+ /** Defaults to the store of the editor's `Comments` extension. */
15
+ store?: CommentStore;
16
+ /** Author of replies. Defaults to the `Comments` extension's author. */
17
+ author?: ReviewAuthor;
18
+ /** Initially show resolved threads. */
19
+ showResolved?: boolean;
20
+ class?: string;
21
+ formatTime?: (timestamp: number) => string;
22
+ emptyMessage?: string;
23
+ }
24
+
25
+ let {
26
+ editor = null,
27
+ store: storeProp = undefined,
28
+ author: authorProp = undefined,
29
+ showResolved: showResolvedInitial = false,
30
+ class: className = "flex flex-col gap-2 text-sm text-foreground",
31
+ formatTime = (t: number) => formatReviewTime(t),
32
+ emptyMessage = "No comments yet. Select text and add a comment.",
33
+ }: Props = $props();
34
+
35
+ const buttonClass =
36
+ "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
37
+ const primaryButtonClass =
38
+ "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
39
+ const textareaClass =
40
+ "w-full resize-y rounded-md border border-input bg-background p-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary";
41
+
42
+ // svelte-ignore state_referenced_locally
43
+ let showResolved = $state(showResolvedInitial);
44
+ let drafts = $state<Record<string, string>>({});
45
+ let comments = $state.raw<SvelteComments>(createComments(null));
46
+
47
+ $effect(() => {
48
+ const c = createComments(storeProp ?? editor);
49
+ comments = c;
50
+ return () => c.destroy();
51
+ });
52
+
53
+ let author = $derived(
54
+ authorProp ?? getCommentAuthor(editor) ?? { id: "anonymous", name: "Anonymous" },
55
+ );
56
+ let threads = $derived(comments.threads);
57
+ let visible = $derived(threads.filter((t) => showResolved || !t.resolved));
58
+ let resolvedCount = $derived(threads.filter((t) => t.resolved).length);
59
+
60
+ const live = () => (editor && !editor.isDestroyed ? editor : null);
61
+
62
+ function focus(thread: CommentThread) {
63
+ if (thread.id === comments.activeThreadId) return;
64
+ const e = live();
65
+ if (e && !thread.orphaned) e.commands.focusComment(thread.id);
66
+ else comments.store?.setActiveThread(thread.id);
67
+ }
68
+
69
+ function remove(thread: CommentThread) {
70
+ const e = live();
71
+ if (e) e.commands.deleteComment(thread.id);
72
+ else comments.store?.deleteThread(thread.id);
73
+ }
74
+
75
+ function setResolved(thread: CommentThread, resolved: boolean) {
76
+ const e = live();
77
+ if (e) e.commands.resolveComment(thread.id, resolved);
78
+ else comments.store?.resolveThread(thread.id, resolved, author);
79
+ }
80
+
81
+ function submit(thread: CommentThread) {
82
+ const body = (drafts[thread.id] ?? "").trim();
83
+ const store = comments.store;
84
+ if (!body || !store) return;
85
+ if (thread.body === "") store.editComment(thread.id, body);
86
+ else store.addReply(thread.id, { body, author });
87
+ drafts[thread.id] = "";
88
+ }
89
+
90
+ function onCardKeydown(event: KeyboardEvent, thread: CommentThread) {
91
+ if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
92
+ event.preventDefault();
93
+ focus(thread);
94
+ }
95
+ }
96
+
97
+ function onTextareaKeydown(event: KeyboardEvent, thread: CommentThread) {
98
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
99
+ event.preventDefault();
100
+ submit(thread);
101
+ }
102
+ }
103
+ </script>
104
+
105
+ <aside class={className} aria-label="Comments">
106
+ <div class="flex items-center justify-between gap-2">
107
+ <span class="text-xs font-medium text-muted-foreground">
108
+ {threads.length - resolvedCount} open{resolvedCount > 0 ? ` · ${resolvedCount} resolved` : ""}
109
+ </span>
110
+ {#if resolvedCount > 0}
111
+ <button
112
+ type="button"
113
+ class={buttonClass}
114
+ aria-pressed={showResolved}
115
+ onclick={() => (showResolved = !showResolved)}
116
+ >
117
+ {showResolved ? "Hide resolved" : "Show resolved"}
118
+ </button>
119
+ {/if}
120
+ </div>
121
+ {#if visible.length === 0}
122
+ <p class="text-xs text-muted-foreground">{emptyMessage}</p>
123
+ {:else}
124
+ <ul class="flex flex-col gap-2" role="list">
125
+ {#each visible as thread (thread.id)}
126
+ {@const active = thread.id === comments.activeThreadId}
127
+ {@const isDraft = thread.body === ""}
128
+ {@const quote = thread.anchor?.quote ?? thread.lastQuote ?? ""}
129
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -->
130
+ <li
131
+ class={`flex flex-col gap-2 rounded-md border bg-background p-3 cursor-pointer ${
132
+ active ? "border-primary ring-1 ring-primary" : "border-border"
133
+ } ${thread.resolved ? "opacity-70" : ""}`}
134
+ data-thread-id={thread.id}
135
+ data-active={active || undefined}
136
+ aria-current={active || undefined}
137
+ tabindex="0"
138
+ onclick={() => focus(thread)}
139
+ onkeydown={(e) => onCardKeydown(e, thread)}
140
+ >
141
+ <div class="flex items-baseline justify-between gap-2">
142
+ <span class="font-medium">{thread.author.name}</span>
143
+ <span class="text-xs text-muted-foreground">{formatTime(thread.createdAt)}</span>
144
+ </div>
145
+ {#if quote}
146
+ <blockquote class="border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2">
147
+ {quote}
148
+ </blockquote>
149
+ {/if}
150
+ {#if thread.orphaned}
151
+ <span class="text-xs text-destructive">The commented text was deleted.</span>
152
+ {/if}
153
+ {#if thread.resolved}
154
+ <span class="text-xs text-muted-foreground">Resolved</span>
155
+ {/if}
156
+ {#if !isDraft}
157
+ <p class="whitespace-pre-wrap break-words">{thread.body}</p>
158
+ {/if}
159
+ {#if thread.replies.length > 0}
160
+ <ul class="flex flex-col gap-2 border-l border-border pl-3" role="list">
161
+ {#each thread.replies as reply (reply.id)}
162
+ <li class="flex flex-col gap-0.5" data-reply-id={reply.id}>
163
+ <span class="text-xs">
164
+ <span class="font-medium">{reply.author.name}</span>
165
+ <span class="text-muted-foreground">{formatTime(reply.createdAt)}</span>
166
+ </span>
167
+ <p class="whitespace-pre-wrap break-words">{reply.body}</p>
168
+ </li>
169
+ {/each}
170
+ </ul>
171
+ {/if}
172
+ {#if (active || isDraft) && !thread.resolved}
173
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
174
+ <div class="flex flex-col gap-1" onclick={(e) => e.stopPropagation()}>
175
+ <!-- svelte-ignore a11y_autofocus -->
176
+ <textarea
177
+ class={textareaClass}
178
+ rows="2"
179
+ aria-label={isDraft ? "Comment" : "Reply"}
180
+ placeholder={isDraft ? "Add a comment…" : "Reply…"}
181
+ autofocus={isDraft}
182
+ value={drafts[thread.id] ?? ""}
183
+ oninput={(e) => (drafts[thread.id] = e.currentTarget.value)}
184
+ onkeydown={(e) => onTextareaKeydown(e, thread)}
185
+ ></textarea>
186
+ <div class="flex justify-end gap-1">
187
+ {#if isDraft}
188
+ <button type="button" class={buttonClass} onclick={() => remove(thread)}>
189
+ Cancel
190
+ </button>
191
+ {/if}
192
+ <button
193
+ type="button"
194
+ class={primaryButtonClass}
195
+ disabled={!(drafts[thread.id] ?? "").trim()}
196
+ onclick={() => submit(thread)}
197
+ >
198
+ {isDraft ? "Comment" : "Reply"}
199
+ </button>
200
+ </div>
201
+ </div>
202
+ {/if}
203
+ {#if !isDraft}
204
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
205
+ <div class="flex justify-end gap-1" onclick={(e) => e.stopPropagation()}>
206
+ <button
207
+ type="button"
208
+ class={buttonClass}
209
+ onclick={() => setResolved(thread, !thread.resolved)}
210
+ >
211
+ {thread.resolved ? "Reopen" : "Resolve"}
212
+ </button>
213
+ <button
214
+ type="button"
215
+ class={`${buttonClass} hover:text-destructive`}
216
+ onclick={() => remove(thread)}
217
+ >
218
+ Delete
219
+ </button>
220
+ </div>
221
+ {/if}
222
+ </li>
223
+ {/each}
224
+ </ul>
225
+ {/if}
226
+ </aside>
@@ -0,0 +1,18 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ import { type CommentStore, type ReviewAuthor } from "@pixerate/editor";
3
+ interface Props {
4
+ editor?: Editor | null;
5
+ /** Defaults to the store of the editor's `Comments` extension. */
6
+ store?: CommentStore;
7
+ /** Author of replies. Defaults to the `Comments` extension's author. */
8
+ author?: ReviewAuthor;
9
+ /** Initially show resolved threads. */
10
+ showResolved?: boolean;
11
+ class?: string;
12
+ formatTime?: (timestamp: number) => string;
13
+ emptyMessage?: string;
14
+ }
15
+ declare const CommentsPanel: import("svelte").Component<Props, {}, "">;
16
+ type CommentsPanel = ReturnType<typeof CommentsPanel>;
17
+ export default CommentsPanel;
18
+ //# sourceMappingURL=CommentsPanel.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentsPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/review/CommentsPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAGH,KAAK,YAAY,EAEjB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAI1B,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kEAAkE;IAClE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,wEAAwE;IACxE,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAiLH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import type { Editor } from "@tiptap/core";
3
+ import { createTrackChanges, type SvelteTrackChanges } from "./review.svelte.js";
4
+
5
+ interface Props {
6
+ editor?: Editor | null;
7
+ class?: string;
8
+ }
9
+
10
+ let {
11
+ editor = null,
12
+ class: className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm",
13
+ }: Props = $props();
14
+
15
+ const buttonClass =
16
+ "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
17
+
18
+ let review = $state.raw<SvelteTrackChanges>(createTrackChanges(null));
19
+
20
+ $effect(() => {
21
+ const r = createTrackChanges(editor);
22
+ review = r;
23
+ return () => r.destroy();
24
+ });
25
+
26
+ let count = $derived(review.changes.length);
27
+ let current = $derived(review.currentChange);
28
+ let index = $derived(current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0);
29
+ </script>
30
+
31
+ <div class={className} role="toolbar" aria-label="Review">
32
+ <button
33
+ type="button"
34
+ class={`${buttonClass} ${review.enabled ? "bg-primary/10 text-primary" : ""}`}
35
+ aria-pressed={review.enabled}
36
+ disabled={!editor}
37
+ onclick={() => review.toggle()}
38
+ >
39
+ {review.enabled ? "Tracking on" : "Track changes"}
40
+ </button>
41
+ <span class="mx-1 h-4 w-px bg-border" aria-hidden="true"></span>
42
+ <button
43
+ type="button"
44
+ class={buttonClass}
45
+ aria-label="Previous change"
46
+ disabled={count === 0}
47
+ onclick={() => review.previous()}
48
+ >
49
+ ‹
50
+ </button>
51
+ <span class="min-w-[4rem] text-center text-xs text-muted-foreground" aria-live="polite">
52
+ {count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes`}
53
+ </span>
54
+ <button
55
+ type="button"
56
+ class={buttonClass}
57
+ aria-label="Next change"
58
+ disabled={count === 0}
59
+ onclick={() => review.next()}
60
+ >
61
+ ›
62
+ </button>
63
+ <span class="mx-1 h-4 w-px bg-border" aria-hidden="true"></span>
64
+ <button
65
+ type="button"
66
+ class={buttonClass}
67
+ disabled={!current}
68
+ onclick={() => current && review.accept(current.id)}
69
+ >
70
+ Accept
71
+ </button>
72
+ <button
73
+ type="button"
74
+ class={buttonClass}
75
+ disabled={!current}
76
+ onclick={() => current && review.reject(current.id)}
77
+ >
78
+ Reject
79
+ </button>
80
+ <button type="button" class={buttonClass} disabled={count === 0} onclick={() => review.acceptAll()}>
81
+ Accept all
82
+ </button>
83
+ <button type="button" class={buttonClass} disabled={count === 0} onclick={() => review.rejectAll()}>
84
+ Reject all
85
+ </button>
86
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ interface Props {
3
+ editor?: Editor | null;
4
+ class?: string;
5
+ }
6
+ declare const ReviewToolbar: import("svelte").Component<Props, {}, "">;
7
+ type ReviewToolbar = ReturnType<typeof ReviewToolbar>;
8
+ export default ReviewToolbar;
9
+ //# sourceMappingURL=ReviewToolbar.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReviewToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/review/ReviewToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAIzC,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0DH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ComponentProps } from "svelte";
2
+ import type CommentsPanelComponent from "./CommentsPanel.svelte";
3
+ import type ReviewToolbarComponent from "./ReviewToolbar.svelte";
4
+ import type CommentButtonComponent from "./CommentButton.svelte";
5
+ export { default as CommentsPanel } from "./CommentsPanel.svelte";
6
+ export { default as ReviewToolbar } from "./ReviewToolbar.svelte";
7
+ export { default as CommentButton } from "./CommentButton.svelte";
8
+ export * from "./review.svelte.js";
9
+ export type CommentsPanelProps = ComponentProps<typeof CommentsPanelComponent>;
10
+ export type ReviewToolbarProps = ComponentProps<typeof ReviewToolbarComponent>;
11
+ export type CommentButtonProps = ComponentProps<typeof CommentButtonComponent>;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/review/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AAEjE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,cAAc,oBAAoB,CAAC;AAEnC,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC/E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC/E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { default as CommentsPanel } from "./CommentsPanel.svelte";
2
+ export { default as ReviewToolbar } from "./ReviewToolbar.svelte";
3
+ export { default as CommentButton } from "./CommentButton.svelte";
4
+ export * from "./review.svelte.js";
@@ -0,0 +1,40 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ import { type CommentStore, type CommentStoreSnapshot, type CommentThread, type TrackChangesSnapshot, type TrackedChange } from "@pixerate/editor";
3
+ export declare const EMPTY_COMMENTS_SNAPSHOT: CommentStoreSnapshot;
4
+ export declare const EMPTY_TRACK_CHANGES_SNAPSHOT: TrackChangesSnapshot;
5
+ export declare function isCommentStore(value: unknown): value is CommentStore;
6
+ /** Resolves a comment store from a store or an editor using `Comments`. */
7
+ export declare function resolveCommentStore(source: Editor | CommentStore | null | undefined): CommentStore | null;
8
+ export interface SvelteComments {
9
+ readonly threads: readonly CommentThread[];
10
+ readonly activeThreadId: string | null;
11
+ readonly store: CommentStore | null;
12
+ subscribe(run: (value: CommentStoreSnapshot) => void): () => void;
13
+ destroy(): void;
14
+ }
15
+ /**
16
+ * Reactive (runes) and Svelte-store-compatible view of a comment store. Pass
17
+ * the store or an editor using the `Comments` extension. Call `destroy()`
18
+ * when done (or create it inside a component, where it lives as long as the
19
+ * component).
20
+ */
21
+ export declare function createComments(source: Editor | CommentStore | null | undefined): SvelteComments;
22
+ export interface SvelteTrackChanges extends TrackChangesSnapshot {
23
+ readonly currentChange: TrackedChange | null;
24
+ setEnabled(enabled: boolean): void;
25
+ toggle(): void;
26
+ accept(id: string): void;
27
+ reject(id: string): void;
28
+ acceptAll(): void;
29
+ rejectAll(): void;
30
+ next(): void;
31
+ previous(): void;
32
+ subscribe(run: (value: TrackChangesSnapshot) => void): () => void;
33
+ destroy(): void;
34
+ }
35
+ /**
36
+ * Reactive (runes) and Svelte-store-compatible view of an editor's tracked
37
+ * changes (`TrackChanges` extension) with its commands.
38
+ */
39
+ export declare function createTrackChanges(editor: Editor | null | undefined): SvelteTrackChanges;
40
+ //# sourceMappingURL=review.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.svelte.d.ts","sourceRoot":"","sources":["../../src/review/review.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,uBAAuB,EAAE,oBAGZ,CAAC;AAE3B,eAAO,MAAM,4BAA4B,EAAE,oBAKjB,CAAC;AAE3B,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAMpE;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,GAAG,SAAS,GAC/C,YAAY,GAAG,IAAI,CAErB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,CA4B/F;AAED,MAAM,WAAW,kBAAmB,SAAQ,oBAAoB;IAC9D,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7C,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,IAAI,IAAI,CAAC;IAClB,SAAS,IAAI,IAAI,CAAC;IAClB,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,IAAI,IAAI,CAAC;IACjB,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,kBAAkB,CA+CxF"}
@@ -0,0 +1,104 @@
1
+ import { getCommentStore, getTrackChangesStore, } from "@pixerate/editor";
2
+ export const EMPTY_COMMENTS_SNAPSHOT = Object.freeze({
3
+ threads: [],
4
+ activeThreadId: null,
5
+ });
6
+ export const EMPTY_TRACK_CHANGES_SNAPSHOT = Object.freeze({
7
+ enabled: false,
8
+ author: { id: "anonymous", name: "Anonymous" },
9
+ changes: [],
10
+ currentChangeId: null,
11
+ });
12
+ export function isCommentStore(value) {
13
+ return (!!value &&
14
+ typeof value.getSnapshot === "function" &&
15
+ typeof value.createThread === "function");
16
+ }
17
+ /** Resolves a comment store from a store or an editor using `Comments`. */
18
+ export function resolveCommentStore(source) {
19
+ return isCommentStore(source) ? source : getCommentStore(source);
20
+ }
21
+ /**
22
+ * Reactive (runes) and Svelte-store-compatible view of a comment store. Pass
23
+ * the store or an editor using the `Comments` extension. Call `destroy()`
24
+ * when done (or create it inside a component, where it lives as long as the
25
+ * component).
26
+ */
27
+ export function createComments(source) {
28
+ const store = resolveCommentStore(source);
29
+ let snapshot = $state.raw(store?.getSnapshot() ?? EMPTY_COMMENTS_SNAPSHOT);
30
+ const unsubscribe = store
31
+ ? store.subscribe((value) => {
32
+ snapshot = value;
33
+ })
34
+ : () => { };
35
+ return {
36
+ get threads() {
37
+ return snapshot.threads;
38
+ },
39
+ get activeThreadId() {
40
+ return snapshot.activeThreadId;
41
+ },
42
+ get store() {
43
+ return store;
44
+ },
45
+ subscribe(run) {
46
+ if (!store) {
47
+ run(EMPTY_COMMENTS_SNAPSHOT);
48
+ return () => { };
49
+ }
50
+ return store.subscribe(run);
51
+ },
52
+ destroy: unsubscribe,
53
+ };
54
+ }
55
+ /**
56
+ * Reactive (runes) and Svelte-store-compatible view of an editor's tracked
57
+ * changes (`TrackChanges` extension) with its commands.
58
+ */
59
+ export function createTrackChanges(editor) {
60
+ const store = getTrackChangesStore(editor ?? null);
61
+ let snapshot = $state.raw(store?.getSnapshot() ?? EMPTY_TRACK_CHANGES_SNAPSHOT);
62
+ const unsubscribe = store
63
+ ? store.subscribe((value) => {
64
+ snapshot = value;
65
+ })
66
+ : () => { };
67
+ const run = (fn) => {
68
+ if (editor && !editor.isDestroyed)
69
+ fn(editor);
70
+ };
71
+ return {
72
+ get enabled() {
73
+ return snapshot.enabled;
74
+ },
75
+ get author() {
76
+ return snapshot.author;
77
+ },
78
+ get changes() {
79
+ return snapshot.changes;
80
+ },
81
+ get currentChangeId() {
82
+ return snapshot.currentChangeId;
83
+ },
84
+ get currentChange() {
85
+ return snapshot.changes.find((c) => c.id === snapshot.currentChangeId) ?? null;
86
+ },
87
+ setEnabled: (enabled) => run((e) => e.commands.setTrackChanges(enabled)),
88
+ toggle: () => run((e) => e.commands.toggleTrackChanges()),
89
+ accept: (id) => run((e) => e.commands.acceptChange(id)),
90
+ reject: (id) => run((e) => e.commands.rejectChange(id)),
91
+ acceptAll: () => run((e) => e.commands.acceptAllChanges()),
92
+ rejectAll: () => run((e) => e.commands.rejectAllChanges()),
93
+ next: () => run((e) => e.chain().focus().selectNextChange().run()),
94
+ previous: () => run((e) => e.chain().focus().selectPreviousChange().run()),
95
+ subscribe(runFn) {
96
+ if (!store) {
97
+ runFn(EMPTY_TRACK_CHANGES_SNAPSHOT);
98
+ return () => { };
99
+ }
100
+ return store.subscribe(runFn);
101
+ },
102
+ destroy: unsubscribe,
103
+ };
104
+ }
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
- import { indexToColName } from '@pixerate/editor';
2
+ import { indexToColName } from '@pixerate/editor/spreadsheet';
3
3
  import type { createReactiveSpreadsheet } from './spreadsheet.svelte.js';
4
+ import FormulaHints from './FormulaHints.svelte';
4
5
 
5
6
  interface Props {
6
7
  sheetState?: ReturnType<typeof createReactiveSpreadsheet>;
@@ -69,17 +70,22 @@
69
70
  }
70
71
 
71
72
  function handleInput(e: Event) {
72
- const target = e.target as HTMLInputElement;
73
+ setValue((e.target as HTMLInputElement).value);
74
+ }
75
+
76
+ function setValue(next: string) {
73
77
  if (sheetState) {
74
- sheetState.draftValue = target.value;
78
+ sheetState.draftValue = next;
75
79
  if (sheetState.activeCell && !sheetState.editingCell) {
76
- sheetState.startEditing(sheetState.activeCell.row, sheetState.activeCell.col, target.value);
80
+ sheetState.startEditing(sheetState.activeCell.row, sheetState.activeCell.col, next);
77
81
  }
78
82
  } else {
79
- value = target.value;
83
+ value = next;
80
84
  }
81
- onChange?.(target.value);
85
+ onChange?.(next);
82
86
  }
87
+
88
+ let inputEl = $state<HTMLInputElement | null>(null);
83
89
  </script>
84
90
 
85
91
  <div class="flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono {className}">
@@ -94,8 +100,10 @@
94
100
  class="flex-1 bg-transparent px-2 py-0.5 outline-none font-sans text-xs text-foreground placeholder:text-muted-foreground/60 focus:bg-background focus:ring-1 focus:ring-primary rounded"
95
101
  placeholder="Enter a value or formula (e.g. =SUM(A1:A5))"
96
102
  aria-label="Formula bar"
103
+ bind:this={inputEl}
97
104
  value={effectiveValue}
98
105
  oninput={handleInput}
99
106
  onkeydown={handleKeyDown}
100
107
  />
108
+ <FormulaHints input={inputEl} value={effectiveValue} onAccept={setValue} />
101
109
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"FormulaBar.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/FormulaBar.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGvE,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IAC1D,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwFH,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"FormulaBar.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/FormulaBar.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAIvE,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IAC1D,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+FH,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}