@refrakt-md/editor 0.8.0 → 0.8.2

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-Ca-wW6uw.js → index-80NtMar1.js} +1 -1
  2. package/app/dist/assets/index-B6H6LF1M.css +1 -0
  3. package/app/dist/assets/{index-Dg4A5Pez.js → index-BDj1XPol.js} +1 -1
  4. package/app/dist/assets/{index-BfYWp0QC.js → index-BXe1fKaT.js} +1 -1
  5. package/app/dist/assets/{index-Cq0Maciq.js → index-BfxTGrHB.js} +1 -1
  6. package/app/dist/assets/{index-BsSUa0GD.js → index-Bn8ajfVl.js} +1 -1
  7. package/app/dist/assets/{index-D6vnTt4b.js → index-CCkzIGTi.js} +2 -2
  8. package/app/dist/assets/{index-BehCztSl.js → index-CXeK-dZx.js} +1 -1
  9. package/app/dist/assets/{index-iGDqoXj_.js → index-CaRBCHaX.js} +1 -1
  10. package/app/dist/assets/index-Cd12jZId.js +479 -0
  11. package/app/dist/assets/{index-D5pMhPrg.js → index-Cgbvx23V.js} +1 -1
  12. package/app/dist/assets/{index-IU6QYZAa.js → index-D5ucdUTo.js} +1 -1
  13. package/app/dist/assets/{index-CdpS6tGk.js → index-DGYxLhpR.js} +1 -1
  14. package/app/dist/assets/{index-RKEq45V5.js → index-DNJBunzP.js} +1 -1
  15. package/app/dist/assets/{index-Cgaw2jCE.js → index-DNtuldOx.js} +1 -1
  16. package/app/dist/assets/{index-BEPqnnsd.js → index-DQUOY-pF.js} +1 -1
  17. package/app/dist/assets/{index-2hOoPFOR.js → index-DskvyNKT.js} +1 -1
  18. package/app/dist/assets/{index-CLZfwYyS.js → index-aPeHMqUX.js} +1 -1
  19. package/app/dist/assets/{index-BobjskUl.js → index-dGztG-54.js} +1 -1
  20. package/app/dist/assets/{index-DHALjxX5.js → index-xo7v6nRB.js} +1 -1
  21. package/app/dist/index.html +2 -2
  22. package/app/src/lib/api/client.ts +81 -0
  23. package/app/src/lib/components/ActionEditPopover.svelte +267 -0
  24. package/app/src/lib/components/BlockCard.svelte +285 -0
  25. package/app/src/lib/components/BlockEditPanel.svelte +640 -260
  26. package/app/src/lib/components/BlockEditor.svelte +513 -52
  27. package/app/src/lib/components/CodeEditPopover.svelte +444 -0
  28. package/app/src/lib/components/ContentModelTree.svelte +835 -0
  29. package/app/src/lib/components/EditorLayout.svelte +1 -6
  30. package/app/src/lib/components/FrontmatterEditPanel.svelte +0 -1
  31. package/app/src/lib/components/IconPickerPopover.svelte +389 -0
  32. package/app/src/lib/components/ImageEditPopover.svelte +519 -0
  33. package/app/src/lib/components/InlineEditPopover.svelte +616 -0
  34. package/app/src/lib/components/RuneAttributes.svelte +51 -0
  35. package/app/src/lib/editor/block-parser.ts +424 -6
  36. package/dist/server.d.ts +1 -0
  37. package/dist/server.d.ts.map +1 -1
  38. package/dist/server.js +189 -2
  39. package/dist/server.js.map +1 -1
  40. package/package.json +6 -6
  41. package/app/dist/assets/index-98ylvoBO.css +0 -1
  42. package/app/dist/assets/index-CVzOx0nV.js +0 -372
@@ -41,7 +41,7 @@
41
41
  {@render center()}
42
42
  </div>
43
43
  </div>
44
- <div class="layout__panel layout__panel--right" class:panel-editing={editorState.editPanelOpen}>
44
+ <div class="layout__panel layout__panel--right">
45
45
  {@render right()}
46
46
  </div>
47
47
  </div>
@@ -113,10 +113,5 @@
113
113
  .layout__panel--right {
114
114
  background: var(--ed-surface-1);
115
115
  align-items: center;
116
- transition: padding-right var(--ed-transition-slow);
117
- }
118
-
119
- .layout__panel--right.panel-editing {
120
- padding-right: 480px;
121
116
  }
122
117
  </style>
@@ -194,7 +194,6 @@
194
194
  .edit-panel {
195
195
  display: flex;
196
196
  flex-direction: column;
197
- height: 100%;
198
197
  }
199
198
 
200
199
  .edit-panel__header {
@@ -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>