@atom-forge/brickee 0.0.2 → 0.0.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 (62) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README-AI.md +10 -7
  3. package/dist/Editor.svelte +691 -833
  4. package/dist/LinkPrompt.svelte +1 -14
  5. package/dist/Preview.svelte +27 -54
  6. package/dist/Preview.svelte.d.ts +2 -4
  7. package/dist/PropsBar.svelte +35 -219
  8. package/dist/code.js +1 -1
  9. package/dist/dom.d.ts +8 -0
  10. package/dist/dom.js +9 -0
  11. package/dist/editor/BlockMenu.svelte +102 -0
  12. package/dist/editor/BlockMenu.svelte.d.ts +32 -0
  13. package/dist/editor/FormatBar.svelte +41 -0
  14. package/dist/editor/FormatBar.svelte.d.ts +16 -0
  15. package/dist/editor/drag.svelte.d.ts +63 -0
  16. package/dist/editor/drag.svelte.js +95 -0
  17. package/dist/editor/history.d.ts +24 -0
  18. package/dist/editor/history.js +86 -0
  19. package/dist/editor/sweep.svelte.d.ts +21 -0
  20. package/dist/editor/sweep.svelte.js +56 -0
  21. package/dist/html.d.ts +12 -0
  22. package/dist/html.js +206 -0
  23. package/dist/i18n.js +1 -1
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.js +3 -2
  26. package/dist/kit/Empty.svelte +4 -15
  27. package/dist/kit/Frame.svelte +7 -37
  28. package/dist/locales/hu.js +4 -1
  29. package/dist/markdown.d.ts +20 -4
  30. package/dist/markdown.js +380 -85
  31. package/dist/model.js +2 -3
  32. package/dist/plugins/Gap.svelte +5 -8
  33. package/dist/plugins/GapPreview.svelte +4 -6
  34. package/dist/plugins/Map.svelte +2 -17
  35. package/dist/plugins/MapPreview.svelte +8 -10
  36. package/dist/plugins/Table.svelte +233 -256
  37. package/dist/plugins/TablePreview.svelte +28 -28
  38. package/dist/plugins/YouTube.svelte +3 -31
  39. package/dist/plugins/YouTubePreview.svelte +45 -43
  40. package/dist/plugins/index.js +3 -1
  41. package/dist/types.d.ts +21 -3
  42. package/dist/types.js +8 -1
  43. package/dist/view.js +1 -1
  44. package/docs/en/bundled-plugins.md +1 -1
  45. package/docs/en/documents-and-markdown.md +20 -14
  46. package/docs/en/editor-and-preview.md +6 -7
  47. package/docs/en/getting-started.md +8 -0
  48. package/docs/en/index.md +1 -1
  49. package/docs/en/recipes-and-pitfalls.md +2 -2
  50. package/docs/en/server-side.md +0 -17
  51. package/docs/en/upgrading.md +14 -0
  52. package/docs/en/writing-plugins.md +36 -10
  53. package/docs/hu/bundled-plugins.md +1 -1
  54. package/docs/hu/documents-and-markdown.md +20 -14
  55. package/docs/hu/editor-and-preview.md +6 -7
  56. package/docs/hu/getting-started.md +8 -0
  57. package/docs/hu/index.md +1 -1
  58. package/docs/hu/recipes-and-pitfalls.md +2 -2
  59. package/docs/hu/server-side.md +0 -17
  60. package/docs/hu/upgrading.md +14 -0
  61. package/docs/hu/writing-plugins.md +36 -10
  62. package/package.json +9 -1
@@ -14,13 +14,11 @@
14
14
  <iframe class="map" title={tr('Map: {place}', { place: String(props.q ?? '') })} {src} loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
15
15
  {/if}
16
16
 
17
- <style>
18
- .map {
19
- display: block;
20
- width: 100%;
21
- height: 300px;
22
- margin: 0 0 1em;
23
- border: 0;
24
- border-radius: 10px;
25
- }
26
- </style>
17
+ <style>.map {
18
+ display: block;
19
+ width: 100%;
20
+ height: 300px;
21
+ margin: 0 0 1em;
22
+ border: 0;
23
+ border-radius: 10px;
24
+ }</style>
@@ -62,6 +62,7 @@
62
62
  Trash2
63
63
  } from '@lucide/svelte';
64
64
  import { tick } from 'svelte';
65
+ import { inline } from '../markdown';
65
66
  import type { PluginViewProps } from '../types';
66
67
 
67
68
  let { props = $bindable(), readonly }: PluginViewProps<TableProps> = $props();
@@ -110,7 +111,14 @@
110
111
  };
111
112
  addEventListener('pointermove', move);
112
113
  addEventListener('pointerup', up);
114
+ stopDrag = () => {
115
+ removeEventListener('pointermove', move);
116
+ removeEventListener('pointerup', up);
117
+ };
113
118
  }
119
+ // A table removed in the middle of a drag leaves no listener behind.
120
+ let stopDrag = () => {};
121
+ $effect(() => () => stopDrag());
114
122
  /** The drop line of a row or column: before it, after it (the last one), or none. */
115
123
  function dropMark(kind: 'row' | 'col', i: number, count: number) {
116
124
  if (drag?.kind !== kind || drag.to === drag.from || drag.to === drag.from + 1) return null;
@@ -151,9 +159,38 @@
151
159
  }
152
160
  const setAlign = (c: number, a: Align) => set({ align: align.map((x, j) => (j === c ? a : x)) });
153
161
 
162
+ /**
163
+ * Wraps the selected text of a cell in a mark (Ctrl+B and the like); then selects the text again, or, for a link,
164
+ * the address to type over.
165
+ */
166
+ async function mark(t: HTMLTextAreaElement, r: number, c: number, open: string, close: string, link = false) {
167
+ const { selectionStart: a, selectionEnd: b, value } = t;
168
+ const text = value.slice(a, b);
169
+ edit(r, c, value.slice(0, a) + open + text + close + value.slice(b));
170
+ await tick();
171
+ const cell = root.querySelector<HTMLTextAreaElement>(`textarea[data-cell="${r}:${c}"]`);
172
+ if (!cell) return;
173
+ cell.focus();
174
+ if (link) cell.setSelectionRange(a + open.length + text.length + 2, a + open.length + text.length + close.length - 1);
175
+ else cell.setSelectionRange(a + open.length, a + open.length + text.length);
176
+ }
177
+ const MARKS: Record<string, [string, string]> = { b: ['**', '**'], i: ['*', '*'], e: ['`', '`'] };
178
+
154
179
  function onKey(e: KeyboardEvent, r: number, c: number) {
155
180
  const t = e.currentTarget as HTMLTextAreaElement;
156
181
  const last = r === rows.length - 1;
182
+ const k = e.key.toLowerCase();
183
+ if ((e.ctrlKey || e.metaKey) && !e.altKey) {
184
+ const m = e.shiftKey ? (k === 'x' ? (['~~', '~~'] as [string, string]) : null) : MARKS[k];
185
+ if (m) {
186
+ e.preventDefault();
187
+ return void mark(t, r, c, m[0], m[1]);
188
+ }
189
+ if (k === 'k' && !e.shiftKey) {
190
+ e.preventDefault();
191
+ return void mark(t, r, c, '[', '](https://)', true);
192
+ }
193
+ }
157
194
  if (e.key === 'Enter' && !e.shiftKey) {
158
195
  // Enter goes down a row (a new one after the last); Shift+Enter breaks the line inside the cell.
159
196
  e.preventDefault();
@@ -199,6 +236,18 @@
199
236
  return { update: fit, destroy: () => node.removeEventListener('input', fit) };
200
237
  }
201
238
 
239
+ // The toolbar's look (Tailwind; whole class names, so Tailwind finds them): segments of small buttons.
240
+ const SEG = 'inline-flex items-center gap-px rounded-lg bg-muted p-0.5';
241
+ const LABEL = 'inline-grid place-items-center pr-1 pl-[5px] text-muted-contrast';
242
+ const TOOL =
243
+ 'inline-grid h-6 min-w-[26px] cursor-pointer place-items-center rounded-md border-0 bg-transparent px-[5px] text-surface-contrast enabled:hover:bg-surface enabled:hover:shadow-[0_1px_2px_rgb(0_0_0/0.08)] disabled:cursor-default disabled:opacity-30';
244
+ const TOOL_ON =
245
+ 'inline-grid h-6 min-w-[26px] cursor-pointer place-items-center rounded-md border-0 bg-surface px-[5px] text-accent shadow-[0_1px_2px_rgb(0_0_0/0.08)]';
246
+ const DANGER = `${TOOL} enabled:hover:text-[#dc2626]`;
247
+ const ADD = $derived(
248
+ `grid cursor-pointer place-items-center rounded-md border-0 bg-muted p-0 text-muted-contrast transition-[opacity,background] duration-[120ms] hover:bg-accent/12 hover:text-accent ${at ? 'opacity-100' : 'opacity-0 group-hover/table:opacity-100'}`
249
+ );
250
+
202
251
  const ALIGNS: { value: Align; icon: typeof TextAlignStart; label: string }[] = [
203
252
  { value: 'left', icon: TextAlignStart, label: 'Align column left' },
204
253
  { value: 'center', icon: TextAlignCenter, label: 'Center column' },
@@ -207,41 +256,42 @@
207
256
  </script>
208
257
 
209
258
  <div
210
- class="table"
211
- class:editing={!!at}
259
+ class="table-editor group/table text-[15px] leading-[1.45]"
212
260
  bind:this={root}
213
261
  onfocusout={(e) => {
214
262
  if (!root.contains(e.relatedTarget as Node)) at = null;
215
263
  }}
216
264
  >
217
265
  {#if !readonly}
218
- <div class="bar" role="toolbar" aria-label={tr('Table')}>
266
+ <!-- The row and column tools of the cell being edited; when it wraps, it pushes the table down. -->
267
+ <div class="mb-2 flex min-h-7 flex-wrap items-center gap-1.5" role="toolbar" aria-label={tr('Table')}>
219
268
  {#if at}
220
269
  {@const { r, c } = at}
221
- <div class="seg">
222
- <span class="label" title={tr('Row')}><Rows3 size={14} /></span>
223
- <button type="button" title={tr('Insert a row above')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertRow(r); focusCell(r, c); }}><BetweenHorizontalStart size={15} /></button>
224
- <button type="button" title={tr('Insert a row below')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertRow(r + 1); focusCell(r + 1, c); }}><BetweenHorizontalEnd size={15} /></button>
225
- <button type="button" class="danger" title={tr('Delete this row')} disabled={rows.length < 2} onclick={() => dropRow(r)}><Trash2 size={14} /></button>
270
+ <div class={SEG}>
271
+ <span class={LABEL} title={tr('Row')}><Rows3 size={14} /></span>
272
+ <button type="button" class={TOOL} title={tr('Insert a row above')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertRow(r); focusCell(r, c); }}><BetweenHorizontalStart size={15} /></button>
273
+ <button type="button" class={TOOL} title={tr('Insert a row below')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertRow(r + 1); focusCell(r + 1, c); }}><BetweenHorizontalEnd size={15} /></button>
274
+ <button type="button" class={DANGER} title={tr('Delete this row')} disabled={rows.length < 2} onclick={() => dropRow(r)}><Trash2 size={14} /></button>
226
275
  </div>
227
- <div class="seg">
228
- <span class="label" title={tr('Column')}><Columns3 size={14} /></span>
229
- <button type="button" title={tr('Insert a column left')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertCol(c); focusCell(r, c); }}><BetweenVerticalStart size={15} /></button>
230
- <button type="button" title={tr('Insert a column right')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertCol(c + 1); focusCell(r, c + 1); }}><BetweenVerticalEnd size={15} /></button>
231
- <button type="button" class="danger" title={tr('Delete this column')} disabled={width < 2} onclick={() => dropCol(c)}><Trash2 size={14} /></button>
276
+ <div class={SEG}>
277
+ <span class={LABEL} title={tr('Column')}><Columns3 size={14} /></span>
278
+ <button type="button" class={TOOL} title={tr('Insert a column left')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertCol(c); focusCell(r, c); }}><BetweenVerticalStart size={15} /></button>
279
+ <button type="button" class={TOOL} title={tr('Insert a column right')} onmousedown={(e) => e.preventDefault()} onclick={() => { insertCol(c + 1); focusCell(r, c + 1); }}><BetweenVerticalEnd size={15} /></button>
280
+ <button type="button" class={DANGER} title={tr('Delete this column')} disabled={width < 2} onclick={() => dropCol(c)}><Trash2 size={14} /></button>
232
281
  </div>
233
- <div class="seg">
282
+ <div class={SEG}>
234
283
  {#each ALIGNS as a (a.value)}
235
- <button type="button" title={tr(a.label)} class:on={align[c] === a.value} onmousedown={(e) => e.preventDefault()} onclick={() => setAlign(c, a.value)}><a.icon size={15} /></button>
284
+ <button type="button" class={align[c] === a.value ? TOOL_ON : TOOL} title={tr(a.label)} onmousedown={(e) => e.preventDefault()} onclick={() => setAlign(c, a.value)}><a.icon size={15} /></button>
236
285
  {/each}
237
286
  </div>
238
287
  {:else}
239
- <span class="idle">{tr('Click a cell to edit its row and column')}</span>
288
+ <span class="text-[12px] text-muted-contrast">{tr('Click a cell to edit its row and column')}</span>
240
289
  {/if}
241
290
  </div>
242
291
  {/if}
243
- <div class="grid">
244
- <div class="frame">
292
+ <!-- The table, with a + bar on the right (column) and below (row) that shows on hover. -->
293
+ <div class="grid grid-cols-[minmax(0,1fr)_18px] grid-rows-[auto_18px] gap-1">
294
+ <div class="overflow-x-auto rounded-[10px] border border-frame bg-surface">
245
295
  <table class:dragging={!!drag}>
246
296
  {#if !readonly}
247
297
  <!-- the column grips -->
@@ -265,12 +315,16 @@
265
315
  </td>
266
316
  {/if}
267
317
  {#each row as cell, c (c)}
318
+ {@const editing = at?.r === r && at?.c === c}
268
319
  <td
269
320
  class={dropMark('col', c, width) ? 'c-' + dropMark('col', c, width) : ''}
270
321
  class:col-on={at?.c === c}
271
322
  class:moving={drag?.kind === 'col' && drag.from === c}
272
323
  >
324
+ <!-- Out of editing a cell shows its text formatted (sizing the cell, over its textarea); editing shows the
325
+ markdown. -->
273
326
  <textarea
327
+ class:hidden-text={!editing && !!cell.trim()}
274
328
  rows="1"
275
329
  value={cell}
276
330
  {readonly}
@@ -284,6 +338,9 @@
284
338
  onkeydown={(e) => onKey(e, r, c)}
285
339
  onpaste={(e) => onPaste(e, r, c)}
286
340
  ></textarea>
341
+ {#if !editing && cell.trim()}
342
+ <div class="shown" style:text-align={align[c]} aria-hidden="true">{@html inline(cell)}</div>
343
+ {/if}
287
344
  </td>
288
345
  {/each}
289
346
  </tr>
@@ -292,246 +349,166 @@
292
349
  </table>
293
350
  </div>
294
351
  {#if !readonly}
295
- <button type="button" class="add-col" title={tr('Add a column')} aria-label={tr('Add a column')} onclick={() => { insertCol(width); focusCell(0, width); }}><Plus size={14} /></button>
296
- <button type="button" class="add-row" title={tr('Add a row')} aria-label={tr('Add a row')} onclick={() => { insertRow(rows.length); focusCell(rows.length, 0); }}><Plus size={14} /></button>
352
+ <button type="button" class={ADD} title={tr('Add a column')} aria-label={tr('Add a column')} onclick={() => { insertCol(width); focusCell(0, width); }}><Plus size={14} /></button>
353
+ <button type="button" class="col-start-1 {ADD}" title={tr('Add a row')} aria-label={tr('Add a row')} onclick={() => { insertRow(rows.length); focusCell(rows.length, 0); }}><Plus size={14} /></button>
297
354
  {/if}
298
355
  </div>
299
356
  </div>
300
357
 
301
- <style>
302
- .table {
303
- font: 15px/1.45 system-ui, -apple-system, 'Segoe UI', sans-serif;
304
- }
305
- /* The row and column tools of the cell being edited; when it wraps, it pushes the table down. */
306
- .bar {
307
- margin: 0 0 8px;
308
- display: flex;
309
- flex-wrap: wrap;
310
- align-items: center;
311
- gap: 6px;
312
- min-height: 28px;
313
- }
314
- .seg {
315
- display: inline-flex;
316
- align-items: center;
317
- gap: 1px;
318
- padding: 2px;
319
- border-radius: 8px;
320
- background: var(--color-muted, #f4f4f6);
321
- }
322
- .idle {
323
- font-size: 12px;
324
- color: var(--color-muted-contrast, #a0a0a8);
325
- }
326
- .label {
327
- display: inline-grid;
328
- place-items: center;
329
- padding: 0 4px 0 5px;
330
- color: var(--color-muted-contrast, #a0a0a8);
331
- }
332
- .bar button {
333
- display: inline-grid;
334
- place-items: center;
335
- height: 24px;
336
- min-width: 26px;
337
- padding: 0 5px;
338
- border: 0;
339
- border-radius: 6px;
340
- background: none;
341
- color: var(--color-surface-contrast, #4b4b55);
342
- cursor: pointer;
343
- }
344
- .bar button:hover:not(:disabled) {
345
- background: var(--color-surface, #fff);
346
- color: var(--color-surface-contrast, #1d1d1f);
347
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
348
- }
349
- .bar button.on {
350
- background: var(--color-surface, #fff);
351
- color: #2563eb;
352
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
353
- }
354
- .bar button.danger:hover:not(:disabled) {
355
- color: #dc2626;
356
- }
357
- .bar button:disabled {
358
- opacity: 0.3;
359
- cursor: default;
360
- }
361
- .bar .toggle {
362
- display: inline-flex;
363
- gap: 5px;
364
- height: 28px;
365
- padding: 0 10px;
366
- border: 1px solid var(--color-frame, #e5e5ea);
367
- border-radius: 8px;
368
- font-size: 12px;
369
- font-weight: 500;
370
- }
371
- .bar .toggle.on {
372
- border-color: color-mix(in srgb, #3b82f6 35%, transparent);
373
- background: color-mix(in srgb, #3b82f6 8%, transparent);
374
- box-shadow: none;
375
- }
358
+ <style>table {
359
+ width: 100%;
360
+ border-collapse: collapse;
361
+ }
362
+ table.dragging, table.dragging * {
363
+ cursor: grabbing !important;
364
+ user-select: none;
365
+ }
376
366
 
377
- /* The table, with a + bar on the right (column) and below (row) that shows on hover. */
378
- .grid {
379
- display: grid;
380
- grid-template-columns: minmax(0, 1fr) 18px;
381
- grid-template-rows: auto 18px;
382
- gap: 4px;
383
- }
384
- .frame {
385
- overflow-x: auto;
386
- border: 1px solid var(--color-frame, #e3e3e8);
387
- border-radius: 10px;
388
- background: var(--color-surface, #fff);
389
- }
390
- table {
391
- width: 100%;
392
- border-collapse: collapse;
393
- }
394
- /* The 1px height lets the editor inside fill the whole cell (height: 100% of the row's tallest cell). */
395
- td {
396
- height: 1px;
397
- padding: 0;
398
- border-right: 1px solid var(--color-frame, #ececf0);
399
- border-bottom: 1px solid var(--color-frame, #ececf0);
400
- vertical-align: top;
401
- transition: background 0.1s;
402
- }
403
- td:last-child {
404
- border-right: 0;
405
- }
406
- tr:last-child td {
407
- border-bottom: 0;
408
- }
409
- tr.row-on td,
410
- td.col-on {
411
- background: color-mix(in srgb, #3b82f6 4%, transparent);
412
- }
413
- /* The header and the footer as on the site: a band above, the sums bold on grey behind a thicker line. */
414
- tr.h-top td:not(.rgrip) {
415
- background: var(--color-muted, #f1f1f4);
416
- border-bottom: 2px solid var(--color-frame, #d8d8de);
417
- }
418
- tr.h-bottom td:not(.rgrip) {
419
- background: var(--color-muted, #f1f1f4);
420
- border-top: 2px solid var(--color-frame, #d8d8de);
421
- }
422
- textarea {
423
- display: block;
424
- width: 100%;
425
- height: 100%;
426
- min-width: 6em;
427
- box-sizing: border-box;
428
- padding: 8px 11px;
429
- border: 0;
430
- border-radius: 0;
431
- background: none;
432
- resize: none;
433
- font: inherit;
434
- color: inherit;
435
- outline: none;
436
- }
437
- textarea::placeholder {
438
- color: var(--color-muted-contrast, #b8b8c0);
439
- }
440
- textarea:focus {
441
- background: var(--color-surface, #fff);
442
- box-shadow: inset 0 0 0 2px #3b82f6;
443
- }
444
- tr.h-top textarea {
445
- font-weight: 600;
446
- font-size: 13px;
447
- letter-spacing: 0.04em;
448
- text-transform: uppercase;
449
- color: var(--color-surface-contrast, #3a3a44);
450
- }
451
- tr.h-bottom textarea {
452
- font-weight: 600;
453
- color: var(--color-surface-contrast, #1d1d1f);
454
- }
455
- /* grips: a thin row above the columns and a thin column before the rows, shown on hover */
456
- td.corner,
457
- td.rgrip {
458
- width: 14px;
459
- border-right: 1px solid var(--color-frame, #ececf0);
460
- }
461
- tr.grips td {
462
- height: 14px;
463
- border-bottom: 1px solid var(--color-frame, #ececf0);
464
- }
465
- .grip {
466
- display: grid;
467
- place-items: center;
468
- width: 100%;
469
- height: 100%;
470
- min-height: 14px;
471
- padding: 0;
472
- border: 0;
473
- background: none;
474
- color: var(--color-muted-contrast, #b0b0b8);
475
- cursor: grab;
476
- opacity: 0;
477
- touch-action: none;
478
- transition: opacity 0.12s;
479
- }
480
- .table:hover .grip,
481
- table.dragging .grip {
482
- opacity: 1;
483
- }
484
- .grip:hover {
485
- color: #2563eb;
486
- background: color-mix(in srgb, #3b82f6 9%, transparent);
487
- }
488
- table.dragging,
489
- table.dragging * {
490
- cursor: grabbing !important;
491
- user-select: none;
492
- }
493
- .moving {
494
- opacity: 0.45;
495
- }
496
- tr.before td {
497
- box-shadow: inset 0 2px 0 #3b82f6;
498
- }
499
- tr.after td {
500
- box-shadow: inset 0 -2px 0 #3b82f6;
501
- }
502
- td.before,
503
- td.c-before {
504
- box-shadow: inset 2px 0 0 #3b82f6;
505
- }
506
- td.after,
507
- td.c-after {
508
- box-shadow: inset -2px 0 0 #3b82f6;
509
- }
510
- .add-col,
511
- .add-row {
512
- display: grid;
513
- place-items: center;
514
- padding: 0;
515
- border: 0;
516
- border-radius: 6px;
517
- background: var(--color-muted, #f4f4f6);
518
- color: var(--color-muted-contrast, #8a8a94);
519
- cursor: pointer;
520
- opacity: 0;
521
- transition: opacity 0.12s, background 0.12s;
522
- }
523
- .add-row {
524
- grid-column: 1;
525
- }
526
- .table:hover .add-col,
527
- .table:hover .add-row,
528
- .table.editing .add-col,
529
- .table.editing .add-row {
530
- opacity: 1;
531
- }
532
- .add-col:hover,
533
- .add-row:hover {
534
- background: color-mix(in srgb, #3b82f6 12%, transparent);
535
- color: #2563eb;
536
- }
537
- </style>
367
+ td {
368
+ height: 1px;
369
+ padding: 0;
370
+ border-right: 1px solid var(--color-frame, #ececf0);
371
+ border-bottom: 1px solid var(--color-frame, #ececf0);
372
+ vertical-align: top;
373
+ transition: background 0.1s;
374
+ }
375
+ td:last-child {
376
+ border-right: 0;
377
+ }
378
+
379
+ tr:last-child td {
380
+ border-bottom: 0;
381
+ }
382
+ tr.row-on td,
383
+ tr td.col-on {
384
+ background: color-mix(in srgb, var(--color-accent, #3b82f6) 4%, transparent);
385
+ }
386
+ tr.h-top td:not(.rgrip) {
387
+ background: var(--color-muted, #f1f1f4);
388
+ border-bottom: 2px solid var(--color-frame, #d8d8de);
389
+ }
390
+ tr.h-bottom td:not(.rgrip) {
391
+ background: var(--color-muted, #f1f1f4);
392
+ border-top: 2px solid var(--color-frame, #d8d8de);
393
+ }
394
+ tr.h-top textarea, tr.h-top .shown {
395
+ font-weight: 600;
396
+ font-size: 13px;
397
+ letter-spacing: 0.04em;
398
+ text-transform: uppercase;
399
+ color: var(--color-surface-contrast, #3a3a44);
400
+ }
401
+ tr.h-bottom textarea, tr.h-bottom .shown {
402
+ font-weight: 600;
403
+ color: var(--color-surface-contrast, #1d1d1f);
404
+ }
405
+
406
+ td {
407
+ position: relative;
408
+ }
409
+
410
+ textarea {
411
+ display: block;
412
+ width: 100%;
413
+ height: 100%;
414
+ min-width: 6em;
415
+ box-sizing: border-box;
416
+ padding: 8px 11px;
417
+ border: 0;
418
+ border-radius: 0;
419
+ background: none;
420
+ resize: none;
421
+ font: inherit;
422
+ color: inherit;
423
+ outline: none;
424
+ }
425
+ textarea::placeholder {
426
+ color: var(--color-muted-contrast, #b8b8c0);
427
+ }
428
+ textarea:focus {
429
+ background: var(--color-surface, #fff);
430
+ box-shadow: inset 0 0 0 2px var(--color-accent, #3b82f6);
431
+ }
432
+ textarea.hidden-text {
433
+ position: absolute;
434
+ inset: 0;
435
+ min-height: 0 !important;
436
+ color: transparent;
437
+ }
438
+
439
+ .shown {
440
+ position: relative;
441
+ box-sizing: border-box;
442
+ min-width: 6em;
443
+ padding: 8px 11px;
444
+ overflow: hidden;
445
+ pointer-events: none;
446
+ white-space: pre-wrap;
447
+ word-break: break-word;
448
+ }
449
+ .shown :global(code) {
450
+ padding: 0.1em 0.35em;
451
+ border-radius: 4px;
452
+ background: rgba(120, 120, 140, 0.14);
453
+ font: 0.88em ui-monospace, SFMono-Regular, Menlo, monospace;
454
+ }
455
+ .shown :global(a) {
456
+ color: var(--color-accent, #3b82f6);
457
+ text-decoration: underline;
458
+ }
459
+
460
+ td.corner,
461
+ td.rgrip {
462
+ width: 14px;
463
+ border-right: 1px solid var(--color-frame, #ececf0);
464
+ }
465
+
466
+ tr.grips td {
467
+ height: 14px;
468
+ border-bottom: 1px solid var(--color-frame, #ececf0);
469
+ }
470
+
471
+ .grip {
472
+ display: grid;
473
+ place-items: center;
474
+ width: 100%;
475
+ height: 100%;
476
+ min-height: 14px;
477
+ padding: 0;
478
+ border: 0;
479
+ background: none;
480
+ color: var(--color-muted-contrast, #b0b0b8);
481
+ cursor: grab;
482
+ opacity: 0;
483
+ touch-action: none;
484
+ transition: opacity 0.12s;
485
+ }
486
+ .table-editor:hover .grip, table.dragging .grip {
487
+ opacity: 1;
488
+ }
489
+ .grip:hover {
490
+ color: var(--color-accent, #3b82f6);
491
+ background: color-mix(in srgb, var(--color-accent, #3b82f6) 9%, transparent);
492
+ }
493
+
494
+ .moving {
495
+ opacity: 0.45;
496
+ }
497
+
498
+ tr.before td {
499
+ box-shadow: inset 0 2px 0 var(--color-accent, #3b82f6);
500
+ }
501
+
502
+ tr.after td {
503
+ box-shadow: inset 0 -2px 0 var(--color-accent, #3b82f6);
504
+ }
505
+
506
+ td.before,
507
+ td.c-before {
508
+ box-shadow: inset 2px 0 0 var(--color-accent, #3b82f6);
509
+ }
510
+
511
+ td.after,
512
+ td.c-after {
513
+ box-shadow: inset -2px 0 0 var(--color-accent, #3b82f6);
514
+ }</style>
@@ -32,31 +32,31 @@
32
32
  </div>
33
33
  {/if}
34
34
 
35
- <style>
36
- /* Wide tables scroll on their own instead of the page. */
37
- .wrap {
38
- margin: 0 0 1em;
39
- overflow-x: auto;
40
- }
41
- table {
42
- width: 100%;
43
- border-collapse: collapse;
44
- font: 15px/1.5 system-ui, sans-serif;
45
- }
46
- th,
47
- td {
48
- padding: 0.45em 0.7em;
49
- border-bottom: 1px solid color-mix(in srgb, currentColor 15%, transparent);
50
- text-align: left;
51
- vertical-align: top;
52
- }
53
- th {
54
- font-weight: 600;
55
- }
56
- th.top {
57
- border-bottom-width: 2px;
58
- }
59
- th.bottom {
60
- border-top: 2px solid color-mix(in srgb, currentColor 22%, transparent);
61
- }
62
- </style>
35
+ <style>.wrap {
36
+ margin: 0 0 1em;
37
+ overflow-x: auto;
38
+ }
39
+
40
+ table {
41
+ width: 100%;
42
+ border-collapse: collapse;
43
+ font: 15px/1.5 system-ui, sans-serif;
44
+ }
45
+
46
+ th,
47
+ td {
48
+ padding: 0.45em 0.7em;
49
+ border-bottom: 1px solid color-mix(in srgb, currentColor 15%, transparent);
50
+ text-align: left;
51
+ vertical-align: top;
52
+ }
53
+
54
+ th {
55
+ font-weight: 600;
56
+ }
57
+ th.top {
58
+ border-bottom-width: 2px;
59
+ }
60
+ th.bottom {
61
+ border-top: 2px solid color-mix(in srgb, currentColor 22%, transparent);
62
+ }</style>