@terrafirmagreg-team/ui 1.2.7 → 1.2.8
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/package.json +2 -2
- package/src/components/blocks/link/link-preview.tsx +2 -1
- package/src/components/blocks/page/heading.tsx +1 -1
- package/src/components/layouts/layout.tsx +8 -2
- package/src/components/layouts/left-sidebar/tree.tsx +24 -0
- package/src/components/mdx/accordion.tsx +8 -1
- package/src/components/mdx/banner.tsx +64 -12
- package/src/components/mdx/callout.tsx +46 -13
- package/src/components/mdx/files.tsx +40 -11
- package/src/components/mdx/gradient.tsx +28 -0
- package/src/components/mdx/video.tsx +28 -0
- package/src/hooks/use-last-result.ts +30 -0
- package/src/lib/config.ts +4 -2
- package/src/lib/mdx/callout.ts +11 -2
- package/src/lib/tree.ts +10 -0
- package/src/types/mdx-directives.ts +21 -12
- package/styles/content/default-colors.css +6 -0
- package/styles.css +19 -1
- package/src/components/mdx/glow-header.tsx +0 -54
- package/src/components/mdx/gradient-text.tsx +0 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrafirmagreg-team/ui",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared UI kit",
|
|
6
6
|
"license": "LGPL-3.0-only",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@react-three/drei": "10.7.9",
|
|
45
45
|
"@react-three/fiber": "9.8.2",
|
|
46
46
|
"@shadcn/react": "^0.3.1",
|
|
47
|
-
"@terrafirmagreg-team/translate": "^1.2.
|
|
47
|
+
"@terrafirmagreg-team/translate": "^1.2.6",
|
|
48
48
|
"@xyflow/react": "12.12.0",
|
|
49
49
|
"beautiful-mermaid": "^1.1.3",
|
|
50
50
|
"class-variance-authority": "^0.7.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useContext, useState } from 'react';
|
|
3
3
|
import type { ComponentProps } from 'react';
|
|
4
4
|
import { useUiComponents } from '@terrafirmagreg-team/ui/provider';
|
|
5
|
-
import { Globe } from 'lucide-react';
|
|
5
|
+
import { Globe, SquareArrowOutUpRight } from 'lucide-react';
|
|
6
6
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
7
7
|
import { buttonVariants } from '@terrafirmagreg-team/ui/components/ui/button';
|
|
8
8
|
import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
|
|
@@ -86,6 +86,7 @@ function ExternalAnchor({
|
|
|
86
86
|
}
|
|
87
87
|
>
|
|
88
88
|
{children}
|
|
89
|
+
<SquareArrowOutUpRight className="ms-0.5 inline-block size-3 align-baseline text-muted-foreground" />
|
|
89
90
|
</HoverCardTrigger>
|
|
90
91
|
<HoverCardContent className="w-fit max-w-[min(18rem,90vw)]">
|
|
91
92
|
<div className="flex w-full min-w-0 items-center gap-2 font-medium">
|
|
@@ -29,6 +29,8 @@ export type LayoutProps = {
|
|
|
29
29
|
sidebar?: ReactNode;
|
|
30
30
|
navTitle?: ComponentType<ComponentProps<'a'>>;
|
|
31
31
|
header?: ReactNode;
|
|
32
|
+
banner?: ReactNode;
|
|
33
|
+
defaultOpenLevel?: number;
|
|
32
34
|
children?: ReactNode;
|
|
33
35
|
};
|
|
34
36
|
|
|
@@ -41,23 +43,27 @@ export function Layout({
|
|
|
41
43
|
sidebar,
|
|
42
44
|
navTitle: Title = EmptyTitle,
|
|
43
45
|
header = <LayoutHeader />,
|
|
46
|
+
banner,
|
|
47
|
+
defaultOpenLevel = 0,
|
|
44
48
|
children,
|
|
45
49
|
}: LayoutProps) {
|
|
46
50
|
return (
|
|
47
51
|
<LayoutContext value={useMemo(() => ({ navTitle: Title }), [Title])}>
|
|
48
52
|
<TreeContextProvider tree={tree}>
|
|
49
|
-
<SidebarProvider>
|
|
53
|
+
<SidebarProvider defaultOpenLevel={defaultOpenLevel}>
|
|
50
54
|
<LeftSidebarPortalProvider>
|
|
51
55
|
<div
|
|
52
56
|
id="spacious-layout"
|
|
53
57
|
data-slot="layout"
|
|
54
58
|
className="relative grid min-h-(--layout-height) bg-card transition-[grid-template-columns] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none [--layout-height:calc(100dvh_-_var(--banner-height,0px))] [--header-height:--spacing(14)] [--page-width:100%] [--sidebar-width:0px] [--sidebar-col:var(--sidebar-width)] [--toc-width:0px] max-md:[--docs-row-3:calc(var(--banner-height,0px)+var(--header-height)+var(--toc-popover-height,0px))] md:h-(--layout-height) md:overflow-clip print:h-auto print:overflow-visible"
|
|
55
59
|
style={{
|
|
56
|
-
gridTemplate: `"
|
|
60
|
+
gridTemplate: `"banner banner banner" auto
|
|
61
|
+
"sidebar header right" auto
|
|
57
62
|
"sidebar toc-popover right" var(--toc-popover-height, 0px)
|
|
58
63
|
"sidebar main right" 1fr / var(--sidebar-col) minmax(0, 1fr) auto`,
|
|
59
64
|
}}
|
|
60
65
|
>
|
|
66
|
+
{banner}
|
|
61
67
|
{sidebar}
|
|
62
68
|
{header}
|
|
63
69
|
<RightSidebar>{children}</RightSidebar>
|
|
@@ -25,6 +25,29 @@ import {
|
|
|
25
25
|
LeftSidebarItem,
|
|
26
26
|
LeftSidebarLabel,
|
|
27
27
|
} from './items';
|
|
28
|
+
import { isSkeletonTree } from '@terrafirmagreg-team/ui/lib/tree';
|
|
29
|
+
import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
|
|
30
|
+
|
|
31
|
+
const TREE_SKELETON_ROWS = [
|
|
32
|
+
'w-3/5',
|
|
33
|
+
'w-4/5 ms-4',
|
|
34
|
+
'w-2/3 ms-4',
|
|
35
|
+
'w-1/2 ms-4',
|
|
36
|
+
'w-3/4',
|
|
37
|
+
'w-1/2 ms-4',
|
|
38
|
+
'w-2/3 ms-4',
|
|
39
|
+
'w-3/5',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export function LeftSidebarTreeSkeleton() {
|
|
43
|
+
return (
|
|
44
|
+
<div aria-hidden className="flex flex-col gap-1 px-1 py-2">
|
|
45
|
+
{TREE_SKELETON_ROWS.map((width, i) => (
|
|
46
|
+
<Skeleton key={i} className={`h-7 ${width}`} />
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
28
51
|
|
|
29
52
|
type TreeRenderer = (props: {
|
|
30
53
|
item: PageTree.Node;
|
|
@@ -61,6 +84,7 @@ export function LeftSidebarTree({
|
|
|
61
84
|
const search = useLocationSearch();
|
|
62
85
|
const { languages } = useLocaleRegistry();
|
|
63
86
|
const resolved = tree ?? root;
|
|
87
|
+
if (isSkeletonTree(resolved)) return <LeftSidebarTreeSkeleton />;
|
|
64
88
|
return (
|
|
65
89
|
<RendererContext
|
|
66
90
|
value={useMemo(
|
|
@@ -13,6 +13,7 @@ import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-transl
|
|
|
13
13
|
export function AccordionRoot({ className, ...props }: ComponentProps<typeof Primitive.Root>) {
|
|
14
14
|
return (
|
|
15
15
|
<Primitive.Root
|
|
16
|
+
data-md="accordions"
|
|
16
17
|
className={(s) =>
|
|
17
18
|
cn(
|
|
18
19
|
'divide-y divide-border overflow-hidden rounded-lg border bg-card',
|
|
@@ -98,6 +99,12 @@ export function Accordions({
|
|
|
98
99
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
99
100
|
const composedRef = mergeRefs(ref, rootRef);
|
|
100
101
|
const [value, setValue] = useState<unknown[]>(defaultValue ?? []);
|
|
102
|
+
const initial = JSON.stringify(defaultValue);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
setValue(defaultValue ?? []);
|
|
106
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
107
|
+
}, [initial]);
|
|
101
108
|
|
|
102
109
|
useEffect(() => {
|
|
103
110
|
const id = window.location.hash.substring(1);
|
|
@@ -144,7 +151,7 @@ export function Accordion({
|
|
|
144
151
|
{id ? <CopyButton id={id} /> : null}
|
|
145
152
|
</AccordionHeader>
|
|
146
153
|
<AccordionContent hiddenUntilFound>
|
|
147
|
-
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin [&[hidden]:not([hidden='until-found'])]:hidden">
|
|
154
|
+
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin [&[hidden]:not([hidden='until-found'])]:hidden [&_[data-md=accordions]]:ms-1 [&_[data-md=accordions]]:border-s-2 [&_[data-md=accordions]]:bg-muted/40 [&_.fd-steps]:bg-muted/40 [&_.fd-steps]:rounded-md [&_.fd-steps]:pe-3">
|
|
148
155
|
{children}
|
|
149
156
|
</div>
|
|
150
157
|
</AccordionContent>
|
|
@@ -1,39 +1,60 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { type HTMLAttributes, useEffect, useState } from 'react';
|
|
3
|
+
import { type CSSProperties, type HTMLAttributes, useEffect, useState } from 'react';
|
|
4
4
|
import { X } from 'lucide-react';
|
|
5
5
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
6
6
|
import { buttonVariants } from '@terrafirmagreg-team/ui/components/ui/button';
|
|
7
7
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
8
|
+
import {
|
|
9
|
+
gradientImage,
|
|
10
|
+
type GradientProps,
|
|
11
|
+
} from '@terrafirmagreg-team/ui/lib/mdx/gradient';
|
|
8
12
|
|
|
9
|
-
type BannerVariant = 'rainbow' | 'normal';
|
|
13
|
+
type BannerVariant = 'rainbow' | 'normal' | 'fade' | 'glow';
|
|
10
14
|
|
|
11
|
-
export type BannerProps = HTMLAttributes<HTMLDivElement> &
|
|
15
|
+
export type BannerProps = HTMLAttributes<HTMLDivElement> &
|
|
16
|
+
GradientProps & {
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
height?: string;
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
variant?: BannerVariant;
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
gradient?: boolean;
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
rainbowColors?: string[];
|
|
25
|
+
|
|
26
|
+
changeLayout?: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const fadeMask = {
|
|
30
|
+
WebkitMask:
|
|
31
|
+
'linear-gradient(#fff 0 0) content-box, linear-gradient(to bottom, #fff 0%, transparent 100%) border-box',
|
|
32
|
+
WebkitMaskComposite: 'destination-out',
|
|
33
|
+
mask: 'linear-gradient(to bottom, #fff 0%, transparent 100%) border-box subtract, linear-gradient(#fff 0 0) content-box',
|
|
34
|
+
} as CSSProperties;
|
|
21
35
|
|
|
22
36
|
export function Banner({
|
|
23
37
|
id,
|
|
24
38
|
variant = 'normal',
|
|
25
39
|
changeLayout = true,
|
|
26
|
-
height
|
|
40
|
+
height,
|
|
41
|
+
gradient,
|
|
42
|
+
from,
|
|
43
|
+
to,
|
|
44
|
+
dir,
|
|
45
|
+
image,
|
|
27
46
|
rainbowColors = [
|
|
28
47
|
'rgba(0,149,255,0.56)',
|
|
29
48
|
'rgba(231,77,255,0.77)',
|
|
30
49
|
'rgba(255,0,0,0.73)',
|
|
31
50
|
'rgba(131,255,166,0.66)',
|
|
32
51
|
],
|
|
52
|
+
style,
|
|
33
53
|
...props
|
|
34
54
|
}: BannerProps) {
|
|
35
55
|
const t = useTranslations();
|
|
36
56
|
const [open, setOpen] = useState(true);
|
|
57
|
+
const glow = variant === 'fade' || variant === 'glow';
|
|
37
58
|
const globalKey = id ? `banner-${encodeBase32(id)}` : null;
|
|
38
59
|
|
|
39
60
|
useEffect(() => {
|
|
@@ -47,19 +68,41 @@ export function Banner({
|
|
|
47
68
|
|
|
48
69
|
if (!open) return null;
|
|
49
70
|
|
|
71
|
+
const gradientBg = gradientImage({ from, to, dir, image });
|
|
72
|
+
const textStyle: CSSProperties | undefined = gradient
|
|
73
|
+
? {
|
|
74
|
+
backgroundImage: gradientBg,
|
|
75
|
+
WebkitBackgroundClip: 'text',
|
|
76
|
+
backgroundClip: 'text',
|
|
77
|
+
WebkitTextFillColor: 'transparent',
|
|
78
|
+
color: 'transparent',
|
|
79
|
+
}
|
|
80
|
+
: undefined;
|
|
81
|
+
|
|
50
82
|
return (
|
|
51
83
|
<div
|
|
52
84
|
id={id}
|
|
53
85
|
{...props}
|
|
54
86
|
className={cn(
|
|
55
|
-
'
|
|
87
|
+
'flex flex-row items-center justify-center px-4 text-center font-medium',
|
|
88
|
+
glow
|
|
89
|
+
? 'relative isolate mt-10 mb-6 overflow-hidden rounded-[20px] p-2.5 text-xl font-semibold [&_:is(h1,h2,h3,h4,h5,h6)]:m-0 [&_:is(h1,h2,h3,h4,h5,h6)]:border-0 [&_:is(h1,h2,h3,h4,h5,h6)]:p-0 [&_:is(h1,h2,h3,h4,h5,h6)_button]:hidden'
|
|
90
|
+
: 'sticky top-0 z-40 rounded-xl text-sm',
|
|
56
91
|
variant === 'normal' && 'bg-secondary',
|
|
57
92
|
variant === 'rainbow' && 'bg-background',
|
|
58
93
|
!open && 'hidden',
|
|
59
94
|
props.className,
|
|
60
95
|
)}
|
|
61
96
|
style={{
|
|
62
|
-
height,
|
|
97
|
+
height: height ?? (glow ? undefined : '3rem'),
|
|
98
|
+
...(variant === 'glow'
|
|
99
|
+
? {
|
|
100
|
+
backgroundImage:
|
|
101
|
+
'radial-gradient(var(--header-bg-inner), var(--header-bg-outer))',
|
|
102
|
+
boxShadow: '0 0 20px var(--header-shadow)',
|
|
103
|
+
}
|
|
104
|
+
: {}),
|
|
105
|
+
...style,
|
|
63
106
|
}}
|
|
64
107
|
>
|
|
65
108
|
{changeLayout && open ? (
|
|
@@ -83,7 +126,16 @@ export function Banner({
|
|
|
83
126
|
colors: rainbowColors,
|
|
84
127
|
})
|
|
85
128
|
: null}
|
|
86
|
-
{
|
|
129
|
+
{variant === 'fade' ? (
|
|
130
|
+
<span
|
|
131
|
+
aria-hidden
|
|
132
|
+
className="pointer-events-none absolute inset-0 rounded-[inherit] p-px"
|
|
133
|
+
style={{ backgroundImage: gradientBg, ...fadeMask }}
|
|
134
|
+
/>
|
|
135
|
+
) : null}
|
|
136
|
+
<div className="relative" style={textStyle}>
|
|
137
|
+
{props.children}
|
|
138
|
+
</div>
|
|
87
139
|
{id ? (
|
|
88
140
|
<button
|
|
89
141
|
type="button"
|
|
@@ -1,35 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Bell,
|
|
3
|
+
Bug,
|
|
4
|
+
CircleAlert,
|
|
5
|
+
CircleCheck,
|
|
6
|
+
CircleHelp,
|
|
7
|
+
CircleX,
|
|
8
|
+
ClipboardList,
|
|
9
|
+
Info,
|
|
10
|
+
Lightbulb,
|
|
11
|
+
ListChecks,
|
|
12
|
+
Megaphone,
|
|
13
|
+
OctagonAlert,
|
|
14
|
+
OctagonX,
|
|
15
|
+
Quote,
|
|
16
|
+
StickyNote,
|
|
17
|
+
TriangleAlert,
|
|
18
|
+
} from 'lucide-react';
|
|
2
19
|
import type { ComponentProps, CSSProperties, ReactNode } from 'react';
|
|
3
20
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
4
21
|
import { calloutStyle } from '@terrafirmagreg-team/ui/lib/mdx/callout';
|
|
5
22
|
|
|
6
23
|
export type CalloutType =
|
|
7
|
-
| 'info'
|
|
8
24
|
| 'note'
|
|
9
25
|
| 'tip'
|
|
26
|
+
| 'info'
|
|
27
|
+
| 'important'
|
|
10
28
|
| 'warn'
|
|
11
29
|
| 'warning'
|
|
12
|
-
| '
|
|
30
|
+
| 'caution'
|
|
31
|
+
| 'attention'
|
|
13
32
|
| 'danger'
|
|
33
|
+
| 'error'
|
|
14
34
|
| 'success'
|
|
15
|
-
| 'idea'
|
|
35
|
+
| 'idea'
|
|
36
|
+
| 'question'
|
|
37
|
+
| 'failure'
|
|
38
|
+
| 'bug'
|
|
39
|
+
| 'abstract'
|
|
40
|
+
| 'example'
|
|
41
|
+
| 'quote';
|
|
16
42
|
|
|
17
43
|
const iconClass = 'size-5 -me-0.5 fill-(--callout-color) text-card';
|
|
44
|
+
const textIconClass =
|
|
45
|
+
'size-5 -me-0.5 fill-(--callout-color) text-(--callout-color)';
|
|
18
46
|
|
|
19
47
|
const ICONS: Record<string, ReactNode> = {
|
|
20
|
-
info: <Info className={iconClass} />,
|
|
21
48
|
note: <StickyNote className={iconClass} />,
|
|
22
|
-
tip:
|
|
23
|
-
|
|
24
|
-
|
|
49
|
+
tip: <Lightbulb className={textIconClass} />,
|
|
50
|
+
info: <Info className={iconClass} />,
|
|
51
|
+
important: <Megaphone className={iconClass} />,
|
|
25
52
|
warn: <TriangleAlert className={iconClass} />,
|
|
26
53
|
warning: <TriangleAlert className={iconClass} />,
|
|
27
|
-
|
|
28
|
-
|
|
54
|
+
caution: <OctagonAlert className={iconClass} />,
|
|
55
|
+
attention: <Bell className={iconClass} />,
|
|
56
|
+
danger: <OctagonX className={iconClass} />,
|
|
57
|
+
error: <CircleAlert className={iconClass} />,
|
|
29
58
|
success: <CircleCheck className={iconClass} />,
|
|
30
|
-
idea:
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
idea: <Lightbulb className={textIconClass} />,
|
|
60
|
+
question: <CircleHelp className={iconClass} />,
|
|
61
|
+
failure: <CircleX className={iconClass} />,
|
|
62
|
+
bug: <Bug className={iconClass} />,
|
|
63
|
+
abstract: <ClipboardList className={iconClass} />,
|
|
64
|
+
example: <ListChecks className={iconClass} />,
|
|
65
|
+
quote: <Quote className={iconClass} />,
|
|
33
66
|
};
|
|
34
67
|
|
|
35
68
|
export function Callout({
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
|
-
import { FileIcon, FolderIcon, FolderOpen } from 'lucide-react';
|
|
4
|
+
import { FileIcon, FolderIcon, FolderOpen, SquareArrowOutUpRight } from 'lucide-react';
|
|
5
|
+
import Link from 'next/link';
|
|
5
6
|
import { type HTMLAttributes, type ReactNode, useState } from 'react';
|
|
6
7
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
7
8
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './collapsible';
|
|
@@ -19,45 +20,73 @@ export function Files({ className, ...props }: HTMLAttributes<HTMLDivElement>):
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export interface FileProps extends HTMLAttributes<HTMLDivElement> {
|
|
22
|
-
name:
|
|
23
|
+
name: ReactNode;
|
|
23
24
|
icon?: ReactNode;
|
|
25
|
+
href?: string;
|
|
26
|
+
description?: string;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export interface FolderProps extends HTMLAttributes<HTMLDivElement> {
|
|
27
|
-
name:
|
|
30
|
+
name: ReactNode;
|
|
28
31
|
|
|
29
32
|
disabled?: boolean;
|
|
30
33
|
|
|
31
34
|
defaultOpen?: boolean;
|
|
35
|
+
|
|
36
|
+
href?: string;
|
|
37
|
+
|
|
38
|
+
description?: string;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
export function File({
|
|
35
42
|
name,
|
|
36
43
|
icon = <FileIcon />,
|
|
44
|
+
href,
|
|
45
|
+
description,
|
|
37
46
|
className,
|
|
38
47
|
...rest
|
|
39
48
|
}: FileProps): React.ReactElement {
|
|
40
49
|
return (
|
|
41
|
-
<div
|
|
50
|
+
<div
|
|
51
|
+
className={cn(itemVariants({ className }))}
|
|
52
|
+
title={description}
|
|
53
|
+
{...rest}
|
|
54
|
+
>
|
|
42
55
|
{icon}
|
|
43
|
-
{
|
|
56
|
+
{href ? (
|
|
57
|
+
<Link href={href} className="hover:underline">
|
|
58
|
+
{name}
|
|
59
|
+
</Link>
|
|
60
|
+
) : (
|
|
61
|
+
name
|
|
62
|
+
)}
|
|
44
63
|
</div>
|
|
45
64
|
);
|
|
46
65
|
}
|
|
47
66
|
|
|
48
|
-
export function Folder({ name, defaultOpen = false, ...props }: FolderProps): React.ReactElement {
|
|
67
|
+
export function Folder({ name, defaultOpen = false, href, description, ...props }: FolderProps): React.ReactElement {
|
|
49
68
|
const [open, setOpen] = useState(defaultOpen);
|
|
50
69
|
|
|
51
70
|
return (
|
|
52
71
|
<Collapsible open={open} onOpenChange={setOpen} {...props}>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
<div className="flex items-center" title={description}>
|
|
73
|
+
<CollapsibleTrigger className={cn(itemVariants({ className: 'w-full' }))}>
|
|
74
|
+
{open ? <FolderOpen /> : <FolderIcon />}
|
|
75
|
+
{name}
|
|
76
|
+
</CollapsibleTrigger>
|
|
77
|
+
{href ? (
|
|
78
|
+
<Link
|
|
79
|
+
href={href}
|
|
80
|
+
className="me-1 rounded p-1 text-muted-foreground hover:text-foreground [&_svg]:size-3.5"
|
|
81
|
+
aria-label={typeof name === 'string' ? name : undefined}
|
|
82
|
+
>
|
|
83
|
+
<SquareArrowOutUpRight />
|
|
84
|
+
</Link>
|
|
85
|
+
) : null}
|
|
86
|
+
</div>
|
|
57
87
|
<CollapsibleContent>
|
|
58
88
|
<div className="ms-2 flex flex-col border-l ps-2">{props.children}</div>
|
|
59
89
|
</CollapsibleContent>
|
|
60
90
|
</Collapsible>
|
|
61
91
|
);
|
|
62
92
|
}
|
|
63
|
-
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
gradientImage,
|
|
4
|
+
type GradientProps,
|
|
5
|
+
} from '@terrafirmagreg-team/ui/lib/mdx/gradient';
|
|
6
|
+
|
|
7
|
+
export function Gradient({
|
|
8
|
+
from,
|
|
9
|
+
to,
|
|
10
|
+
dir,
|
|
11
|
+
image,
|
|
12
|
+
style,
|
|
13
|
+
...props
|
|
14
|
+
}: HTMLAttributes<HTMLDivElement> & GradientProps) {
|
|
15
|
+
return (
|
|
16
|
+
<div
|
|
17
|
+
{...props}
|
|
18
|
+
style={{
|
|
19
|
+
backgroundImage: gradientImage({ from, to, dir, image }),
|
|
20
|
+
WebkitBackgroundClip: 'text',
|
|
21
|
+
backgroundClip: 'text',
|
|
22
|
+
WebkitTextFillColor: 'transparent',
|
|
23
|
+
color: 'transparent',
|
|
24
|
+
...style,
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
3
|
+
|
|
4
|
+
export function Video({
|
|
5
|
+
src,
|
|
6
|
+
title,
|
|
7
|
+
className,
|
|
8
|
+
...props
|
|
9
|
+
}: ComponentProps<'div'> & { src: string; title?: string }) {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
className={cn(
|
|
13
|
+
'not-prose my-6 mx-auto w-full max-w-2xl overflow-hidden rounded-xl border bg-card',
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
<iframe
|
|
19
|
+
src={src}
|
|
20
|
+
title={title}
|
|
21
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
22
|
+
referrerPolicy="strict-origin-when-cross-origin"
|
|
23
|
+
allowFullScreen
|
|
24
|
+
className="aspect-video w-full"
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
export function useLastResult<T>(result: {
|
|
6
|
+
value: T | undefined;
|
|
7
|
+
pending: boolean;
|
|
8
|
+
error: unknown;
|
|
9
|
+
}, key?: string): {
|
|
10
|
+
value: T | undefined;
|
|
11
|
+
stale: boolean;
|
|
12
|
+
pending: boolean;
|
|
13
|
+
error: unknown;
|
|
14
|
+
} {
|
|
15
|
+
const [held, setHeld] = useState<{ key?: string; value: T } | undefined>();
|
|
16
|
+
const keyMismatch = held !== undefined && held.key !== key;
|
|
17
|
+
if (
|
|
18
|
+
result.value !== undefined &&
|
|
19
|
+
(keyMismatch || !Object.is(held?.value, result.value))
|
|
20
|
+
) {
|
|
21
|
+
setHeld({ key, value: result.value });
|
|
22
|
+
}
|
|
23
|
+
const value = result.value ?? (keyMismatch ? undefined : held?.value);
|
|
24
|
+
return {
|
|
25
|
+
value,
|
|
26
|
+
stale: value !== undefined && result.value === undefined,
|
|
27
|
+
pending: result.pending && value === undefined,
|
|
28
|
+
error: result.error,
|
|
29
|
+
};
|
|
30
|
+
}
|
package/src/lib/config.ts
CHANGED
|
@@ -56,8 +56,10 @@ export const DEFAULT_EXTERNAL_META_PROVIDERS: ExternalMetaProvider[] = [
|
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
host: /.*/,
|
|
59
|
-
url: 'https://api.
|
|
60
|
-
kind: '
|
|
59
|
+
url: 'https://api.microlink.io?url={href}',
|
|
60
|
+
kind: 'json',
|
|
61
|
+
title: 'data.title',
|
|
62
|
+
description: 'data.description',
|
|
61
63
|
},
|
|
62
64
|
];
|
|
63
65
|
|
package/src/lib/mdx/callout.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import type { CSSProperties } from 'react';
|
|
2
2
|
|
|
3
3
|
export const COLORS: Record<string, string> = {
|
|
4
|
-
info: 'var(--color-info)',
|
|
5
4
|
note: 'var(--color-note)',
|
|
6
5
|
tip: 'var(--color-tip)',
|
|
6
|
+
info: 'var(--color-info)',
|
|
7
|
+
important: 'var(--color-important)',
|
|
7
8
|
warn: 'var(--color-warning)',
|
|
8
9
|
warning: 'var(--color-warning)',
|
|
10
|
+
caution: 'var(--color-caution)',
|
|
11
|
+
attention: 'var(--color-attention)',
|
|
12
|
+
danger: 'var(--color-danger)',
|
|
9
13
|
error: 'var(--color-error)',
|
|
10
|
-
danger: 'var(--color-error)',
|
|
11
14
|
success: 'var(--color-success)',
|
|
12
15
|
idea: 'var(--color-idea)',
|
|
16
|
+
question: 'var(--color-question)',
|
|
17
|
+
failure: 'var(--color-failure)',
|
|
18
|
+
bug: 'var(--color-bug)',
|
|
19
|
+
abstract: 'var(--color-abstract)',
|
|
20
|
+
example: 'var(--color-example)',
|
|
21
|
+
quote: 'var(--color-quote)',
|
|
13
22
|
};
|
|
14
23
|
|
|
15
24
|
export function calloutStyle(
|
package/src/lib/tree.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type * as PageTree from '@terrafirmagreg-team/translate/types/tree';
|
|
2
2
|
|
|
3
|
+
export function skeletonTree(id = 'sidebar:skeleton'): PageTree.Root {
|
|
4
|
+
return { name: '', children: [], $id: id, $skeleton: true } as PageTree.Root;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isSkeletonTree(
|
|
8
|
+
tree: PageTree.Root | PageTree.Folder | undefined,
|
|
9
|
+
): boolean {
|
|
10
|
+
return (tree as { $skeleton?: boolean } | undefined)?.$skeleton === true;
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
export function normalizeUrl(url: string): string {
|
|
4
14
|
if (url.startsWith('http://') || url.startsWith('https://')) return url;
|
|
5
15
|
if (!url.startsWith('/')) url = `/${url}`;
|
|
@@ -17,17 +17,6 @@ export type GradientDirectiveProps = {
|
|
|
17
17
|
className?: string;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
export type GlowDirectiveProps = {
|
|
21
|
-
from?: string;
|
|
22
|
-
to?: string;
|
|
23
|
-
dir?: string;
|
|
24
|
-
image?: string;
|
|
25
|
-
gradient?: boolean;
|
|
26
|
-
fade?: boolean;
|
|
27
|
-
children: string;
|
|
28
|
-
className?: string;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
20
|
export type CalloutDirectiveProps = {
|
|
32
21
|
type?: CalloutType;
|
|
33
22
|
title?: string;
|
|
@@ -48,7 +37,12 @@ export type CardDirectiveProps = {
|
|
|
48
37
|
export type BannerDirectiveProps = {
|
|
49
38
|
id?: string;
|
|
50
39
|
height?: string;
|
|
51
|
-
variant?: 'rainbow' | '
|
|
40
|
+
variant?: 'normal' | 'rainbow' | 'fade' | 'glow';
|
|
41
|
+
gradient?: boolean;
|
|
42
|
+
from?: string;
|
|
43
|
+
to?: string;
|
|
44
|
+
dir?: string;
|
|
45
|
+
image?: string;
|
|
52
46
|
rainbowColors?: string[];
|
|
53
47
|
changeLayout?: boolean;
|
|
54
48
|
children?: string;
|
|
@@ -64,6 +58,7 @@ export type TabsDirectiveProps = {
|
|
|
64
58
|
};
|
|
65
59
|
|
|
66
60
|
export type AccordionsDirectiveProps = {
|
|
61
|
+
format?: 'details' | 'accordions' | 'jsx';
|
|
67
62
|
multiple?: boolean;
|
|
68
63
|
defaultValue?: string | string[];
|
|
69
64
|
children: string;
|
|
@@ -76,6 +71,13 @@ export type StepsDirectiveProps = {
|
|
|
76
71
|
};
|
|
77
72
|
|
|
78
73
|
export type FilesDirectiveProps = {
|
|
74
|
+
format?: 'list' | 'fenced';
|
|
75
|
+
name?: string;
|
|
76
|
+
icon?: string;
|
|
77
|
+
href?: string;
|
|
78
|
+
description?: string;
|
|
79
|
+
disabled?: boolean;
|
|
80
|
+
defaultOpen?: boolean;
|
|
79
81
|
children: string;
|
|
80
82
|
className?: string;
|
|
81
83
|
};
|
|
@@ -93,6 +95,13 @@ export type MermaidDirectiveProps = {
|
|
|
93
95
|
};
|
|
94
96
|
|
|
95
97
|
export type MathDirectiveProps = {
|
|
98
|
+
format?:
|
|
99
|
+
| 'inline'
|
|
100
|
+
| 'dollar'
|
|
101
|
+
| 'leaf'
|
|
102
|
+
| 'container'
|
|
103
|
+
| 'fenced'
|
|
104
|
+
| 'display';
|
|
96
105
|
children: string;
|
|
97
106
|
block?: boolean;
|
|
98
107
|
};
|
|
@@ -44,6 +44,12 @@
|
|
|
44
44
|
--color-error: oklch(63.7% 0.237 25.331);
|
|
45
45
|
--color-success: oklch(72.3% 0.219 149.579);
|
|
46
46
|
--color-idea: oklch(70.5% 0.209 60.849);
|
|
47
|
+
--color-question: oklch(80% 0.16 95);
|
|
48
|
+
--color-failure: oklch(60% 0.22 0);
|
|
49
|
+
--color-bug: oklch(57% 0.19 340);
|
|
50
|
+
--color-abstract: oklch(75% 0.15 195);
|
|
51
|
+
--color-example: oklch(62% 0.2 310);
|
|
52
|
+
--color-quote: oklch(60% 0.02 260);
|
|
47
53
|
--color-diff-remove: rgba(200, 10, 100, 0.12);
|
|
48
54
|
--color-diff-remove-symbol: rgb(230, 10, 100);
|
|
49
55
|
--color-diff-add: rgba(14, 180, 100, 0.1);
|
package/styles.css
CHANGED
|
@@ -40,10 +40,22 @@
|
|
|
40
40
|
--color-sidebar-ring: hsl(0, 0%, 63.9%);
|
|
41
41
|
|
|
42
42
|
--color-info: oklch(62.3% 0.214 259.815);
|
|
43
|
+
--color-note: oklch(65% 0.03 260);
|
|
44
|
+
--color-tip: oklch(70% 0.19 165);
|
|
45
|
+
--color-important: oklch(60% 0.22 295);
|
|
43
46
|
--color-warning: oklch(76.9% 0.188 70.08);
|
|
47
|
+
--color-caution: oklch(68% 0.2 45);
|
|
48
|
+
--color-attention: oklch(65% 0.22 350);
|
|
49
|
+
--color-danger: oklch(56% 0.23 18);
|
|
44
50
|
--color-error: oklch(63.7% 0.237 25.331);
|
|
45
51
|
--color-success: oklch(72.3% 0.219 149.579);
|
|
46
|
-
--color-idea: oklch(
|
|
52
|
+
--color-idea: oklch(78% 0.17 85);
|
|
53
|
+
--color-question: oklch(80% 0.16 95);
|
|
54
|
+
--color-failure: oklch(60% 0.22 0);
|
|
55
|
+
--color-bug: oklch(57% 0.19 340);
|
|
56
|
+
--color-abstract: oklch(75% 0.15 195);
|
|
57
|
+
--color-example: oklch(62% 0.2 310);
|
|
58
|
+
--color-quote: oklch(60% 0.02 260);
|
|
47
59
|
--color-diff-remove: rgba(200, 10, 100, 0.12);
|
|
48
60
|
--color-diff-remove-symbol: rgb(230, 10, 100);
|
|
49
61
|
--color-diff-add: rgba(14, 180, 100, 0.1);
|
|
@@ -95,6 +107,9 @@
|
|
|
95
107
|
--color-chart-5: oklch(0.645 0.246 16.439);
|
|
96
108
|
|
|
97
109
|
--color-float: hsl(0, 0%, 11.6%);
|
|
110
|
+
|
|
111
|
+
--header-bg-inner: rgb(255 255 255 / 0.07);
|
|
112
|
+
--header-shadow: rgb(0 0 0 / 0.7);
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
:root {
|
|
@@ -103,6 +118,9 @@
|
|
|
103
118
|
var(--color-primary) 10%,
|
|
104
119
|
transparent
|
|
105
120
|
);
|
|
121
|
+
--header-bg-inner: rgb(0 0 0 / 0.03);
|
|
122
|
+
--header-bg-outer: rgb(0 0 0 / 0);
|
|
123
|
+
--header-shadow: rgb(94 94 94 / 0.4);
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
html {
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
2
|
-
import { gradientImage, type GradientProps } from '@terrafirmagreg-team/ui/lib/mdx/gradient';
|
|
3
|
-
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
4
|
-
|
|
5
|
-
export function GlowHeader({
|
|
6
|
-
fade,
|
|
7
|
-
gradient,
|
|
8
|
-
from,
|
|
9
|
-
to,
|
|
10
|
-
dir,
|
|
11
|
-
image,
|
|
12
|
-
style,
|
|
13
|
-
className,
|
|
14
|
-
children,
|
|
15
|
-
...props
|
|
16
|
-
}: GradientProps & { fade?: boolean; gradient?: boolean } & ComponentProps<'span'>) {
|
|
17
|
-
const boxFrom = from ?? '#fa18cf';
|
|
18
|
-
const boxTo = to ?? '#ff7967';
|
|
19
|
-
const gradientBg = gradientImage({ from, to, dir, image });
|
|
20
|
-
const boxBg = `radial-gradient(color-mix(in srgb, ${boxFrom} 18%, transparent), color-mix(in srgb, ${boxTo} 8%, transparent))`;
|
|
21
|
-
return (
|
|
22
|
-
<span
|
|
23
|
-
className={cn(
|
|
24
|
-
'relative mb-6 inline-block rounded-[20px] px-4 py-2.5 text-center',
|
|
25
|
-
className,
|
|
26
|
-
)}
|
|
27
|
-
style={{
|
|
28
|
-
backgroundImage: gradient ? `${gradientBg}, ${boxBg}` : boxBg,
|
|
29
|
-
...(gradient && {
|
|
30
|
-
WebkitBackgroundClip: 'text, border-box',
|
|
31
|
-
backgroundClip: 'text, border-box',
|
|
32
|
-
WebkitTextFillColor: 'transparent',
|
|
33
|
-
}),
|
|
34
|
-
boxShadow: `0 10px 20px color-mix(in srgb, ${boxFrom} 25%, transparent)`,
|
|
35
|
-
...style,
|
|
36
|
-
}}
|
|
37
|
-
{...props}
|
|
38
|
-
>
|
|
39
|
-
{children}
|
|
40
|
-
{fade && (
|
|
41
|
-
<span
|
|
42
|
-
aria-hidden
|
|
43
|
-
className="pointer-events-none absolute inset-x-4 -bottom-0.5 h-0.5 rounded-full"
|
|
44
|
-
style={{
|
|
45
|
-
backgroundImage: gradientBg,
|
|
46
|
-
maskImage:
|
|
47
|
-
'linear-gradient(to right, transparent, black 25%, black 75%, transparent)',
|
|
48
|
-
}}
|
|
49
|
-
/>
|
|
50
|
-
)}
|
|
51
|
-
</span>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
2
|
-
import { gradientImage, type GradientProps } from '@terrafirmagreg-team/ui/lib/mdx/gradient';
|
|
3
|
-
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
4
|
-
|
|
5
|
-
export function GradientText({
|
|
6
|
-
from,
|
|
7
|
-
to,
|
|
8
|
-
dir,
|
|
9
|
-
image,
|
|
10
|
-
style,
|
|
11
|
-
className,
|
|
12
|
-
...props
|
|
13
|
-
}: GradientProps & ComponentProps<'span'>) {
|
|
14
|
-
return (
|
|
15
|
-
<span
|
|
16
|
-
className={cn('text-transparent', className)}
|
|
17
|
-
style={{
|
|
18
|
-
backgroundImage: gradientImage({ from, to, dir, image }),
|
|
19
|
-
WebkitBackgroundClip: 'text',
|
|
20
|
-
backgroundClip: 'text',
|
|
21
|
-
WebkitTextFillColor: 'transparent',
|
|
22
|
-
...style,
|
|
23
|
-
}}
|
|
24
|
-
{...props}
|
|
25
|
-
/>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|