Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Redis

नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

<पी> एआई के अधिक सुलभ होने के साथ, रेप्लिकेट जैसी कंपनियों ने मशीन लर्निंग मॉडल को परियोजनाओं में सहजता से एकीकृत करना आसान बना दिया है।

<पी> इस लेख में, मैं चर्चा करने जा रहा हूं कि मैंने कैप्शनएआई कैसे बनाया, एक वेब एप्लिकेशन जो उपयोगकर्ताओं को एक छवि अपलोड करने और एआई जेनरेटेड टेक्स्ट कैप्शन प्राप्त करने की अनुमति देता है। मैंने इस प्रोजेक्ट को इस वर्सेल टेम्पलेट का उपयोग करके बनाया है। यह वीडियो भी है जिसमें बताया गया है कि यह प्रोजेक्ट कैसे बनाया गया था। <पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

हम क्या उपयोग करेंगे

  • Next.js 13 (फ्रंट-एंड और बैक-एंड)
  • अपस्टैश रेडिस (दर सीमित)
  • प्रतिकृति (मशीन लर्निंग एपीआई)
  • टेलविंड सीएसएस (स्टाइलिंग)
  • वर्सेल (तैनाती)

आपको क्या चाहिए

  • डेटाबेस बनाने के लिए एक अपस्टैश खाता
  • मशीन लर्निंग एपीआई तक पहुंचने के लिए एक रेप्लिकेट खाता

अपस्टैश रेडिस की स्थापना

<पी> एक बार जब आप एक अपस्टैश खाता बना लेते हैं और लॉग इन हो जाते हैं तो आप रेडिस टैब पर जाएंगे और एक डेटाबेस बनाएंगे।

<पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

<पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

<पी> अपना डेटाबेस बनाने के बाद, आप विवरण टैब पर जा रहे हैं। REST API अनुभाग मिलने तक नीचे स्क्रॉल करें और .env बटन चुनें। सामग्री को कॉपी करें और इसे कहीं सुरक्षित सहेजें।

<पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

रेप्लिकेट सेट अप करना

<पी> एक बार जब आप एक रेप्लिकेट अकाउंट बना लेते हैं और लॉग इन हो जाते हैं, तो आप अकाउंट टैब पर जाएंगे और एपीआई टोकन को कहीं सुरक्षित सेव करेंगे।

<पी> *ध्यान दें:आप रिप्लिकेट का निःशुल्क उपयोग कर सकते हैं, लेकिन थोड़ी देर बाद आपसे अपना क्रेडिट कार्ड दर्ज करने के लिए कहा जाएगा। आपके द्वारा उपयोग किए जाने वाले मॉडल के आधार पर कीमत भिन्न होती है। हम जिस मॉडल का उपयोग कर रहे हैं, सेल्सफोर्स/ब्लिप, उसे चलाने में लगभग $0.00042 का खर्च आता है।

<पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

प्रोजेक्ट की स्थापना

<पी> स्क्रैच से प्रोजेक्ट बनाने के बजाय, आप GitHub से रिपॉजिटरी को क्लोन कर सकते हैं।

<पी> एक बार जब आप रेपो क्लोन कर लेते हैं, तो आप एक .env फ़ाइल बनाने जा रहे हैं। .example.env फ़ाइल से जानकारी को .env फ़ाइल में कॉपी करें। एक बार जब आप इसे कॉपी कर लेंगे, तो आप उपरोक्त अनुभागों से हमारे द्वारा सहेजे गए आइटम जोड़ देंगे।

<पी> इसे कुछ इस तरह दिखना चाहिए:

// .env
 
REPLICATE_API_KEY="your_replicate_api_key_from_above"
 
// Optional, if you're doing rate limiting
UPSTASH_REDIS_REST_URL="your_upstash_redis_rest__url_from_above"
UPSTASH_REDIS_REST_TOKEN="your_upstash_redis_rest__token_from_above"
<पी> एक बार जब आप यह जानकारी शामिल कर लेते हैं, तो आप इन आदेशों को टर्मिनल में दर्ज करके प्रोजेक्ट चलाने में सक्षम हो जाएंगे:

npm install
npm run dev

भंडार संरचना

<पी> यह प्रोजेक्ट के लिए मुख्य फ़ोल्डर संरचना है. मैंने उन फ़ाइलों पर लाल घेरा बना दिया है जिन पर इस पोस्ट में आगे चर्चा की जाएगी जो छवियों को अपलोड करने, दर सीमित करने और बीएलआईपी एमएल एपीआई को लागू करने से संबंधित है। <पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

उच्च-स्तरीय डेटा प्रवाह

<पी> यह एक उच्च-स्तरीय आरेख है कि डेटा कैसे प्रवाहित हो रहा है। हमारा इनपुट, उपयोगकर्ता द्वारा अपलोड की गई छवि, अपलोड घटक के माध्यम से बीएलआईपी एमएल एपीआई प्रसंस्करण के लिए बैकएंड तक जाती है और फिर यूआई में प्रतिक्रिया पाठ प्रदर्शित करती है। <पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

रेडिस इंस्टेंस बनाएं

<पी> प्रोजेक्ट के अंदर, हम अपना अपस्टैश रेडिस क्लाइंट स्थापित करने जा रहे हैं जिसे हम आवश्यकता पड़ने पर पूरे प्रोजेक्ट में संदर्भित कर सकते हैं।

// `/utils/redis.ts`
 
import { Redis } from "@upstash/redis";
 
const redis =
 !!process.env.UPSTASH_REDIS_REST_URL && !!process.env.UPSTASH_REDIS_REST_TOKEN
 ? new Redis({
 url: process.env.UPSTASH_REDIS_REST_URL,
 token: process.env.UPSTASH_REDIS_REST_TOKEN,
 })
 : undefined;
 
export default redis;
<पी> यह कोड स्निपेट "@upstash/redis" पैकेज से Redis मॉड्यूल आयात करता है और एक नया Redis उदाहरण बनाता है। उदाहरण सशर्त रूप से दो पर्यावरण चर, UPSTASH_REDIS_REST_URL और UPSTASH_REDIS_REST_TOKEN की उपस्थिति के आधार पर बनाया गया है।

<पी> यदि दोनों चर परिभाषित किए गए हैं, तो निर्दिष्ट यूआरएल और टोकन के साथ एक नया रेडिस इंस्टेंस बनाया जाता है। यदि एक या दोनों वेरिएबल अपरिभाषित हैं, तो रेडिस वेरिएबल अपरिभाषित पर सेट है। अंत में, रेडिस वैरिएबल को एप्लिकेशन के अन्य भागों में उपयोग के लिए मॉड्यूल से निर्यात किया जाता है।

एक छवि अपलोड करना

// `/pages/captions.tsx`
 
const uploader = Uploader({
 apiKey: !!process.env.NEXT_PUBLIC_UPLOAD_API_KEY
 ? process.env.NEXT_PUBLIC_UPLOAD_API_KEY
 : "free",
});
 
const options = {
 maxFileCount: 1,
 mimeTypes: ["image/jpeg", "image/png", "image/jpg"],
 editor: { images: { crop: false } },
 styles: {
 colors: {
 primary: "#5a5cd1", // Primary buttons & links
 error: "#d23f4d", // Error messages
 shade100: "#fff", // Standard text
 shade200: "#fffe", // Secondary button text
 shade300: "#fffd", // Secondary button text (hover)
 shade400: "#fffc", // Welcome text
 shade500: "#fff9", // Modal close button
 shade600: "#fff7", // Border
 shade700: "#fff2", // Progress indicator background
 shade800: "#fff1", // File item background
 shade900: "#ffff", // Various (draggable crop buttons, etc.)
 },
 },
 onValidate: async (file: File): Promise<undefined | string> => {
 let isSafe = false;
 try {
 isSafe = await NSFWPredictor.isSafeImg(file);
 if (!isSafe) va.track("NSFW Image blocked");
 } catch (error) {
 console.error("NSFW predictor threw an error", error);
 }
 return isSafe
 ? undefined
 : "Detected a NSFW image which is not allowed. If this was a mistake, please contact me at hosna.qasmei@gmail.com";
 },
};
<पी> यह कोड अपलोडर घटक के लिए कॉन्फ़िगरेशन विकल्प सेट करता है। अपलोडर को अपलोडर() फ़ंक्शन का उपयोग करके बनाया जाता है और विकल्पों को इसमें एक ऑब्जेक्ट के रूप में पास किया जाता है।

<पी> पहला कॉन्फ़िगरेशन विकल्प apiKey है जिसका उपयोग अपलोडर सेवा के साथ प्रमाणित करने के लिए किया जाता है। ApiKey का मान इस आधार पर निर्धारित किया जाता है कि पर्यावरण चर NEXT_PUBLIC_UPLOAD_API_KEY सेट है या नहीं। यदि इसे सेट किया जाता है, तो पर्यावरण चर का मान उपयोग किया जाता है, अन्यथा, "मुक्त" मान का उपयोग किया जाता है।

<पी> विकल्प ऑब्जेक्ट में अपलोडर के लिए विभिन्न विकल्प होते हैं। इनमें शामिल हैं:

  • maxFileCount:एक बार में अपलोड की जा सकने वाली फ़ाइलों की अधिकतम संख्या 1 पर सेट करता है।
  • mimeTypes:अपलोड की गई फ़ाइलों के लिए अनुमत MIME प्रकारों को "image/jpeg", "image/png", और "image/jpg" पर सेट करता है।
  • संपादक:छवि संपादक के लिए विकल्पों को कॉन्फ़िगर करता है, जो इस मामले में फ़सल को गलत पर सेट करके अक्षम कर दिया गया है।
  • शैलियाँ:अपलोडर यूआई के लिए कस्टम शैलियों को परिभाषित करता है।
  • onValidate:एक फ़ंक्शन को परिभाषित करता है जिसे अपलोड करने से पहले प्रत्येक फ़ाइल को सत्यापित करने के लिए बुलाया जाता है। इस मामले में, फ़ंक्शन यह जांचने के लिए NSFWPredictor का उपयोग करता है कि छवि काम के लिए सुरक्षित है या नहीं। यदि छवि सुरक्षित नहीं है, तो एक त्रुटि संदेश यह दर्शाता है कि छवि की अनुमति नहीं है।
// `/pages/captions.tsx` continued
 
const Home: NextPage = () => {
 const [originalPhoto, setOriginalPhoto] = useState<string | null>(null);
 const [caption, setCaption] = useState<string | null>(null);
 const [buttonText, setButtonText] = useState("Copy");
 const [loading, setLoading] = useState<boolean>(false);
 const [error, setError] = useState<string | null>(null);
 
 const copyToClipboard = () => {
 navigator.clipboard.writeText(caption!);
 
 setButtonText("Copied!"); // set the button text to "Copied!" when text is copied
 setTimeout(() => {
 setButtonText("Copy"); // set the button text back to "Copy" after 2 seconds
 }, 2000);
 };
 
 const UploadDropZone = () => (
 <UploadDropzone
 uploader={uploader}
 options={options}
 onUpdate={(file) => {
 if (file.length !== 0) {
 setOriginalPhoto(file[0].fileUrl.replace("raw", "thumbnail"));
 generateCaption(file[0].fileUrl.replace("raw", "thumbnail"));
 }
 }}
 width="670px"
 height="250px"
 />
 );
 
 async function generateCaption( fileUrl: string )
 {
 await new Promise((resolve) => setTimeout(resolve, 500));
 setLoading(true);
 const res = await fetch("/api/generate", {
 method: "POST",
 headers: {
 "Content-Type": "application/json",
 },
 body: JSON.stringify({ imageUrl: fileUrl }),
 });
 
 let newCaption = await res.json();
 if (res.status !== 200) {
 setError(newCaption);
 } else {
 setCaption(newCaption);
 }
 setLoading(false);
 }
 
 ...
 
<पी> यूज़स्टेट हुक के साथ कई स्टेट वेरिएबल परिभाषित हैं।

  • originalPhoto एक स्ट्रिंग है जो अपलोड की गई छवि के URL का प्रतिनिधित्व करती है।
  • caption एक स्ट्रिंग है जिसमें अपलोड की गई छवि के लिए जेनरेट किया गया कैप्शन शामिल है।
  • buttonText एक स्ट्रिंग है जो कॉपी बटन पर टेक्स्ट का प्रतिनिधित्व करती है।
  • loading एक बूलियन है जो इंगित करता है कि घटक वर्तमान में डेटा प्राप्त कर रहा है या नहीं।
  • error एक स्ट्रिंग है जिसमें कैप्शन जनरेशन प्रक्रिया के दौरान कोई त्रुटि होने पर त्रुटि संदेश होता है।
<पी> घटक में CopyToClipboard नामक एक फ़ंक्शन होता है, जो क्लिपबोर्ड पर कैप्शन वेरिएबल को कॉपी करने के लिए navigator.clipboard.writeText विधि का उपयोग करता है। जब टेक्स्ट कॉपी किया जाता है, तो यह बटनटेक्स्ट वेरिएबल को "कॉपी किया गया!" में बदल देता है। "कॉपी" पर रीसेट करने से पहले दो सेकंड के लिए।

<पी> अपलोडड्रॉपज़ोन नामक एक उप-घटक भी है जो निर्दिष्ट अपलोडर और विकल्पों के साथ अपलोडड्रॉपज़ोन घटक का एक उदाहरण प्रस्तुत करता है। ऑनअपडेट कॉलबैक का उपयोग अपलोड की गई छवि के यूआरएल और जेनरेट किए गए कैप्शन के साथ मूल फोटो और कैप्शन वेरिएबल्स को अपडेट करने के लिए किया जाता है।

<पी> अंत में, generateCaption नामक एक एसिंक्रोनस फ़ंक्शन है जो एक फ़ाइलयूआरएल पैरामीटर लेता है, जो अपलोड की गई छवि का यूआरएल है। यह POST अनुरोध के साथ /api/generate एंडपॉइंट को कॉल करने के लिए फ़ेच का उपयोग करता है और JSON पेलोड के रूप में फ़ाइल यूआरएल में पास करता है। फिर प्रतिक्रिया को JSON के रूप में पार्स किया जाता है और या तो कैप्शन या त्रुटि वैरिएबल सेट करता है, यह इस पर निर्भर करता है कि प्रतिक्रिया सफल थी या नहीं। लोडिंग वैरिएबल को यह इंगित करने के लिए भी अपडेट किया जाता है कि अनुरोध अभी भी प्रगति पर है या नहीं। फ़ंक्शन में एपीआई दर सीमा से बचने के लिए सेटटाइमआउट फ़ंक्शन का उपयोग करके 500ms की देरी भी शामिल है।

दर सीमित

// `/pages/api/generate.ts`
 
import redis from "../../utils/redis";
import requestIp from "request-ip";
 
import { Ratelimit } from "@upstash/ratelimit";
import type { NextApiRequest, NextApiResponse } from "next";
 
type Data = string;
interface ExtendedNextApiRequest extends NextApiRequest {
 body: {
 imageUrl: string;
 };
}
 
// Create a new ratelimiter, that allows 3 requests every 15 minutes
const ratelimit = redis
 ? new Ratelimit({
 redis: redis,
 limiter: Ratelimit.fixedWindow(5, "1440 m"),
 analytics: true,
 })
 : undefined;
 
 ...
<पी> यह कोड Next.js में एक एपीआई एंडपॉइंट बनाने के लिए आवश्यक मॉड्यूल और प्रकारों को आयात करता है, साथ ही एक अपस्टैश रेडिस डेटाबेस क्लाइंट और "@upstash/ratelimit" नामक एरेट लिमिटर लाइब्रेरी भी आयात करता है।

<पी> रेटलिमिट स्थिरांक रेटलिमिट वर्ग का एक नया उदाहरण बनाता है, जो एक निश्चित-विंडो रेटलिमिटर बनाता है जो हर 1440 मिनट (24 घंटे) में 5 अनुरोधों की अनुमति देता है। रेडिस प्रॉपर्टी को एप्लिकेशन के कई उदाहरणों में दर सीमित करने में सक्षम करने के लिए रेटलिमिट कंस्ट्रक्टर के पैरामीटर के रूप में पारित किया जाता है। यदि रेडिस अपरिभाषित है (उदाहरण के लिए, यदि रेडिस डेटाबेस कॉन्फ़िगर नहीं किया गया है), तो रेटलिमिट भी अपरिभाषित पर सेट है। इसका मतलब यह है कि यदि रेडिस उपलब्ध नहीं है तो दर सीमा लागू नहीं की जाएगी।

// `/pages/api/generate.ts` continued
 
export default async function handler(
 req: ExtendedNextApiRequest,
 res: NextApiResponse<Data>
) {
 // Rate Limiter Code
 if (ratelimit) {
 const identifier = requestIp.getClientIp(req);
 const result = await ratelimit.limit(identifier!);
 res.setHeader("X-RateLimit-Limit", result.limit);
 res.setHeader("X-RateLimit-Remaining", result.remaining);
 
 if (!result.success) {
 res
 .status(429)
 .json("Too many uploads in 1 day. Please try again after 24 hours.");
 return;
 }
 }
 
 ...
<पी> यह कोड ब्लॉक एक एपीआई हैंडलर फ़ंक्शन का हिस्सा है जो क्लाइंट द्वारा एपीआई को किए जाने वाले अनुरोधों की दर को सीमित करता है। यह पहले जांचता है कि क्या दर सीमक उदाहरण उपलब्ध है और यदि हां, तो अनुरोध-आईपी पैकेज का उपयोग करके ग्राहक का आईपी पता निकालता है और इसे रेटलिमिट.लिमिट विधि में भेजता है। यह विधि एक ऑब्जेक्ट लौटाती है जिसमें निर्दिष्ट समय सीमा के भीतर शेष अनुरोधों की संख्या और अनुरोध सफल था या नहीं।

<पी> यदि अनुरोध सफल होता है, तो X-RateLimit-Limitand X-RateLimit-Remaining हेडर प्रतिक्रिया में सेट किए जाते हैं। यदि अनुरोध सीमा पार हो गई है, तो प्रतिक्रिया में एक 429 स्टेटस कोड और एक त्रुटि संदेश भेजा जाता है, और आगे के निष्पादन को रोकने के लिए फ़ंक्शन जल्दी वापस आ जाता है।

बीएलआईपी एमएल एपीआई

// `/pages/api/generate.ts` continued
 
 const imageUrl = req.body.imageUrl;
 let startResponse = await fetch("https://api.replicate.com/v1/predictions", {
 method: "POST",
 headers: {
 "Content-Type": "application/json",
 Authorization: "Token " + process.env.REPLICATE_API_KEY,
 },
 body: JSON.stringify({
 version:
 "2e1dddc8621f72155f24cf2e0adbde548458d3cab9f00c0139eea840d0ac4746",
 input: {
 image: imageUrl,
 task: "image_captioning",
 },
 }),
 });
 
 ...
 
<पी> कोड का यह हिस्सा अनुरोध निकाय से इमेज यूआरएल लेता है और इमेज_कैप्शनिंग कार्य का उपयोग करके छवि कैप्शन प्राप्त करने के लिए "https://api.repliate.com/v1/predictions" एंडपॉइंट पर एक POST अनुरोध भेजता है। अनुरोध में प्राधिकरण हेडर शामिल है जिसमें प्रमाणीकरण के लिए रेप्लिकेट एपीआई कुंजी शामिल है, और सामग्री-प्रकार हेडर "एप्लिकेशन/जेसन" पर सेट है। एपीआई से प्रतिक्रिया को JSON के रूप में पार्स किया जाता है, और एंडपॉइंट यूआरएल को jsonStartResponse ऑब्जेक्ट से निकाला जाता है।

<पी> आप जिस मॉडल का उपयोग करना चाहते हैं उसे चुनकर आप मॉडल की संस्करण संख्या पा सकते हैं।

<पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

<पी> एपीआई टैब चुनें.

<पी> नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

<पी> लाल रंग में उल्लिखित संस्करण संख्या दिखाई देने तक नीचे स्क्रॉल करें। और नीले रंग में उल्लिखित इनपुट पैरामीटर हैं जिनका आप उपयोग कर सकते हैं। नेक्स्ट.जेएस, रेप्लिकेट और रेडिस के साथ एक एआई-पावर्ड इमेज कैप्शनिंग ऐप बनाएं

// `/pages/api/generate.ts` continued
 
 let jsonStartResponse = await startResponse.json();
 let endpointUrl = jsonStartResponse.urls.get;
 
 // GET request to get the status of the image restoration process & return the result when it's ready
 let caption: string | null = null;
 while (!caption) {
 // Loop in 1s intervals until the alt text is ready
 console.log("polling for result...");
 let finalResponse = await fetch(endpointUrl, {
 method: "GET",
 headers: {
 "Content-Type": "application/json",
 Authorization: "Token " + process.env.REPLICATE_API_KEY,
 },
 });
 let jsonFinalResponse = await finalResponse.json();
 
 if (jsonFinalResponse.status === "succeeded") {
 caption = jsonFinalResponse.output;
 } else if (jsonFinalResponse.status === "failed") {
 break;
 } else {
 await new Promise((resolve) => setTimeout(resolve, 1000));
 }
 }
 res.status(200).json(caption ? caption : "Failed to generate caption");
}
<पी> इसके बाद, कैप्शन तैयार होने तक 1-सेकंड के अंतराल में एंडपॉइंट यूआरएल को पोल करने के लिए थोड़ी देर के लूप का उपयोग किया जाता है। लूप समान प्राधिकरण और सामग्री-प्रकार हेडर के साथ एंडपॉइंट यूआरएल को एक GET अनुरोध भेजता है, और प्रतिक्रिया को JSON के रूप में भी पार्स किया जाता है। यदि jsonFinalResponse ऑब्जेक्ट में स्थिति "सफल" है, तो आउटपुट प्रॉपर्टी से कैप्शन निकाला जाता है। यदि स्थिति "विफल" है, तो लूप समाप्त हो जाता है। यदि स्थिति न तो "सफल" और न ही "असफल" है, तो लूप दोबारा मतदान करने से पहले सेटटाइमआउट विधि का उपयोग करके 1 सेकंड तक प्रतीक्षा करता है।

<पी> अंत में, यदि कैप्शन शून्य नहीं है तो उसे 200 के स्टेटस कोड के साथ JSON प्रतिक्रिया के रूप में लौटाया जाता है, अन्यथा, "कैप्शन जनरेट करने में विफल" संदेश के साथ एक प्रतिक्रिया 200 के स्टेटस कोड के साथ लौटाई जाती है।

निष्कर्ष

<पी> अंत में, इस परियोजना ने छवि अपलोडिंग, दर सीमित करने और मशीन लर्निंग एपीआई को एकीकृत करने में मूल्यवान अनुभव प्रदान किया है। इस परियोजना को सफलतापूर्वक पूरा करने से, हमने इन प्रौद्योगिकियों की बेहतर समझ प्राप्त की है और भविष्य में और अधिक उन्नत परियोजनाएं बनाने के लिए उनका उपयोग कैसे किया जा सकता है।


  1. Direnv - Linux में प्रोजेक्ट-विशिष्ट पर्यावरण चर प्रबंधित करें Direnv - Linux में प्रोजेक्ट-विशिष्ट पर्यावरण चर प्रबंधित करें

    डायरेनव लिनक्स और मैकओएस जैसे यूनिक्स ऑपरेटिंग सिस्टम पर आपके शेल के लिए एक निफ्टी ओपन-सोर्स एक्सटेंशन है। इसे एकल स्थिर निष्पादन योग्य में संकलित किया गया है और यह bash . जैसे शेल का समर्थन करता है , zsh , टीसीएसएच , और मछली। direnv . का मुख्य उद्देश्य ~/.profile . को अव्यवस्थित किए बिना परियोजना

  1. IOS में टाइप करते समय टेक्स्ट बॉक्स में वर्णों की संख्या कैसे गिनें? IOS में टाइप करते समय टेक्स्ट बॉक्स में वर्णों की संख्या कैसे गिनें?

    IOS डेवलपर के रूप में किसी को पता होना चाहिए कि टेक्स्ट फ़ील्ड और उसके संचालन में हेरफेर कैसे किया जाता है, Apple ने पहले ही UITextFieldDelegate प्रोटोकॉल प्रदान किया है। इसके बारे में अधिक पढ़ने के लिए https://developer.apple.com/documentation/uikit/uitextfielddelegate आपने देखा होगा कि जहां फॉर्

  1. एंड्रॉइड निर्धारित ऐप पहली बार शुरू होता है या प्रोग्रामेटिक रूप से नहीं? एंड्रॉइड निर्धारित ऐप पहली बार शुरू होता है या प्रोग्रामेटिक रूप से नहीं?

    यह उदाहरण दर्शाता है कि Android निर्धारित ऐप पहली बार शुरू होता है या प्रोग्रामेटिक रूप से नहीं। चरण 1 - एंड्रॉइड स्टूडियो में एक नया प्रोजेक्ट बनाएं, फाइल ⇒ न्यू प्रोजेक्ट पर जाएं और एक नया प्रोजेक्ट बनाने के लिए सभी आवश्यक विवरण भरें। चरण 2 - निम्न कोड को res/layout/activity_main.xml में जोड़ें।