@jxrstudios/jxr 1.0.9 → 1.0.11

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 (89) hide show
  1. package/bin/jxr.js +6 -0
  2. package/dist/index.d.ts +43 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +57 -2
  5. package/dist/jxr-server-manager.d.ts.map +1 -1
  6. package/package.json +1 -1
  7. package/src/jxr-server-manager.ts +57 -1
  8. package/zzz_react_template/App.tsx +43 -156
  9. package/zzz_react_template/components/ErrorBoundary.tsx +62 -0
  10. package/zzz_react_template/components/ManusDialog.tsx +85 -0
  11. package/zzz_react_template/components/Map.tsx +155 -0
  12. package/zzz_react_template/components/jxr/CodeEditor.tsx +313 -0
  13. package/zzz_react_template/components/jxr/FileExplorer.tsx +230 -0
  14. package/zzz_react_template/components/jxr/IDEShell.tsx +159 -0
  15. package/zzz_react_template/components/jxr/LandingPage.tsx +414 -0
  16. package/zzz_react_template/components/jxr/LivePreview.tsx +169 -0
  17. package/zzz_react_template/components/jxr/PerformanceDashboard.tsx +379 -0
  18. package/zzz_react_template/components/jxr/TopBar.tsx +149 -0
  19. package/zzz_react_template/components/ui/accordion.tsx +64 -0
  20. package/zzz_react_template/components/ui/alert-dialog.tsx +155 -0
  21. package/zzz_react_template/components/ui/alert.tsx +66 -0
  22. package/zzz_react_template/components/ui/aspect-ratio.tsx +9 -0
  23. package/zzz_react_template/components/ui/avatar.tsx +51 -0
  24. package/zzz_react_template/components/ui/badge.tsx +46 -0
  25. package/zzz_react_template/components/ui/breadcrumb.tsx +109 -0
  26. package/zzz_react_template/components/ui/button-group.tsx +83 -0
  27. package/zzz_react_template/components/ui/button.tsx +60 -0
  28. package/zzz_react_template/components/ui/calendar.tsx +211 -0
  29. package/zzz_react_template/components/ui/card.tsx +92 -0
  30. package/zzz_react_template/components/ui/carousel.tsx +239 -0
  31. package/zzz_react_template/components/ui/chart.tsx +355 -0
  32. package/zzz_react_template/components/ui/checkbox.tsx +30 -0
  33. package/zzz_react_template/components/ui/collapsible.tsx +31 -0
  34. package/zzz_react_template/components/ui/command.tsx +184 -0
  35. package/zzz_react_template/components/ui/context-menu.tsx +250 -0
  36. package/zzz_react_template/components/ui/dialog.tsx +209 -0
  37. package/zzz_react_template/components/ui/drawer.tsx +133 -0
  38. package/zzz_react_template/components/ui/dropdown-menu.tsx +255 -0
  39. package/zzz_react_template/components/ui/empty.tsx +104 -0
  40. package/zzz_react_template/components/ui/field.tsx +242 -0
  41. package/zzz_react_template/components/ui/form.tsx +168 -0
  42. package/zzz_react_template/components/ui/hover-card.tsx +42 -0
  43. package/zzz_react_template/components/ui/input-group.tsx +168 -0
  44. package/zzz_react_template/components/ui/input-otp.tsx +75 -0
  45. package/zzz_react_template/components/ui/input.tsx +70 -0
  46. package/zzz_react_template/components/ui/item.tsx +193 -0
  47. package/zzz_react_template/components/ui/kbd.tsx +28 -0
  48. package/zzz_react_template/components/ui/label.tsx +22 -0
  49. package/zzz_react_template/components/ui/menubar.tsx +274 -0
  50. package/zzz_react_template/components/ui/navigation-menu.tsx +168 -0
  51. package/zzz_react_template/components/ui/pagination.tsx +127 -0
  52. package/zzz_react_template/components/ui/popover.tsx +46 -0
  53. package/zzz_react_template/components/ui/progress.tsx +29 -0
  54. package/zzz_react_template/components/ui/radio-group.tsx +43 -0
  55. package/zzz_react_template/components/ui/resizable.tsx +54 -0
  56. package/zzz_react_template/components/ui/scroll-area.tsx +56 -0
  57. package/zzz_react_template/components/ui/select.tsx +185 -0
  58. package/zzz_react_template/components/ui/separator.tsx +26 -0
  59. package/zzz_react_template/components/ui/sheet.tsx +139 -0
  60. package/zzz_react_template/components/ui/sidebar.tsx +734 -0
  61. package/zzz_react_template/components/ui/skeleton.tsx +13 -0
  62. package/zzz_react_template/components/ui/slider.tsx +61 -0
  63. package/zzz_react_template/components/ui/sonner.tsx +23 -0
  64. package/zzz_react_template/components/ui/spinner.tsx +16 -0
  65. package/zzz_react_template/components/ui/switch.tsx +29 -0
  66. package/zzz_react_template/components/ui/table.tsx +114 -0
  67. package/zzz_react_template/components/ui/tabs.tsx +64 -0
  68. package/zzz_react_template/components/ui/textarea.tsx +67 -0
  69. package/zzz_react_template/components/ui/toggle-group.tsx +73 -0
  70. package/zzz_react_template/components/ui/toggle.tsx +45 -0
  71. package/zzz_react_template/components/ui/tooltip.tsx +59 -0
  72. package/zzz_react_template/const.ts +17 -0
  73. package/zzz_react_template/contexts/JXRContext.tsx +264 -0
  74. package/zzz_react_template/contexts/ThemeContext.tsx +64 -0
  75. package/zzz_react_template/hooks/useComposition.ts +81 -0
  76. package/zzz_react_template/hooks/useMobile.tsx +21 -0
  77. package/zzz_react_template/hooks/usePersistFn.ts +20 -0
  78. package/zzz_react_template/index.css +518 -11
  79. package/zzz_react_template/lib/jxr-runtime/index.ts +201 -0
  80. package/zzz_react_template/lib/jxr-runtime/module-resolver.ts +520 -0
  81. package/zzz_react_template/lib/jxr-runtime/moq-transport.ts +267 -0
  82. package/zzz_react_template/lib/jxr-runtime/web-crypto.ts +279 -0
  83. package/zzz_react_template/lib/jxr-runtime/worker-pool.ts +321 -0
  84. package/zzz_react_template/lib/utils.ts +6 -0
  85. package/zzz_react_template/main.tsx +4 -9
  86. package/zzz_react_template/pages/Docs.tsx +955 -0
  87. package/zzz_react_template/pages/Home.tsx +1080 -0
  88. package/zzz_react_template/pages/NotFound.tsx +105 -0
  89. package/zzz_react_template/tsconfig.json +24 -0
@@ -0,0 +1,168 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+ import { Slot } from "@radix-ui/react-slot";
6
+ import {
7
+ Controller,
8
+ FormProvider,
9
+ useFormContext,
10
+ useFormState,
11
+ type ControllerProps,
12
+ type FieldPath,
13
+ type FieldValues,
14
+ } from "react-hook-form";
15
+
16
+ import { cn } from "@/lib/utils";
17
+ import { Label } from "@/components/ui/label";
18
+
19
+ const Form = FormProvider;
20
+
21
+ type FormFieldContextValue<
22
+ TFieldValues extends FieldValues = FieldValues,
23
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
24
+ > = {
25
+ name: TName;
26
+ };
27
+
28
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
29
+ {} as FormFieldContextValue
30
+ );
31
+
32
+ const FormField = <
33
+ TFieldValues extends FieldValues = FieldValues,
34
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
35
+ >({
36
+ ...props
37
+ }: ControllerProps<TFieldValues, TName>) => {
38
+ return (
39
+ <FormFieldContext.Provider value={{ name: props.name }}>
40
+ <Controller {...props} />
41
+ </FormFieldContext.Provider>
42
+ );
43
+ };
44
+
45
+ const useFormField = () => {
46
+ const fieldContext = React.useContext(FormFieldContext);
47
+ const itemContext = React.useContext(FormItemContext);
48
+ const { getFieldState } = useFormContext();
49
+ const formState = useFormState({ name: fieldContext.name });
50
+ const fieldState = getFieldState(fieldContext.name, formState);
51
+
52
+ if (!fieldContext) {
53
+ throw new Error("useFormField should be used within <FormField>");
54
+ }
55
+
56
+ const { id } = itemContext;
57
+
58
+ return {
59
+ id,
60
+ name: fieldContext.name,
61
+ formItemId: `${id}-form-item`,
62
+ formDescriptionId: `${id}-form-item-description`,
63
+ formMessageId: `${id}-form-item-message`,
64
+ ...fieldState,
65
+ };
66
+ };
67
+
68
+ type FormItemContextValue = {
69
+ id: string;
70
+ };
71
+
72
+ const FormItemContext = React.createContext<FormItemContextValue>(
73
+ {} as FormItemContextValue
74
+ );
75
+
76
+ function FormItem({ className, ...props }: React.ComponentProps<"div">) {
77
+ const id = React.useId();
78
+
79
+ return (
80
+ <FormItemContext.Provider value={{ id }}>
81
+ <div
82
+ data-slot="form-item"
83
+ className={cn("grid gap-2", className)}
84
+ {...props}
85
+ />
86
+ </FormItemContext.Provider>
87
+ );
88
+ }
89
+
90
+ function FormLabel({
91
+ className,
92
+ ...props
93
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
94
+ const { error, formItemId } = useFormField();
95
+
96
+ return (
97
+ <Label
98
+ data-slot="form-label"
99
+ data-error={!!error}
100
+ className={cn("data-[error=true]:text-destructive", className)}
101
+ htmlFor={formItemId}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
108
+ const { error, formItemId, formDescriptionId, formMessageId } =
109
+ useFormField();
110
+
111
+ return (
112
+ <Slot
113
+ data-slot="form-control"
114
+ id={formItemId}
115
+ aria-describedby={
116
+ !error
117
+ ? `${formDescriptionId}`
118
+ : `${formDescriptionId} ${formMessageId}`
119
+ }
120
+ aria-invalid={!!error}
121
+ {...props}
122
+ />
123
+ );
124
+ }
125
+
126
+ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
127
+ const { formDescriptionId } = useFormField();
128
+
129
+ return (
130
+ <p
131
+ data-slot="form-description"
132
+ id={formDescriptionId}
133
+ className={cn("text-muted-foreground text-sm", className)}
134
+ {...props}
135
+ />
136
+ );
137
+ }
138
+
139
+ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
140
+ const { error, formMessageId } = useFormField();
141
+ const body = error ? String(error?.message ?? "") : props.children;
142
+
143
+ if (!body) {
144
+ return null;
145
+ }
146
+
147
+ return (
148
+ <p
149
+ data-slot="form-message"
150
+ id={formMessageId}
151
+ className={cn("text-destructive text-sm", className)}
152
+ {...props}
153
+ >
154
+ {body}
155
+ </p>
156
+ );
157
+ }
158
+
159
+ export {
160
+ useFormField,
161
+ Form,
162
+ FormItem,
163
+ FormLabel,
164
+ FormControl,
165
+ FormDescription,
166
+ FormMessage,
167
+ FormField,
168
+ };
@@ -0,0 +1,42 @@
1
+ import * as React from "react";
2
+ import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ function HoverCard({
7
+ ...props
8
+ }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
9
+ return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
10
+ }
11
+
12
+ function HoverCardTrigger({
13
+ ...props
14
+ }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
15
+ return (
16
+ <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
17
+ );
18
+ }
19
+
20
+ function HoverCardContent({
21
+ className,
22
+ align = "center",
23
+ sideOffset = 4,
24
+ ...props
25
+ }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
26
+ return (
27
+ <HoverCardPrimitive.Portal data-slot="hover-card-portal">
28
+ <HoverCardPrimitive.Content
29
+ data-slot="hover-card-content"
30
+ align={align}
31
+ sideOffset={sideOffset}
32
+ className={cn(
33
+ "bg-popover text-popover-foreground 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 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ </HoverCardPrimitive.Portal>
39
+ );
40
+ }
41
+
42
+ export { HoverCard, HoverCardTrigger, HoverCardContent };
@@ -0,0 +1,168 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+
4
+ import { cn } from "@/lib/utils";
5
+ import { Button } from "@/components/ui/button";
6
+ import { Input } from "@/components/ui/input";
7
+ import { Textarea } from "@/components/ui/textarea";
8
+
9
+ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
10
+ return (
11
+ <div
12
+ data-slot="input-group"
13
+ role="group"
14
+ className={cn(
15
+ "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
16
+ "h-9 min-w-0 has-[>textarea]:h-auto",
17
+
18
+ // Variants based on alignment.
19
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
20
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
21
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
22
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
23
+
24
+ // Focus state.
25
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
26
+
27
+ // Error state.
28
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
29
+
30
+ className
31
+ )}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ const inputGroupAddonVariants = cva(
38
+ "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
39
+ {
40
+ variants: {
41
+ align: {
42
+ "inline-start":
43
+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
44
+ "inline-end":
45
+ "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
46
+ "block-start":
47
+ "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
48
+ "block-end":
49
+ "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
50
+ },
51
+ },
52
+ defaultVariants: {
53
+ align: "inline-start",
54
+ },
55
+ }
56
+ );
57
+
58
+ function InputGroupAddon({
59
+ className,
60
+ align = "inline-start",
61
+ ...props
62
+ }: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
63
+ return (
64
+ <div
65
+ role="group"
66
+ data-slot="input-group-addon"
67
+ data-align={align}
68
+ className={cn(inputGroupAddonVariants({ align }), className)}
69
+ onClick={e => {
70
+ if ((e.target as HTMLElement).closest("button")) {
71
+ return;
72
+ }
73
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
74
+ }}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ const inputGroupButtonVariants = cva(
81
+ "text-sm shadow-none flex gap-2 items-center",
82
+ {
83
+ variants: {
84
+ size: {
85
+ xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
86
+ sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
87
+ "icon-xs":
88
+ "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
89
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
90
+ },
91
+ },
92
+ defaultVariants: {
93
+ size: "xs",
94
+ },
95
+ }
96
+ );
97
+
98
+ function InputGroupButton({
99
+ className,
100
+ type = "button",
101
+ variant = "ghost",
102
+ size = "xs",
103
+ ...props
104
+ }: Omit<React.ComponentProps<typeof Button>, "size"> &
105
+ VariantProps<typeof inputGroupButtonVariants>) {
106
+ return (
107
+ <Button
108
+ type={type}
109
+ data-size={size}
110
+ variant={variant}
111
+ className={cn(inputGroupButtonVariants({ size }), className)}
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
118
+ return (
119
+ <span
120
+ className={cn(
121
+ "text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
122
+ className
123
+ )}
124
+ {...props}
125
+ />
126
+ );
127
+ }
128
+
129
+ function InputGroupInput({
130
+ className,
131
+ ...props
132
+ }: React.ComponentProps<"input">) {
133
+ return (
134
+ <Input
135
+ data-slot="input-group-control"
136
+ className={cn(
137
+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
138
+ className
139
+ )}
140
+ {...props}
141
+ />
142
+ );
143
+ }
144
+
145
+ function InputGroupTextarea({
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<"textarea">) {
149
+ return (
150
+ <Textarea
151
+ data-slot="input-group-control"
152
+ className={cn(
153
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
154
+ className
155
+ )}
156
+ {...props}
157
+ />
158
+ );
159
+ }
160
+
161
+ export {
162
+ InputGroup,
163
+ InputGroupAddon,
164
+ InputGroupButton,
165
+ InputGroupText,
166
+ InputGroupInput,
167
+ InputGroupTextarea,
168
+ };
@@ -0,0 +1,75 @@
1
+ import * as React from "react";
2
+ import { OTPInput, OTPInputContext } from "input-otp";
3
+ import { MinusIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function InputOTP({
8
+ className,
9
+ containerClassName,
10
+ ...props
11
+ }: React.ComponentProps<typeof OTPInput> & {
12
+ containerClassName?: string;
13
+ }) {
14
+ return (
15
+ <OTPInput
16
+ data-slot="input-otp"
17
+ containerClassName={cn(
18
+ "flex items-center gap-2 has-disabled:opacity-50",
19
+ containerClassName
20
+ )}
21
+ className={cn("disabled:cursor-not-allowed", className)}
22
+ {...props}
23
+ />
24
+ );
25
+ }
26
+
27
+ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
28
+ return (
29
+ <div
30
+ data-slot="input-otp-group"
31
+ className={cn("flex items-center", className)}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ function InputOTPSlot({
38
+ index,
39
+ className,
40
+ ...props
41
+ }: React.ComponentProps<"div"> & {
42
+ index: number;
43
+ }) {
44
+ const inputOTPContext = React.useContext(OTPInputContext);
45
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
46
+
47
+ return (
48
+ <div
49
+ data-slot="input-otp-slot"
50
+ data-active={isActive}
51
+ className={cn(
52
+ "data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
53
+ className
54
+ )}
55
+ {...props}
56
+ >
57
+ {char}
58
+ {hasFakeCaret && (
59
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
60
+ <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
61
+ </div>
62
+ )}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div data-slot="input-otp-separator" role="separator" {...props}>
70
+ <MinusIcon />
71
+ </div>
72
+ );
73
+ }
74
+
75
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
@@ -0,0 +1,70 @@
1
+ import { useDialogComposition } from "@/components/ui/dialog";
2
+ import { useComposition } from "@/hooks/useComposition";
3
+ import { cn } from "@/lib/utils";
4
+ import * as React from "react";
5
+
6
+ function Input({
7
+ className,
8
+ type,
9
+ onKeyDown,
10
+ onCompositionStart,
11
+ onCompositionEnd,
12
+ ...props
13
+ }: React.ComponentProps<"input">) {
14
+ // Get dialog composition context if available (will be no-op if not inside Dialog)
15
+ const dialogComposition = useDialogComposition();
16
+
17
+ // Add composition event handlers to support input method editor (IME) for CJK languages.
18
+ const {
19
+ onCompositionStart: handleCompositionStart,
20
+ onCompositionEnd: handleCompositionEnd,
21
+ onKeyDown: handleKeyDown,
22
+ } = useComposition<HTMLInputElement>({
23
+ onKeyDown: (e) => {
24
+ // Check if this is an Enter key that should be blocked
25
+ const isComposing = (e.nativeEvent as any).isComposing || dialogComposition.justEndedComposing();
26
+
27
+ // If Enter key is pressed while composing or just after composition ended,
28
+ // don't call the user's onKeyDown (this blocks the business logic)
29
+ if (e.key === "Enter" && isComposing) {
30
+ return;
31
+ }
32
+
33
+ // Otherwise, call the user's onKeyDown
34
+ onKeyDown?.(e);
35
+ },
36
+ onCompositionStart: e => {
37
+ dialogComposition.setComposing(true);
38
+ onCompositionStart?.(e);
39
+ },
40
+ onCompositionEnd: e => {
41
+ // Mark that composition just ended - this helps handle the Enter key that confirms input
42
+ dialogComposition.markCompositionEnd();
43
+ // Delay setting composing to false to handle Safari's event order
44
+ // In Safari, compositionEnd fires before the ESC keydown event
45
+ setTimeout(() => {
46
+ dialogComposition.setComposing(false);
47
+ }, 100);
48
+ onCompositionEnd?.(e);
49
+ },
50
+ });
51
+
52
+ return (
53
+ <input
54
+ type={type}
55
+ data-slot="input"
56
+ className={cn(
57
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
58
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
59
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
60
+ className
61
+ )}
62
+ onCompositionStart={handleCompositionStart}
63
+ onCompositionEnd={handleCompositionEnd}
64
+ onKeyDown={handleKeyDown}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ export { Input };