@dorsk/tsumikit 0.70.0 → 0.72.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.
- package/README.md +49 -8
- package/dist/autoresize.js +13 -0
- package/dist/clipboard.js +12 -0
- package/dist/components/atoms/Artwork.svelte +10 -3
- package/dist/components/atoms/Artwork.svelte.d.ts +2 -0
- package/dist/components/atoms/Avatar.svelte +4 -3
- package/dist/components/atoms/Badge.svelte +23 -8
- package/dist/components/atoms/Badge.svelte.d.ts +2 -1
- package/dist/components/atoms/Button.svelte +39 -11
- package/dist/components/atoms/Card.svelte +2 -0
- package/dist/components/atoms/Dot.svelte +1 -1
- package/dist/components/atoms/Icon.svelte +2 -0
- package/dist/components/atoms/Icon.svelte.d.ts +2 -0
- package/dist/components/atoms/Input.svelte +3 -0
- package/dist/components/atoms/Kbd.svelte +28 -16
- package/dist/components/atoms/Link.svelte +18 -4
- package/dist/components/atoms/Scrim.svelte +18 -1
- package/dist/components/atoms/Select.svelte +1 -0
- package/dist/components/atoms/Slider.svelte +11 -1
- package/dist/components/atoms/Swatch.svelte +12 -0
- package/dist/components/atoms/Switch.svelte +12 -0
- package/dist/components/layouts/AppShell.svelte +70 -39
- package/dist/components/layouts/Container.svelte +25 -3
- package/dist/components/layouts/MasterDetail.svelte +59 -33
- package/dist/components/layouts/MasterDetail.svelte.d.ts +1 -1
- package/dist/components/layouts/NavItem.svelte +8 -4
- package/dist/components/layouts/NavSection.svelte +8 -2
- package/dist/components/layouts/ResizablePanel.svelte +14 -0
- package/dist/components/layouts/TabBar.svelte +217 -0
- package/dist/components/layouts/TabBar.svelte.d.ts +27 -0
- package/dist/components/layouts/Toolbar.svelte +5 -0
- package/dist/components/layouts/toolbar-roving.d.ts +16 -0
- package/dist/components/layouts/toolbar-roving.js +47 -1
- package/dist/components/molecules/Breadcrumb.svelte +44 -2
- package/dist/components/molecules/Breadcrumb.svelte.d.ts +6 -0
- package/dist/components/molecules/FilterInput.svelte +7 -0
- package/dist/components/molecules/FilterInput.svelte.d.ts +2 -0
- package/dist/components/molecules/PullToRefresh.svelte +205 -0
- package/dist/components/molecules/PullToRefresh.svelte.d.ts +18 -0
- package/dist/components/molecules/SegmentedControl.svelte +44 -1
- package/dist/components/molecules/SwipeRow.svelte +349 -0
- package/dist/components/molecules/SwipeRow.svelte.d.ts +37 -0
- package/dist/components/molecules/Toggle.svelte +12 -0
- package/dist/components/organisms/ConversationFeed.svelte +22 -8
- package/dist/components/organisms/ConversationFeed.svelte.d.ts +2 -0
- package/dist/components/organisms/DataTable.svelte +72 -12
- package/dist/components/organisms/DataTable.svelte.d.ts +7 -2
- package/dist/components/organisms/FilterSearchBar.svelte +4 -0
- package/dist/components/organisms/FilterSearchBar.svelte.d.ts +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/markdown.js +15 -7
- package/dist/styles/reset.css +30 -0
- package/dist/styles/syntax.css +0 -29
- package/dist/styles/tokens.css +6 -0
- package/package.json +1 -1
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
* detail cells share lines (`responsive="stack"` only, default true). */
|
|
26
26
|
stackLabel?: boolean;
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
let rootFontPx: number | undefined;
|
|
30
|
+
|
|
31
|
+
function rootFontSize(): number {
|
|
32
|
+
if (rootFontPx === undefined) {
|
|
33
|
+
rootFontPx = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
34
|
+
window.addEventListener('resize', () => (rootFontPx = undefined), { once: true });
|
|
35
|
+
}
|
|
36
|
+
return rootFontPx;
|
|
37
|
+
}
|
|
28
38
|
</script>
|
|
29
39
|
|
|
30
40
|
<script lang="ts" generics="T">
|
|
@@ -60,6 +70,7 @@
|
|
|
60
70
|
loadMoreLabel = 'Load more',
|
|
61
71
|
responsive = 'scroll',
|
|
62
72
|
stackBelow = '48rem',
|
|
73
|
+
stackActions = 'start',
|
|
63
74
|
class: klass = '',
|
|
64
75
|
style: styleProp = '',
|
|
65
76
|
tableClass = '',
|
|
@@ -76,9 +87,11 @@
|
|
|
76
87
|
/** Replaces a column's header content (e.g. a select-all checkbox). */
|
|
77
88
|
headerSnippets?: Record<string, Snippet<[Column<T>]>>;
|
|
78
89
|
empty?: string | Snippet;
|
|
90
|
+
/** Sticks within the table's own scroller, which also scrolls horizontally,
|
|
91
|
+
* so it only sticks when the table has a bounded height
|
|
92
|
+
* (e.g. `style="max-height: 60vh"`), not to the page. */
|
|
79
93
|
stickyHeader?: boolean;
|
|
80
|
-
/** Distance from the
|
|
81
|
-
* `var(--header-h)` when the page header overlaps. Defaults to 0. */
|
|
94
|
+
/** Distance from the scroller's top for the sticky header. Defaults to 0. */
|
|
82
95
|
stickyOffset?: string;
|
|
83
96
|
/** `fixed` makes declared column widths authoritative (needed for truncate). */
|
|
84
97
|
layout?: 'auto' | 'fixed';
|
|
@@ -101,6 +114,9 @@
|
|
|
101
114
|
responsive?: 'scroll' | 'stack';
|
|
102
115
|
/** Width (px/em/rem) under which `stack` mode kicks in. */
|
|
103
116
|
stackBelow?: string;
|
|
117
|
+
/** Where actions sit in a stacked card: `start` beside the title (wraps
|
|
118
|
+
* under a long one), `end` always closing the card. */
|
|
119
|
+
stackActions?: 'start' | 'end';
|
|
104
120
|
class?: string;
|
|
105
121
|
style?: string;
|
|
106
122
|
/** Class on the inner <table>. */
|
|
@@ -121,22 +137,31 @@
|
|
|
121
137
|
return { destroy: () => observer.disconnect() };
|
|
122
138
|
}
|
|
123
139
|
|
|
140
|
+
function markEmpty(node: HTMLElement) {
|
|
141
|
+
const update = () => node.toggleAttribute('data-empty', !node.firstElementChild && !node.textContent?.trim());
|
|
142
|
+
update();
|
|
143
|
+
const observer = new MutationObserver(update);
|
|
144
|
+
observer.observe(node, { childList: true, subtree: true, characterData: true });
|
|
145
|
+
return () => observer.disconnect();
|
|
146
|
+
}
|
|
147
|
+
|
|
124
148
|
function toPx(length: string, el: HTMLElement): number {
|
|
125
149
|
const n = Number.parseFloat(length);
|
|
126
|
-
|
|
127
|
-
if (length.endsWith('
|
|
128
|
-
if (length.endsWith('em')) return n * fontSize(el);
|
|
150
|
+
if (length.endsWith('rem')) return n * rootFontSize();
|
|
151
|
+
if (length.endsWith('em')) return n * Number.parseFloat(getComputedStyle(el).fontSize);
|
|
129
152
|
return n;
|
|
130
153
|
}
|
|
131
154
|
|
|
155
|
+
// Font sizes are read in the observer callback, after layout, so mounting never forces a style recalc.
|
|
132
156
|
$effect(() => {
|
|
133
157
|
if (responsive !== 'stack' || !wrapEl) {
|
|
134
158
|
stacked = false;
|
|
135
159
|
return;
|
|
136
160
|
}
|
|
137
161
|
const el = wrapEl;
|
|
138
|
-
const
|
|
162
|
+
const below = stackBelow;
|
|
139
163
|
const observer = new ResizeObserver(([entry]) => {
|
|
164
|
+
const limit = toPx(below, el);
|
|
140
165
|
stacked = entry.contentRect.width < limit;
|
|
141
166
|
});
|
|
142
167
|
observer.observe(el);
|
|
@@ -148,6 +173,13 @@
|
|
|
148
173
|
|
|
149
174
|
const colCount = $derived(columns.length + (rowActions ? 1 : 0));
|
|
150
175
|
|
|
176
|
+
const NESTED_CONTROL = 'a,button,input,select,textarea,label,summary,[role="button"],[contenteditable]';
|
|
177
|
+
|
|
178
|
+
function fromNestedControl(e: Event & { currentTarget: HTMLElement }): boolean {
|
|
179
|
+
const hit = (e.target as Element).closest(NESTED_CONTROL);
|
|
180
|
+
return !!hit && hit !== e.currentTarget && e.currentTarget.contains(hit);
|
|
181
|
+
}
|
|
182
|
+
|
|
151
183
|
function display(col: Column<T>, row: T): unknown {
|
|
152
184
|
if (col.get) return col.get(row);
|
|
153
185
|
return (row as Record<string, unknown>)[col.key];
|
|
@@ -188,6 +220,7 @@
|
|
|
188
220
|
data-tsu="DataTable"
|
|
189
221
|
data-size={size}
|
|
190
222
|
data-stacked={stacked || undefined}
|
|
223
|
+
data-stack-actions={responsive === 'stack' && stackActions === 'end' ? 'end' : undefined}
|
|
191
224
|
aria-busy={loading || undefined}
|
|
192
225
|
>
|
|
193
226
|
<table
|
|
@@ -221,7 +254,7 @@
|
|
|
221
254
|
{:else if col.sortable}
|
|
222
255
|
<button type="button" class="dt-sort" onclick={() => toggleSort(col)}>
|
|
223
256
|
<span>{col.label}</span>
|
|
224
|
-
<span class="dt-arrow" class:active={sortKey === col.key}>
|
|
257
|
+
<span class="dt-arrow" aria-hidden="true" class:active={sortKey === col.key}>
|
|
225
258
|
{sortKey === col.key ? (sortDir === 'asc' ? '↑' : '↓') : '↕'}
|
|
226
259
|
</span>
|
|
227
260
|
</button>
|
|
@@ -256,9 +289,14 @@
|
|
|
256
289
|
class:clickable={!!onrowclick}
|
|
257
290
|
class:toned={!!tone && tone !== 'neutral'}
|
|
258
291
|
tabindex={onrowclick ? 0 : undefined}
|
|
259
|
-
onclick={onrowclick
|
|
292
|
+
onclick={onrowclick
|
|
293
|
+
? (e) => {
|
|
294
|
+
if (!fromNestedControl(e)) onrowclick(row);
|
|
295
|
+
}
|
|
296
|
+
: undefined}
|
|
260
297
|
onkeydown={onrowclick
|
|
261
298
|
? (e) => {
|
|
299
|
+
if (e.target !== e.currentTarget) return;
|
|
262
300
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
263
301
|
e.preventDefault();
|
|
264
302
|
onrowclick(row);
|
|
@@ -267,20 +305,25 @@
|
|
|
267
305
|
: undefined}
|
|
268
306
|
>
|
|
269
307
|
{#each columns as col (col.key)}
|
|
308
|
+
{@const value = cellSnippets[col.key] ? undefined : display(col, row)}
|
|
270
309
|
<td
|
|
271
310
|
data-part="cell"
|
|
272
311
|
data-hide-below={col.hideBelow}
|
|
273
312
|
data-role={responsive === 'stack' ? (col.role ?? 'detail') : undefined}
|
|
274
313
|
data-label={responsive === 'stack' ? col.label : undefined}
|
|
275
314
|
data-stack-label={responsive === 'stack' && col.stackLabel === false ? 'false' : undefined}
|
|
315
|
+
data-empty={responsive === 'stack' && !cellSnippets[col.key] && (value == null || value === '')
|
|
316
|
+
? ''
|
|
317
|
+
: undefined}
|
|
276
318
|
class:truncate={col.truncate}
|
|
277
319
|
class:nowrap={col.nowrap}
|
|
278
320
|
style:text-align={stacked ? undefined : (col.align ?? 'left')}
|
|
321
|
+
{@attach responsive === 'stack' && cellSnippets[col.key] ? markEmpty : undefined}
|
|
279
322
|
>
|
|
280
323
|
{#if cellSnippets[col.key]}
|
|
281
324
|
{@render cellSnippets[col.key](row)}
|
|
282
325
|
{:else}
|
|
283
|
-
{
|
|
326
|
+
{value}
|
|
284
327
|
{/if}
|
|
285
328
|
</td>
|
|
286
329
|
{/each}
|
|
@@ -403,7 +446,10 @@
|
|
|
403
446
|
tr.clickable:hover,
|
|
404
447
|
tr.clickable:focus-visible {
|
|
405
448
|
background: var(--bg-elevated-2);
|
|
406
|
-
|
|
449
|
+
}
|
|
450
|
+
tr.clickable:focus-visible {
|
|
451
|
+
outline: var(--focus-ring);
|
|
452
|
+
outline-offset: -2px;
|
|
407
453
|
}
|
|
408
454
|
td.truncate {
|
|
409
455
|
white-space: nowrap;
|
|
@@ -479,6 +525,9 @@
|
|
|
479
525
|
[data-stacked] {
|
|
480
526
|
overflow-x: visible;
|
|
481
527
|
}
|
|
528
|
+
[data-stacked] .dt {
|
|
529
|
+
table-layout: fixed;
|
|
530
|
+
}
|
|
482
531
|
[data-stacked] thead {
|
|
483
532
|
position: absolute;
|
|
484
533
|
width: 1px;
|
|
@@ -552,10 +601,17 @@
|
|
|
552
601
|
[data-stacked] td[data-stack-label='false']::before {
|
|
553
602
|
content: none;
|
|
554
603
|
}
|
|
555
|
-
[data-stacked] td[data-
|
|
604
|
+
[data-stacked] td[data-part='cell'][data-empty] {
|
|
605
|
+
display: none;
|
|
606
|
+
}
|
|
607
|
+
[data-stacked]
|
|
608
|
+
td[data-role='meta']:not([data-empty])
|
|
609
|
+
~ td[data-role='meta'][data-stack-label='false']:not([data-empty]) {
|
|
556
610
|
margin-inline-start: calc(var(--sp-1) - var(--sp-3));
|
|
557
611
|
}
|
|
558
|
-
[data-stacked]
|
|
612
|
+
[data-stacked]
|
|
613
|
+
td[data-role='meta']:not([data-empty])
|
|
614
|
+
~ td[data-role='meta'][data-stack-label='false']:not([data-empty])::before {
|
|
559
615
|
content: '·';
|
|
560
616
|
margin-inline-end: var(--sp-1);
|
|
561
617
|
color: var(--text-faint);
|
|
@@ -568,6 +624,10 @@
|
|
|
568
624
|
width: auto;
|
|
569
625
|
opacity: 1;
|
|
570
626
|
}
|
|
627
|
+
[data-stacked][data-stack-actions='end'] td[data-role='actions'],
|
|
628
|
+
[data-stacked][data-stack-actions='end'] td.dt-actions {
|
|
629
|
+
order: 2;
|
|
630
|
+
}
|
|
571
631
|
[data-stacked] td[data-role='hidden'] {
|
|
572
632
|
display: none;
|
|
573
633
|
}
|
|
@@ -38,9 +38,11 @@ declare function $$render<T>(): {
|
|
|
38
38
|
/** Replaces a column's header content (e.g. a select-all checkbox). */
|
|
39
39
|
headerSnippets?: Record<string, Snippet<[Column<T>]>>;
|
|
40
40
|
empty?: string | Snippet;
|
|
41
|
+
/** Sticks within the table's own scroller, which also scrolls horizontally,
|
|
42
|
+
* so it only sticks when the table has a bounded height
|
|
43
|
+
* (e.g. `style="max-height: 60vh"`), not to the page. */
|
|
41
44
|
stickyHeader?: boolean;
|
|
42
|
-
/** Distance from the
|
|
43
|
-
* `var(--header-h)` when the page header overlaps. Defaults to 0. */
|
|
45
|
+
/** Distance from the scroller's top for the sticky header. Defaults to 0. */
|
|
44
46
|
stickyOffset?: string;
|
|
45
47
|
/** `fixed` makes declared column widths authoritative (needed for truncate). */
|
|
46
48
|
layout?: "auto" | "fixed";
|
|
@@ -63,6 +65,9 @@ declare function $$render<T>(): {
|
|
|
63
65
|
responsive?: "scroll" | "stack";
|
|
64
66
|
/** Width (px/em/rem) under which `stack` mode kicks in. */
|
|
65
67
|
stackBelow?: string;
|
|
68
|
+
/** Where actions sit in a stacked card: `start` beside the title (wraps
|
|
69
|
+
* under a long one), `end` always closing the card. */
|
|
70
|
+
stackActions?: "start" | "end";
|
|
66
71
|
class?: string;
|
|
67
72
|
style?: string;
|
|
68
73
|
/** Class on the inner <table>. */
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
size = 'md',
|
|
30
30
|
shape = 'square',
|
|
31
31
|
surface = 'base',
|
|
32
|
+
font = 'mono',
|
|
32
33
|
hotkey,
|
|
33
34
|
showHotkey = false,
|
|
34
35
|
grow = false,
|
|
@@ -56,6 +57,8 @@
|
|
|
56
57
|
size?: ControlSize;
|
|
57
58
|
shape?: 'square' | 'pill';
|
|
58
59
|
surface?: 'base' | 'raised' | 'sunken';
|
|
60
|
+
/** `sans` renders the typed text and placeholder in `--font-sans`; chips stay mono. */
|
|
61
|
+
font?: 'mono' | 'sans';
|
|
59
62
|
hotkey?: string;
|
|
60
63
|
showHotkey?: boolean;
|
|
61
64
|
grow?: boolean;
|
|
@@ -81,6 +84,7 @@
|
|
|
81
84
|
{size}
|
|
82
85
|
{shape}
|
|
83
86
|
{surface}
|
|
87
|
+
{font}
|
|
84
88
|
{hotkey}
|
|
85
89
|
{showHotkey}
|
|
86
90
|
{grow}
|
|
@@ -19,6 +19,8 @@ type Own = {
|
|
|
19
19
|
size?: ControlSize;
|
|
20
20
|
shape?: 'square' | 'pill';
|
|
21
21
|
surface?: 'base' | 'raised' | 'sunken';
|
|
22
|
+
/** `sans` renders the typed text and placeholder in `--font-sans`; chips stay mono. */
|
|
23
|
+
font?: 'mono' | 'sans';
|
|
22
24
|
hotkey?: string;
|
|
23
25
|
showHotkey?: boolean;
|
|
24
26
|
grow?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export { default as NavSection } from './components/layouts/NavSection.svelte';
|
|
|
41
41
|
export { default as ResizablePanel } from './components/layouts/ResizablePanel.svelte';
|
|
42
42
|
export { type ResizeHandleParams, resizeHandle, } from './components/layouts/resizable-panel-frame.js';
|
|
43
43
|
export { default as Stack } from './components/layouts/Stack.svelte';
|
|
44
|
+
export { default as TabBar, type TabBarItem } from './components/layouts/TabBar.svelte';
|
|
44
45
|
export { default as Toolbar } from './components/layouts/Toolbar.svelte';
|
|
45
46
|
export { type AccordionHeaderContext, type AccordionItem, type AccordionVariant, default as Accordion, } from './components/molecules/Accordion.svelte';
|
|
46
47
|
export { type Attachment, default as AttachmentList, } from './components/molecules/AttachmentList.svelte';
|
|
@@ -79,6 +80,7 @@ export { default as OptionButton } from './components/molecules/OptionButton.sve
|
|
|
79
80
|
export { default as Pagination } from './components/molecules/Pagination.svelte';
|
|
80
81
|
export { default as Popover } from './components/molecules/Popover.svelte';
|
|
81
82
|
export { default as Prose } from './components/molecules/Prose.svelte';
|
|
83
|
+
export { default as PullToRefresh } from './components/molecules/PullToRefresh.svelte';
|
|
82
84
|
export { default as RadialMenu, type RadialArc, } from './components/molecules/RadialMenu.svelte';
|
|
83
85
|
export { default as RadioGroup, type RadioOption, } from './components/molecules/RadioGroup.svelte';
|
|
84
86
|
export { default as SectionHeader } from './components/molecules/SectionHeader.svelte';
|
|
@@ -87,6 +89,7 @@ export { default as SelectButton } from './components/molecules/SelectButton.sve
|
|
|
87
89
|
export { default as SortableList, SORTABLE_LIST_MESSAGES, type SortableListMessages, type SortableListMove, type SortableListRowContext, } from './components/molecules/SortableList.svelte';
|
|
88
90
|
export { default as SplitButton } from './components/molecules/SplitButton.svelte';
|
|
89
91
|
export { default as StatusBar } from './components/molecules/StatusBar.svelte';
|
|
92
|
+
export { default as SwipeRow, type SwipeAction } from './components/molecules/SwipeRow.svelte';
|
|
90
93
|
export { default as Tabs, type TabItem } from './components/molecules/Tabs.svelte';
|
|
91
94
|
export { default as ThemePicker } from './components/molecules/ThemePicker.svelte';
|
|
92
95
|
export { default as Timestamp } from './components/molecules/Timestamp.svelte';
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ export { default as NavSection } from './components/layouts/NavSection.svelte';
|
|
|
46
46
|
export { default as ResizablePanel } from './components/layouts/ResizablePanel.svelte';
|
|
47
47
|
export { resizeHandle, } from './components/layouts/resizable-panel-frame.js';
|
|
48
48
|
export { default as Stack } from './components/layouts/Stack.svelte';
|
|
49
|
+
export { default as TabBar } from './components/layouts/TabBar.svelte';
|
|
49
50
|
export { default as Toolbar } from './components/layouts/Toolbar.svelte';
|
|
50
51
|
export { default as Accordion, } from './components/molecules/Accordion.svelte';
|
|
51
52
|
export { default as AttachmentList, } from './components/molecules/AttachmentList.svelte';
|
|
@@ -87,6 +88,7 @@ export { default as OptionButton } from './components/molecules/OptionButton.sve
|
|
|
87
88
|
export { default as Pagination } from './components/molecules/Pagination.svelte';
|
|
88
89
|
export { default as Popover } from './components/molecules/Popover.svelte';
|
|
89
90
|
export { default as Prose } from './components/molecules/Prose.svelte';
|
|
91
|
+
export { default as PullToRefresh } from './components/molecules/PullToRefresh.svelte';
|
|
90
92
|
export { default as RadialMenu, } from './components/molecules/RadialMenu.svelte';
|
|
91
93
|
export { default as RadioGroup, } from './components/molecules/RadioGroup.svelte';
|
|
92
94
|
export { default as SectionHeader } from './components/molecules/SectionHeader.svelte';
|
|
@@ -95,6 +97,7 @@ export { default as SelectButton } from './components/molecules/SelectButton.sve
|
|
|
95
97
|
export { default as SortableList, SORTABLE_LIST_MESSAGES, } from './components/molecules/SortableList.svelte';
|
|
96
98
|
export { default as SplitButton } from './components/molecules/SplitButton.svelte';
|
|
97
99
|
export { default as StatusBar } from './components/molecules/StatusBar.svelte';
|
|
100
|
+
export { default as SwipeRow } from './components/molecules/SwipeRow.svelte';
|
|
98
101
|
export { default as Tabs } from './components/molecules/Tabs.svelte';
|
|
99
102
|
export { default as ThemePicker } from './components/molecules/ThemePicker.svelte';
|
|
100
103
|
export { default as Timestamp } from './components/molecules/Timestamp.svelte';
|
package/dist/markdown.js
CHANGED
|
@@ -15,6 +15,7 @@ const BLOCK_RE = new RegExp(`${BLOCK_L}(\\d+)${BLOCK_R}`, 'g');
|
|
|
15
15
|
const SPAN_L = '\x10';
|
|
16
16
|
const SPAN_R = '\x11';
|
|
17
17
|
const SPAN_RE = new RegExp(`${SPAN_L}(\\d+)${SPAN_R}`, 'g');
|
|
18
|
+
const BARE_URL_RE = new RegExp(`\\bhttps?://(?:&|[^\\s&<>"${SPAN_L}${SPAN_R}])+`, 'g');
|
|
18
19
|
// Terminal escape sequences (the ansi-regex pattern), then the remaining C0
|
|
19
20
|
// controls except tab and newline, plus DEL.
|
|
20
21
|
const ANSI_RE =
|
|
@@ -62,20 +63,27 @@ function autolinkUrls(s) {
|
|
|
62
63
|
});
|
|
63
64
|
return s.replace(SPAN_RE, (_m, i) => saved[Number(i)]);
|
|
64
65
|
}
|
|
65
|
-
// Inline passes on already-escaped text.
|
|
66
|
+
// Inline passes on already-escaped text; emphasis never reaches inside code, link tags or URLs.
|
|
66
67
|
function inline(s, autolink) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
s = s.replace(
|
|
70
|
-
s = s.replace(/(^|[^\w])_([^_\n]+)_(?!\w)/g, '$1<em>$2</em>');
|
|
71
|
-
s = s.replace(/~~([^~]+)~~/g, '<del>$1</del>');
|
|
68
|
+
const held = [];
|
|
69
|
+
const hold = (html) => `${SPAN_L}${held.push(html) - 1}${SPAN_R}`;
|
|
70
|
+
s = s.replace(/`([^`]+)`/g, (_m, code) => hold(`<code>${code}</code>`));
|
|
72
71
|
s = s.replace(/\[([^\]]+)\]\(([^\s)]+)\)/g, (m, text, url) => {
|
|
73
72
|
const href = safeHref(url);
|
|
74
73
|
if (!href)
|
|
75
74
|
return m;
|
|
76
75
|
const external = /^https?:/i.test(href) ? ' target="_blank" rel="noopener noreferrer"' : '';
|
|
77
|
-
return `<a href="${href}"${external}
|
|
76
|
+
return `${hold(`<a href="${href}"${external}>`)}${text}${hold('</a>')}`;
|
|
77
|
+
});
|
|
78
|
+
s = s.replace(BARE_URL_RE, (raw) => {
|
|
79
|
+
const url = raw.replace(/[*_~]+$/, '');
|
|
80
|
+
return hold(url) + raw.slice(url.length);
|
|
78
81
|
});
|
|
82
|
+
s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
83
|
+
s = s.replace(/(^|[^*\w])\*([^*\n]+)\*/g, '$1<em>$2</em>');
|
|
84
|
+
s = s.replace(/(^|[^\w])_([^_\n]+)_(?!\w)/g, '$1<em>$2</em>');
|
|
85
|
+
s = s.replace(/~~([^~]+)~~/g, '<del>$1</del>');
|
|
86
|
+
s = s.replace(SPAN_RE, (_m, i) => held[Number(i)]);
|
|
79
87
|
return autolink ? autolinkUrls(s) : s;
|
|
80
88
|
}
|
|
81
89
|
function splitRow(row) {
|
package/dist/styles/reset.css
CHANGED
|
@@ -78,6 +78,11 @@ select {
|
|
|
78
78
|
code,
|
|
79
79
|
pre {
|
|
80
80
|
font-family: var(--font-mono);
|
|
81
|
+
}
|
|
82
|
+
code {
|
|
83
|
+
font-size: var(--fs-xs);
|
|
84
|
+
}
|
|
85
|
+
pre {
|
|
81
86
|
font-size: 0.875em;
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -101,3 +106,28 @@ pre {
|
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
@media (prefers-reduced-motion: reduce) {
|
|
110
|
+
*,
|
|
111
|
+
*::before,
|
|
112
|
+
*::after {
|
|
113
|
+
animation-duration: 0.01ms !important;
|
|
114
|
+
animation-iteration-count: 1 !important;
|
|
115
|
+
transition-duration: 0.01ms !important;
|
|
116
|
+
scroll-behavior: auto !important;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Windows High Contrast: keep the focus ring on a system color. */
|
|
121
|
+
@media (forced-colors: active) {
|
|
122
|
+
:focus-visible {
|
|
123
|
+
outline: 2px solid Highlight;
|
|
124
|
+
outline-offset: 2px;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Stronger separators when the user asks for more contrast. */
|
|
129
|
+
@media (prefers-contrast: more) {
|
|
130
|
+
:root {
|
|
131
|
+
--border: var(--border-strong);
|
|
132
|
+
}
|
|
133
|
+
}
|
package/dist/styles/syntax.css
CHANGED
|
@@ -63,32 +63,3 @@
|
|
|
63
63
|
.hljs-deletion {
|
|
64
64
|
color: var(--danger);
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
@media (prefers-reduced-motion: reduce) {
|
|
68
|
-
*,
|
|
69
|
-
*::before,
|
|
70
|
-
*::after {
|
|
71
|
-
animation-duration: 0.01ms !important;
|
|
72
|
-
animation-iteration-count: 1 !important;
|
|
73
|
-
transition-duration: 0.01ms !important;
|
|
74
|
-
scroll-behavior: auto !important;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/* Forced colors (Windows High Contrast). The OS replaces our palette with its
|
|
79
|
-
own; our job is to not fight it and to keep affordances visible. Keep the
|
|
80
|
-
focus ring mapped to a system color, and make sure the focus outline shows
|
|
81
|
-
even where we'd normally rely on a token border. */
|
|
82
|
-
@media (forced-colors: active) {
|
|
83
|
-
:focus-visible {
|
|
84
|
-
outline: 2px solid Highlight;
|
|
85
|
-
outline-offset: 2px;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/* Stronger separators when the user asks for more contrast. */
|
|
90
|
-
@media (prefers-contrast: more) {
|
|
91
|
-
:root {
|
|
92
|
-
--border: var(--border-strong);
|
|
93
|
-
}
|
|
94
|
-
}
|
package/dist/styles/tokens.css
CHANGED
|
@@ -184,6 +184,12 @@
|
|
|
184
184
|
/* Minimum hit area on coarse pointers (WCAG 2.5.8); px so it never shrinks
|
|
185
185
|
under a small root font-size. */
|
|
186
186
|
--touch-target: 44px;
|
|
187
|
+
/* Compact controls on coarse pointers: an absolute ::before with inset-block
|
|
188
|
+
and inset-inline-start at var(--touch-slab) and inset-inline-end: 0 (no
|
|
189
|
+
scrollable overflow sideways). Controls that clip their overflow grow
|
|
190
|
+
min-height to --touch-target-compact instead. */
|
|
191
|
+
--touch-target-compact: 40px;
|
|
192
|
+
--touch-slab: min(-8px, calc((100% - var(--touch-target-compact)) / 2));
|
|
187
193
|
|
|
188
194
|
/* ---- Layout ---- */
|
|
189
195
|
/* Fixed (px, not rem): the header is chrome that must NOT scale with the
|
package/package.json
CHANGED