@junwan666/remotion-design 4.0.448-zh.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 ADDED
@@ -0,0 +1,18 @@
1
+ # @remotion/design
2
+
3
+ Design system
4
+
5
+ [![NPM Downloads](https://img.shields.io/npm/dm/@remotion/design.svg?style=flat&color=black&label=Downloads)](https://npmcharts.com/compare/@remotion/design?minimal=true)
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @remotion/design --save-exact
11
+ ```
12
+
13
+ When installing a Remotion package, make sure to align the version of all `remotion` and `@remotion/*` packages to the same version.
14
+ Remove the `^` character from the version number to use the exact version.
15
+
16
+ ## Usage
17
+
18
+ See the [documentation](https://www.remotion.dev/design) for more information.
package/bunfig.toml ADDED
@@ -0,0 +1,2 @@
1
+ [serve.static]
2
+ plugins = ["bun-plugin-tailwind"]
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ type CommonProps = {
3
+ readonly depth?: number;
4
+ readonly loading?: boolean;
5
+ readonly disabled?: boolean;
6
+ };
7
+ type ButtonAsButton = CommonProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'href' | 'disabled'> & {
8
+ readonly href?: undefined;
9
+ };
10
+ type ButtonAsAnchor = CommonProps & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'type'> & {
11
+ readonly href: string;
12
+ };
13
+ export type ButtonProps = ButtonAsButton | ButtonAsAnchor;
14
+ export declare const Button: React.FC<ButtonProps>;
15
+ export {};
package/dist/Button.js ADDED
@@ -0,0 +1,58 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useRef, useState } from 'react';
3
+ import { cn } from './helpers/cn';
4
+ import { useHoverTransforms } from './helpers/hover-transforms';
5
+ import { Outer } from './helpers/Outer';
6
+ import { Spinner } from './Spinner';
7
+ export const Button = ({ children, className, disabled, depth, loading, ...rest }) => {
8
+ const [dimensions, setDimensions] = useState(null);
9
+ const ref = useRef(null);
10
+ const { isActive, progress } = useHoverTransforms(ref, Boolean(disabled || loading));
11
+ const onPointerEnter = useCallback((e) => {
12
+ if (e.pointerType !== 'mouse') {
13
+ return;
14
+ }
15
+ setDimensions((prevDim) => {
16
+ if (!ref.current) {
17
+ throw new Error('Ref is not set');
18
+ }
19
+ const { childNodes } = ref.current;
20
+ if (childNodes.length === 0) {
21
+ throw new Error('No child nodes');
22
+ }
23
+ const childNode = childNodes[0];
24
+ if (!childNode) {
25
+ throw new Error('No child node');
26
+ }
27
+ const rect = childNode.getBoundingClientRect();
28
+ const { borderRadius } = getComputedStyle(childNode);
29
+ const cornerRadius = borderRadius.includes('e')
30
+ ? Infinity
31
+ : parseInt(borderRadius ?? '0', 10);
32
+ const newCornerRadius = Math.min(cornerRadius, rect.width / 2, rect.height / 2);
33
+ if (prevDim) {
34
+ return {
35
+ width: rect.width,
36
+ height: rect.height,
37
+ borderRadius: prevDim.borderRadius,
38
+ };
39
+ }
40
+ return {
41
+ width: rect.width,
42
+ height: rect.height,
43
+ borderRadius: newCornerRadius,
44
+ };
45
+ });
46
+ }, []);
47
+ const isDisabled = disabled || loading;
48
+ const sharedClasses = cn('text-text', 'inline-flex', 'justify-center', 'bg-button-bg', 'items-center', 'font-brand', 'border-solid', 'text-[1em]', 'rounded-lg', 'border-black', 'border-2', 'border-b-4', 'cursor-pointer', 'px-4', 'h-12', 'flex-row', 'items-center', 'relative', 'overflow-hidden', isDisabled && 'cursor-default opacity-50', className);
49
+ const innerContent = (_jsxs(_Fragment, { children: [
50
+ _jsx("div", { className: cn(loading && 'invisible', 'inline-flex items-center'), children: children }), loading ? (_jsx("div", { className: cn('absolute w-full h-full flex inset-0 items-center justify-center text-inherit bg-inherit'), children: _jsx(Spinner, { size: 20, duration: 1 }) })) : null] }));
51
+ const isAnchor = 'href' in rest && rest.href !== undefined;
52
+ const content = isAnchor ? (_jsx("a", { ...rest, className: cn('no-underline text-inherit', sharedClasses), "aria-disabled": isDisabled || undefined, tabIndex: isDisabled ? -1 : undefined, onClick: isDisabled
53
+ ? (e) => {
54
+ e.preventDefault();
55
+ }
56
+ : rest.onClick, children: innerContent })) : (_jsx("button", { type: "button", disabled: isDisabled, className: sharedClasses, ...rest, children: innerContent }));
57
+ return (_jsx("div", { ref: ref, className: "contents", onPointerEnter: onPointerEnter, children: dimensions && (isActive || progress > 0) ? (_jsx(Outer, { parentRef: ref, width: dimensions.width, height: dimensions.height, cornerRadius: dimensions.borderRadius, hoverTransform: progress, depthFactor: depth ?? 1, children: content })) : (content) }));
58
+ };
package/dist/Card.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const Card: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
package/dist/Card.js ADDED
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { cn } from './helpers/cn';
4
+ export const Card = React.forwardRef(({ children, className, ...rest }, ref) => {
5
+ return (_jsx("div", { ref: ref, className: cn('rounded-lg border-2 border-black bg-card-bg text-text border-b-4', className), ...rest, children: children }));
6
+ });
7
+ Card.displayName = 'Card';
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const CheckIcon: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const d = 'M143.056 81.4934C147.25 77.2994 147.25 70.4506 143.056 66.2566C138.862 62.0627 132.013 62.0627 127.819 66.2566L86.1875 107.888L69.1809 90.8816C64.9869 86.6877 58.1381 86.6877 53.9441 90.8816C49.7502 95.0756 49.7502 101.924 53.9441 106.118L78.5691 130.743C82.7631 134.937 89.6119 134.937 93.8059 130.743L143.056 81.4934Z';
3
+ export const CheckIcon = (props) => {
4
+ return (_jsx("svg", { ...props, viewBox: '50.79867500000001 63.11117499999997 95.40282500000015 70.77732499999999', fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: d, fill: "currentcolor" }) }));
5
+ };
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ interface CounterProps {
3
+ readonly count: number;
4
+ readonly setCount: (count: number) => void;
5
+ readonly minCount: number;
6
+ readonly step: number;
7
+ readonly incrementStep: number;
8
+ }
9
+ export declare const Counter: React.FC<CounterProps>;
10
+ export {};
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Card } from './Card';
3
+ import { cn } from './helpers/cn';
4
+ const Triangle = ({ rotated }) => (_jsx("svg", { width: "12px", height: "7px", viewBox: "0 0 12 7", fill: "none", style: {
5
+ transform: rotated ? 'rotate(180deg)' : 'rotate(0deg)',
6
+ }, children: _jsx("path", { className: "fill-text", d: "M7.17096 0.475588C6.73198 0.0764969 6.01906 0.0764969 5.58007 0.475588L1.08483 4.56228C0.761737 4.85601 0.666915 5.29341 0.84251 5.67654C1.01811 6.05966 1.42549 6.3087 1.88203 6.3087H10.8725C11.3255 6.3087 11.7364 6.05966 11.912 5.67654C12.0876 5.29341 11.9893 4.85601 11.6697 4.56228L7.17448 0.475588H7.17096Z" }) }));
7
+ const container = {
8
+ display: 'flex',
9
+ flexDirection: 'row',
10
+ justifyContent: 'flex-end',
11
+ alignItems: 'center',
12
+ height: 42,
13
+ flexShrink: 0,
14
+ };
15
+ const buttonContainer = {
16
+ display: 'flex',
17
+ width: 30,
18
+ height: 20,
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ cursor: 'pointer',
22
+ };
23
+ const MAX_COUNT = 9999999;
24
+ export const Counter = ({ count, setCount, minCount, step, incrementStep, }) => {
25
+ const decrement = () => {
26
+ if (count > minCount) {
27
+ setCount(Math.max(minCount, count - incrementStep));
28
+ }
29
+ };
30
+ const increment = () => {
31
+ const nextValue = count + incrementStep;
32
+ const roundedValue = Math.round(nextValue / incrementStep) * incrementStep;
33
+ setCount(Math.min(MAX_COUNT, roundedValue));
34
+ };
35
+ const shrink = String(count).length > 6;
36
+ return (_jsxs(Card, { style: container, className: cn('w-[140px] flex flex-row overflow-hidden'), children: [
37
+ _jsx("input", { className: cn('fontbrand font-medium min-w-[80px] border-0 text-end outline-0 text-text overflow-hidden flex-1 px-0 py-0 pr-2 w-full h-full tabular-nums', shrink ? 'text-lg' : 'text-2xl'), type: "number", value: count, onClick: (e) => e.currentTarget.select(), onChange: (e) => {
38
+ if (e.target.value.trim() === '') {
39
+ setCount(step === 1 ? 1 : minCount);
40
+ return;
41
+ }
42
+ const inputValue = parseInt(e.target.value, 10);
43
+ const validValue = Math.min(MAX_COUNT, Math.max(inputValue, minCount));
44
+ // For steps > 1, round to the nearest valid step
45
+ if (step > 1) {
46
+ const roundedValue = Math.round(validValue / step) * step;
47
+ setCount(Math.max(roundedValue, minCount));
48
+ }
49
+ else {
50
+ setCount(validValue);
51
+ }
52
+ } }), _jsxs("div", { className: "flex flex-col h-full", children: [
53
+ _jsx("button", { type: "button", className: "border-0 flex-1 p-0 pt-[5px] bg-transparent", style: {
54
+ ...buttonContainer,
55
+ }, onClick: increment, children: _jsx(Triangle, { rotated: false }) }), _jsx("button", { type: "button", className: "border-0 flex-1 p-0 bg-transparent pb-[5px] pl-[1px]", style: {
56
+ ...buttonContainer,
57
+ }, onClick: decrement, children: _jsx(Triangle, { rotated: true }) })
58
+ ] })
59
+ ] }));
60
+ };
@@ -0,0 +1,3 @@
1
+ export declare const InlineCode: React.FC<{
2
+ readonly children: React.ReactNode;
3
+ }>;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export const InlineCode = ({ children }) => {
3
+ return _jsx("code", { className: "font-brand text-brand", children: children });
4
+ };
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
3
+ export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
4
+ export {};
package/dist/Input.js ADDED
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { cn } from './helpers/cn';
4
+ export const Input = React.forwardRef(({ className, ...props }, ref) => {
5
+ return (_jsx("input", { ref: ref, className: cn('w-full bg-white dark:bg-[#121212] rounded-lg border-2 border-b-4 border-black outline-none h-14 px-3 fontbrand text-lg box-border', className), ...props }));
6
+ });
7
+ Input.displayName = 'Input';
package/dist/Link.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const Link: React.FC<React.AnchorHTMLAttributes<HTMLAnchorElement>>;
package/dist/Link.js ADDED
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from './helpers/cn';
3
+ export const Link = ({ className, ...props }) => {
4
+ return (_jsx("a", { ...props, className: cn(className, 'text-brand underline underline-offset-4'), children: props.children }));
5
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const PlanePaperIcon: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export const PlanePaperIcon = (props) => {
3
+ return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", ...props, viewBox: "0 0 576 512", children: _jsx("path", { fill: "currentcolor", d: "M169.9 280L94.9 448.6 461.2 280 169.9 280zm291.3-48L94.9 63.4 169.9 232 461.2 232zM34.8 465.6L128 256 34.8 46.4C33 42.2 32 37.6 32 33 32 14.8 46.7 0 64.8 0 69.5 0 74.2 1 78.5 3L554.2 222c13.3 6.1 21.8 19.4 21.8 34s-8.5 27.9-21.8 34L78.5 509c-4.3 2-9 3-13.7 3-18.1 0-32.8-14.8-32.8-33 0-4.6 1-9.2 2.8-13.4z" }) }));
4
+ };
@@ -0,0 +1,13 @@
1
+ import * as SelectPrimitive from '@radix-ui/react-select';
2
+ import * as React from 'react';
3
+ declare const Select: React.FC<SelectPrimitive.SelectProps>;
4
+ declare const SelectGroup: React.ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & React.RefAttributes<HTMLDivElement>>;
5
+ declare const SelectValue: React.ForwardRefExoticComponent<SelectPrimitive.SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
6
+ declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
7
+ declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
+ declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
9
+ declare const SelectContent: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
10
+ declare const SelectLabel: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
11
+ declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
12
+ declare const SelectSeparator: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
package/dist/Select.js ADDED
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as SelectPrimitive from '@radix-ui/react-select';
3
+ import { Check, ChevronDown, ChevronUp } from 'lucide-react';
4
+ import * as React from 'react';
5
+ import { cn } from './helpers/cn';
6
+ const Select = SelectPrimitive.Root;
7
+ const SelectGroup = SelectPrimitive.Group;
8
+ const SelectValue = SelectPrimitive.Value;
9
+ const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, className: cn('flex h-10 w-full items-center justify-between rounded-md border-black border-2 border-b-4 bg-card-bg px-3 py-5 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 font-brand', className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
10
+ ] })));
11
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
12
+ const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollUpButton, { ref: ref, className: cn('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(ChevronUp, { className: "h-4 w-4" }) })));
13
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
14
+ const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollDownButton, { ref: ref, className: cn('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(ChevronDown, { className: "h-4 w-4" }) })));
15
+ SelectScrollDownButton.displayName =
16
+ SelectPrimitive.ScrollDownButton.displayName;
17
+ const SelectContent = React.forwardRef(({ className, children, position = 'popper', ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, className: cn(' border-2 border-black relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md font-brand bg-card-bg text-text shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', position === 'popper' &&
18
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className), position: position, ...props, children: [
19
+ _jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn('p-1', position === 'popper' &&
20
+ 'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)'), children: children }), _jsx(SelectScrollDownButton, {})
21
+ ] }) })));
22
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
23
+ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, className: cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className), ...props })));
24
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
25
+ const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, className: cn('relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-slate-200 dark:focus:bg-white/10 data-disabled:pointer-events-none data-disabled:opacity-50 font-brand', className), ...props, children: [
26
+ _jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })
27
+ ] })));
28
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
29
+ const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, className: cn('-mx-1 my-1 h-px bg-muted', className), ...props })));
30
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
31
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export declare const Spinner: React.FC<{
3
+ readonly size: number;
4
+ readonly duration: number;
5
+ }>;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef } from 'react';
3
+ const viewBox = 100;
4
+ const lines = 8;
5
+ // Generated from https://github.com/remotion-dev/template-next/commit/9282c93f7c51ada31a42e18a94680fa09a14eee3
6
+ const translated = 'M 44 0 L 50 0 a 6 6 0 0 1 6 6 L 56 26 a 6 6 0 0 1 -6 6 L 50 32 a 6 6 0 0 1 -6 -6 L 44 6 a 6 6 0 0 1 6 -6 Z';
7
+ export const Spinner = ({ size, duration }) => {
8
+ const style = useMemo(() => {
9
+ return {
10
+ width: size,
11
+ height: size,
12
+ };
13
+ }, [size]);
14
+ const pathsRef = useRef([]);
15
+ useEffect(() => {
16
+ const animate = () => {
17
+ const now = performance.now();
18
+ for (let index = 0; index < lines; index++) {
19
+ const path = pathsRef.current[index];
20
+ if (!path)
21
+ continue;
22
+ // Calculate current opacity using the same animation as before
23
+ const progress = ((now / 1000 - index * (duration / lines)) % duration) / duration;
24
+ // Animation: opacity 1 → 0.15
25
+ const opacity = 1 - 0.85 * progress;
26
+ path.style.opacity = opacity.toString();
27
+ }
28
+ requestAnimationFrame(animate);
29
+ };
30
+ const id = requestAnimationFrame(animate);
31
+ return () => {
32
+ cancelAnimationFrame(id);
33
+ };
34
+ }, [duration]);
35
+ return (_jsx("svg", { style: style, viewBox: `0 0 ${viewBox} ${viewBox}`, children: new Array(lines).fill(true).map((_, index) => {
36
+ return (_jsx("path", {
37
+ // @ts-expect-error
38
+ // eslint-disable-next-line no-return-assign
39
+ ref: (el) => (pathsRef.current[index] = el), style: {
40
+ rotate: `${(index * Math.PI * 2) / lines}rad`,
41
+ transformOrigin: 'center center',
42
+ opacity: 1,
43
+ }, d: translated, fill: 'currentColor' }, index));
44
+ }) }));
45
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export declare const Switch: React.FC<{
3
+ readonly active: boolean;
4
+ readonly onToggle: () => void;
5
+ }>;
package/dist/Switch.js ADDED
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export const Switch = ({ active, onToggle }) => {
3
+ return (_jsx("div", { "data-active": active, className: "h-[34px] transition-all rounded-full w-[56px] border-2 border-b-4 bg-gray-200 p-[2px] cursor-pointer data-[active=true]:bg-brand border-black relative shrink-0", onClick: onToggle, children: _jsx("div", { "data-active": active, className: "h-[20px] w-[20px] left-[4px] top-[4px] transition-all rounded-full bg-white border-2 border-black absolute data-[active=true]:left-[calc(100%-24px)]" }) }));
4
+ };
package/dist/Tabs.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
2
+ import * as React from 'react';
3
+ declare const Tabs: React.ForwardRefExoticComponent<TabsPrimitive.TabsProps & React.RefAttributes<HTMLDivElement>>;
4
+ declare const TabsList: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsListProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
5
+ declare const TabsTrigger: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
6
+ declare const TabsContent: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
package/dist/Tabs.js ADDED
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
3
+ import * as React from 'react';
4
+ import { cn } from './helpers/cn';
5
+ const Tabs = TabsPrimitive.Root;
6
+ const TabsList = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.List, { ref: ref, className: cn('inline-flex items-center justify-center bg-white p-1 border-2 border-black border-b-4 rounded-full', className), ...props })));
7
+ TabsList.displayName = TabsPrimitive.List.displayName;
8
+ const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Trigger, { ref: ref, className: cn('inline-flex items-center border-none font-brand bg-transparent justify-center whitespace-nowrap rounded-full px-3 py-1.5 text-sm ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-brand data-[state=active]:text-white data-[state=inactive]:hover:bg-gray-100', className), ...props })));
9
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
10
+ const TabsContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Content, { ref: ref, className: cn('mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', className), ...props })));
11
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
12
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
3
+ export declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
4
+ export {};
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { cn } from './helpers/cn';
4
+ export const Textarea = React.forwardRef(({ className, style, ...props }, ref) => {
5
+ return (_jsx("textarea", { ref: ref, className: cn('w-full bg-white dark:bg-[#121212] rounded-lg border-2 border-b-4 border-black outline-none px-3 py-3 fontbrand text-lg box-border field-sizing-content min-h-[90px]', className), style: {
6
+ ...style,
7
+ }, ...props }));
8
+ });
9
+ Textarea.displayName = 'Textarea';
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const Triangle: React.FC<React.SVGProps<SVGSVGElement>>;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export const Triangle = (props) => {
3
+ return (_jsx("svg", { viewBox: "0 0 127 131", fill: "none", ...props, children: _jsx("path", { d: "M28.5644 0.011261C25.8196 0.159241 23.6077 0.591782 21.3786 1.43413C20.2669 1.84959 18.4446 2.75455 17.4418 3.38062C13.2472 5.993 10.0496 9.9201 8.38209 14.4903C8.04973 15.3953 7.15007 18.2809 6.5713 20.2672C2.71476 33.5453 0.525761 48.0643 0.0558711 63.4312C-0.0186237 65.8785 -0.0186237 71.7066 0.0558711 74.1141C0.371041 84.3018 1.35093 93.4992 3.12735 102.879C3.84937 106.675 5.00691 111.774 5.67736 114.091C7.04692 118.798 9.84334 122.805 13.8202 125.741C16.4848 127.711 19.5105 129.031 22.8627 129.68C24.4787 129.993 26.6104 130.135 28.1805 130.033C30.3523 129.89 34.6616 129.316 38.1628 128.695C53.9442 125.901 68.5223 120.898 81.7422 113.738C90.1143 109.202 97.2715 104.29 104.177 98.3312C111.059 92.4007 116.927 86.0206 122.09 78.8608C123.287 77.2045 123.889 76.237 124.491 75.019C126.038 71.8773 126.766 68.7527 126.76 65.2582C126.76 62.0027 126.141 59.1114 124.806 56.1518C124.164 54.7233 123.551 53.6988 122.176 51.7523C117.11 44.5868 111.489 38.3433 104.635 32.2762C94.011 22.8739 81.3927 15.1619 67.3017 9.45339C64.2474 8.21835 61.239 7.13128 57.6174 5.95315C49.9502 3.46598 40.4607 1.30891 32.4324 0.233231C31.1718 0.0624847 29.4584 -0.0342712 28.5644 0.011261Z", fill: "currentcolor" }) }));
4
+ };
@@ -0,0 +1,5 @@
1
+ import type { FaceType } from '@remotion/svg-3d-engine';
2
+ import React from 'react';
3
+ export declare const Faces: React.FC<{
4
+ readonly elements: FaceType[];
5
+ }>;
@@ -0,0 +1,7 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { threeDIntoSvgPath } from '@remotion/svg-3d-engine';
3
+ export const Faces = ({ elements, ...svgProps }) => {
4
+ return (_jsx(_Fragment, { children: elements.map(({ points, color, crispEdges }, idx) => {
5
+ return (_jsx("path", { d: threeDIntoSvgPath(points), fill: color, shapeRendering: crispEdges ? 'crispEdges' : undefined, ...svgProps }, idx));
6
+ }) }));
7
+ };
@@ -0,0 +1,9 @@
1
+ export declare const Outer: React.FC<{
2
+ children: React.ReactNode;
3
+ width: number;
4
+ height: number;
5
+ cornerRadius: number;
6
+ hoverTransform: number;
7
+ parentRef: React.RefObject<HTMLDivElement | null>;
8
+ depthFactor: number;
9
+ }>;
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { parsePath, PathInternals, reduceInstructions } from '@remotion/paths';
3
+ import { makeRect } from '@remotion/shapes';
4
+ import { extrudeAndTransformElement, interpolateMatrix4d, makeMatrix3dTransform, reduceMatrices, rotateX, rotateY, scaled, translateX, translateY, translateZ, } from '@remotion/svg-3d-engine';
5
+ import { interpolate } from 'remotion';
6
+ import { Faces } from './Faces';
7
+ import { useClickTransforms, useMousePosition } from './hover-transforms';
8
+ export const Outer = ({ children, width, height, cornerRadius, hoverTransform, parentRef, depthFactor, }) => {
9
+ const clickTransform = useClickTransforms(parentRef);
10
+ const angle = useMousePosition(parentRef);
11
+ const appropriateScale = Math.min(1 + 0.1 * depthFactor, (20 + width) / width);
12
+ const transformationUnhovered = reduceMatrices([
13
+ rotateX(-Math.PI / 20),
14
+ ]);
15
+ const transformationHovered = reduceMatrices([
16
+ scaled(appropriateScale),
17
+ rotateX(-Math.PI / 16),
18
+ rotateX(Math.sin(angle) / 4),
19
+ rotateY(-Math.cos(angle) / 4),
20
+ ]);
21
+ const transformation = interpolateMatrix4d(hoverTransform, transformationUnhovered, transformationHovered);
22
+ const depthFromClick = clickTransform * 15 * depthFactor;
23
+ const depthFromHover = interpolate(hoverTransform, [0, 1], [10, 20]) * depthFactor;
24
+ const depth = depthFromHover;
25
+ const frontFace = reduceMatrices([
26
+ translateZ(-depth / 2 + depthFromClick),
27
+ translateZ(1.1),
28
+ transformation,
29
+ ]);
30
+ const centerOriented = reduceMatrices([
31
+ translateX(-width / 2),
32
+ translateY(-height / 2),
33
+ transformation,
34
+ translateX(width / 2),
35
+ translateY(height / 2),
36
+ ]);
37
+ const { path, instructions } = makeRect({ height, width, cornerRadius });
38
+ const { viewBox } = PathInternals.getBoundingBoxFromInstructions(reduceInstructions(instructions));
39
+ const inbetween = extrudeAndTransformElement({
40
+ sideColor: 'black',
41
+ crispEdges: false,
42
+ depth,
43
+ pressInDepth: depthFromClick,
44
+ points: parsePath(path),
45
+ description: 'rect',
46
+ transformations: centerOriented,
47
+ });
48
+ return (_jsxs("div", { className: "relative", style: { width, height }, children: [
49
+ _jsx("svg", { viewBox: viewBox, style: {
50
+ overflow: 'visible',
51
+ height,
52
+ width,
53
+ position: 'absolute',
54
+ top: 0,
55
+ left: 0,
56
+ }, pointerEvents: "none", children: _jsx(Faces, { elements: inbetween }) }), _jsx("div", { style: {
57
+ transform: makeMatrix3dTransform(frontFace),
58
+ width,
59
+ height,
60
+ }, children: children })
61
+ ] }));
62
+ };
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1 @@
1
+ export declare const getChildNodeFrom: (htmlElement: HTMLDivElement | null) => HTMLElement | null;
@@ -0,0 +1,14 @@
1
+ export const getChildNodeFrom = (htmlElement) => {
2
+ if (!htmlElement) {
3
+ return null;
4
+ }
5
+ const { childNodes } = htmlElement;
6
+ if (childNodes.length === 0) {
7
+ return null;
8
+ }
9
+ const childNode = childNodes[0];
10
+ if (!childNode) {
11
+ return null;
12
+ }
13
+ return childNode;
14
+ };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ type State = {
3
+ progress: number;
4
+ isActive: boolean;
5
+ };
6
+ export declare const useHoverTransforms: (ref: React.RefObject<HTMLDivElement | null>, disabled: boolean) => State;
7
+ export declare const useClickTransforms: (ref: React.RefObject<HTMLDivElement | null>) => number;
8
+ export declare const useMousePosition: (ref: React.RefObject<HTMLDivElement | null>) => number;
9
+ export {};
@@ -0,0 +1,177 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { getChildNodeFrom } from './get-child-node-from';
3
+ export const useHoverTransforms = (ref, disabled) => {
4
+ const [state, setState] = React.useState({
5
+ progress: 0,
6
+ isActive: false,
7
+ });
8
+ const eventTarget = useMemo(() => new EventTarget(), []);
9
+ useEffect(() => {
10
+ if (disabled) {
11
+ eventTarget.dispatchEvent(new Event('disabled'));
12
+ }
13
+ else {
14
+ eventTarget.dispatchEvent(new Event('enabled'));
15
+ }
16
+ }, [disabled, eventTarget]);
17
+ React.useEffect(() => {
18
+ const element = ref.current;
19
+ if (!element)
20
+ return;
21
+ let animationFrame;
22
+ let start = null;
23
+ let isHovered = false;
24
+ let internalDisabled = disabled;
25
+ const getIsActive = () => {
26
+ return isHovered && !internalDisabled;
27
+ };
28
+ const animate = (timestamp) => {
29
+ if (start === null)
30
+ start = timestamp;
31
+ const progress = Math.min((timestamp - start) / 150, 1);
32
+ setState(() => ({
33
+ isActive: getIsActive(),
34
+ progress: getIsActive() ? progress : 1 - progress,
35
+ }));
36
+ if (progress < 1) {
37
+ animationFrame = requestAnimationFrame(animate);
38
+ }
39
+ };
40
+ const animateIn = () => {
41
+ start = null;
42
+ cancelAnimationFrame(animationFrame);
43
+ animationFrame = requestAnimationFrame(animate);
44
+ };
45
+ const handleMouseEnter = () => {
46
+ const prevIsActive = getIsActive();
47
+ isHovered = true;
48
+ if (prevIsActive === getIsActive()) {
49
+ return;
50
+ }
51
+ animateIn();
52
+ };
53
+ const handleMouseLeave = () => {
54
+ const prevIsActive = getIsActive();
55
+ isHovered = false;
56
+ if (prevIsActive === getIsActive()) {
57
+ return;
58
+ }
59
+ animateIn();
60
+ };
61
+ const handleDisabled = () => {
62
+ const prevIsActive = getIsActive();
63
+ internalDisabled = true;
64
+ if (prevIsActive === getIsActive()) {
65
+ return;
66
+ }
67
+ animateIn();
68
+ };
69
+ const handleEnabled = () => {
70
+ const prevIsActive = getIsActive();
71
+ internalDisabled = false;
72
+ if (prevIsActive === getIsActive()) {
73
+ return;
74
+ }
75
+ animateIn();
76
+ };
77
+ element.addEventListener('mouseenter', handleMouseEnter);
78
+ element.addEventListener('mouseleave', handleMouseLeave);
79
+ eventTarget.addEventListener('disabled', handleDisabled);
80
+ eventTarget.addEventListener('enabled', handleEnabled);
81
+ return () => {
82
+ element.removeEventListener('mouseenter', handleMouseEnter);
83
+ element.removeEventListener('mouseleave', handleMouseLeave);
84
+ eventTarget.removeEventListener('disabled', handleDisabled);
85
+ eventTarget.removeEventListener('enabled', handleEnabled);
86
+ cancelAnimationFrame(animationFrame);
87
+ };
88
+ // eslint-disable-next-line react-hooks/exhaustive-deps
89
+ }, [ref, eventTarget]);
90
+ return state;
91
+ };
92
+ export const useClickTransforms = (ref) => {
93
+ const [hoverProgress, setHoverProgress] = React.useState(0);
94
+ React.useEffect(() => {
95
+ const element = getChildNodeFrom(ref.current);
96
+ if (!element) {
97
+ return;
98
+ }
99
+ let animationFrame;
100
+ let start = null;
101
+ let isHovered = false;
102
+ const animate = (timestamp) => {
103
+ if (start === null)
104
+ start = timestamp;
105
+ const progress = Math.min((timestamp - start) / 150, 1);
106
+ setHoverProgress(isHovered ? progress : 1 - progress);
107
+ if (progress < 1) {
108
+ animationFrame = requestAnimationFrame(animate);
109
+ }
110
+ };
111
+ const handleMouseEnter = () => {
112
+ isHovered = true;
113
+ start = null;
114
+ cancelAnimationFrame(animationFrame);
115
+ animationFrame = requestAnimationFrame(animate);
116
+ };
117
+ const handleMouseLeave = () => {
118
+ isHovered = false;
119
+ start = null;
120
+ cancelAnimationFrame(animationFrame);
121
+ animationFrame = requestAnimationFrame(animate);
122
+ };
123
+ element.addEventListener('pointerdown', handleMouseEnter);
124
+ element.addEventListener('pointerup', handleMouseLeave);
125
+ return () => {
126
+ element.removeEventListener('pointerdown', handleMouseEnter);
127
+ element.removeEventListener('pointerup', handleMouseLeave);
128
+ cancelAnimationFrame(animationFrame);
129
+ };
130
+ }, [ref]);
131
+ return hoverProgress;
132
+ };
133
+ const getAngle = (ref, coordinates) => {
134
+ const element = getChildNodeFrom(ref);
135
+ if (!element) {
136
+ return 0;
137
+ }
138
+ if (coordinates === null) {
139
+ return 0;
140
+ }
141
+ const clientRect = element.getClientRects();
142
+ if (!clientRect) {
143
+ return 0;
144
+ }
145
+ if (clientRect.length === 0) {
146
+ return 0;
147
+ }
148
+ const center = {
149
+ x: clientRect[0].x + clientRect[0].width / 2,
150
+ y: clientRect[0].y + clientRect[0].height / 2,
151
+ };
152
+ const angle = Math.atan2(coordinates.y - center.y, coordinates.x - center.x);
153
+ return angle;
154
+ };
155
+ let lastCoordinates = null;
156
+ export const useMousePosition = (ref) => {
157
+ const [angle, setAngle] = useState(getAngle(ref.current, lastCoordinates));
158
+ useEffect(() => {
159
+ const element = ref.current;
160
+ if (!element) {
161
+ return;
162
+ }
163
+ const onMouseMove = (e) => {
164
+ const clientRect = element.getClientRects();
165
+ if (!clientRect) {
166
+ return;
167
+ }
168
+ lastCoordinates = { y: e.clientY, x: e.clientX };
169
+ setAngle(getAngle(element, { y: e.clientY, x: e.clientX }));
170
+ };
171
+ window.addEventListener('mousemove', onMouseMove);
172
+ return () => {
173
+ window.removeEventListener('mousemove', onMouseMove);
174
+ };
175
+ }, [ref]);
176
+ return angle;
177
+ };
@@ -0,0 +1,13 @@
1
+ export { Button } from './Button';
2
+ export { Card } from './Card';
3
+ export { CheckIcon } from './CheckIcon';
4
+ export { Counter } from './Counter';
5
+ export { InlineCode } from './InlineCode';
6
+ export { Input } from './Input';
7
+ export { Link } from './Link';
8
+ export { PlanePaperIcon } from './PaperPlaneIcon';
9
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from './Select';
10
+ export { Switch } from './Switch';
11
+ export { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs';
12
+ export { Textarea } from './Textarea';
13
+ export { Triangle } from './Triangle';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export { Button } from './Button';
2
+ export { Card } from './Card';
3
+ export { CheckIcon } from './CheckIcon';
4
+ export { Counter } from './Counter';
5
+ export { InlineCode } from './InlineCode';
6
+ export { Input } from './Input';
7
+ export { Link } from './Link';
8
+ export { PlanePaperIcon } from './PaperPlaneIcon';
9
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from './Select';
10
+ export { Switch } from './Switch';
11
+ export { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs';
12
+ export { Textarea } from './Textarea';
13
+ export { Triangle } from './Triangle';
package/index.css ADDED
@@ -0,0 +1,14 @@
1
+ @source "./dist/esm/index.mjs";
2
+
3
+ @theme {
4
+ --font-brand: 'GTPlanar', sans-serif;
5
+ --color-brand: #0b84f3;
6
+ --color-warn: #ff3232;
7
+ --color-text: black;
8
+ --color-button-bg: white;
9
+ --color-card-bg: white;
10
+ }
11
+
12
+ @theme inline {
13
+ --font-brand--font-feature-settings: 'ss03' 1;
14
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@junwan666/remotion-design",
3
+ "version": "4.0.448-zh.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "module": "dist/esm/index.mjs",
7
+ "repository": {
8
+ "url": "https://github.com/JunWan666/remotion-zh/tree/main/packages/design"
9
+ },
10
+ "sideEffects": false,
11
+ "author": "Jonny Burger <jonny@remotion.dev>",
12
+ "bugs": {
13
+ "url": "https://github.com/remotion-dev/remotion/issues"
14
+ },
15
+ "scripts": {
16
+ "formatting": "oxfmt src --check",
17
+ "format": "oxfmt src",
18
+ "lint": "eslint src",
19
+ "watch": "tsgo -w",
20
+ "make": "tsgo -d && bun --env-file=../.env.bundle bundle.ts"
21
+ },
22
+ "dependencies": {
23
+ "@remotion/paths": "npm:@junwan666/remotion-paths@4.0.448-zh.0",
24
+ "@remotion/shapes": "npm:@junwan666/remotion-shapes@4.0.448-zh.0",
25
+ "@remotion/svg-3d-engine": "npm:@junwan666/remotion-svg-3d-engine@4.0.448-zh.0",
26
+ "clsx": "^2.1.1",
27
+ "remotion": "npm:@junwan666/remotion@4.0.448-zh.0",
28
+ "@radix-ui/react-select": "2.1.1",
29
+ "@radix-ui/react-tabs": "^1.1.0",
30
+ "lucide-react": "0.439.0"
31
+ },
32
+ "peerDependencies": {
33
+ "react": ">=16.8.0",
34
+ "react-dom": ">=16.8.0"
35
+ },
36
+ "devDependencies": {
37
+ "@remotion/eslint-config-internal": "npm:@junwan666/remotion-eslint-config-internal@4.0.448-zh.0",
38
+ "eslint": "9.19.0",
39
+ "react": "19.2.3",
40
+ "react-dom": "19.2.3",
41
+ "tailwindcss": "4.2.0",
42
+ "@tailwindcss/cli": "4.2.0",
43
+ "tailwind-merge": "^2.6.0",
44
+ "bun-plugin-tailwind": "0.1.2",
45
+ "@typescript/native-preview": "7.0.0-dev.20260217.1"
46
+ },
47
+ "keywords": [],
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "exports": {
52
+ ".": {
53
+ "types": "./dist/index.d.ts",
54
+ "require": "./dist/index.js",
55
+ "module": "./dist/esm/index.mjs",
56
+ "import": "./dist/esm/index.mjs"
57
+ },
58
+ "./package.json": "./package.json",
59
+ "./register": "./index.css",
60
+ "./tailwind.css": "./dist/tailwind.css"
61
+ },
62
+ "description": "Design system",
63
+ "homepage": "https://www.remotion.dev/design"
64
+ }