@refrakt-md/editor 0.8.1 → 0.8.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.
Files changed (42) hide show
  1. package/app/dist/assets/{index-D3TQo8gu.js → index-3MvwKRVQ.js} +1 -1
  2. package/app/dist/assets/{index-CeU_s7BB.js → index-B7e694w6.js} +1 -1
  3. package/app/dist/assets/{index-DzHt8ZRh.js → index-BBljOYQu.js} +1 -1
  4. package/app/dist/assets/{index-C72UC2ga.js → index-BEGy_i8o.js} +1 -1
  5. package/app/dist/assets/{index-CqHjo2YT.js → index-BGy7ixjW.js} +1 -1
  6. package/app/dist/assets/{index-DVM3uoxc.js → index-BaLgiiKk.js} +1 -1
  7. package/app/dist/assets/{index-CW02bulk.js → index-BjlNcvOf.js} +1 -1
  8. package/app/dist/assets/{index-DmY6uqAw.js → index-CKfKYVw7.js} +1 -1
  9. package/app/dist/assets/{index-BLuaHLN3.js → index-COFbngzR.js} +1 -1
  10. package/app/dist/assets/{index-BBinZAiy.js → index-CPEo_rvd.js} +1 -1
  11. package/app/dist/assets/{index-D_Y6J00B.js → index-CQDCT-XT.js} +1 -1
  12. package/app/dist/assets/{index-COIPZ34u.js → index-CUmEjEeR.js} +1 -1
  13. package/app/dist/assets/{index-BgCNqcSo.js → index-CeV-Af4N.js} +1 -1
  14. package/app/dist/assets/{index-DW2zI-Ss.js → index-ChbH55h5.js} +1 -1
  15. package/app/dist/assets/index-CzvG5PZT.css +1 -0
  16. package/app/dist/assets/{index-ZLvRNfLb.js → index-D9-aYc3I.js} +1 -1
  17. package/app/dist/assets/{index-BwFn9q4x.js → index-DezxtfNV.js} +1 -1
  18. package/app/dist/assets/{index-CXFMPmtf.js → index-DrI4IfXE.js} +1 -1
  19. package/app/dist/assets/{index-DgIg-QAA.js → index-DwfxgjnU.js} +2 -2
  20. package/app/dist/assets/index-ogrpJNou.js +555 -0
  21. package/app/dist/index.html +2 -2
  22. package/app/src/lib/api/client.ts +32 -0
  23. package/app/src/lib/components/ActionEditPopover.svelte +41 -19
  24. package/app/src/lib/components/BlockCard.svelte +74 -17
  25. package/app/src/lib/components/BlockEditPanel.svelte +142 -9
  26. package/app/src/lib/components/BlockEditor.svelte +534 -48
  27. package/app/src/lib/components/CodeEditPopover.svelte +281 -63
  28. package/app/src/lib/components/ContentModelTree.svelte +340 -67
  29. package/app/src/lib/components/IconPickerPopover.svelte +389 -0
  30. package/app/src/lib/components/ImageEditPopover.svelte +519 -0
  31. package/app/src/lib/components/InlineEditPopover.svelte +79 -56
  32. package/app/src/lib/components/InlineEditor.svelte +15 -5
  33. package/app/src/lib/components/ProseBlockCard.svelte +446 -0
  34. package/app/src/lib/components/ProseEditPanel.svelte +470 -0
  35. package/app/src/lib/components/RuneAttributes.svelte +51 -0
  36. package/app/src/lib/editor/block-parser.ts +211 -9
  37. package/dist/server.d.ts.map +1 -1
  38. package/dist/server.js +129 -1
  39. package/dist/server.js.map +1 -1
  40. package/package.json +6 -6
  41. package/app/dist/assets/index-BD2EBUrQ.css +0 -1
  42. package/app/dist/assets/index-BlAOhWAQ.js +0 -453
@@ -0,0 +1,389 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+
4
+ interface IconEntry {
5
+ name: string;
6
+ qualifiedName: string;
7
+ group: string;
8
+ svg: string;
9
+ }
10
+
11
+ interface Props {
12
+ /** All icons from themeConfig.icons — Record<group, Record<name, svgString>> */
13
+ icons: Record<string, Record<string, string>>;
14
+ /** Current icon name (e.g., "rocket" or "hint/warning") */
15
+ currentIcon: string;
16
+ onchange: (newIconName: string) => void;
17
+ onclose: () => void;
18
+ }
19
+
20
+ let { icons, currentIcon, onchange, onclose }: Props = $props();
21
+
22
+ let searchInput: HTMLInputElement;
23
+ let selectedIcon = $state(currentIcon);
24
+ let search = $state('');
25
+
26
+ const allIcons = $derived.by(() => {
27
+ const entries: IconEntry[] = [];
28
+ for (const [group, groupIcons] of Object.entries(icons)) {
29
+ for (const [name, svg] of Object.entries(groupIcons)) {
30
+ const qualifiedName = group === 'global' ? name : `${group}/${name}`;
31
+ entries.push({ name, qualifiedName, group, svg });
32
+ }
33
+ }
34
+ return entries;
35
+ });
36
+
37
+ const filtered = $derived(
38
+ search
39
+ ? allIcons.filter(icon =>
40
+ icon.name.toLowerCase().includes(search.toLowerCase()) ||
41
+ icon.qualifiedName.toLowerCase().includes(search.toLowerCase())
42
+ )
43
+ : allIcons
44
+ );
45
+
46
+ const groupedFiltered = $derived.by(() => {
47
+ const map = new Map<string, IconEntry[]>();
48
+ for (const icon of filtered) {
49
+ const list = map.get(icon.group) ?? [];
50
+ list.push(icon);
51
+ map.set(icon.group, list);
52
+ }
53
+ return map;
54
+ });
55
+
56
+ onMount(() => {
57
+ searchInput?.focus();
58
+
59
+ function handleKeydown(e: KeyboardEvent) {
60
+ if (e.key === 'Escape') onclose();
61
+ }
62
+
63
+ document.addEventListener('keydown', handleKeydown);
64
+ return () => document.removeEventListener('keydown', handleKeydown);
65
+ });
66
+
67
+ function handleBackdropClick(e: MouseEvent) {
68
+ if (e.target === e.currentTarget) {
69
+ onclose();
70
+ }
71
+ }
72
+
73
+ function selectIcon(icon: IconEntry) {
74
+ selectedIcon = icon.qualifiedName;
75
+ }
76
+
77
+ function handleApply() {
78
+ if (selectedIcon) {
79
+ onchange(selectedIcon);
80
+ }
81
+ }
82
+ </script>
83
+
84
+ <div
85
+ class="icon-picker-backdrop"
86
+ onclick={handleBackdropClick}
87
+ onkeydown={() => {}}
88
+ role="dialog"
89
+ aria-modal="true"
90
+ aria-label="Select icon"
91
+ >
92
+ <div class="icon-picker-modal">
93
+ <!-- Header -->
94
+ <div class="icon-picker-modal__header">
95
+ <span class="icon-picker-modal__label">icon</span>
96
+ <button class="icon-picker-modal__close" onclick={onclose} aria-label="Close">&times;</button>
97
+ </div>
98
+
99
+ <!-- Search -->
100
+ <div class="icon-picker-modal__search">
101
+ <input
102
+ class="icon-picker-modal__search-input"
103
+ type="text"
104
+ placeholder="Search icons..."
105
+ bind:value={search}
106
+ bind:this={searchInput}
107
+ />
108
+ </div>
109
+
110
+ <!-- Icon grid -->
111
+ <div class="icon-picker-modal__grid-area">
112
+ {#if allIcons.length === 0}
113
+ <div class="icon-picker-modal__empty">No icons available.</div>
114
+ {:else if filtered.length === 0}
115
+ <div class="icon-picker-modal__empty">No icons match "{search}"</div>
116
+ {:else}
117
+ {#each [...groupedFiltered] as [group, groupIcons] (group)}
118
+ <div class="icon-picker-modal__group">
119
+ <div class="icon-picker-modal__group-label">{group}</div>
120
+ <div class="icon-picker-modal__grid">
121
+ {#each groupIcons as icon (icon.qualifiedName)}
122
+ <button
123
+ class="icon-picker-modal__icon"
124
+ class:selected={selectedIcon === icon.qualifiedName}
125
+ onclick={() => selectIcon(icon)}
126
+ title={icon.qualifiedName}
127
+ >
128
+ <span class="icon-picker-modal__icon-svg">{@html icon.svg}</span>
129
+ <span class="icon-picker-modal__icon-name">{icon.name}</span>
130
+ </button>
131
+ {/each}
132
+ </div>
133
+ </div>
134
+ {/each}
135
+ {/if}
136
+ </div>
137
+
138
+ <!-- Footer -->
139
+ <div class="icon-picker-modal__footer">
140
+ <button
141
+ class="icon-picker-modal__btn icon-picker-modal__btn--apply"
142
+ onclick={handleApply}
143
+ disabled={!selectedIcon}
144
+ >Apply</button>
145
+ <button
146
+ class="icon-picker-modal__btn icon-picker-modal__btn--cancel"
147
+ onclick={onclose}
148
+ >Cancel</button>
149
+ </div>
150
+ </div>
151
+ </div>
152
+
153
+ <style>
154
+ /* ── Backdrop ─────────────────────────────────────────── */
155
+
156
+ .icon-picker-backdrop {
157
+ position: fixed;
158
+ inset: 0;
159
+ z-index: 1100;
160
+ display: flex;
161
+ align-items: center;
162
+ justify-content: center;
163
+ background: rgba(0, 0, 0, 0.3);
164
+ animation: icon-picker-backdrop-in 120ms ease-out;
165
+ }
166
+
167
+ @keyframes icon-picker-backdrop-in {
168
+ from { opacity: 0; }
169
+ to { opacity: 1; }
170
+ }
171
+
172
+ /* ── Modal ────────────────────────────────────────────── */
173
+
174
+ .icon-picker-modal {
175
+ z-index: 1101;
176
+ display: flex;
177
+ flex-direction: column;
178
+ width: 90vw;
179
+ max-width: 580px;
180
+ max-height: 80vh;
181
+ background: var(--ed-surface-0, #fff);
182
+ border: 1px solid var(--ed-border-default, #e2e8f0);
183
+ border-radius: var(--ed-radius-lg, 8px);
184
+ box-shadow: var(--ed-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1));
185
+ animation: icon-picker-enter 120ms ease-out;
186
+ overflow: hidden;
187
+ }
188
+
189
+ @keyframes icon-picker-enter {
190
+ from { opacity: 0; transform: scale(0.97) translateY(8px); }
191
+ to { opacity: 1; transform: scale(1) translateY(0); }
192
+ }
193
+
194
+ /* ── Header ──────────────────────────────────────────── */
195
+
196
+ .icon-picker-modal__header {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: space-between;
200
+ padding: var(--ed-space-2, 0.5rem) var(--ed-space-3, 0.75rem);
201
+ border-bottom: 1px solid var(--ed-border-default, #e2e8f0);
202
+ }
203
+
204
+ .icon-picker-modal__label {
205
+ font-size: 10px;
206
+ font-weight: 700;
207
+ color: var(--ed-text-muted, #94a3b8);
208
+ text-transform: uppercase;
209
+ letter-spacing: 0.04em;
210
+ }
211
+
212
+ .icon-picker-modal__close {
213
+ background: none;
214
+ border: none;
215
+ font-size: 18px;
216
+ line-height: 1;
217
+ color: var(--ed-text-muted, #94a3b8);
218
+ cursor: pointer;
219
+ padding: 0 4px;
220
+ }
221
+
222
+ .icon-picker-modal__close:hover {
223
+ color: var(--ed-text-primary, #1a1a2e);
224
+ }
225
+
226
+ /* ── Search ──────────────────────────────────────────── */
227
+
228
+ .icon-picker-modal__search {
229
+ padding: var(--ed-space-2, 0.5rem) var(--ed-space-3, 0.75rem);
230
+ }
231
+
232
+ .icon-picker-modal__search-input {
233
+ width: 100%;
234
+ padding: var(--ed-space-1, 0.25rem) var(--ed-space-2, 0.5rem);
235
+ border: 1px solid var(--ed-border-default, #e2e8f0);
236
+ border-radius: var(--ed-radius-sm, 4px);
237
+ font-size: var(--ed-text-sm, 13px);
238
+ color: var(--ed-text-primary, #1a1a2e);
239
+ background: var(--ed-surface-0, #fff);
240
+ outline: none;
241
+ font-family: inherit;
242
+ box-sizing: border-box;
243
+ }
244
+
245
+ .icon-picker-modal__search-input:focus {
246
+ border-color: var(--ed-accent, #3b82f6);
247
+ box-shadow: 0 0 0 2px var(--ed-accent-ring, rgba(59, 130, 246, 0.2));
248
+ }
249
+
250
+ /* ── Grid area ───────────────────────────────────────── */
251
+
252
+ .icon-picker-modal__grid-area {
253
+ flex: 1;
254
+ overflow-y: auto;
255
+ padding: 0 var(--ed-space-3, 0.75rem);
256
+ min-height: 120px;
257
+ max-height: 360px;
258
+ }
259
+
260
+ .icon-picker-modal__empty {
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ height: 120px;
265
+ color: var(--ed-text-muted, #94a3b8);
266
+ font-size: var(--ed-text-sm, 13px);
267
+ }
268
+
269
+ /* ── Group ───────────────────────────────────────────── */
270
+
271
+ .icon-picker-modal__group {
272
+ margin-bottom: var(--ed-space-2, 0.5rem);
273
+ }
274
+
275
+ .icon-picker-modal__group-label {
276
+ font-size: 10px;
277
+ font-weight: 600;
278
+ color: var(--ed-text-muted, #94a3b8);
279
+ text-transform: uppercase;
280
+ letter-spacing: 0.04em;
281
+ padding: var(--ed-space-1, 0.25rem) 0;
282
+ margin-bottom: var(--ed-space-1, 0.25rem);
283
+ }
284
+
285
+ .icon-picker-modal__grid {
286
+ display: grid;
287
+ grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
288
+ gap: 6px;
289
+ }
290
+
291
+ /* ── Icon button ─────────────────────────────────────── */
292
+
293
+ .icon-picker-modal__icon {
294
+ display: flex;
295
+ flex-direction: column;
296
+ align-items: center;
297
+ gap: 4px;
298
+ padding: 8px 4px 6px;
299
+ border: 2px solid var(--ed-border-default, #e2e8f0);
300
+ border-radius: var(--ed-radius-sm, 4px);
301
+ background: var(--ed-surface-0, #fff);
302
+ cursor: pointer;
303
+ transition: border-color 100ms, background 100ms;
304
+ }
305
+
306
+ .icon-picker-modal__icon:hover {
307
+ border-color: var(--ed-text-muted, #94a3b8);
308
+ background: var(--ed-surface-1, #f8fafc);
309
+ }
310
+
311
+ .icon-picker-modal__icon.selected {
312
+ border-color: var(--ed-accent, #3b82f6);
313
+ box-shadow: 0 0 0 2px var(--ed-accent-ring, rgba(59, 130, 246, 0.3));
314
+ background: var(--ed-accent-muted, rgba(59, 130, 246, 0.06));
315
+ }
316
+
317
+ .icon-picker-modal__icon-svg {
318
+ display: flex;
319
+ align-items: center;
320
+ justify-content: center;
321
+ width: 28px;
322
+ height: 28px;
323
+ color: var(--ed-text-secondary, #475569);
324
+ }
325
+
326
+ .icon-picker-modal__icon-svg :global(svg) {
327
+ width: 100%;
328
+ height: 100%;
329
+ }
330
+
331
+ .icon-picker-modal__icon-name {
332
+ font-size: 9px;
333
+ color: var(--ed-text-muted, #94a3b8);
334
+ text-align: center;
335
+ overflow: hidden;
336
+ text-overflow: ellipsis;
337
+ white-space: nowrap;
338
+ max-width: 100%;
339
+ line-height: 1.2;
340
+ }
341
+
342
+ /* ── Footer ──────────────────────────────────────────── */
343
+
344
+ .icon-picker-modal__footer {
345
+ display: flex;
346
+ gap: var(--ed-space-2, 0.5rem);
347
+ padding: var(--ed-space-2, 0.5rem) var(--ed-space-3, 0.75rem);
348
+ border-top: 1px solid var(--ed-border-default, #e2e8f0);
349
+ }
350
+
351
+ .icon-picker-modal__btn {
352
+ display: flex;
353
+ align-items: center;
354
+ justify-content: center;
355
+ gap: var(--ed-space-2);
356
+ height: 28px;
357
+ padding: 0 var(--ed-space-3);
358
+ border: 1px solid var(--ed-text-secondary);
359
+ border-radius: var(--ed-radius-sm);
360
+ background: transparent;
361
+ color: var(--ed-text-secondary);
362
+ font-size: var(--ed-text-sm);
363
+ font-weight: 500;
364
+ cursor: pointer;
365
+ transition: background var(--ed-transition-fast), color var(--ed-transition-fast), border-color var(--ed-transition-fast);
366
+ white-space: nowrap;
367
+ }
368
+
369
+ .icon-picker-modal__btn:hover:not(.icon-picker-modal__btn--apply):not(:disabled) {
370
+ border-color: var(--ed-text-primary);
371
+ color: var(--ed-text-primary);
372
+ }
373
+
374
+ .icon-picker-modal__btn--apply {
375
+ background: var(--ed-text-primary);
376
+ color: #ffffff;
377
+ border-color: var(--ed-text-primary);
378
+ }
379
+
380
+ .icon-picker-modal__btn--apply:hover:not(:disabled) {
381
+ background: var(--ed-text-secondary);
382
+ border-color: var(--ed-text-secondary);
383
+ }
384
+
385
+ .icon-picker-modal__btn--apply:disabled {
386
+ opacity: 0.4;
387
+ cursor: not-allowed;
388
+ }
389
+ </style>