@rtzll/reflow 0.4.0 → 0.6.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/dist/avatar.css +116 -0
- package/dist/avatar.d.ts +42 -0
- package/dist/avatar.js +74 -0
- package/dist/basic.d.ts +1 -20
- package/dist/basic.js +3 -27
- package/dist/chat-layout.css +121 -0
- package/dist/chat-layout.d.ts +24 -0
- package/dist/chat-layout.js +108 -0
- package/dist/citation.css +119 -0
- package/dist/citation.d.ts +35 -0
- package/dist/citation.js +45 -0
- package/dist/code-block.css +292 -0
- package/dist/code-block.d.ts +59 -0
- package/dist/code-block.js +117 -0
- package/dist/colors.d.ts +21 -0
- package/dist/colors.js +21 -0
- package/dist/compositions.css +54 -3
- package/dist/compositions.d.ts +8 -1
- package/dist/compositions.js +2 -0
- package/dist/drawer.css +236 -0
- package/dist/drawer.d.ts +45 -0
- package/dist/drawer.js +228 -0
- package/dist/foundations.css +6 -0
- package/dist/highlight.d.ts +36 -0
- package/dist/highlight.js +134 -0
- package/dist/hover-card.d.ts +4 -0
- package/dist/hover-card.js +5 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/sidebar.css +14 -57
- package/dist/sidebar.js +9 -13
- package/dist/status-icon.d.ts +3 -0
- package/dist/status-icon.js +7 -0
- package/dist/styles.css +6 -0
- package/dist/toaster.css +268 -0
- package/dist/toaster.d.ts +79 -0
- package/dist/toaster.js +419 -0
- package/package.json +19 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@rtzll/reflow/highlight`: Shiki behind one function, for CodeBlock.
|
|
3
|
+
*
|
|
4
|
+
* This entry is optional. It needs the `shiki` and `@shikijs/transformers`
|
|
5
|
+
* peer dependencies, and nothing else in Reflow imports it, so an app that
|
|
6
|
+
* never imports it never installs or loads Shiki.
|
|
7
|
+
*/
|
|
8
|
+
import { type BundledTheme, type ShikiTransformer, type ThemeRegistrationAny } from 'shiki';
|
|
9
|
+
import type { HighlightedCode } from './code-block.tsx';
|
|
10
|
+
export type { CodeLine, CodeToken, HighlightedCode } from './code-block.tsx';
|
|
11
|
+
type Theme = BundledTheme | ThemeRegistrationAny;
|
|
12
|
+
export interface HighlightOptions {
|
|
13
|
+
/** Any language Shiki bundles, or one of its aliases. Unknown languages render as plain text. Default text. */
|
|
14
|
+
lang?: string;
|
|
15
|
+
/** A Shiki theme, by name or definition. Defaults to Reflow's, which colours tokens with `--rf-code-token-*` variables. */
|
|
16
|
+
theme?: Theme;
|
|
17
|
+
/** A light and a dark theme; the block follows the page's color scheme between them. */
|
|
18
|
+
themes?: {
|
|
19
|
+
light: Theme;
|
|
20
|
+
dark: Theme;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Reads `// [!code highlight]`, `[!code ++]`, `[!code --]`, `[!code focus]`,
|
|
24
|
+
* `[!code error]`, `[!code warning]` and `[!code word:name]` comments into
|
|
25
|
+
* line and word marks, and removes them from the code. Default true.
|
|
26
|
+
*/
|
|
27
|
+
notation?: boolean;
|
|
28
|
+
/** More Shiki transformers, run after the notation ones. */
|
|
29
|
+
transformers?: ShikiTransformer[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Highlights code for CodeBlock. Call it where the code is known: in a route
|
|
33
|
+
* handler, so the page arrives highlighted and the browser loads nothing, or
|
|
34
|
+
* in the browser for code that arrives later, such as a streamed reply.
|
|
35
|
+
*/
|
|
36
|
+
export declare function highlight(code: string, options?: HighlightOptions): Promise<HighlightedCode>;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@rtzll/reflow/highlight`: Shiki behind one function, for CodeBlock.
|
|
3
|
+
*
|
|
4
|
+
* This entry is optional. It needs the `shiki` and `@shikijs/transformers`
|
|
5
|
+
* peer dependencies, and nothing else in Reflow imports it, so an app that
|
|
6
|
+
* never imports it never installs or loads Shiki.
|
|
7
|
+
*/
|
|
8
|
+
import { bundledLanguages, bundledLanguagesAlias, createCssVariablesTheme, createHighlighter, createJavaScriptRegexEngine, } from 'shiki';
|
|
9
|
+
import { transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight, } from '@shikijs/transformers';
|
|
10
|
+
// Reflow's theme: every colour is a CSS variable, set in code-block.css for
|
|
11
|
+
// the light and dark themes, so highlighted code follows the page.
|
|
12
|
+
const reflowTheme = createCssVariablesTheme({
|
|
13
|
+
name: 'reflow',
|
|
14
|
+
variablePrefix: '--rf-code-',
|
|
15
|
+
fontStyle: true,
|
|
16
|
+
});
|
|
17
|
+
let shared;
|
|
18
|
+
// One highlighter for the process or page, with the JavaScript regex engine,
|
|
19
|
+
// which needs no WebAssembly. Languages and themes load as they are first used.
|
|
20
|
+
function highlighter() {
|
|
21
|
+
shared ??= createHighlighter({
|
|
22
|
+
themes: [reflowTheme],
|
|
23
|
+
langs: [],
|
|
24
|
+
engine: createJavaScriptRegexEngine({ forgiving: true }),
|
|
25
|
+
});
|
|
26
|
+
return shared;
|
|
27
|
+
}
|
|
28
|
+
function bundled(lang) {
|
|
29
|
+
return lang in bundledLanguages || lang in bundledLanguagesAlias;
|
|
30
|
+
}
|
|
31
|
+
function themeName(theme) {
|
|
32
|
+
return typeof theme === 'string' ? theme : (theme.name ?? 'custom');
|
|
33
|
+
}
|
|
34
|
+
function classes(node) {
|
|
35
|
+
const value = node.properties?.class ?? node.properties?.className;
|
|
36
|
+
return Array.isArray(value) ? value.join(' ') : typeof value === 'string' ? value : '';
|
|
37
|
+
}
|
|
38
|
+
// Flattens a line into runs of text, each with the innermost style and
|
|
39
|
+
// every class around it, such as a token inside a highlighted word.
|
|
40
|
+
function tokens(node, style = '', classList = []) {
|
|
41
|
+
if (node.type === 'text') {
|
|
42
|
+
if (!node.value)
|
|
43
|
+
return [];
|
|
44
|
+
const token = { content: node.value };
|
|
45
|
+
if (style)
|
|
46
|
+
token.style = style;
|
|
47
|
+
if (classList.length)
|
|
48
|
+
token.class = classList.join(' ');
|
|
49
|
+
return [token];
|
|
50
|
+
}
|
|
51
|
+
const own = typeof node.properties?.style === 'string' ? node.properties.style : '';
|
|
52
|
+
const name = classes(node);
|
|
53
|
+
const nested = name ? [...classList, name] : classList;
|
|
54
|
+
return (node.children ?? []).flatMap((child) => tokens(child, own || style, nested));
|
|
55
|
+
}
|
|
56
|
+
function trimEnd(list) {
|
|
57
|
+
while (list.length) {
|
|
58
|
+
const last = list[list.length - 1];
|
|
59
|
+
last.content = last.content.replace(/[ \t]+$/, '');
|
|
60
|
+
if (last.content)
|
|
61
|
+
return;
|
|
62
|
+
list.pop();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Highlights code for CodeBlock. Call it where the code is known: in a route
|
|
67
|
+
* handler, so the page arrives highlighted and the browser loads nothing, or
|
|
68
|
+
* in the browser for code that arrives later, such as a streamed reply.
|
|
69
|
+
*/
|
|
70
|
+
export async function highlight(code, options = {}) {
|
|
71
|
+
const instance = await highlighter();
|
|
72
|
+
const requested = options.lang ?? 'text';
|
|
73
|
+
const lang = bundled(requested) ? requested : 'text';
|
|
74
|
+
if (lang !== 'text' && !instance.getLoadedLanguages().includes(lang))
|
|
75
|
+
await instance.loadLanguage(lang);
|
|
76
|
+
const themes = options.themes ? [options.themes.light, options.themes.dark] : [options.theme];
|
|
77
|
+
for (const theme of themes)
|
|
78
|
+
if (theme && !instance.getLoadedThemes().includes(themeName(theme)))
|
|
79
|
+
await instance.loadTheme(theme);
|
|
80
|
+
const transformers = [
|
|
81
|
+
...(options.notation === false
|
|
82
|
+
? []
|
|
83
|
+
: [
|
|
84
|
+
transformerNotationHighlight(),
|
|
85
|
+
transformerNotationDiff(),
|
|
86
|
+
transformerNotationFocus(),
|
|
87
|
+
transformerNotationErrorLevel(),
|
|
88
|
+
transformerNotationWordHighlight(),
|
|
89
|
+
]),
|
|
90
|
+
...(options.transformers ?? []),
|
|
91
|
+
];
|
|
92
|
+
const root = instance.codeToHast(code, options.themes
|
|
93
|
+
? {
|
|
94
|
+
lang,
|
|
95
|
+
themes: {
|
|
96
|
+
light: themeName(options.themes.light),
|
|
97
|
+
dark: themeName(options.themes.dark),
|
|
98
|
+
},
|
|
99
|
+
defaultColor: false,
|
|
100
|
+
transformers,
|
|
101
|
+
}
|
|
102
|
+
: { lang, theme: options.theme ? themeName(options.theme) : 'reflow', transformers });
|
|
103
|
+
const pre = root.children?.find((node) => node.tagName === 'pre');
|
|
104
|
+
const body = pre?.children?.find((node) => node.tagName === 'code');
|
|
105
|
+
const lines = (body?.children ?? [])
|
|
106
|
+
.filter((node) => node.type === 'element')
|
|
107
|
+
.map((node) => {
|
|
108
|
+
const mark = classes(node)
|
|
109
|
+
.split(/\s+/)
|
|
110
|
+
.filter((name) => name && name !== 'line')
|
|
111
|
+
.join(' ');
|
|
112
|
+
const line = { tokens: tokens({ ...node, properties: {} }) };
|
|
113
|
+
// Removing a notation comment leaves the space before it.
|
|
114
|
+
if (options.notation !== false)
|
|
115
|
+
trimEnd(line.tokens);
|
|
116
|
+
if (mark)
|
|
117
|
+
line.class = mark;
|
|
118
|
+
return line;
|
|
119
|
+
});
|
|
120
|
+
const flags = classes(pre ?? { type: 'element' })
|
|
121
|
+
.split(/\s+/)
|
|
122
|
+
.filter((name) => name.startsWith('has-'));
|
|
123
|
+
const style = typeof pre?.properties?.style === 'string' ? pre.properties.style : undefined;
|
|
124
|
+
const result = {
|
|
125
|
+
lang: requested === 'text' || lang === 'text' ? 'text' : requested,
|
|
126
|
+
code: lines.map((line) => line.tokens.map((token) => token.content).join('')).join('\n'),
|
|
127
|
+
lines,
|
|
128
|
+
flags,
|
|
129
|
+
};
|
|
130
|
+
// Reflow's own theme takes its colours from CSS; others bring theirs inline.
|
|
131
|
+
if (style && (options.theme || options.themes))
|
|
132
|
+
result.style = style;
|
|
133
|
+
return result;
|
|
134
|
+
}
|
package/dist/hover-card.d.ts
CHANGED
|
@@ -28,5 +28,9 @@ export interface HoverCardProps {
|
|
|
28
28
|
* follows it, and the card never takes focus or locks scrolling. The pointer
|
|
29
29
|
* can cross into the card and use its links; it closes once the pointer has
|
|
30
30
|
* left both, or on Escape. It shares Popover's surface and motion.
|
|
31
|
+
*
|
|
32
|
+
* The card is a span, so a trigger can sit inside a paragraph. Keep its
|
|
33
|
+
* content to phrasing elements there, such as span and a, since a div or a
|
|
34
|
+
* list would end the paragraph when the browser parses server-rendered HTML.
|
|
31
35
|
*/
|
|
32
36
|
export declare function HoverCard(handle: Handle<HoverCardProps>): () => import("remix/component").RemixElement;
|
package/dist/hover-card.js
CHANGED
|
@@ -8,6 +8,10 @@ import { anchor } from '@remix-run/ui/anchor';
|
|
|
8
8
|
* follows it, and the card never takes focus or locks scrolling. The pointer
|
|
9
9
|
* can cross into the card and use its links; it closes once the pointer has
|
|
10
10
|
* left both, or on Escape. It shares Popover's surface and motion.
|
|
11
|
+
*
|
|
12
|
+
* The card is a span, so a trigger can sit inside a paragraph. Keep its
|
|
13
|
+
* content to phrasing elements there, such as span and a, since a div or a
|
|
14
|
+
* list would end the paragraph when the browser parses server-rendered HTML.
|
|
11
15
|
*/
|
|
12
16
|
export function HoverCard(handle) {
|
|
13
17
|
let trigger = null;
|
|
@@ -114,6 +118,6 @@ export function HoverCard(handle) {
|
|
|
114
118
|
});
|
|
115
119
|
return () => {
|
|
116
120
|
handle.queueTask(sync);
|
|
117
|
-
return (_jsxs(_Fragment, { children: [handle.props.renderTrigger({ mix: triggerMix }), _jsx("
|
|
121
|
+
return (_jsxs(_Fragment, { children: [handle.props.renderTrigger({ mix: triggerMix }), _jsx("span", { class: "rf-menu-surface rf-popover-surface rf-hover-card", popover: "manual", "data-density": handle.props.size, mix: cardMix, children: handle.props.children })] }));
|
|
118
122
|
};
|
|
119
123
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,12 @@ export * from './breadcrumb.tsx';
|
|
|
13
13
|
export * from './segmented-control.tsx';
|
|
14
14
|
export * from './popover.tsx';
|
|
15
15
|
export * from './hover-card.tsx';
|
|
16
|
+
export * from './toaster.tsx';
|
|
17
|
+
export * from './drawer.tsx';
|
|
18
|
+
export * from './code-block.tsx';
|
|
19
|
+
export * from './citation.tsx';
|
|
20
|
+
export * from './avatar.tsx';
|
|
21
|
+
export * from './chat-layout.tsx';
|
|
16
22
|
export * from './systems.ts';
|
|
17
23
|
export * from './queued-stack.tsx';
|
|
18
24
|
export * from './scroll-area.tsx';
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,12 @@ export * from './breadcrumb.js';
|
|
|
13
13
|
export * from './segmented-control.js';
|
|
14
14
|
export * from './popover.js';
|
|
15
15
|
export * from './hover-card.js';
|
|
16
|
+
export * from './toaster.js';
|
|
17
|
+
export * from './drawer.js';
|
|
18
|
+
export * from './code-block.js';
|
|
19
|
+
export * from './citation.js';
|
|
20
|
+
export * from './avatar.js';
|
|
21
|
+
export * from './chat-layout.js';
|
|
16
22
|
export * from './systems.js';
|
|
17
23
|
export * from './queued-stack.js';
|
|
18
24
|
export * from './scroll-area.js';
|
package/dist/sidebar.css
CHANGED
|
@@ -926,68 +926,27 @@
|
|
|
926
926
|
margin-inline-start: 8px;
|
|
927
927
|
}
|
|
928
928
|
|
|
929
|
-
/* Mobile drawer: below 768px the rail
|
|
929
|
+
/* Mobile drawer: below 768px the rail moves into a left Drawer, which brings
|
|
930
|
+
the slide, the drag to close and the backdrop; it keeps the rail's width,
|
|
931
|
+
surface and shadow. */
|
|
930
932
|
.rf-sidebar-drawer-trigger {
|
|
931
933
|
display: none;
|
|
932
934
|
}
|
|
933
|
-
.rf-sidebar-drawer {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
inset-inline-start: 0;
|
|
937
|
-
box-sizing: border-box;
|
|
938
|
-
width: min(288px, 100vw);
|
|
939
|
-
max-width: none;
|
|
940
|
-
height: 100dvh;
|
|
941
|
-
max-height: none;
|
|
942
|
-
margin: 0;
|
|
943
|
-
padding: 0;
|
|
935
|
+
.rf-drawer.rf-sidebar-drawer[data-side] {
|
|
936
|
+
--rf-drawer-bg: var(--rf-surface-2);
|
|
937
|
+
inline-size: min(288px, 100vw);
|
|
944
938
|
overflow: hidden;
|
|
945
|
-
|
|
946
|
-
background: var(--rf-surface-2);
|
|
947
|
-
box-shadow: var(--rf-sb-surface-shadow);
|
|
948
|
-
color: var(--rf-fg);
|
|
949
|
-
translate: -100% 0;
|
|
950
|
-
transition:
|
|
951
|
-
translate var(--rf-exit-moderate),
|
|
952
|
-
display var(--rf-exit-moderate) allow-discrete,
|
|
953
|
-
overlay var(--rf-exit-moderate) allow-discrete;
|
|
954
|
-
}
|
|
955
|
-
.rf-sidebar-drawer[open] {
|
|
956
|
-
translate: 0 0;
|
|
957
|
-
transition:
|
|
958
|
-
translate var(--rf-spring-moderate),
|
|
959
|
-
display var(--rf-spring-moderate) allow-discrete,
|
|
960
|
-
overlay var(--rf-spring-moderate) allow-discrete;
|
|
961
|
-
}
|
|
962
|
-
@starting-style {
|
|
963
|
-
.rf-sidebar-drawer[open] {
|
|
964
|
-
translate: -100% 0;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
.rf-sidebar-drawer::backdrop {
|
|
968
|
-
background: light-dark(rgb(0 0 0 / 40%), rgb(0 0 0 / 80%));
|
|
969
|
-
opacity: 0;
|
|
970
|
-
transition:
|
|
971
|
-
opacity var(--rf-exit-moderate),
|
|
972
|
-
display var(--rf-exit-moderate) allow-discrete,
|
|
973
|
-
overlay var(--rf-exit-moderate) allow-discrete;
|
|
974
|
-
}
|
|
975
|
-
.rf-sidebar-drawer[open]::backdrop {
|
|
976
|
-
opacity: 1;
|
|
977
|
-
transition:
|
|
978
|
-
opacity 160ms ease,
|
|
979
|
-
display 160ms allow-discrete,
|
|
980
|
-
overlay 160ms allow-discrete;
|
|
939
|
+
box-shadow: var(--rf-drawer-tail), var(--rf-sb-surface-shadow);
|
|
981
940
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
}
|
|
941
|
+
.rf-sidebar-drawer .rf-drawer-body {
|
|
942
|
+
display: flex;
|
|
943
|
+
flex-direction: column;
|
|
986
944
|
}
|
|
987
|
-
.rf-sidebar-drawer-
|
|
988
|
-
|
|
945
|
+
.rf-sidebar-drawer .rf-sidebar-inner {
|
|
946
|
+
flex: 1;
|
|
947
|
+
min-block-size: 0;
|
|
989
948
|
}
|
|
990
|
-
.rf-sidebar-drawer-
|
|
949
|
+
.rf-sidebar-drawer .rf-drawer-body > .rf-basic-sr-only:focus-visible {
|
|
991
950
|
clip-path: none;
|
|
992
951
|
width: auto;
|
|
993
952
|
height: auto;
|
|
@@ -996,8 +955,6 @@
|
|
|
996
955
|
position: static;
|
|
997
956
|
}
|
|
998
957
|
@media (prefers-reduced-motion: reduce) {
|
|
999
|
-
.rf-sidebar-drawer,
|
|
1000
|
-
.rf-sidebar-drawer::backdrop,
|
|
1001
958
|
.rf-sidebar-layout,
|
|
1002
959
|
.rf-sidebar-card {
|
|
1003
960
|
transition-duration: 0s !important;
|
package/dist/sidebar.js
CHANGED
|
@@ -4,6 +4,7 @@ import { on, ref } from 'remix/component';
|
|
|
4
4
|
import { Button, Card, CardGroup, CardHeader, CardTitle, CardDescription, CardMedia, CardImage, } from './basic.js';
|
|
5
5
|
import { fluidHover } from './interactive.js';
|
|
6
6
|
import { ScrollArea } from './scroll-area.js';
|
|
7
|
+
import { Drawer } from './drawer.js';
|
|
7
8
|
/** How far past the minimum width a drag has to travel before it previews the collapse. */
|
|
8
9
|
const collapseSlop = 56;
|
|
9
10
|
function SidebarNavigation(handle) {
|
|
@@ -113,13 +114,14 @@ export function Sidebar(handle) {
|
|
|
113
114
|
let exitingPeek;
|
|
114
115
|
let peekTimer;
|
|
115
116
|
let drag;
|
|
116
|
-
let
|
|
117
|
+
let drawerOpen = false;
|
|
117
118
|
const panelId = `${handle.id}-sidebar`;
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
const setDrawer = (open) => {
|
|
120
|
+
if (drawerOpen === open)
|
|
121
|
+
return;
|
|
122
|
+
drawerOpen = open;
|
|
123
|
+
void handle.update();
|
|
124
|
+
};
|
|
123
125
|
function limits() {
|
|
124
126
|
const minimum = handle.props.minWidth ?? 160;
|
|
125
127
|
return { minimum, maximum: Math.max(minimum, handle.props.maxWidth ?? 360) };
|
|
@@ -375,13 +377,7 @@ export function Sidebar(handle) {
|
|
|
375
377
|
if (!peeking)
|
|
376
378
|
cancelPeekTimer();
|
|
377
379
|
}),
|
|
378
|
-
], children: _jsx(Icon, { name: "panel-left" }) }), _jsx(Button, { class: "rf-sidebar-drawer-trigger", variant: "ghost", size: "compact", "
|
|
379
|
-
captureDrawer,
|
|
380
|
-
on('click', (event) => {
|
|
381
|
-
if (event.target === drawer)
|
|
382
|
-
drawer?.close();
|
|
383
|
-
}),
|
|
384
|
-
], children: _jsxs("div", { class: "rf-sidebar-drawer-content", children: [_jsx("button", { type: "button", class: "rf-basic-sr-only", mix: on('click', () => drawer?.close()), children: "Close navigation" }), _jsx(SidebarNavigation, { ...navigation, variant: "default", onNavigate: () => drawer?.close() })] }) })] }));
|
|
380
|
+
], children: _jsx(Icon, { name: "panel-left" }) }), _jsxs(Drawer, { side: "left", title: label, flush: true, class: "rf-sidebar-drawer", open: drawerOpen, onOpenChange: setDrawer, renderTrigger: (trigger) => (_jsx(Button, { class: "rf-sidebar-drawer-trigger", variant: "ghost", size: "compact", ...trigger, children: "Open navigation" })), children: [_jsx("button", { type: "button", class: "rf-basic-sr-only", mix: on('click', () => setDrawer(false)), children: "Close navigation" }), _jsx(SidebarNavigation, { ...navigation, variant: "default", onNavigate: () => setDrawer(false) })] })] })), toolbar != null && _jsx("div", { class: "rf-sidebar-toolbar-content", children: toolbar })] })), children] })] }));
|
|
385
381
|
};
|
|
386
382
|
}
|
|
387
383
|
/** A measured, dismissible pile of real Cards. Keyboard focus opens the inline pile too. */
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** The status glyphs Banner and toasts share. Internal: not exported from the package. */
|
|
2
|
+
export type StatusIconName = 'default' | 'info' | 'success' | 'warning' | 'error';
|
|
3
|
+
export declare function statusIcon(status: StatusIconName): import("remix/component").RemixElement;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "remix/component/jsx-runtime";
|
|
2
|
+
export function statusIcon(status) {
|
|
3
|
+
if (status === 'default') {
|
|
4
|
+
return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("path", { d: "M12 16v-4m0-4h.01" })] }));
|
|
5
|
+
}
|
|
6
|
+
return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", children: [status === 'warning' ? (_jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z", fill: "currentColor", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" })) : (_jsx("circle", { cx: "12", cy: "12", r: "10.5", fill: "currentColor" })), _jsx("g", { stroke: "var(--rf-bg)", "stroke-width": "2.25", "stroke-linecap": "round", "stroke-linejoin": "round", children: status === 'success' ? (_jsx("path", { d: "m8.5 12.25 2.5 2.5 4.5-5" })) : status === 'warning' ? (_jsx("path", { d: "M12 9.5v3.5M12 17h.01" })) : status === 'error' ? (_jsx("path", { d: "M12 7.5v5M12 16.5h.01" })) : (_jsx("path", { d: "M12 16.5v-5M12 7.5h.01" })) })] }));
|
|
7
|
+
}
|
package/dist/styles.css
CHANGED
|
@@ -12,4 +12,10 @@
|
|
|
12
12
|
@import './breadcrumb.css';
|
|
13
13
|
@import './segmented-control.css';
|
|
14
14
|
@import './popover.css';
|
|
15
|
+
@import './toaster.css';
|
|
16
|
+
@import './drawer.css';
|
|
17
|
+
@import './code-block.css';
|
|
18
|
+
@import './citation.css';
|
|
19
|
+
@import './avatar.css';
|
|
20
|
+
@import './chat-layout.css';
|
|
15
21
|
@import './systems.css';
|
package/dist/toaster.css
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/* Toaster: a pile of toasts fixed to one corner or edge of the viewport.
|
|
2
|
+
The newest toast sits in front; the ones behind peek out and narrow on
|
|
3
|
+
QueuedStack's stacking values. Pointed at or focused, the pile spreads
|
|
4
|
+
out a gap apart. The script writes each toast's depth, height and spread
|
|
5
|
+
offset; the lift turns offsets upward for the bottom positions. */
|
|
6
|
+
.rf-toaster-region {
|
|
7
|
+
display: block;
|
|
8
|
+
outline: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.rf-toaster {
|
|
12
|
+
--rf-toaster-offset: 24px;
|
|
13
|
+
--rf-toast-width: 356px;
|
|
14
|
+
--rf-toast-lift: -1;
|
|
15
|
+
position: fixed;
|
|
16
|
+
z-index: 2147483000;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
inline-size: var(--rf-toast-width);
|
|
19
|
+
max-inline-size: calc(100vw - 2 * var(--rf-toaster-offset));
|
|
20
|
+
block-size: var(--rf-toaster-piled-height, 0px);
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 0;
|
|
23
|
+
list-style: none;
|
|
24
|
+
color: var(--rf-fg);
|
|
25
|
+
font: var(--rf-text-body) / var(--rf-leading-body) var(--rf-font);
|
|
26
|
+
-webkit-font-smoothing: antialiased;
|
|
27
|
+
}
|
|
28
|
+
.rf-toaster[data-expanded] {
|
|
29
|
+
block-size: var(--rf-toaster-spread-height, 0px);
|
|
30
|
+
}
|
|
31
|
+
.rf-toaster[data-position^='top'] {
|
|
32
|
+
--rf-toast-lift: 1;
|
|
33
|
+
top: var(--rf-toaster-offset);
|
|
34
|
+
}
|
|
35
|
+
.rf-toaster[data-position^='bottom'] {
|
|
36
|
+
bottom: var(--rf-toaster-offset);
|
|
37
|
+
}
|
|
38
|
+
.rf-toaster[data-position$='left'] {
|
|
39
|
+
left: var(--rf-toaster-offset);
|
|
40
|
+
}
|
|
41
|
+
.rf-toaster[data-position$='right'] {
|
|
42
|
+
right: var(--rf-toaster-offset);
|
|
43
|
+
}
|
|
44
|
+
.rf-toaster[data-position$='center'] {
|
|
45
|
+
left: 50%;
|
|
46
|
+
translate: -50% 0;
|
|
47
|
+
}
|
|
48
|
+
@media (max-width: 600px) {
|
|
49
|
+
.rf-toaster {
|
|
50
|
+
--rf-toaster-offset: 16px;
|
|
51
|
+
inset-inline: var(--rf-toaster-offset);
|
|
52
|
+
inline-size: auto;
|
|
53
|
+
translate: none;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.rf-toast {
|
|
58
|
+
--rf-status-color: var(--rf-fg);
|
|
59
|
+
--rf-toast-y: calc(var(--rf-toast-lift) * var(--rf-toast-depth, 0) * var(--rf-stack-peek));
|
|
60
|
+
position: absolute;
|
|
61
|
+
inset-inline: 0;
|
|
62
|
+
z-index: calc(100 - var(--rf-toast-depth, 0));
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
block-size: var(--rf-toast-front-height, auto);
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
border-radius: var(--rf-panel-radius);
|
|
67
|
+
outline: none;
|
|
68
|
+
background: var(--rf-surface-3, white);
|
|
69
|
+
box-shadow:
|
|
70
|
+
inset 0 1px light-dark(transparent, rgb(255 255 255 / 2%)),
|
|
71
|
+
inset 0 0 0 1px light-dark(transparent, rgb(255 255 255 / 2%)),
|
|
72
|
+
0 0 0 1px light-dark(rgb(0 0 0 / 6%), rgb(0 0 0 / 12%)),
|
|
73
|
+
0 1px 1px -0.5px light-dark(rgb(0 0 0 / 6%), rgb(0 0 0 / 18%)),
|
|
74
|
+
0 3px 3px -1.5px light-dark(rgb(0 0 0 / 6%), rgb(0 0 0 / 18%)),
|
|
75
|
+
0 12px 28px light-dark(rgb(0 0 0 / 10%), rgb(0 0 0 / 24%));
|
|
76
|
+
translate: var(--rf-swipe-x, 0px) calc(var(--rf-toast-y) + var(--rf-swipe-y, 0px));
|
|
77
|
+
scale: calc(1 - var(--rf-toast-depth, 0) * var(--rf-stack-scale-step)) 1;
|
|
78
|
+
touch-action: none;
|
|
79
|
+
transition:
|
|
80
|
+
translate var(--rf-spring-moderate),
|
|
81
|
+
scale var(--rf-spring-moderate),
|
|
82
|
+
block-size var(--rf-spring-moderate),
|
|
83
|
+
opacity var(--rf-spring-moderate);
|
|
84
|
+
}
|
|
85
|
+
.rf-toaster[data-position^='top'] > .rf-toast {
|
|
86
|
+
top: 0;
|
|
87
|
+
}
|
|
88
|
+
.rf-toaster[data-position^='bottom'] > .rf-toast {
|
|
89
|
+
bottom: 0;
|
|
90
|
+
}
|
|
91
|
+
.rf-toast[data-front] {
|
|
92
|
+
block-size: var(--rf-toast-height, auto);
|
|
93
|
+
}
|
|
94
|
+
.rf-toast:focus-visible {
|
|
95
|
+
outline: 2px solid var(--rf-accent);
|
|
96
|
+
outline-offset: 2px;
|
|
97
|
+
}
|
|
98
|
+
/* A new toast slides in from the edge. */
|
|
99
|
+
@starting-style {
|
|
100
|
+
.rf-toast {
|
|
101
|
+
--rf-toast-y: calc(var(--rf-toast-lift) * -100%);
|
|
102
|
+
opacity: 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.rf-toaster[data-expanded] > .rf-toast {
|
|
107
|
+
--rf-toast-y: calc(var(--rf-toast-lift) * var(--rf-toast-offset, 0px));
|
|
108
|
+
block-size: var(--rf-toast-height, auto);
|
|
109
|
+
scale: 1;
|
|
110
|
+
}
|
|
111
|
+
.rf-toast[data-hidden] {
|
|
112
|
+
visibility: hidden;
|
|
113
|
+
opacity: 0;
|
|
114
|
+
pointer-events: none;
|
|
115
|
+
}
|
|
116
|
+
.rf-toast[data-swiping] {
|
|
117
|
+
cursor: grabbing;
|
|
118
|
+
user-select: none;
|
|
119
|
+
transition: none;
|
|
120
|
+
}
|
|
121
|
+
/* Leaving, the front toast drops back to its edge; a swiped one carries on
|
|
122
|
+
the way it was thrown; the others fade where they are. */
|
|
123
|
+
.rf-toast[data-removed] {
|
|
124
|
+
opacity: 0;
|
|
125
|
+
pointer-events: none;
|
|
126
|
+
transition:
|
|
127
|
+
translate var(--rf-exit-slow),
|
|
128
|
+
scale var(--rf-exit-slow),
|
|
129
|
+
opacity var(--rf-exit-moderate);
|
|
130
|
+
}
|
|
131
|
+
.rf-toast[data-removed][data-front]:not([data-swipe-out]) {
|
|
132
|
+
--rf-toast-y: calc(var(--rf-toast-lift) * -100%);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.rf-toast-inner {
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: flex-start;
|
|
138
|
+
gap: 12px;
|
|
139
|
+
padding: 16px;
|
|
140
|
+
transition: opacity var(--rf-spring-moderate);
|
|
141
|
+
}
|
|
142
|
+
/* Piled, only the front toast shows its content. */
|
|
143
|
+
.rf-toaster:not([data-expanded]) > .rf-toast:not([data-front]) > .rf-toast-inner {
|
|
144
|
+
opacity: 0;
|
|
145
|
+
}
|
|
146
|
+
.rf-toast[data-status='info'] {
|
|
147
|
+
--rf-status-color: light-dark(#3b82f6, var(--rf-info));
|
|
148
|
+
}
|
|
149
|
+
.rf-toast[data-status='success'] {
|
|
150
|
+
--rf-status-color: light-dark(#22c55e, var(--rf-success));
|
|
151
|
+
}
|
|
152
|
+
.rf-toast[data-status='warning'] {
|
|
153
|
+
--rf-status-color: light-dark(#f59e0b, var(--rf-warning));
|
|
154
|
+
}
|
|
155
|
+
.rf-toast[data-status='error'] {
|
|
156
|
+
--rf-status-color: light-dark(#ef4444, var(--rf-danger));
|
|
157
|
+
}
|
|
158
|
+
.rf-toast[data-status='loading'] {
|
|
159
|
+
--rf-status-color: var(--rf-muted);
|
|
160
|
+
}
|
|
161
|
+
.rf-toast-icon {
|
|
162
|
+
display: grid;
|
|
163
|
+
flex: none;
|
|
164
|
+
place-items: center;
|
|
165
|
+
inline-size: 20px;
|
|
166
|
+
block-size: var(--rf-leading-subtitle);
|
|
167
|
+
color: var(--rf-status-color);
|
|
168
|
+
}
|
|
169
|
+
.rf-toast-icon > svg {
|
|
170
|
+
inline-size: 20px;
|
|
171
|
+
block-size: 20px;
|
|
172
|
+
}
|
|
173
|
+
.rf-toast-spinner g {
|
|
174
|
+
transform-box: fill-box;
|
|
175
|
+
transform-origin: center;
|
|
176
|
+
animation: rf-button-spinner-turn 1s cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
|
177
|
+
}
|
|
178
|
+
.rf-toast-spinner circle {
|
|
179
|
+
stroke-dasharray: 0.01 49.99;
|
|
180
|
+
stroke-dashoffset: -25;
|
|
181
|
+
animation: rf-button-spinner-breathe 1s cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
|
182
|
+
}
|
|
183
|
+
.rf-toast-content {
|
|
184
|
+
display: flex;
|
|
185
|
+
flex: 1;
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
gap: 2px;
|
|
188
|
+
min-inline-size: 0;
|
|
189
|
+
}
|
|
190
|
+
.rf-toast-title {
|
|
191
|
+
font-size: var(--rf-text-subtitle);
|
|
192
|
+
line-height: var(--rf-leading-subtitle);
|
|
193
|
+
font-variation-settings: var(--rf-weight-strong);
|
|
194
|
+
overflow-wrap: anywhere;
|
|
195
|
+
}
|
|
196
|
+
.rf-toast-description {
|
|
197
|
+
color: var(--rf-muted);
|
|
198
|
+
overflow-wrap: anywhere;
|
|
199
|
+
}
|
|
200
|
+
.rf-toast-action {
|
|
201
|
+
flex: none;
|
|
202
|
+
margin-block: calc((var(--rf-leading-subtitle) - 28px) / 2);
|
|
203
|
+
}
|
|
204
|
+
.rf-toast-close {
|
|
205
|
+
display: flex;
|
|
206
|
+
flex: none;
|
|
207
|
+
align-items: center;
|
|
208
|
+
justify-content: center;
|
|
209
|
+
box-sizing: border-box;
|
|
210
|
+
inline-size: 28px;
|
|
211
|
+
block-size: 28px;
|
|
212
|
+
margin-block: calc((var(--rf-leading-subtitle) - 28px) / 2);
|
|
213
|
+
margin-inline: -4px -6px;
|
|
214
|
+
padding: 0;
|
|
215
|
+
border: 0;
|
|
216
|
+
border-radius: var(--rf-radius);
|
|
217
|
+
outline: none;
|
|
218
|
+
background: transparent;
|
|
219
|
+
color: var(--rf-muted);
|
|
220
|
+
cursor: pointer;
|
|
221
|
+
transition:
|
|
222
|
+
color var(--rf-fast),
|
|
223
|
+
background-color var(--rf-fast);
|
|
224
|
+
}
|
|
225
|
+
.rf-toast-close:focus-visible {
|
|
226
|
+
box-shadow: 0 0 0 1px var(--rf-accent);
|
|
227
|
+
}
|
|
228
|
+
@media (hover: hover) {
|
|
229
|
+
.rf-toast-close:hover {
|
|
230
|
+
background: var(--rf-hover);
|
|
231
|
+
color: var(--rf-fg);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
.rf-toast-close > svg {
|
|
235
|
+
inline-size: 15px;
|
|
236
|
+
block-size: 15px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
[data-density='compact'] .rf-toast-inner {
|
|
240
|
+
gap: 10px;
|
|
241
|
+
padding: 12px;
|
|
242
|
+
}
|
|
243
|
+
[data-density='compact'] .rf-toast-icon,
|
|
244
|
+
[data-density='compact'] .rf-toast-icon > svg {
|
|
245
|
+
inline-size: 16px;
|
|
246
|
+
}
|
|
247
|
+
[data-density='compact'] .rf-toast-icon > svg {
|
|
248
|
+
block-size: 16px;
|
|
249
|
+
}
|
|
250
|
+
[data-density='compact'] .rf-toast-close > svg {
|
|
251
|
+
inline-size: 13px;
|
|
252
|
+
block-size: 13px;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@media (prefers-reduced-motion: reduce) {
|
|
256
|
+
.rf-toast,
|
|
257
|
+
.rf-toast[data-removed] {
|
|
258
|
+
transition: opacity var(--rf-spring-moderate);
|
|
259
|
+
}
|
|
260
|
+
.rf-toast-spinner :is(g, circle) {
|
|
261
|
+
animation: none;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
@media (forced-colors: active) {
|
|
265
|
+
.rf-toast {
|
|
266
|
+
border: 1px solid CanvasText;
|
|
267
|
+
}
|
|
268
|
+
}
|