1
1
import { useState } from "react" ;
2
2
import { Input } from "@/components/ui/input" ;
3
3
import { Button } from "@/components/ui/button" ;
4
- import { LinkIcon , ArrowRightIcon , AlertCircleIcon } from "lucide-react" ;
4
+ import { LinkIcon , ArrowRightIcon , AlertCircleIcon , UploadIcon } from "lucide-react" ;
5
5
6
6
interface UrlInputFormProps {
7
7
onSubmit : ( url : string ) => void ;
@@ -15,15 +15,26 @@ const UrlInputForm = ({ onSubmit }: UrlInputFormProps) => {
15
15
// Validate if string is a valid URL
16
16
const isValidUrl = ( urlString : string ) : boolean => {
17
17
try {
18
- new URL ( urlString ) ;
19
- return true ;
18
+ // Handle blob URLs (from file input)
19
+ if ( urlString . startsWith ( 'blob:' ) ) {
20
+ return true ;
21
+ }
22
+
23
+ const url = new URL ( urlString ) ;
24
+ // Allow http, https, and file protocols
25
+ return url . protocol === 'http:' || url . protocol === 'https:' || url . protocol === 'file:' ;
20
26
} catch ( err ) {
21
27
return false ;
22
28
}
23
29
} ;
24
30
25
31
// Validate if URL points to a YAML file
26
32
const isYamlFile = ( urlString : string ) : boolean => {
33
+ // For blob URLs, we trust the file input's accept attribute
34
+ if ( urlString . startsWith ( 'blob:' ) ) {
35
+ return true ;
36
+ }
37
+
27
38
const lowerUrl = urlString . toLowerCase ( ) ;
28
39
return lowerUrl . endsWith ( '.yml' ) || lowerUrl . endsWith ( '.yaml' ) ;
29
40
} ;
@@ -34,7 +45,7 @@ const UrlInputForm = ({ onSubmit }: UrlInputFormProps) => {
34
45
}
35
46
36
47
if ( ! isValidUrl ( urlString ) ) {
37
- return "Please enter a valid URL" ;
48
+ return "Please enter a valid URL (http, https, or file://) " ;
38
49
}
39
50
40
51
if ( ! isYamlFile ( urlString ) ) {
@@ -44,6 +55,19 @@ const UrlInputForm = ({ onSubmit }: UrlInputFormProps) => {
44
55
return null ;
45
56
} ;
46
57
58
+ const handleFileChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
59
+ const file = e . target . files ?. [ 0 ] ;
60
+ if ( file ) {
61
+ // Revoke previous blob URL if it exists
62
+ if ( url . startsWith ( 'blob:' ) ) {
63
+ URL . revokeObjectURL ( url ) ;
64
+ }
65
+ const fileUrl = URL . createObjectURL ( file ) ;
66
+ setUrl ( fileUrl ) ;
67
+ setError ( null ) ;
68
+ }
69
+ } ;
70
+
47
71
const handleSubmit = ( e : React . FormEvent < HTMLFormElement > ) => {
48
72
e . preventDefault ( ) ;
49
73
@@ -59,6 +83,12 @@ const UrlInputForm = ({ onSubmit }: UrlInputFormProps) => {
59
83
60
84
const handleUrlChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
61
85
const newUrl = e . target . value ;
86
+
87
+ // Revoke previous blob URL if it exists and user is changing to a different URL
88
+ if ( url . startsWith ( 'blob:' ) && newUrl !== url ) {
89
+ URL . revokeObjectURL ( url ) ;
90
+ }
91
+
62
92
setUrl ( newUrl ) ;
63
93
64
94
// Clear error when user starts typing again
@@ -76,7 +106,8 @@ const UrlInputForm = ({ onSubmit }: UrlInputFormProps) => {
76
106
< div className = "w-full" >
77
107
< form onSubmit = { handleSubmit } className = "space-y-4" >
78
108
< div className = "space-y-2" >
79
- < div className = "relative flex items-center" >
109
+ < div className = " flex items-center" >
110
+ < div className = "relative flex-1" >
80
111
< div className = "absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none" >
81
112
< LinkIcon className = "h-5 w-5 text-muted-foreground" />
82
113
</ div >
@@ -85,11 +116,21 @@ const UrlInputForm = ({ onSubmit }: UrlInputFormProps) => {
85
116
value = { url }
86
117
onChange = { handleUrlChange }
87
118
placeholder = "Enter YAML file URL"
88
- className = { `pl-10 pr-4 py-6 text-base rounded-l-md rounded-r-none border-r-0 ${
119
+ className = { `pl-10 pr-4 py-6 text-base rounded-l-md rounded-r-none border-r-0 bg-white ${
89
120
error ? 'border-red-500 focus-visible:ring-red-500' : ''
90
121
} `}
91
122
required
92
123
/>
124
+ < label className = "h-full aspect-square flex items-center justify-center cursor-pointer bg-white absolute right-0 top-0 border border-input border-l-0 border-r-0" >
125
+ < input
126
+ type = "file"
127
+ accept = ".yml,.yaml"
128
+ onChange = { handleFileChange }
129
+ className = "hidden"
130
+ />
131
+ < UploadIcon className = "h-4 w-4 text-muted-foreground" />
132
+ </ label >
133
+ </ div >
93
134
< Button
94
135
type = "submit"
95
136
className = "py-6 px-6 rounded-l-none text-base font-medium flex items-center justify-center gap-2 transition-all h-[50px]"
0 commit comments