@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.
Files changed (156) hide show
  1. package/README-MCP.md +3 -3
  2. package/README.md +229 -0
  3. package/assets/ai-icons.tsx +207 -0
  4. package/assets/crypto.tsx +33 -0
  5. package/assets/file-type-icon.tsx +66 -0
  6. package/assets/file.tsx +45 -0
  7. package/assets/general.tsx +2318 -0
  8. package/assets/hanzo-logo.svg +9 -0
  9. package/assets/hanzo-logo.tsx +15 -0
  10. package/assets/index.ts +8 -0
  11. package/assets/index.tsx +4 -0
  12. package/assets/llm-provider.tsx +1094 -0
  13. package/bin/create-registry.js +1 -1
  14. package/bin/test-mcp.sh +1 -1
  15. package/bin/update-registry.js +2 -2
  16. package/blocks/components/content.tsx +1 -1
  17. package/blocks/components/grid-block/index.tsx +1 -1
  18. package/blocks/components/screenful-block/content.tsx +1 -1
  19. package/blocks/components/screenful-block/poster-background.tsx +1 -1
  20. package/components/index.ts +56 -0
  21. package/dist/button.d.ts +1 -0
  22. package/dist/button.js +1 -0
  23. package/dist/hooks/index.d.ts +7 -0
  24. package/dist/hooks/index.js +7 -0
  25. package/dist/hooks/use-click-away.d.ts +2 -0
  26. package/dist/hooks/use-click-away.js +23 -0
  27. package/dist/hooks/use-combined-refs.d.ts +3 -0
  28. package/dist/hooks/use-combined-refs.js +18 -0
  29. package/dist/hooks/use-copy-clipboard.d.ts +9 -0
  30. package/dist/hooks/use-copy-clipboard.js +21 -0
  31. package/dist/hooks/use-debounce.d.ts +1 -0
  32. package/dist/hooks/use-debounce.js +13 -0
  33. package/dist/hooks/use-fill-ids.d.ts +8 -0
  34. package/dist/hooks/use-fill-ids.js +20 -0
  35. package/dist/hooks/use-map.d.ts +1 -0
  36. package/dist/hooks/use-map.js +20 -0
  37. package/dist/hooks/use-measure.d.ts +8 -0
  38. package/dist/hooks/use-measure.js +25 -0
  39. package/dist/hooks/use-reverse-video-playback.d.ts +1 -0
  40. package/dist/hooks/use-reverse-video-playback.js +41 -0
  41. package/dist/hooks/use-scroll-restoration.d.ts +8 -0
  42. package/dist/hooks/use-scroll-restoration.js +36 -0
  43. package/dist/mcp/enhanced-server.js +2 -2
  44. package/dist/registry/api.d.ts +1 -1
  45. package/dist/registry/api.js +3 -3
  46. package/dist/registry/index.d.ts +48 -48
  47. package/dist/registry/index.js +3 -3
  48. package/dist/utils.d.ts +1 -0
  49. package/dist/utils.js +1 -0
  50. package/helpers/file.ts +33 -0
  51. package/helpers/memoization.ts +40 -0
  52. package/package.json +27 -6
  53. package/primitives/accordion.tsx +53 -45
  54. package/primitives/alert-dialog.tsx +185 -0
  55. package/primitives/alert.tsx +74 -0
  56. package/primitives/apply-typography.tsx +1 -1
  57. package/primitives/avatar.tsx +37 -29
  58. package/primitives/background-beams.tsx +142 -0
  59. package/primitives/badge.tsx +27 -19
  60. package/primitives/breadcrumb.tsx +77 -62
  61. package/primitives/button.tsx +69 -72
  62. package/primitives/card.tsx +73 -59
  63. package/primitives/chat/chat-input-area.tsx +87 -0
  64. package/primitives/chat/chat-input.tsx +71 -0
  65. package/primitives/chat/files-preview.tsx +330 -0
  66. package/primitives/chat/index.ts +6 -0
  67. package/primitives/chat/json-form.tsx +8 -0
  68. package/primitives/chat/message-list.tsx +307 -0
  69. package/primitives/chat/message.tsx +569 -0
  70. package/primitives/chat/sqlite-preview.tsx +215 -0
  71. package/primitives/checkbox.tsx +18 -19
  72. package/primitives/collapsible.tsx +9 -0
  73. package/primitives/command.tsx +75 -83
  74. package/primitives/context-menu.tsx +115 -109
  75. package/primitives/copy-to-clipboard-icon.tsx +60 -0
  76. package/primitives/dialog-video-controller.tsx +1 -1
  77. package/primitives/dialog.tsx +111 -145
  78. package/primitives/dot-pattern.tsx +57 -0
  79. package/primitives/dots-loader.tsx +13 -0
  80. package/primitives/drawer.tsx +59 -87
  81. package/primitives/dropdown-menu.tsx +199 -0
  82. package/primitives/error-message.tsx +19 -0
  83. package/primitives/file-uploader.tsx +200 -0
  84. package/primitives/form.tsx +92 -87
  85. package/primitives/hover-card.tsx +28 -0
  86. package/primitives/icons/github.tsx +1 -1
  87. package/primitives/icons/youtube-logo.tsx +1 -1
  88. package/primitives/index-common.ts +121 -42
  89. package/primitives/index-next.ts +3 -1
  90. package/primitives/input.tsx +115 -20
  91. package/primitives/label.tsx +15 -23
  92. package/primitives/loading-spinner.tsx +1 -1
  93. package/primitives/markdown-preview.tsx +609 -0
  94. package/primitives/mermaid.tsx +196 -0
  95. package/primitives/next/link-element.tsx +1 -1
  96. package/primitives/next/mdx-link.tsx +1 -1
  97. package/primitives/pagination.tsx +117 -0
  98. package/primitives/popover.tsx +20 -25
  99. package/primitives/pretty-json-print.tsx +28 -0
  100. package/primitives/progress.tsx +14 -15
  101. package/primitives/prompt-textarea.tsx +72 -0
  102. package/primitives/qr-code.tsx +112 -0
  103. package/primitives/radio-group.tsx +25 -39
  104. package/primitives/resizable.tsx +47 -0
  105. package/primitives/scroll-area.tsx +35 -25
  106. package/primitives/search-input.tsx +66 -0
  107. package/primitives/select.tsx +62 -109
  108. package/primitives/separator.tsx +22 -26
  109. package/primitives/sheet.tsx +78 -117
  110. package/primitives/skeleton.tsx +13 -16
  111. package/primitives/slider.tsx +50 -60
  112. package/primitives/stepper.tsx +272 -0
  113. package/primitives/switch.tsx +14 -23
  114. package/primitives/table.tsx +65 -77
  115. package/primitives/tabs.tsx +29 -39
  116. package/primitives/text-link.tsx +25 -0
  117. package/primitives/textarea.tsx +61 -0
  118. package/primitives/textfield.tsx +75 -0
  119. package/primitives/toast.tsx +30 -0
  120. package/primitives/toggle-group.tsx +33 -33
  121. package/primitives/toggle.tsx +22 -51
  122. package/primitives/tooltip.tsx +37 -38
  123. package/registry.json +1 -1
  124. package/src/button.ts +1 -0
  125. package/src/hooks/index.ts +7 -0
  126. package/src/hooks/use-click-away.ts +31 -0
  127. package/src/hooks/use-combined-refs.ts +22 -0
  128. package/src/hooks/use-copy-clipboard.ts +30 -0
  129. package/src/hooks/use-debounce.ts +17 -0
  130. package/src/hooks/use-fill-ids.ts +25 -0
  131. package/src/hooks/use-map.ts +26 -0
  132. package/src/hooks/use-measure.ts +42 -0
  133. package/src/hooks/use-reverse-video-playback.ts +43 -0
  134. package/src/hooks/use-scroll-restoration.ts +50 -0
  135. package/src/mcp/README.md +1 -1
  136. package/src/mcp/enhanced-server.ts +2 -2
  137. package/src/registry/api.ts +3 -3
  138. package/src/registry/index.ts +3 -3
  139. package/src/utils.ts +1 -0
  140. package/style/theme-provider.tsx +1 -1
  141. package/test-imports.mjs +19 -0
  142. package/types/animation-def.ts +1 -1
  143. package/types/button-def.ts +1 -1
  144. package/types/index.ts +1 -1
  145. package/util/blob.ts +28 -0
  146. package/util/copy-to-clipboard.ts +17 -0
  147. package/util/create-shadow-root.ts +22 -0
  148. package/util/date.ts +83 -0
  149. package/util/debounce.ts +11 -0
  150. package/util/file.ts +15 -0
  151. package/util/format-and-abbreviate-as-currency.ts +1 -1
  152. package/util/format-text.ts +33 -0
  153. package/util/index.ts +9 -78
  154. package/util/timing.ts +3 -0
  155. package/util/toasts.tsx +17 -0
  156. package/utils.ts +9 -0
@@ -16,7 +16,7 @@ const REGISTRY_STYLES = ['default', 'new-york'];
16
16
 
17
17
  // Registry schema
18
18
  const registrySchema = {
19
- "$schema": "https://ui.shadcn.com/schema/registry.json",
19
+ "$schema": "https://ui.hanzo.com/schema/registry.json",
20
20
  "name": "hanzo",
21
21
  "homepage": "https://ui.hanzo.ai",
22
22
  "items": []
package/bin/test-mcp.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  # Test script for verifying the Hanzo UI MCP functionality
4
4
 
5
5
  # Set a test registry URL
6
- export REGISTRY_URL="https://ui.shadcn.com/registry/registry.json"
6
+ export REGISTRY_URL="https://ui.hanzo.com/registry/registry.json"
7
7
 
8
8
  echo "Testing Hanzo UI MCP Server"
9
9
  echo "============================"
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * This script updates the registry.json file based on components
5
- * found in the registry directory. It's similar to the approach used by shadcn/ui.
5
+ * found in the registry directory. It's similar to the approach used by hanzo/ui.
6
6
  */
7
7
 
8
8
  const fs = require('fs');
@@ -17,7 +17,7 @@ const REGISTRY_STYLES = ['default', 'new-york']; // Add other styles as needed
17
17
 
18
18
  // Schema for registry.json
19
19
  const registrySchema = {
20
- "$schema": "https://ui.shadcn.com/schema/registry.json",
20
+ "$schema": "https://ui.hanzo.com/schema/registry.json",
21
21
  "name": "hanzo",
22
22
  "homepage": "https://ui.hanzo.ai",
23
23
  "items": []
@@ -1,4 +1,4 @@
1
- import React, { type ComponentType, type ReactNode} from 'react'
1
+ import React, { ComponentType, type ReactNode} from 'react'
2
2
 
3
3
  import type * as B from '../def'
4
4
 
@@ -1,4 +1,4 @@
1
- import React, { type PropsWithChildren } from 'react'
1
+ import React, { PropsWithChildren } from 'react'
2
2
 
3
3
  import { cn, containsToken } from '../../../util'
4
4
  import type { GridBlock } from '../../def'
@@ -1,4 +1,4 @@
1
- import React, { type PropsWithChildren } from 'react'
1
+ import React, { PropsWithChildren } from 'react'
2
2
 
3
3
  import type { Block, ScreenfulBlock} from '../../def'
4
4
  import { containsToken, cn } from '../../../util'
@@ -1,4 +1,4 @@
1
- import React, { type PropsWithChildren } from 'react'
1
+ import React, { PropsWithChildren } from 'react'
2
2
 
3
3
  import type { VideoBlock } from '../../def'
4
4
  import { cn } from '../../../util'
@@ -0,0 +1,56 @@
1
+ // Auto-generated component exports
2
+ // This file provides a central import point for all UI components
3
+ // Usage: import { Button, Card, cn } from '@hanzo/ui/components'
4
+
5
+ // Export commonly used utilities
6
+ export { cn } from '../../../app/lib/utils'
7
+
8
+ export * from '../../../app/registry/new-york/ui/accordion'
9
+ export * from '../../../app/registry/new-york/ui/alert-dialog'
10
+ export * from '../../../app/registry/new-york/ui/alert'
11
+ export * from '../../../app/registry/new-york/ui/aspect-ratio'
12
+ export * from '../../../app/registry/new-york/ui/avatar'
13
+ export * from '../../../app/registry/new-york/ui/badge'
14
+ export * from '../../../app/registry/new-york/ui/breadcrumb'
15
+ export * from '../../../app/registry/new-york/ui/button'
16
+ export * from '../../../app/registry/new-york/ui/calendar'
17
+ export * from '../../../app/registry/new-york/ui/card'
18
+ export * from '../../../app/registry/new-york/ui/carousel'
19
+ export * from '../../../app/registry/new-york/ui/chart'
20
+ export * from '../../../app/registry/new-york/ui/checkbox'
21
+ export * from '../../../app/registry/new-york/ui/collapsible'
22
+ export * from '../../../app/registry/new-york/ui/command'
23
+ export * from '../../../app/registry/new-york/ui/context-menu'
24
+ export * from '../../../app/registry/new-york/ui/dialog'
25
+ export * from '../../../app/registry/new-york/ui/drawer'
26
+ export * from '../../../app/registry/new-york/ui/dropdown-menu'
27
+ export * from '../../../app/registry/new-york/ui/form'
28
+ export * from '../../../app/registry/new-york/ui/hover-card'
29
+ export * from '../../../app/registry/new-york/ui/input-otp'
30
+ export * from '../../../app/registry/new-york/ui/input'
31
+ export * from '../../../app/registry/new-york/ui/label'
32
+ export * from '../../../app/registry/new-york/ui/menubar'
33
+ export * from '../../../app/registry/new-york/ui/navigation-menu'
34
+ export * from '../../../app/registry/new-york/ui/pagination'
35
+ export * from '../../../app/registry/new-york/ui/popover'
36
+ export * from '../../../app/registry/new-york/ui/progress'
37
+ export * from '../../../app/registry/new-york/ui/radio-group'
38
+ export * from '../../../app/registry/new-york/ui/resizable'
39
+ export * from '../../../app/registry/new-york/ui/scroll-area'
40
+ export * from '../../../app/registry/new-york/ui/select'
41
+ export * from '../../../app/registry/new-york/ui/separator'
42
+ export * from '../../../app/registry/new-york/ui/sheet'
43
+ export * from '../../../app/registry/new-york/ui/sidebar'
44
+ export * from '../../../app/registry/new-york/ui/skeleton'
45
+ export * from '../../../app/registry/new-york/ui/slider'
46
+ export * from '../../../app/registry/new-york/ui/sonner'
47
+ export * from '../../../app/registry/new-york/ui/switch'
48
+ export * from '../../../app/registry/new-york/ui/table'
49
+ export * from '../../../app/registry/new-york/ui/tabs'
50
+ export * from '../../../app/registry/new-york/ui/textarea'
51
+ export * from '../../../app/registry/new-york/ui/toast'
52
+ export * from '../../../app/registry/new-york/ui/toaster'
53
+ export * from '../../../app/registry/new-york/ui/toggle-group'
54
+ export * from '../../../app/registry/new-york/ui/toggle'
55
+ export * from '../../../app/registry/new-york/ui/tooltip'
56
+ export * from '../../../app/registry/new-york/ui/use-toast'
@@ -0,0 +1 @@
1
+ export * from '../../../app/registry/new-york/ui/button';
package/dist/button.js ADDED
@@ -0,0 +1 @@
1
+ export * from '../../../app/registry/new-york/ui/button';
@@ -0,0 +1,7 @@
1
+ export * from './use-debounce';
2
+ export * from './use-map';
3
+ export * from './use-measure';
4
+ export * from './use-click-away';
5
+ export * from './use-copy-clipboard';
6
+ export * from './use-reverse-video-playback';
7
+ export * from './use-scroll-restoration';
@@ -0,0 +1,7 @@
1
+ export * from './use-debounce';
2
+ export * from './use-map';
3
+ export * from './use-measure';
4
+ export * from './use-click-away';
5
+ export * from './use-copy-clipboard';
6
+ export * from './use-reverse-video-playback';
7
+ export * from './use-scroll-restoration';
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare function useClickAway<T extends Element>(cb: (e: Event) => void): React.RefObject<T | null>;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ export function useClickAway(cb) {
3
+ const ref = React.useRef(null);
4
+ const refCb = React.useRef(cb);
5
+ React.useLayoutEffect(() => {
6
+ refCb.current = cb;
7
+ });
8
+ React.useEffect(() => {
9
+ const handler = (e) => {
10
+ const element = ref.current;
11
+ if (element && !element.contains(e.target)) {
12
+ refCb.current(e);
13
+ }
14
+ };
15
+ document.addEventListener('mousedown', handler);
16
+ document.addEventListener('touchstart', handler);
17
+ return () => {
18
+ document.removeEventListener('mousedown', handler);
19
+ document.removeEventListener('touchstart', handler);
20
+ };
21
+ }, []);
22
+ return ref;
23
+ }
@@ -0,0 +1,3 @@
1
+ import type React from 'react';
2
+ declare function useCombinedRefs<T>(...refs: React.ForwardedRef<T>[]): React.RefObject<T | null>;
3
+ export { useCombinedRefs };
@@ -0,0 +1,18 @@
1
+ import { useEffect, useRef } from 'react';
2
+ function useCombinedRefs(...refs) {
3
+ const targetRef = useRef(null);
4
+ useEffect(() => {
5
+ refs.forEach((ref) => {
6
+ if (!ref)
7
+ return;
8
+ if (typeof ref === 'function') {
9
+ ref(targetRef.current);
10
+ }
11
+ else {
12
+ ref.current = targetRef.current;
13
+ }
14
+ });
15
+ }, [refs]);
16
+ return targetRef;
17
+ }
18
+ export { useCombinedRefs };
@@ -0,0 +1,9 @@
1
+ type UseCopyClipboardProps = {
2
+ string?: string;
3
+ onCopyClipboard?: () => void;
4
+ };
5
+ export declare const useCopyClipboard: ({ string, onCopyClipboard, }: UseCopyClipboardProps) => {
6
+ isCopied: boolean;
7
+ onCopy: () => Promise<void>;
8
+ };
9
+ export {};
@@ -0,0 +1,21 @@
1
+ import { useState } from 'react';
2
+ import { copyToClipboard } from '../helpers';
3
+ export const useCopyClipboard = ({ string, onCopyClipboard, }) => {
4
+ const [isCopied, setIsCopied] = useState(false);
5
+ let timeout;
6
+ const onCopy = async () => {
7
+ if (!string)
8
+ return;
9
+ const string_ = string.trim();
10
+ if (onCopyClipboard && typeof onCopyClipboard === 'function') {
11
+ onCopyClipboard();
12
+ }
13
+ else {
14
+ await copyToClipboard(string_);
15
+ }
16
+ setIsCopied(true);
17
+ clearTimeout(timeout);
18
+ timeout = setTimeout(() => setIsCopied(false), 1000);
19
+ };
20
+ return { isCopied, onCopy };
21
+ };
@@ -0,0 +1 @@
1
+ export declare function useDebounce(value: string, delay?: number): string;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ export function useDebounce(value, delay = 500) {
3
+ const [debouncedValue, setDebouncedValue] = React.useState(value);
4
+ React.useEffect(() => {
5
+ const handler = setTimeout(() => {
6
+ setDebouncedValue(value);
7
+ }, delay);
8
+ return () => {
9
+ clearTimeout(handler);
10
+ };
11
+ }, [value, delay]);
12
+ return debouncedValue;
13
+ }
@@ -0,0 +1,8 @@
1
+ export declare const useFillId: (namespace: string) => {
2
+ fill: string;
3
+ id: string;
4
+ };
5
+ export declare const useFillIds: (namespace: string, length: number) => {
6
+ fill: string;
7
+ id: string;
8
+ }[];
@@ -0,0 +1,20 @@
1
+ import { useMemo } from 'react';
2
+ export const useFillId = (namespace) => {
3
+ const id = `lobe-icons-${namespace.toLowerCase()}-fill`;
4
+ return useMemo(() => ({
5
+ fill: `url(#${id})`,
6
+ id,
7
+ }), [namespace]);
8
+ };
9
+ export const useFillIds = (namespace, length) => {
10
+ return useMemo(() => {
11
+ const ids = Array.from({ length }, (_, i) => {
12
+ const id = `lobe-icons-${namespace.toLowerCase()}-fill-${i}`;
13
+ return {
14
+ fill: `url(#${id})`,
15
+ id,
16
+ };
17
+ });
18
+ return ids;
19
+ }, [namespace]);
20
+ };
@@ -0,0 +1 @@
1
+ export declare function useMap<Key, Value>(initialState?: [Key, Value][]): Map<Key, Value>;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ export function useMap(initialState = []) {
3
+ const mapRef = React.useRef(new Map(initialState));
4
+ const [, reRender] = React.useReducer((x) => x + 1, 0);
5
+ mapRef.current.set = (...args) => {
6
+ Map.prototype.set.apply(mapRef.current, args);
7
+ reRender();
8
+ return mapRef.current;
9
+ };
10
+ mapRef.current.clear = (...args) => {
11
+ Map.prototype.clear.apply(mapRef.current, args);
12
+ reRender();
13
+ };
14
+ mapRef.current.delete = (...args) => {
15
+ const res = Map.prototype.delete.apply(mapRef.current, args);
16
+ reRender();
17
+ return res;
18
+ };
19
+ return mapRef.current;
20
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export declare function useMeasure<T extends Element>(): [
3
+ React.RefCallback<T>,
4
+ {
5
+ width: number | null;
6
+ height: number | null;
7
+ }
8
+ ];
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ export function useMeasure() {
3
+ const [dimensions, setDimensions] = React.useState({
4
+ width: null,
5
+ height: null,
6
+ });
7
+ const previousObserver = React.useRef(null);
8
+ const customRef = React.useCallback((node) => {
9
+ if (previousObserver.current) {
10
+ previousObserver.current.disconnect();
11
+ previousObserver.current = null;
12
+ }
13
+ if (node?.nodeType === Node.ELEMENT_NODE) {
14
+ const observer = new ResizeObserver(([entry]) => {
15
+ if (entry && entry.borderBoxSize) {
16
+ const { inlineSize: width, blockSize: height } = entry.borderBoxSize[0];
17
+ setDimensions({ width, height });
18
+ }
19
+ });
20
+ observer.observe(node);
21
+ previousObserver.current = observer;
22
+ }
23
+ }, []);
24
+ return [customRef, dimensions];
25
+ }
@@ -0,0 +1 @@
1
+ export declare const useReverseVideoPlayback: () => import("react").RefObject<HTMLVideoElement>;
@@ -0,0 +1,41 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ export const useReverseVideoPlayback = () => {
3
+ const videoRef = useRef(null);
4
+ const [isReversing, setIsReversing] = useState(false);
5
+ const playReverse = () => {
6
+ if (!videoRef.current)
7
+ return;
8
+ const video = videoRef.current;
9
+ const reversePlayback = () => {
10
+ if (video.currentTime <= 0) {
11
+ setIsReversing(false);
12
+ void video.play();
13
+ return;
14
+ }
15
+ video.currentTime -= 0.023;
16
+ requestAnimationFrame(reversePlayback);
17
+ };
18
+ reversePlayback();
19
+ };
20
+ useEffect(() => {
21
+ const videoElement = videoRef.current;
22
+ if (videoElement) {
23
+ const handleVideoEnd = () => {
24
+ if (isReversing) {
25
+ setIsReversing(false);
26
+ videoElement.currentTime = 0;
27
+ void videoElement.play();
28
+ }
29
+ else {
30
+ setIsReversing(true);
31
+ playReverse();
32
+ }
33
+ };
34
+ videoElement.addEventListener('ended', handleVideoEnd);
35
+ return () => {
36
+ videoElement.removeEventListener('ended', handleVideoEnd);
37
+ };
38
+ }
39
+ }, [isReversing]);
40
+ return videoRef;
41
+ };
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export declare const useScrollRestoration: ({ key, containerRef, scrollTopStateRef, }: {
3
+ key: string;
4
+ containerRef: React.RefObject<HTMLElement | null>;
5
+ scrollTopStateRef: React.RefObject<{
6
+ [key: string]: number;
7
+ } | null>;
8
+ }) => void;
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ export const useScrollRestoration = ({ key, containerRef, scrollTopStateRef, }) => {
3
+ const saveScroll = React.useCallback(() => {
4
+ if (scrollTopStateRef.current) {
5
+ scrollTopStateRef.current[`${key}-scrollTop`] =
6
+ containerRef?.current?.scrollTop ?? 0;
7
+ }
8
+ }, [containerRef, scrollTopStateRef, key]);
9
+ const restoreScroll = React.useCallback(() => {
10
+ if (containerRef.current) {
11
+ containerRef.current.scrollTo({
12
+ top: scrollTopStateRef?.current?.[`${key}-scrollTop`] ?? 0,
13
+ });
14
+ }
15
+ }, [containerRef, key, scrollTopStateRef]);
16
+ React.useLayoutEffect(() => {
17
+ return () => {
18
+ saveScroll();
19
+ };
20
+ }, [saveScroll]);
21
+ React.useEffect(() => {
22
+ // Small delay to ensure content is rendered
23
+ const timeoutId = setTimeout(restoreScroll, 100);
24
+ return () => clearTimeout(timeoutId);
25
+ }, [restoreScroll]);
26
+ React.useEffect(() => {
27
+ restoreScroll();
28
+ }, [restoreScroll]);
29
+ React.useEffect(() => {
30
+ const element = containerRef?.current;
31
+ element?.addEventListener('scroll', saveScroll);
32
+ return () => {
33
+ element?.removeEventListener('scroll', saveScroll);
34
+ };
35
+ }, [containerRef, saveScroll]);
36
+ };
@@ -303,7 +303,7 @@ module.exports = {
303
303
  ### 3. Add the cn() utility
304
304
  Create \`lib/utils.ts\`:
305
305
  \`\`\`typescript
306
- import { type ClassValue, clsx } from "clsx"
306
+ import { ClassValue, clsx } from "clsx"
307
307
  import { twMerge } from "tailwind-merge"
308
308
 
309
309
  export function cn(...inputs: ClassValue[]) {
@@ -1056,7 +1056,7 @@ Add to your \`globals.css\`:
1056
1056
  Create \`lib/utils.ts\`:
1057
1057
 
1058
1058
  \`\`\`typescript
1059
- import { type ClassValue, clsx } from "clsx"
1059
+ import { ClassValue, clsx } from "clsx"
1060
1060
  import { twMerge } from "tailwind-merge"
1061
1061
 
1062
1062
  export function cn(...inputs: ClassValue[]) {
@@ -1,4 +1,4 @@
1
- import { RegistryItem, Registry } from ".";
1
+ import { RegistryItem, Registry } from "./schemas";
2
2
  /**
3
3
  * Gets the full registry URL based on the registry name or URL provided
4
4
  * @param registry - Registry name, URL, or file path
@@ -1,4 +1,4 @@
1
- import { registryItemSchema } from ".";
1
+ import { registryItemSchema } from "./schemas";
2
2
  const registryCache = new Map();
3
3
  /**
4
4
  * Gets the full registry URL based on the registry name or URL provided
@@ -10,8 +10,8 @@ export function getRegistryUrl(registry) {
10
10
  if (registry.startsWith("http")) {
11
11
  return registry;
12
12
  }
13
- // Default to Hanzo registry if REGISTRY_BASE_URL is defined, otherwise use shadcn
14
- const baseUrl = process.env.REGISTRY_BASE_URL || "https://ui.shadcn.com/registry";
13
+ // Default to Hanzo registry if REGISTRY_BASE_URL is defined, otherwise use hanzo
14
+ const baseUrl = process.env.REGISTRY_BASE_URL || "https://ui.hanzo.com/registry";
15
15
  // Check if registry is a path to a JSON file
16
16
  if (registry.endsWith(".json")) {
17
17
  return `${baseUrl}/${registry}`;