@hanzo/ui 4.6.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9458 -0
- package/dist/index.mjs +9449 -0
- package/dist/lib/utils.d.mts +2 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +47 -0
- package/dist/lib/utils.mjs +28 -0
- package/dist/src/utils.d.mts +7 -0
- package/dist/src/utils.d.ts +7 -0
- package/dist/src/utils.js +47 -0
- package/dist/src/utils.mjs +28 -0
- package/dist/tailwind/index.d.mts +2 -0
- package/dist/tailwind/index.d.ts +2 -0
- package/dist/tailwind/index.js +2048 -0
- package/dist/tailwind/index.mjs +2017 -0
- package/dist/types/index.d.mts +12 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.js +79 -0
- package/dist/types/index.mjs +56 -0
- package/package.json +151 -25
- package/MCP-INSTRUCTIONS.md +0 -73
- package/README-MCP.md +0 -175
- package/assets/ai-icons.tsx +0 -207
- package/assets/crypto.tsx +0 -33
- package/assets/file-type-icon.tsx +0 -66
- package/assets/file.tsx +0 -45
- package/assets/general.tsx +0 -2318
- package/assets/hanzo-logo.svg +0 -9
- package/assets/hanzo-logo.tsx +0 -15
- package/assets/index.ts +0 -8
- package/assets/index.tsx +0 -4
- package/assets/llm-provider.tsx +0 -1094
- package/blocks/components/accordian-block.tsx +0 -48
- package/blocks/components/block-component-props.ts +0 -11
- package/blocks/components/bullet-cards-block.tsx +0 -46
- package/blocks/components/card-block/index.tsx +0 -171
- package/blocks/components/card-block/link-out-button.tsx +0 -20
- package/blocks/components/card-block/util.ts +0 -28
- package/blocks/components/carte-blanche-block/index.tsx +0 -127
- package/blocks/components/carte-blanche-block/variant-content-left.tsx +0 -49
- package/blocks/components/content.tsx +0 -70
- package/blocks/components/cta-block.tsx +0 -115
- package/blocks/components/enh-heading-block.tsx +0 -204
- package/blocks/components/grid-block/grid-block-mutator.ts +0 -12
- package/blocks/components/grid-block/index.tsx +0 -83
- package/blocks/components/grid-block/mutator-registry.ts +0 -10
- package/blocks/components/grid-block/table-borders.mutator.ts +0 -47
- package/blocks/components/group-block.tsx +0 -83
- package/blocks/components/heading-block.tsx +0 -88
- package/blocks/components/image-block.tsx +0 -111
- package/blocks/components/index.ts +0 -30
- package/blocks/components/screenful-block/content.tsx +0 -123
- package/blocks/components/screenful-block/index.tsx +0 -107
- package/blocks/components/screenful-block/poster-background.tsx +0 -34
- package/blocks/components/screenful-block/video-background.tsx +0 -45
- package/blocks/components/space-block.tsx +0 -66
- package/blocks/components/video-block.tsx +0 -138
- package/blocks/def/accordian-block.ts +0 -14
- package/blocks/def/block.ts +0 -7
- package/blocks/def/bullet-cards-block.ts +0 -22
- package/blocks/def/card-block.ts +0 -22
- package/blocks/def/carte-blanche-block.ts +0 -21
- package/blocks/def/cta-block.ts +0 -19
- package/blocks/def/element-block.ts +0 -11
- package/blocks/def/enh-heading-block.ts +0 -44
- package/blocks/def/grid-block.ts +0 -16
- package/blocks/def/group-block.ts +0 -11
- package/blocks/def/heading-block.ts +0 -15
- package/blocks/def/image-block.ts +0 -31
- package/blocks/def/index.ts +0 -35
- package/blocks/def/screenful-block.ts +0 -54
- package/blocks/def/space-block.ts +0 -64
- package/blocks/def/video-block.ts +0 -9
- package/blocks/index.ts +0 -2
- package/components/index.ts +0 -56
- package/dist/button.d.ts +0 -1
- package/dist/button.js +0 -1
- package/dist/hooks/index.d.ts +0 -7
- package/dist/hooks/index.js +0 -7
- package/dist/hooks/use-click-away.d.ts +0 -2
- package/dist/hooks/use-click-away.js +0 -23
- package/dist/hooks/use-combined-refs.d.ts +0 -3
- package/dist/hooks/use-combined-refs.js +0 -18
- package/dist/hooks/use-copy-clipboard.d.ts +0 -9
- package/dist/hooks/use-copy-clipboard.js +0 -21
- package/dist/hooks/use-debounce.d.ts +0 -1
- package/dist/hooks/use-debounce.js +0 -13
- package/dist/hooks/use-fill-ids.d.ts +0 -8
- package/dist/hooks/use-fill-ids.js +0 -20
- package/dist/hooks/use-map.d.ts +0 -1
- package/dist/hooks/use-map.js +0 -20
- package/dist/hooks/use-measure.d.ts +0 -8
- package/dist/hooks/use-measure.js +0 -25
- package/dist/hooks/use-reverse-video-playback.d.ts +0 -1
- package/dist/hooks/use-reverse-video-playback.js +0 -41
- package/dist/hooks/use-scroll-restoration.d.ts +0 -8
- package/dist/hooks/use-scroll-restoration.js +0 -36
- package/dist/mcp/enhanced-server.d.ts +0 -29
- package/dist/mcp/enhanced-server.js +0 -1128
- package/dist/mcp/index.d.ts +0 -28
- package/dist/mcp/index.js +0 -436
- package/dist/registry/api.d.ts +0 -37
- package/dist/registry/api.js +0 -129
- package/dist/registry/index.d.ts +0 -353
- package/dist/registry/index.js +0 -45
- package/dist/utils.d.ts +0 -1
- package/dist/utils.js +0 -1
- package/environment.d.ts +0 -6
- package/helpers/file.ts +0 -33
- package/helpers/memoization.ts +0 -40
- package/primitives/accordion.tsx +0 -74
- package/primitives/action-button.tsx +0 -42
- package/primitives/alert-dialog.tsx +0 -185
- package/primitives/alert.tsx +0 -74
- package/primitives/apply-typography.tsx +0 -55
- package/primitives/aspect-ratio.tsx +0 -5
- package/primitives/avatar.tsx +0 -57
- package/primitives/background-beams.tsx +0 -142
- package/primitives/badge.tsx +0 -44
- package/primitives/breadcrumb.tsx +0 -130
- package/primitives/breakpoint-indicator.tsx +0 -19
- package/primitives/button.tsx +0 -82
- package/primitives/calendar.tsx +0 -72
- package/primitives/card.tsx +0 -97
- package/primitives/carousel.tsx +0 -237
- package/primitives/chat/chat-input-area.tsx +0 -87
- package/primitives/chat/chat-input.tsx +0 -71
- package/primitives/chat/files-preview.tsx +0 -330
- package/primitives/chat/index.ts +0 -6
- package/primitives/chat/json-form.tsx +0 -8
- package/primitives/chat/message-list.tsx +0 -307
- package/primitives/chat/message.tsx +0 -569
- package/primitives/chat/sqlite-preview.tsx +0 -215
- package/primitives/checkbox.tsx +0 -31
- package/primitives/collapsible.tsx +0 -9
- package/primitives/combobox.tsx +0 -239
- package/primitives/command.tsx +0 -149
- package/primitives/context-menu.tsx +0 -206
- package/primitives/copy-to-clipboard-icon.tsx +0 -60
- package/primitives/dialog-video-controller.tsx +0 -38
- package/primitives/dialog.tsx +0 -123
- package/primitives/dot-pattern.tsx +0 -57
- package/primitives/dots-loader.tsx +0 -13
- package/primitives/drawer.tsx +0 -110
- package/primitives/dropdown-menu.tsx +0 -199
- package/primitives/error-message.tsx +0 -19
- package/primitives/file-uploader.tsx +0 -200
- package/primitives/form.tsx +0 -183
- package/primitives/hover-card.tsx +0 -28
- package/primitives/icons/github.tsx +0 -14
- package/primitives/icons/index.ts +0 -18
- package/primitives/icons/youtube-logo.tsx +0 -59
- package/primitives/index-common.ts +0 -303
- package/primitives/index-next.ts +0 -4
- package/primitives/input-otp.tsx +0 -65
- package/primitives/input.tsx +0 -125
- package/primitives/label.tsx +0 -20
- package/primitives/list-adaptor.ts +0 -12
- package/primitives/list-box.tsx +0 -74
- package/primitives/loading-spinner.tsx +0 -33
- package/primitives/markdown-preview.tsx +0 -609
- package/primitives/mermaid.tsx +0 -196
- package/primitives/navigation-menu.tsx +0 -147
- package/primitives/next/image.tsx +0 -90
- package/primitives/next/index.ts +0 -7
- package/primitives/next/inline-icon.tsx +0 -36
- package/primitives/next/link-element.tsx +0 -109
- package/primitives/next/mdx-link.tsx +0 -22
- package/primitives/next/media-stack.tsx +0 -69
- package/primitives/next/nav-items.tsx +0 -45
- package/primitives/next/youtube-embed.tsx +0 -83
- package/primitives/pagination.tsx +0 -117
- package/primitives/popover.tsx +0 -32
- package/primitives/pretty-json-print.tsx +0 -28
- package/primitives/progress.tsx +0 -26
- package/primitives/prompt-textarea.tsx +0 -72
- package/primitives/qr-code.tsx +0 -112
- package/primitives/radio-group.tsx +0 -42
- package/primitives/resizable.tsx +0 -47
- package/primitives/scroll-area.tsx +0 -57
- package/primitives/search-input.tsx +0 -66
- package/primitives/select.tsx +0 -122
- package/primitives/separator.tsx +0 -25
- package/primitives/sheet.tsx +0 -139
- package/primitives/skeleton.tsx +0 -17
- package/primitives/slider.tsx +0 -62
- package/primitives/sonner.tsx +0 -35
- package/primitives/step-indicator.tsx +0 -69
- package/primitives/stepper.tsx +0 -272
- package/primitives/switch.tsx +0 -26
- package/primitives/table.tsx +0 -105
- package/primitives/tabs.tsx +0 -50
- package/primitives/text-area.tsx +0 -26
- package/primitives/text-link.tsx +0 -25
- package/primitives/textarea.tsx +0 -61
- package/primitives/textfield.tsx +0 -75
- package/primitives/toast.tsx +0 -30
- package/primitives/toggle-group.tsx +0 -63
- package/primitives/toggle.tsx +0 -44
- package/primitives/tooltip.tsx +0 -47
- package/primitives/video-player.tsx +0 -23
- package/public/r/accordion.json +0 -11
- package/public/r/alert.json +0 -11
- package/public/r/avatar.json +0 -11
- package/public/r/badge.json +0 -11
- package/public/r/button.json +0 -11
- package/public/r/card.json +0 -11
- package/public/r/checkbox.json +0 -11
- package/public/r/default.json +0 -6
- package/public/r/dialog.json +0 -11
- package/public/r/input.json +0 -11
- package/public/r/label.json +0 -11
- package/public/r/new-york.json +0 -6
- package/public/r/popover.json +0 -11
- package/public/r/select.json +0 -11
- package/public/r/table.json +0 -11
- package/public/r/tabs.json +0 -11
- package/public/r/toast.json +0 -11
- package/registry.json +0 -184
- package/src/button.ts +0 -1
- package/src/hooks/index.ts +0 -7
- package/src/hooks/use-click-away.ts +0 -31
- package/src/hooks/use-combined-refs.ts +0 -22
- package/src/hooks/use-copy-clipboard.ts +0 -30
- package/src/hooks/use-debounce.ts +0 -17
- package/src/hooks/use-fill-ids.ts +0 -25
- package/src/hooks/use-map.ts +0 -26
- package/src/hooks/use-measure.ts +0 -42
- package/src/hooks/use-reverse-video-playback.ts +0 -43
- package/src/hooks/use-scroll-restoration.ts +0 -50
- package/src/mcp/README.md +0 -141
- package/src/mcp/enhanced-server.ts +0 -1208
- package/src/mcp/index.ts +0 -518
- package/src/mcp/package.json +0 -10
- package/src/registry/api.ts +0 -164
- package/src/registry/index.ts +0 -60
- package/src/registry/package.json +0 -10
- package/src/utils.ts +0 -1
- package/tailwind/colors.tailwind.js +0 -53
- package/tailwind/fontFamily.tailwind.ts +0 -7
- package/tailwind/fontSize.tailwind.ts +0 -13
- package/tailwind/index.ts +0 -7
- package/tailwind/safelist.tailwind.js +0 -26
- package/tailwind/screens.tailwind.js +0 -8
- package/tailwind/spacing.tailwind.js +0 -65
- package/tailwind/tailwind.config.hanzo-preset.d.ts +0 -5
- package/tailwind/tailwind.config.hanzo-preset.js +0 -915
- package/tailwind/tw-font-desc.ts +0 -15
- package/tailwind/typo-plugin/get-plugin-styles.js +0 -679
- package/tailwind/typo-plugin/index.d.ts +0 -9
- package/tailwind/typo-plugin/index.js +0 -141
- package/tailwind/typo-plugin/utils.js +0 -60
- package/tailwind/typography-test.mdx +0 -35
- package/tailwind/z-index.tailwind.js +0 -71
- package/test/test-registry.js +0 -73
- package/test-imports.mjs +0 -19
- package/tsconfig.json +0 -22
- package/types/animation-def.ts +0 -3
- package/types/breakpoints.ts +0 -11
- package/types/bullet-item.ts +0 -10
- package/types/button-def.ts +0 -39
- package/types/dimensions.ts +0 -8
- package/types/grid-def.ts +0 -56
- package/types/image-def.ts +0 -32
- package/types/index.ts +0 -29
- package/types/link-def.ts +0 -56
- package/types/media-stack-def.ts +0 -31
- package/types/t-shirt-size.ts +0 -5
- package/types/tshirt-dimensions.ts +0 -20
- package/types/video-def.ts +0 -25
- package/util/blob.ts +0 -28
- package/util/copy-to-clipboard.ts +0 -17
- package/util/create-shadow-root.ts +0 -22
- package/util/date.ts +0 -83
- package/util/debounce.ts +0 -11
- package/util/file.ts +0 -15
- package/util/format-and-abbreviate-as-currency.ts +0 -125
- package/util/format-text.ts +0 -33
- package/util/format-to-max-char.ts +0 -68
- package/util/index-client.ts +0 -3
- package/util/index.ts +0 -9
- package/util/number-abbreviate.ts +0 -49
- package/util/specifier.ts +0 -43
- package/util/spread-to-transform.ts +0 -24
- package/util/step-animation.ts +0 -90
- package/util/timing.ts +0 -3
- package/util/toasts.tsx +0 -17
- package/util/two-way-map.ts +0 -19
- package/utils.ts +0 -9
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { useTranslation } from '@hanzo_network/hanzo-i18n';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
|
|
4
|
-
import { useCombinedRefs } from '../../hooks/use-combined-refs';
|
|
5
|
-
import { cn } from '../src/utils';
|
|
6
|
-
import { ChatInput } from './chat-input';
|
|
7
|
-
|
|
8
|
-
type ChatInputAreaProps = {
|
|
9
|
-
value: string;
|
|
10
|
-
onChange: (value: string) => void;
|
|
11
|
-
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
12
|
-
onPaste?: (e: React.ClipboardEvent<HTMLTextAreaElement>) => void;
|
|
13
|
-
onSubmit: () => void;
|
|
14
|
-
disabled?: boolean;
|
|
15
|
-
autoFocus?: boolean;
|
|
16
|
-
isLoading?: boolean;
|
|
17
|
-
placeholder?: string;
|
|
18
|
-
topAddons?: React.ReactNode;
|
|
19
|
-
bottomAddons?: React.ReactNode;
|
|
20
|
-
textareaClassName?: string;
|
|
21
|
-
className?: string;
|
|
22
|
-
alternateElement?: React.ReactNode;
|
|
23
|
-
ref?: React.RefObject<HTMLTextAreaElement | null>;
|
|
24
|
-
};
|
|
25
|
-
export const ChatInputArea = ({
|
|
26
|
-
value,
|
|
27
|
-
onChange,
|
|
28
|
-
onPaste,
|
|
29
|
-
onKeyDown,
|
|
30
|
-
autoFocus,
|
|
31
|
-
onSubmit,
|
|
32
|
-
disabled,
|
|
33
|
-
isLoading,
|
|
34
|
-
placeholder,
|
|
35
|
-
topAddons,
|
|
36
|
-
bottomAddons,
|
|
37
|
-
textareaClassName,
|
|
38
|
-
alternateElement,
|
|
39
|
-
className,
|
|
40
|
-
ref,
|
|
41
|
-
}: ChatInputAreaProps) => {
|
|
42
|
-
const { t } = useTranslation();
|
|
43
|
-
const textareaRef = useCombinedRefs<HTMLTextAreaElement>(
|
|
44
|
-
ref as React.RefObject<HTMLTextAreaElement>,
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div
|
|
49
|
-
className={cn(
|
|
50
|
-
'bg-bg-secondary flex w-full max-w-full flex-col rounded-xl text-sm aria-disabled:cursor-not-allowed aria-disabled:opacity-50',
|
|
51
|
-
'shadow-border-input focus-within:shadow-border-input-focus overflow-hidden shadow-[0_0_0_1px_currentColor] transition-shadow',
|
|
52
|
-
className,
|
|
53
|
-
)}
|
|
54
|
-
>
|
|
55
|
-
{topAddons}
|
|
56
|
-
<div
|
|
57
|
-
aria-disabled={disabled}
|
|
58
|
-
className="flex cursor-text flex-col aria-disabled:cursor-not-allowed"
|
|
59
|
-
onClick={(e) => {
|
|
60
|
-
if (e.target === e.currentTarget) {
|
|
61
|
-
textareaRef?.current?.focus();
|
|
62
|
-
}
|
|
63
|
-
}}
|
|
64
|
-
>
|
|
65
|
-
{alternateElement ? (
|
|
66
|
-
alternateElement
|
|
67
|
-
) : (
|
|
68
|
-
<ChatInput
|
|
69
|
-
autoFocus={autoFocus}
|
|
70
|
-
className={textareaClassName}
|
|
71
|
-
disabled={disabled || isLoading}
|
|
72
|
-
onChange={(e) => onChange(e.target.value)}
|
|
73
|
-
onKeyDown={onKeyDown}
|
|
74
|
-
onPaste={onPaste}
|
|
75
|
-
onSend={onSubmit}
|
|
76
|
-
placeholder={placeholder ?? t('chat.sendMessagePlaceholder')}
|
|
77
|
-
ref={textareaRef}
|
|
78
|
-
value={value}
|
|
79
|
-
/>
|
|
80
|
-
)}
|
|
81
|
-
{bottomAddons}
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
ChatInputArea.displayName = 'ChatInputArea';
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
import { cn } from '../src/utils';
|
|
4
|
-
|
|
5
|
-
export interface ChatInputProps
|
|
6
|
-
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
7
|
-
onSend?: () => void;
|
|
8
|
-
ref: React.RefObject<HTMLTextAreaElement | null>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const useAutoResizeTextarea = (
|
|
12
|
-
ref: React.RefObject<HTMLTextAreaElement | null>,
|
|
13
|
-
value: string | number | readonly string[] | undefined,
|
|
14
|
-
autoResize = true, //later to unify
|
|
15
|
-
) => {
|
|
16
|
-
const textAreaRef = React.useRef<HTMLTextAreaElement>(null);
|
|
17
|
-
|
|
18
|
-
React.useImperativeHandle(ref, () => textAreaRef.current!);
|
|
19
|
-
|
|
20
|
-
React.useEffect(() => {
|
|
21
|
-
const ref = textAreaRef?.current;
|
|
22
|
-
|
|
23
|
-
const updateTextareaHeight = () => {
|
|
24
|
-
if (ref && autoResize) {
|
|
25
|
-
ref.style.height = 'auto';
|
|
26
|
-
ref.style.height = ref?.scrollHeight + 'px';
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
updateTextareaHeight();
|
|
31
|
-
|
|
32
|
-
ref?.addEventListener('input', updateTextareaHeight);
|
|
33
|
-
return () => ref?.removeEventListener('input', updateTextareaHeight);
|
|
34
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
-
}, [value]);
|
|
36
|
-
|
|
37
|
-
return { textAreaRef };
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const ChatInputBase = ({
|
|
41
|
-
className,
|
|
42
|
-
onSend,
|
|
43
|
-
onKeyDown,
|
|
44
|
-
ref,
|
|
45
|
-
...props
|
|
46
|
-
}: ChatInputProps) => {
|
|
47
|
-
const { textAreaRef } = useAutoResizeTextarea(ref, props.value);
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<textarea
|
|
51
|
-
className={cn(
|
|
52
|
-
'placeholder:!text-text-placeholder flex max-h-[40vh] min-h-[80px] w-full resize-none overflow-y-auto border-none bg-transparent px-3 py-2 text-base leading-normal break-words focus:outline-hidden focus-visible:ring-0 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
|
53
|
-
className,
|
|
54
|
-
)}
|
|
55
|
-
id="chat-input"
|
|
56
|
-
onKeyDown={(event) => {
|
|
57
|
-
onKeyDown?.(event);
|
|
58
|
-
if (event.key === 'Enter' && !event.shiftKey) {
|
|
59
|
-
event.preventDefault();
|
|
60
|
-
onSend?.();
|
|
61
|
-
}
|
|
62
|
-
}}
|
|
63
|
-
ref={textAreaRef}
|
|
64
|
-
spellCheck={false}
|
|
65
|
-
{...props}
|
|
66
|
-
/>
|
|
67
|
-
);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
ChatInputBase.displayName = 'ChatInput';
|
|
71
|
-
export const ChatInput = React.memo(ChatInputBase);
|
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
import { DialogClose } from '@radix-ui/react-dialog';
|
|
2
|
-
import {
|
|
3
|
-
type Attachment,
|
|
4
|
-
FileTypeSupported,
|
|
5
|
-
} from '@hanzo_network/hanzo-node-state/v2/queries/getChatConversation/types';
|
|
6
|
-
import { save } from '@tauri-apps/plugin-dialog';
|
|
7
|
-
import * as fs from '@tauri-apps/plugin-fs';
|
|
8
|
-
import { BaseDirectory } from '@tauri-apps/plugin-fs';
|
|
9
|
-
import { partial } from 'filesize';
|
|
10
|
-
import { AnimatePresence, motion } from 'framer-motion';
|
|
11
|
-
import { CircleSlashIcon, XIcon } from 'lucide-react';
|
|
12
|
-
import React, { useState } from 'react';
|
|
13
|
-
import { toast } from 'sonner';
|
|
14
|
-
|
|
15
|
-
import { fileIconMap, FileTypeIcon, PaperClipIcon } from '../../assets';
|
|
16
|
-
import { getFileExt } from '../../helpers/file';
|
|
17
|
-
import { cn } from '../../src/utils';
|
|
18
|
-
import { Avatar, AvatarFallback, AvatarImage } from '../avatar';
|
|
19
|
-
import { Button } from '../button';
|
|
20
|
-
import { Dialog, DialogContent } from '../dialog';
|
|
21
|
-
import {
|
|
22
|
-
Tooltip,
|
|
23
|
-
TooltipContent,
|
|
24
|
-
TooltipPortal,
|
|
25
|
-
TooltipTrigger,
|
|
26
|
-
} from '../tooltip';
|
|
27
|
-
import { SqlitePreview } from './sqlite-preview';
|
|
28
|
-
|
|
29
|
-
export type FileListProps = {
|
|
30
|
-
files: Attachment[];
|
|
31
|
-
className?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const isImageFile = (file: string) => {
|
|
35
|
-
return file.match(/\.(jpg|jpeg|png|gif)$/i);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const size = partial({ standard: 'jedec' });
|
|
39
|
-
|
|
40
|
-
const ImagePreview = ({
|
|
41
|
-
name,
|
|
42
|
-
size,
|
|
43
|
-
url,
|
|
44
|
-
onFullscreen,
|
|
45
|
-
}: Pick<Attachment, 'name' | 'size' | 'url'> & {
|
|
46
|
-
onFullscreen: (open: boolean) => void;
|
|
47
|
-
}) => (
|
|
48
|
-
<button
|
|
49
|
-
className="border-divider hover:bg-bg-secondary flex h-14 w-full max-w-[210px] min-w-[210px] shrink-0 cursor-pointer items-center gap-2 rounded-md border py-1.5 pr-1.5 pl-2 text-left"
|
|
50
|
-
onClick={() => onFullscreen(true)}
|
|
51
|
-
>
|
|
52
|
-
<Avatar className="bg-bg-quaternary text-text-default flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xs transition-colors">
|
|
53
|
-
<AvatarImage
|
|
54
|
-
alt={name}
|
|
55
|
-
className="border-divider aspect-square h-full w-full rounded-xs border object-cover"
|
|
56
|
-
src={url}
|
|
57
|
-
/>
|
|
58
|
-
<AvatarFallback>
|
|
59
|
-
<CircleSlashIcon className="text-text-secondary h-4 w-4" />
|
|
60
|
-
</AvatarFallback>
|
|
61
|
-
</Avatar>
|
|
62
|
-
<FileInfo fileName={name} fileSize={size} />
|
|
63
|
-
</button>
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const FileInfo = ({
|
|
67
|
-
fileSize,
|
|
68
|
-
fileName,
|
|
69
|
-
}: {
|
|
70
|
-
fileSize?: number;
|
|
71
|
-
fileName: string;
|
|
72
|
-
}) => (
|
|
73
|
-
<div className="text-text-secondary text-em-sm grid flex-1 -translate-x-px gap-1 py-0.5 leading-none">
|
|
74
|
-
<div className="text-text-default truncate overflow-hidden font-medium">
|
|
75
|
-
{decodeURIComponent(fileName.split('/').at(-1) ?? '')}
|
|
76
|
-
</div>
|
|
77
|
-
{fileSize && (
|
|
78
|
-
<div className="text-text-secondary line-clamp-1 aspect-auto font-normal">
|
|
79
|
-
{size(fileSize)}
|
|
80
|
-
</div>
|
|
81
|
-
)}
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
type FileContentViewerProps = Pick<
|
|
86
|
-
Attachment,
|
|
87
|
-
'name' | 'url' | 'content' | 'type'
|
|
88
|
-
>;
|
|
89
|
-
|
|
90
|
-
export const FileContentViewer: React.FC<FileContentViewerProps> = ({
|
|
91
|
-
name,
|
|
92
|
-
content,
|
|
93
|
-
url,
|
|
94
|
-
type,
|
|
95
|
-
}) => {
|
|
96
|
-
switch (type) {
|
|
97
|
-
case FileTypeSupported.Text: {
|
|
98
|
-
return (
|
|
99
|
-
<pre className="bg-bg-dark h-full overflow-auto p-4 pt-10 font-mono text-xs break-words whitespace-pre-wrap">
|
|
100
|
-
{content}
|
|
101
|
-
</pre>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
case FileTypeSupported.Image: {
|
|
105
|
-
return (
|
|
106
|
-
<div className="flex h-full w-full items-center justify-center">
|
|
107
|
-
<img
|
|
108
|
-
alt={name}
|
|
109
|
-
className="max-h-full max-w-full object-contain"
|
|
110
|
-
src={url}
|
|
111
|
-
/>
|
|
112
|
-
</div>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
case FileTypeSupported.Video: {
|
|
116
|
-
return (
|
|
117
|
-
<div className="flex h-full w-full items-center justify-center">
|
|
118
|
-
<video className="max-h-full max-w-full" controls src={url}>
|
|
119
|
-
Your browser does not support the video tag.
|
|
120
|
-
</video>
|
|
121
|
-
</div>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
case FileTypeSupported.Audio: {
|
|
125
|
-
return (
|
|
126
|
-
<div className="flex h-full w-full items-center justify-center">
|
|
127
|
-
<audio className="w-full" controls src={url}>
|
|
128
|
-
Your browser does not support the audio tag.
|
|
129
|
-
</audio>
|
|
130
|
-
</div>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
case FileTypeSupported.Html: {
|
|
134
|
-
return (
|
|
135
|
-
<div className="flex h-full w-full items-center justify-center">
|
|
136
|
-
<iframe
|
|
137
|
-
className="h-full w-full bg-gray-100"
|
|
138
|
-
sandbox="allow-same-origin"
|
|
139
|
-
src={url}
|
|
140
|
-
title={name}
|
|
141
|
-
/>
|
|
142
|
-
</div>
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
case FileTypeSupported.SqliteDatabase: {
|
|
146
|
-
return <SqlitePreview url={url || ''} />;
|
|
147
|
-
}
|
|
148
|
-
default:
|
|
149
|
-
return (
|
|
150
|
-
<div className="text-text-secondary flex h-full flex-col items-center justify-center gap-6">
|
|
151
|
-
<span>Preview not available for this file type</span>
|
|
152
|
-
</div>
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const FullscreenDialog = ({
|
|
158
|
-
open,
|
|
159
|
-
name,
|
|
160
|
-
type,
|
|
161
|
-
url,
|
|
162
|
-
content,
|
|
163
|
-
setOpen,
|
|
164
|
-
onDownload,
|
|
165
|
-
}: Pick<Attachment, 'name' | 'url' | 'content' | 'type'> & {
|
|
166
|
-
open: boolean;
|
|
167
|
-
setOpen: (open: boolean) => void;
|
|
168
|
-
onDownload?: () => void;
|
|
169
|
-
}) => (
|
|
170
|
-
<Dialog onOpenChange={setOpen} open={open}>
|
|
171
|
-
<DialogContent className="flex size-full max-h-[99vh] max-w-[99vw] flex-col gap-2 bg-transparent p-1 py-8">
|
|
172
|
-
<div className="flex w-full items-center justify-between gap-16 px-10">
|
|
173
|
-
<div className="text-text-default max-w-3xl truncate text-left text-sm">
|
|
174
|
-
{name}
|
|
175
|
-
</div>
|
|
176
|
-
<div className="flex items-center gap-4">
|
|
177
|
-
<Button onClick={onDownload} size="xs" variant="outline">
|
|
178
|
-
Download
|
|
179
|
-
</Button>
|
|
180
|
-
<DialogClose>
|
|
181
|
-
<XIcon className="text-text-secondary h-6 w-6" />
|
|
182
|
-
<span className="sr-only">Close</span>
|
|
183
|
-
</DialogClose>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
<div className="text-text-default flex size-full flex-col overflow-hidden rounded-l-xl p-10">
|
|
187
|
-
<FileContentViewer
|
|
188
|
-
content={content}
|
|
189
|
-
name={name}
|
|
190
|
-
type={type}
|
|
191
|
-
url={url}
|
|
192
|
-
/>
|
|
193
|
-
</div>
|
|
194
|
-
</DialogContent>
|
|
195
|
-
</Dialog>
|
|
196
|
-
);
|
|
197
|
-
const FileButton = ({
|
|
198
|
-
name,
|
|
199
|
-
size,
|
|
200
|
-
onFullscreen,
|
|
201
|
-
}: Pick<Attachment, 'name' | 'size'> & {
|
|
202
|
-
onFullscreen: (open: boolean) => void;
|
|
203
|
-
}) => (
|
|
204
|
-
<button
|
|
205
|
-
className="border-divider hover:bg-bg-secondary flex h-14 w-full max-w-[210px] min-w-[210px] shrink-0 cursor-pointer items-center gap-2 rounded-md border py-1.5 pr-1.5 pl-2 text-left"
|
|
206
|
-
onClick={() => onFullscreen(true)}
|
|
207
|
-
>
|
|
208
|
-
<span className="bg-bg-quaternary text-text-default flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xs transition-colors">
|
|
209
|
-
{fileIconMap[getFileExt(name)] ? (
|
|
210
|
-
<FileTypeIcon
|
|
211
|
-
className="text-text-secondary h-5 w-5"
|
|
212
|
-
type={getFileExt(name)}
|
|
213
|
-
/>
|
|
214
|
-
) : (
|
|
215
|
-
<PaperClipIcon className="text-text-secondary h-4 w-4" />
|
|
216
|
-
)}
|
|
217
|
-
</span>
|
|
218
|
-
<FileInfo fileName={name} fileSize={size} />
|
|
219
|
-
</button>
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
export const FilePreview = ({
|
|
223
|
-
name,
|
|
224
|
-
url,
|
|
225
|
-
size,
|
|
226
|
-
content,
|
|
227
|
-
blob,
|
|
228
|
-
type,
|
|
229
|
-
}: Attachment) => {
|
|
230
|
-
const [open, setOpen] = useState(false);
|
|
231
|
-
|
|
232
|
-
const fileName = decodeURIComponent(name).split('/').at(-1) ?? '';
|
|
233
|
-
|
|
234
|
-
const children = isImageFile(name) ? (
|
|
235
|
-
<ImagePreview name={name} onFullscreen={setOpen} size={size} url={url} />
|
|
236
|
-
) : (
|
|
237
|
-
<FileButton name={name} onFullscreen={setOpen} size={size} />
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
return (
|
|
241
|
-
<Tooltip>
|
|
242
|
-
<TooltipTrigger asChild>
|
|
243
|
-
<div>{children}</div>
|
|
244
|
-
</TooltipTrigger>
|
|
245
|
-
<TooltipPortal>
|
|
246
|
-
<TooltipContent className="container break-words" side="top">
|
|
247
|
-
<p>{fileName}</p>
|
|
248
|
-
</TooltipContent>
|
|
249
|
-
</TooltipPortal>
|
|
250
|
-
<FullscreenDialog
|
|
251
|
-
content={content}
|
|
252
|
-
name={fileName}
|
|
253
|
-
onDownload={async () => {
|
|
254
|
-
const currentFile =
|
|
255
|
-
blob ??
|
|
256
|
-
new Blob([content || url || ''], {
|
|
257
|
-
type: 'application/octet-stream',
|
|
258
|
-
});
|
|
259
|
-
const arrayBuffer = await currentFile.arrayBuffer();
|
|
260
|
-
const currentContent = new Uint8Array(arrayBuffer);
|
|
261
|
-
const savePath = await save({
|
|
262
|
-
defaultPath: fileName,
|
|
263
|
-
filters: [
|
|
264
|
-
{
|
|
265
|
-
name: 'File',
|
|
266
|
-
extensions: [getFileExt(name)],
|
|
267
|
-
},
|
|
268
|
-
],
|
|
269
|
-
});
|
|
270
|
-
if (!savePath) {
|
|
271
|
-
toast.info('File saving cancelled');
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
await fs.writeFile(savePath, currentContent, {
|
|
276
|
-
baseDir: BaseDirectory.Download,
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
toast.success(`${fileName} downloaded successfully`);
|
|
280
|
-
}}
|
|
281
|
-
open={open}
|
|
282
|
-
setOpen={setOpen}
|
|
283
|
-
type={type}
|
|
284
|
-
url={url}
|
|
285
|
-
/>
|
|
286
|
-
</Tooltip>
|
|
287
|
-
);
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
const animations = {
|
|
291
|
-
initial: { scale: 0.97, opacity: 0, y: 10 },
|
|
292
|
-
animate: {
|
|
293
|
-
scale: 1,
|
|
294
|
-
opacity: 1,
|
|
295
|
-
y: 0,
|
|
296
|
-
transition: {
|
|
297
|
-
type: 'spring',
|
|
298
|
-
stiffness: 200,
|
|
299
|
-
damping: 20,
|
|
300
|
-
mass: 0.8,
|
|
301
|
-
velocity: 1,
|
|
302
|
-
duration: 0.6,
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
exit: {
|
|
306
|
-
scale: 0.97,
|
|
307
|
-
opacity: 0,
|
|
308
|
-
y: -10,
|
|
309
|
-
transition: {
|
|
310
|
-
type: 'spring',
|
|
311
|
-
stiffness: 150,
|
|
312
|
-
damping: 15,
|
|
313
|
-
duration: 0.4,
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
export const FileList = ({ files, className }: FileListProps) => {
|
|
319
|
-
return (
|
|
320
|
-
<ul className={cn('flex flex-wrap gap-3', className)}>
|
|
321
|
-
<AnimatePresence>
|
|
322
|
-
{files?.map((file, index) => (
|
|
323
|
-
<motion.li {...animations} key={index}>
|
|
324
|
-
<FilePreview {...file} />
|
|
325
|
-
</motion.li>
|
|
326
|
-
))}
|
|
327
|
-
</AnimatePresence>
|
|
328
|
-
</ul>
|
|
329
|
-
);
|
|
330
|
-
};
|
package/primitives/chat/index.ts
DELETED