@hanzo/ui 4.5.4 → 4.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/README-MCP.md +3 -3
- package/README.md +229 -0
- package/assets/ai-icons.tsx +207 -0
- package/assets/crypto.tsx +33 -0
- package/assets/file-type-icon.tsx +66 -0
- package/assets/file.tsx +45 -0
- package/assets/general.tsx +2318 -0
- package/assets/hanzo-logo.svg +9 -0
- package/assets/hanzo-logo.tsx +15 -0
- package/assets/index.ts +8 -0
- package/assets/index.tsx +4 -0
- package/assets/llm-provider.tsx +1094 -0
- package/bin/create-registry.js +1 -1
- package/bin/test-mcp.sh +1 -1
- package/bin/update-registry.js +2 -2
- package/blocks/components/content.tsx +1 -1
- package/blocks/components/grid-block/index.tsx +1 -1
- package/blocks/components/screenful-block/content.tsx +1 -1
- package/blocks/components/screenful-block/poster-background.tsx +1 -1
- package/components/index.ts +56 -0
- package/dist/button.d.ts +1 -0
- package/dist/button.js +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/use-click-away.d.ts +2 -0
- package/dist/hooks/use-click-away.js +23 -0
- package/dist/hooks/use-combined-refs.d.ts +3 -0
- package/dist/hooks/use-combined-refs.js +18 -0
- package/dist/hooks/use-copy-clipboard.d.ts +9 -0
- package/dist/hooks/use-copy-clipboard.js +21 -0
- package/dist/hooks/use-debounce.d.ts +1 -0
- package/dist/hooks/use-debounce.js +13 -0
- package/dist/hooks/use-fill-ids.d.ts +8 -0
- package/dist/hooks/use-fill-ids.js +20 -0
- package/dist/hooks/use-map.d.ts +1 -0
- package/dist/hooks/use-map.js +20 -0
- package/dist/hooks/use-measure.d.ts +8 -0
- package/dist/hooks/use-measure.js +25 -0
- package/dist/hooks/use-reverse-video-playback.d.ts +1 -0
- package/dist/hooks/use-reverse-video-playback.js +41 -0
- package/dist/hooks/use-scroll-restoration.d.ts +8 -0
- package/dist/hooks/use-scroll-restoration.js +36 -0
- package/dist/mcp/enhanced-server.js +2 -2
- package/dist/registry/api.d.ts +1 -1
- package/dist/registry/api.js +3 -3
- package/dist/registry/index.d.ts +48 -48
- package/dist/registry/index.js +3 -3
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -0
- package/helpers/file.ts +33 -0
- package/helpers/memoization.ts +40 -0
- package/package.json +27 -6
- package/primitives/accordion.tsx +53 -45
- package/primitives/alert-dialog.tsx +185 -0
- package/primitives/alert.tsx +74 -0
- package/primitives/apply-typography.tsx +1 -1
- package/primitives/avatar.tsx +37 -29
- package/primitives/background-beams.tsx +142 -0
- package/primitives/badge.tsx +27 -19
- package/primitives/breadcrumb.tsx +77 -62
- package/primitives/button.tsx +69 -72
- package/primitives/card.tsx +73 -59
- package/primitives/chat/chat-input-area.tsx +87 -0
- package/primitives/chat/chat-input.tsx +71 -0
- package/primitives/chat/files-preview.tsx +330 -0
- package/primitives/chat/index.ts +6 -0
- package/primitives/chat/json-form.tsx +8 -0
- package/primitives/chat/message-list.tsx +307 -0
- package/primitives/chat/message.tsx +569 -0
- package/primitives/chat/sqlite-preview.tsx +215 -0
- package/primitives/checkbox.tsx +18 -19
- package/primitives/collapsible.tsx +9 -0
- package/primitives/command.tsx +75 -83
- package/primitives/context-menu.tsx +115 -109
- package/primitives/copy-to-clipboard-icon.tsx +60 -0
- package/primitives/dialog-video-controller.tsx +1 -1
- package/primitives/dialog.tsx +111 -145
- package/primitives/dot-pattern.tsx +57 -0
- package/primitives/dots-loader.tsx +13 -0
- package/primitives/drawer.tsx +59 -87
- package/primitives/dropdown-menu.tsx +199 -0
- package/primitives/error-message.tsx +19 -0
- package/primitives/file-uploader.tsx +200 -0
- package/primitives/form.tsx +92 -87
- package/primitives/hover-card.tsx +28 -0
- package/primitives/icons/github.tsx +1 -1
- package/primitives/icons/youtube-logo.tsx +1 -1
- package/primitives/index-common.ts +121 -42
- package/primitives/index-next.ts +3 -1
- package/primitives/input.tsx +115 -20
- package/primitives/label.tsx +15 -23
- package/primitives/loading-spinner.tsx +1 -1
- package/primitives/markdown-preview.tsx +609 -0
- package/primitives/mermaid.tsx +196 -0
- package/primitives/next/link-element.tsx +1 -1
- package/primitives/next/mdx-link.tsx +1 -1
- package/primitives/pagination.tsx +117 -0
- package/primitives/popover.tsx +20 -25
- package/primitives/pretty-json-print.tsx +28 -0
- package/primitives/progress.tsx +14 -15
- package/primitives/prompt-textarea.tsx +72 -0
- package/primitives/qr-code.tsx +112 -0
- package/primitives/radio-group.tsx +25 -39
- package/primitives/resizable.tsx +47 -0
- package/primitives/scroll-area.tsx +35 -25
- package/primitives/search-input.tsx +66 -0
- package/primitives/select.tsx +62 -109
- package/primitives/separator.tsx +22 -26
- package/primitives/sheet.tsx +78 -117
- package/primitives/skeleton.tsx +13 -16
- package/primitives/slider.tsx +50 -60
- package/primitives/stepper.tsx +272 -0
- package/primitives/switch.tsx +14 -23
- package/primitives/table.tsx +65 -77
- package/primitives/tabs.tsx +29 -39
- package/primitives/text-link.tsx +25 -0
- package/primitives/textarea.tsx +61 -0
- package/primitives/textfield.tsx +75 -0
- package/primitives/toast.tsx +30 -0
- package/primitives/toggle-group.tsx +33 -33
- package/primitives/toggle.tsx +22 -51
- package/primitives/tooltip.tsx +37 -38
- package/registry.json +1 -1
- package/src/button.ts +1 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/use-click-away.ts +31 -0
- package/src/hooks/use-combined-refs.ts +22 -0
- package/src/hooks/use-copy-clipboard.ts +30 -0
- package/src/hooks/use-debounce.ts +17 -0
- package/src/hooks/use-fill-ids.ts +25 -0
- package/src/hooks/use-map.ts +26 -0
- package/src/hooks/use-measure.ts +42 -0
- package/src/hooks/use-reverse-video-playback.ts +43 -0
- package/src/hooks/use-scroll-restoration.ts +50 -0
- package/src/mcp/README.md +1 -1
- package/src/mcp/enhanced-server.ts +2 -2
- package/src/registry/api.ts +3 -3
- package/src/registry/index.ts +3 -3
- package/src/utils.ts +1 -0
- package/style/theme-provider.tsx +1 -1
- package/test-imports.mjs +19 -0
- package/types/animation-def.ts +1 -1
- package/types/button-def.ts +1 -1
- package/types/index.ts +1 -1
- package/util/blob.ts +28 -0
- package/util/copy-to-clipboard.ts +17 -0
- package/util/create-shadow-root.ts +22 -0
- package/util/date.ts +83 -0
- package/util/debounce.ts +11 -0
- package/util/file.ts +15 -0
- package/util/format-and-abbreviate-as-currency.ts +1 -1
- package/util/format-text.ts +33 -0
- package/util/index.ts +9 -78
- package/util/timing.ts +3 -0
- package/util/toasts.tsx +17 -0
- package/utils.ts +9 -0
package/primitives/toggle.tsx
CHANGED
|
@@ -1,73 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
import * as TogglePrimitive from '@radix-ui/react-toggle';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
import * as React from 'react';
|
|
2
4
|
|
|
3
|
-
import
|
|
4
|
-
import * as TogglePrimitive from '@radix-ui/react-toggle'
|
|
5
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
6
|
-
|
|
7
|
-
import { cn } from '../util'
|
|
5
|
+
import { cn } from '../src/utils';
|
|
8
6
|
|
|
9
7
|
const toggleVariants = cva(
|
|
10
|
-
'inline-flex items-center justify-center text-sm font-medium
|
|
11
|
-
'ring-offset-background ' +
|
|
12
|
-
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ' +
|
|
13
|
-
'focus-visible:ring-offset-2 disabled:pointer-events-none ' +
|
|
14
|
-
'active:bg-level-3 active:text-accent ',
|
|
8
|
+
'ring-offset-background hover:bg-muted hover:text-text-secondary focus-visible:ring-ring data-[state=on]:bg-bg-quaternary inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-[state=on]:text-white',
|
|
15
9
|
{
|
|
16
10
|
variants: {
|
|
17
11
|
variant: {
|
|
18
|
-
default: '
|
|
19
|
-
|
|
20
|
-
'
|
|
21
|
-
'data-[state=on]:hover:bg-level-3 ',
|
|
22
|
-
|
|
23
|
-
outline: 'border border-muted-3 text-muted-1 ' +
|
|
24
|
-
'data-[state=off]:bg-level-1 hover:data-[state=off]:text-foreground ' +
|
|
25
|
-
'data-[state=on]:bg-level-3 data-[state=on]:border-muted data-[state=on]:text-foreground',
|
|
12
|
+
default: 'bg-bg-tertiary',
|
|
13
|
+
outline:
|
|
14
|
+
'border-divider hover:bg-bg-tertiary border bg-transparent hover:text-white',
|
|
26
15
|
},
|
|
27
16
|
size: {
|
|
28
17
|
default: 'h-10 px-3',
|
|
29
|
-
|
|
30
|
-
sm: 'h-9 px-2',
|
|
18
|
+
sm: 'h-9 px-2.5',
|
|
31
19
|
lg: 'h-11 px-5',
|
|
32
20
|
},
|
|
33
|
-
rounded: {
|
|
34
|
-
full: 'rounded-full',
|
|
35
|
-
sm: 'rounded-sm',
|
|
36
|
-
lsm: 'rounded-l-sm',
|
|
37
|
-
rsm: 'rounded-r-sm',
|
|
38
|
-
md: 'rounded-md',
|
|
39
|
-
lmd: 'rounded-l-md',
|
|
40
|
-
rmd: 'rounded-r-md',
|
|
41
|
-
lg: 'rounded-lg',
|
|
42
|
-
llg: 'rounded-l-lg',
|
|
43
|
-
rlg: 'rounded-r-lg',
|
|
44
|
-
xl: 'rounded-xl',
|
|
45
|
-
lxl: 'rounded-l-xl',
|
|
46
|
-
rxl: 'rounded-r-xl',
|
|
47
|
-
none: 'rounded-none'
|
|
48
|
-
}
|
|
49
|
-
|
|
50
21
|
},
|
|
51
22
|
defaultVariants: {
|
|
52
23
|
variant: 'default',
|
|
53
24
|
size: 'default',
|
|
54
|
-
rounded: 'md'
|
|
55
25
|
},
|
|
56
|
-
}
|
|
57
|
-
)
|
|
26
|
+
},
|
|
27
|
+
);
|
|
58
28
|
|
|
59
|
-
const Toggle =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
29
|
+
const Toggle = ({
|
|
30
|
+
className,
|
|
31
|
+
variant,
|
|
32
|
+
size,
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<typeof TogglePrimitive.Root> &
|
|
35
|
+
VariantProps<typeof toggleVariants>) => (
|
|
64
36
|
<TogglePrimitive.Root
|
|
65
|
-
|
|
66
|
-
className={cn(toggleVariants({ variant, size, rounded} ), className )}
|
|
37
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
67
38
|
{...props}
|
|
68
39
|
/>
|
|
69
|
-
)
|
|
40
|
+
);
|
|
70
41
|
|
|
71
|
-
Toggle.displayName = TogglePrimitive.Root.displayName
|
|
42
|
+
Toggle.displayName = TogglePrimitive.Root.displayName;
|
|
72
43
|
|
|
73
|
-
export { Toggle, toggleVariants }
|
|
44
|
+
export { Toggle, toggleVariants };
|
package/primitives/tooltip.tsx
CHANGED
|
@@ -1,48 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
|
|
3
|
-
import
|
|
4
|
-
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
|
|
4
|
+
import { cn } from '../src/utils';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const TooltipPortal = TooltipPrimitive.Portal;
|
|
13
|
+
const TooltipArrow = TooltipPrimitive.TooltipArrow;
|
|
13
14
|
|
|
14
|
-
const TooltipContent =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const TooltipContent = ({
|
|
16
|
+
className,
|
|
17
|
+
sideOffset = 4,
|
|
18
|
+
showArrow = true,
|
|
19
|
+
...props
|
|
20
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content> & {
|
|
21
|
+
showArrow?: boolean;
|
|
22
|
+
}) => (
|
|
18
23
|
<TooltipPrimitive.Content
|
|
19
|
-
ref={ref}
|
|
20
|
-
sideOffset={sideOffset}
|
|
21
24
|
className={cn(
|
|
22
|
-
'
|
|
23
|
-
className
|
|
25
|
+
'border-divider bg-bg-dark animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 relative z-50 max-w-[280px] rounded-lg border px-3 py-1.5 text-xs text-white',
|
|
26
|
+
className,
|
|
24
27
|
)}
|
|
28
|
+
sideOffset={sideOffset}
|
|
25
29
|
{...props}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
</TooltipContent>
|
|
45
|
-
</Tooltip>
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipWrapper }
|
|
30
|
+
>
|
|
31
|
+
{props.children}
|
|
32
|
+
|
|
33
|
+
{showArrow && (
|
|
34
|
+
<TooltipPrimitive.Arrow className="fill-bg-dark -my-px drop-shadow-[0_1px_0_#313336]" />
|
|
35
|
+
)}
|
|
36
|
+
</TooltipPrimitive.Content>
|
|
37
|
+
);
|
|
38
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
Tooltip,
|
|
42
|
+
TooltipTrigger,
|
|
43
|
+
TooltipContent,
|
|
44
|
+
TooltipProvider,
|
|
45
|
+
TooltipPortal,
|
|
46
|
+
TooltipArrow,
|
|
47
|
+
};
|
package/registry.json
CHANGED
package/src/button.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../../app/registry/new-york/ui/button'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export function useClickAway<T extends Element>(
|
|
4
|
+
cb: (e: Event) => void,
|
|
5
|
+
): React.RefObject<T | null> {
|
|
6
|
+
const ref = React.useRef<T | null>(null);
|
|
7
|
+
const refCb = React.useRef(cb);
|
|
8
|
+
|
|
9
|
+
React.useLayoutEffect(() => {
|
|
10
|
+
refCb.current = cb;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
const handler = (e: Event) => {
|
|
15
|
+
const element = ref.current;
|
|
16
|
+
if (element && !element.contains(e.target as Node)) {
|
|
17
|
+
refCb.current(e);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
document.addEventListener('mousedown', handler);
|
|
22
|
+
document.addEventListener('touchstart', handler);
|
|
23
|
+
|
|
24
|
+
return () => {
|
|
25
|
+
document.removeEventListener('mousedown', handler);
|
|
26
|
+
document.removeEventListener('touchstart', handler);
|
|
27
|
+
};
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
return ref;
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
function useCombinedRefs<T>(
|
|
5
|
+
...refs: React.ForwardedRef<T>[]
|
|
6
|
+
): React.RefObject<T | null> {
|
|
7
|
+
const targetRef = useRef<T>(null);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
refs.forEach((ref) => {
|
|
10
|
+
if (!ref) return;
|
|
11
|
+
|
|
12
|
+
if (typeof ref === 'function') {
|
|
13
|
+
ref(targetRef.current);
|
|
14
|
+
} else {
|
|
15
|
+
ref.current = targetRef.current;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}, [refs]);
|
|
19
|
+
return targetRef;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { useCombinedRefs };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { copyToClipboard } from '../helpers';
|
|
3
|
+
|
|
4
|
+
type UseCopyClipboardProps = {
|
|
5
|
+
string?: string;
|
|
6
|
+
onCopyClipboard?: () => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const useCopyClipboard = ({
|
|
10
|
+
string,
|
|
11
|
+
onCopyClipboard,
|
|
12
|
+
}: UseCopyClipboardProps) => {
|
|
13
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
14
|
+
|
|
15
|
+
let timeout: ReturnType<typeof setTimeout>;
|
|
16
|
+
const onCopy = async () => {
|
|
17
|
+
if (!string) return;
|
|
18
|
+
const string_ = string.trim();
|
|
19
|
+
if (onCopyClipboard && typeof onCopyClipboard === 'function') {
|
|
20
|
+
onCopyClipboard();
|
|
21
|
+
} else {
|
|
22
|
+
await copyToClipboard(string_);
|
|
23
|
+
}
|
|
24
|
+
setIsCopied(true);
|
|
25
|
+
clearTimeout(timeout);
|
|
26
|
+
timeout = setTimeout(() => setIsCopied(false), 1000);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return { isCopied, onCopy };
|
|
30
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export function useDebounce(value: string, delay = 500) {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = React.useState(value);
|
|
5
|
+
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
const handler: NodeJS.Timeout = setTimeout(() => {
|
|
8
|
+
setDebouncedValue(value);
|
|
9
|
+
}, delay);
|
|
10
|
+
|
|
11
|
+
return () => {
|
|
12
|
+
clearTimeout(handler);
|
|
13
|
+
};
|
|
14
|
+
}, [value, delay]);
|
|
15
|
+
|
|
16
|
+
return debouncedValue;
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useFillId = (namespace: string) => {
|
|
4
|
+
const id = `lobe-icons-${namespace.toLowerCase()}-fill`;
|
|
5
|
+
return useMemo(
|
|
6
|
+
() => ({
|
|
7
|
+
fill: `url(#${id})`,
|
|
8
|
+
id,
|
|
9
|
+
}),
|
|
10
|
+
[namespace],
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const useFillIds = (namespace: string, length: number) => {
|
|
15
|
+
return useMemo(() => {
|
|
16
|
+
const ids = Array.from({ length }, (_, i) => {
|
|
17
|
+
const id = `lobe-icons-${namespace.toLowerCase()}-fill-${i}`;
|
|
18
|
+
return {
|
|
19
|
+
fill: `url(#${id})`,
|
|
20
|
+
id,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
return ids;
|
|
24
|
+
}, [namespace]);
|
|
25
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export function useMap<Key, Value>(initialState: [Key, Value][] = []) {
|
|
4
|
+
const mapRef = React.useRef(new Map<Key, Value>(initialState));
|
|
5
|
+
const [, reRender] = React.useReducer((x) => x + 1, 0);
|
|
6
|
+
|
|
7
|
+
mapRef.current.set = (...args) => {
|
|
8
|
+
Map.prototype.set.apply(mapRef.current, args);
|
|
9
|
+
reRender();
|
|
10
|
+
return mapRef.current;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
mapRef.current.clear = (...args) => {
|
|
14
|
+
Map.prototype.clear.apply(mapRef.current, args);
|
|
15
|
+
reRender();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
mapRef.current.delete = (...args) => {
|
|
19
|
+
const res = Map.prototype.delete.apply(mapRef.current, args);
|
|
20
|
+
reRender();
|
|
21
|
+
|
|
22
|
+
return res;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return mapRef.current;
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export function useMeasure<T extends Element>(): [
|
|
4
|
+
React.RefCallback<T>,
|
|
5
|
+
{
|
|
6
|
+
width: number | null;
|
|
7
|
+
height: number | null;
|
|
8
|
+
},
|
|
9
|
+
] {
|
|
10
|
+
const [dimensions, setDimensions] = React.useState<{
|
|
11
|
+
width: number | null;
|
|
12
|
+
height: number | null;
|
|
13
|
+
}>({
|
|
14
|
+
width: null,
|
|
15
|
+
height: null,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const previousObserver = React.useRef<ResizeObserver | null>(null);
|
|
19
|
+
|
|
20
|
+
const customRef = React.useCallback((node: T) => {
|
|
21
|
+
if (previousObserver.current) {
|
|
22
|
+
previousObserver.current.disconnect();
|
|
23
|
+
previousObserver.current = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
27
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
28
|
+
if (entry && entry.borderBoxSize) {
|
|
29
|
+
const { inlineSize: width, blockSize: height } =
|
|
30
|
+
entry.borderBoxSize[0];
|
|
31
|
+
|
|
32
|
+
setDimensions({ width, height });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
observer.observe(node);
|
|
37
|
+
previousObserver.current = observer;
|
|
38
|
+
}
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
return [customRef, dimensions];
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useReverseVideoPlayback = () => {
|
|
4
|
+
const videoRef = useRef<HTMLVideoElement>(null);
|
|
5
|
+
|
|
6
|
+
const [isReversing, setIsReversing] = useState(false);
|
|
7
|
+
const playReverse = () => {
|
|
8
|
+
if (!videoRef.current) return;
|
|
9
|
+
const video = videoRef.current;
|
|
10
|
+
const reversePlayback = () => {
|
|
11
|
+
if (video.currentTime <= 0) {
|
|
12
|
+
setIsReversing(false);
|
|
13
|
+
void video.play();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
video.currentTime -= 0.023;
|
|
17
|
+
requestAnimationFrame(reversePlayback);
|
|
18
|
+
};
|
|
19
|
+
reversePlayback();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const videoElement = videoRef.current;
|
|
24
|
+
if (videoElement) {
|
|
25
|
+
const handleVideoEnd = () => {
|
|
26
|
+
if (isReversing) {
|
|
27
|
+
setIsReversing(false);
|
|
28
|
+
videoElement.currentTime = 0;
|
|
29
|
+
void videoElement.play();
|
|
30
|
+
} else {
|
|
31
|
+
setIsReversing(true);
|
|
32
|
+
playReverse();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
videoElement.addEventListener('ended', handleVideoEnd);
|
|
37
|
+
return () => {
|
|
38
|
+
videoElement.removeEventListener('ended', handleVideoEnd);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}, [isReversing]);
|
|
42
|
+
return videoRef;
|
|
43
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export const useScrollRestoration = ({
|
|
4
|
+
key,
|
|
5
|
+
containerRef,
|
|
6
|
+
scrollTopStateRef,
|
|
7
|
+
}: {
|
|
8
|
+
key: string;
|
|
9
|
+
containerRef: React.RefObject<HTMLElement | null>;
|
|
10
|
+
scrollTopStateRef: React.RefObject<{ [key: string]: number } | null>;
|
|
11
|
+
}): void => {
|
|
12
|
+
const saveScroll = React.useCallback(() => {
|
|
13
|
+
if (scrollTopStateRef.current) {
|
|
14
|
+
scrollTopStateRef.current[`${key}-scrollTop`] =
|
|
15
|
+
containerRef?.current?.scrollTop ?? 0;
|
|
16
|
+
}
|
|
17
|
+
}, [containerRef, scrollTopStateRef, key]);
|
|
18
|
+
|
|
19
|
+
const restoreScroll = React.useCallback(() => {
|
|
20
|
+
if (containerRef.current) {
|
|
21
|
+
containerRef.current.scrollTo({
|
|
22
|
+
top: scrollTopStateRef?.current?.[`${key}-scrollTop`] ?? 0,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}, [containerRef, key, scrollTopStateRef]);
|
|
26
|
+
|
|
27
|
+
React.useLayoutEffect(() => {
|
|
28
|
+
return () => {
|
|
29
|
+
saveScroll();
|
|
30
|
+
};
|
|
31
|
+
}, [saveScroll]);
|
|
32
|
+
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
// Small delay to ensure content is rendered
|
|
35
|
+
const timeoutId = setTimeout(restoreScroll, 100);
|
|
36
|
+
return () => clearTimeout(timeoutId);
|
|
37
|
+
}, [restoreScroll]);
|
|
38
|
+
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
restoreScroll();
|
|
41
|
+
}, [restoreScroll]);
|
|
42
|
+
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
const element = containerRef?.current;
|
|
45
|
+
element?.addEventListener('scroll', saveScroll);
|
|
46
|
+
return () => {
|
|
47
|
+
element?.removeEventListener('scroll', saveScroll);
|
|
48
|
+
};
|
|
49
|
+
}, [containerRef, saveScroll]);
|
|
50
|
+
};
|
package/src/mcp/README.md
CHANGED
|
@@ -122,7 +122,7 @@ This will compile the TypeScript files and make the MCP server available.
|
|
|
122
122
|
|
|
123
123
|
## Registry Format
|
|
124
124
|
|
|
125
|
-
The registry follows the same format as
|
|
125
|
+
The registry follows the same format as hanzo/ui registries, with items defined as:
|
|
126
126
|
|
|
127
127
|
```json
|
|
128
128
|
{
|
|
@@ -339,7 +339,7 @@ module.exports = {
|
|
|
339
339
|
### 3. Add the cn() utility
|
|
340
340
|
Create \`lib/utils.ts\`:
|
|
341
341
|
\`\`\`typescript
|
|
342
|
-
import {
|
|
342
|
+
import { ClassValue, clsx } from "clsx"
|
|
343
343
|
import { twMerge } from "tailwind-merge"
|
|
344
344
|
|
|
345
345
|
export function cn(...inputs: ClassValue[]) {
|
|
@@ -1132,7 +1132,7 @@ Add to your \`globals.css\`:
|
|
|
1132
1132
|
Create \`lib/utils.ts\`:
|
|
1133
1133
|
|
|
1134
1134
|
\`\`\`typescript
|
|
1135
|
-
import {
|
|
1135
|
+
import { ClassValue, clsx } from "clsx"
|
|
1136
1136
|
import { twMerge } from "tailwind-merge"
|
|
1137
1137
|
|
|
1138
1138
|
export function cn(...inputs: ClassValue[]) {
|
package/src/registry/api.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { registryItemSchema, RegistryItem, Registry } from "
|
|
1
|
+
import { registryItemSchema, RegistryItem, Registry } from "./schemas"
|
|
2
2
|
import { z } from "zod"
|
|
3
3
|
|
|
4
4
|
const registryCache = new Map<string, Promise<any>>()
|
|
@@ -14,8 +14,8 @@ export function getRegistryUrl(registry: string): string {
|
|
|
14
14
|
return registry
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// Default to Hanzo registry if REGISTRY_BASE_URL is defined, otherwise use
|
|
18
|
-
const baseUrl = process.env.REGISTRY_BASE_URL || "https://ui.
|
|
17
|
+
// Default to Hanzo registry if REGISTRY_BASE_URL is defined, otherwise use hanzo
|
|
18
|
+
const baseUrl = process.env.REGISTRY_BASE_URL || "https://ui.hanzo.com/registry"
|
|
19
19
|
|
|
20
20
|
// Check if registry is a path to a JSON file
|
|
21
21
|
if (registry.endsWith(".json")) {
|
package/src/registry/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod"
|
|
2
2
|
|
|
3
|
-
// Define the file schema to match
|
|
3
|
+
// Define the file schema to match hanzo/ui's format
|
|
4
4
|
export const fileSchema = z.object({
|
|
5
5
|
path: z.string(),
|
|
6
6
|
content: z.string().optional(),
|
|
@@ -8,7 +8,7 @@ export const fileSchema = z.object({
|
|
|
8
8
|
target: z.string().optional(),
|
|
9
9
|
})
|
|
10
10
|
|
|
11
|
-
// Registry item schema aligned with
|
|
11
|
+
// Registry item schema aligned with hanzo/ui format
|
|
12
12
|
export const registryItemSchema = z.object({
|
|
13
13
|
name: z.string(),
|
|
14
14
|
type: z.string(),
|
|
@@ -26,7 +26,7 @@ export const registryItemSchema = z.object({
|
|
|
26
26
|
subcategory: z.string().optional(),
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
-
// Registry schema aligned with
|
|
29
|
+
// Registry schema aligned with hanzo/ui format
|
|
30
30
|
export const registrySchema = z.object({
|
|
31
31
|
$schema: z.string().optional(),
|
|
32
32
|
name: z.string(),
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../../app/lib/utils'
|
package/style/theme-provider.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React from "react"
|
|
4
4
|
import { ThemeProvider as NextThemesProvider } from "next-themes"
|
|
5
|
-
import {
|
|
5
|
+
import { ThemeProviderProps } from "next-themes/dist/types"
|
|
6
6
|
|
|
7
7
|
const ThemeProvider: React.FC<ThemeProviderProps> = ({ children, ...props }) => (
|
|
8
8
|
<NextThemesProvider
|
package/test-imports.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Test file to verify all import styles work correctly
|
|
2
|
+
|
|
3
|
+
// Test 1: Direct import from @hanzo/ui (top-level)
|
|
4
|
+
import { Button, Card } from '@hanzo/ui'
|
|
5
|
+
console.log('✓ Top-level import works:', typeof Button, typeof Card)
|
|
6
|
+
|
|
7
|
+
// Test 2: Import from /components (same as top-level)
|
|
8
|
+
import { Badge, Alert } from '@hanzo/ui/components'
|
|
9
|
+
console.log('✓ Components import works:', typeof Badge, typeof Alert)
|
|
10
|
+
|
|
11
|
+
// Test 3: Utility imports
|
|
12
|
+
import { cn } from '@hanzo/ui/lib/utils'
|
|
13
|
+
console.log('✓ Utils import works:', typeof cn)
|
|
14
|
+
|
|
15
|
+
console.log('\n🎉 All main import styles are working correctly!')
|
|
16
|
+
console.log('\nSupported import styles:')
|
|
17
|
+
console.log(' - import { Button } from "@hanzo/ui"')
|
|
18
|
+
console.log(' - import { Button } from "@hanzo/ui/components"')
|
|
19
|
+
console.log(' - import { cn } from "@hanzo/ui/lib/utils"')
|
package/types/animation-def.ts
CHANGED
package/types/button-def.ts
CHANGED
package/types/index.ts
CHANGED
package/util/blob.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
|
|
3
|
+
export const blobToBase64 = (blob: Blob): Promise<string> => {
|
|
4
|
+
const reader = new FileReader();
|
|
5
|
+
reader.readAsDataURL(blob);
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
reader.onloadend = () => {
|
|
8
|
+
resolve(reader.result as string);
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const dataUrlToFile = (
|
|
14
|
+
dataUrl: string,
|
|
15
|
+
filename: string,
|
|
16
|
+
): File | undefined => {
|
|
17
|
+
const arr = dataUrl.split(',');
|
|
18
|
+
if (arr.length < 2) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const mimeArr = arr[0].match(/:(.*?);/);
|
|
22
|
+
if (!mimeArr || mimeArr.length < 2) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const mime = mimeArr[1];
|
|
26
|
+
const buff = Buffer.from(arr[1], 'base64');
|
|
27
|
+
return new File([buff], filename, { type: mime });
|
|
28
|
+
};
|