ToCode
Відкрити в Telegram
1 417
Підписники
-224 години
-37 днів
-630 день
Архів дописів
1 417
בדוגמאות של ריאקט באותו עמוד הם מראים איך להשתמש ב useDeferredValue גם לעבודה עם בקשות רשת, אבל פה צריך להדגיש שמנגנון זה לא מווסת את כמות הקריאות לשרת אלא רק את עדכוני ממשק המשתמש ולכן לא מספיק לבדו כדי לווסת קוד תקשורת.
1 417
הפקודות debounce ו deferred בריאקט
בואו נדבר על שני מנגנונים פופולריים ביישומי ריאקט עם סינון רשימות ונבין מה ההבדל ביניהם ומתי נשתמש בכל אחד (או בשניהם יחד).
עדכון עם Debounce בריאקט
כשיש לי רשימה גדולה של פריטים ואני רוצה לסנן אותה הסינון עצמו עלול להיות פעולה כבדה. יותר מזה, אם הסינון עצמו כולל גישה לשרת הפעלת יותר מדי פעולות סינון יכולה ליצור עומס על השרת. לכן אנחנו משתמשים במנגנון debounce כדי לייצב את הקריאות, כלומר נרצה לשלוח את בקשת הסינון לשרת רק אחרי שהמשתמש סיים רצף הקלדות. בדרך כלל debounce ממומש על ידי המתנה של חצי שניה או שניה מרגע השינוי האחרון ועד לרגע שבאמת שולחים את הודעת החיפוש לשרת. אם במהלך הזמן הזה המשתמש מקליד משהו נוסף אז נתחיל את הספירה מחדש.
הפונקציה
useDebounce מתוך האוסף useHooks ממומשת כך:
export function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = React.useState(value);
React.useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
היא לוקחת ערך שהוא לרוב משתנה סטייט כלשהו ומספר מילי שניות להמתנה. היא מגדירה משתנה סטייט פנימי נוסף שיתעדכן רק אחרי שהערך המקורי לא השתנה במשך delay מילי-שניות. בעזרת אפקט הפונקציה מגדירה timeout ומאפסת את השעון כל פעם ש value משתנה.
אני משתמש בפונקציה באופן הבא:
function TextWithDebounce() {
const [text, setText] = useState('');
const debouncedText = useDebounce(text, 1000);
return (
<>
<input type="text" value={text} onChange={e => setText(e.target.value)} />
<p>Character Count: {debouncedText.length}</p>
</>
)
}
הקומפוננטה תציג תיבת טקסט ורק אחרי שמשתמש מפסיק לעדכן את הטקסט בתיבה היא תציג את מספר התווים שהוקלדו.
עדכון עם useDeferredValue בריאקט
הפונקציה useDeferredValue היא כבר חלק מובנה בריאקט ומטרתה לייצב את ממשק המשתמש באמצעות הרצת קוד ריאקט ברקע. אני אסביר:
1. כשריאקט מזהה שינוי במשתנה סטייט הוא מיד מריץ את פונקציית הקומפוננטה בה נמצא אותו משתנה סטייט, ובעקבותיה ממשיך לחשב את כל הקומפוננטות שבתוכה, כדי לבנות את ה Virtual DOM החדש שמושפע מהשינוי.
2. הרצת תהליך החישוב עשויה להיות איטית, ובזמן הזה ממשק המשתמש לא מעודכן. יותר מזה, במהלך תהליך החישוב יכולים לקרות אירועי UI נוספים שיטופלו באיחור כי הדפדפן עסוק בחישוב ה Virtual DOM של השינוי הקודם.
פונקציית useDeferredValue מציעה פתח מילוט מבעיה זו - היא מאפשרת לנו להמשיך להציג את הערך הישן ולחשב את ה Virtual DOM החדש ברקע. אם יהיו אירועי UI נוספים שישנו את הסטייט ריאקט יוכל לעצור את החישוב ברקע ולהתחיל חישוב מחדש עם הערך החדש, כל זאת בלי לפגוע במידע שמוצג על המסך. רק אחרי שהחישוב ברקע יסתיים ריאקט יחליף את התוכן שעל המסך "במכה אחת" בלי לפגוע בחווית המשתמש.
נתבונן בדוגמת הקוד הבאה מדף התיעוד של הפונקציה:
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';
export default function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={deferredText} />
</>
);
}
בהנחה שלוקח הרבה זמן לבנות את SlowList עבור טקסט מסוים, השימוש ב deferredValue מאפשר לריאקט להגיב לשינויים ב text מיד כשהם קורים ולהריץ את החישוב של SlowList ברקע. אם החישוב מסתיים ו text לא השתנה אז במכה אחת הערך של deferredText יוחלף לערך החדש של text וריאקט ישתמש ב Virtual DOM שהוא כבר חישב. אם text משתנה באמצע החישוב אז ריאקט פשוט יעצור את החישוב הקודם ויתחיל לחשב מחדש את SlowList עם הטקסט החדש.
נ.ב. בשביל שדוגמה זו תעבוד SlowList חייב להיות מוגדר עם memo כי כש text משתנה ריאקט מנסה לחשב מחדש את App ובלי memo גם אם deferredText לא משתנה עדיין ריאקט יצטרך לחשב מחדש את SlowList.1 417
הצעה לפרומפט - איך לשבור את הקוד
אחד הקשיים של מתכנתים, במיוחד בעולם של אבטחת מידע אבל גם מחוץ, הוא לראות באג בקוד שלא גורם לבעיה בהתנהגות המערכת. באג שמסתתר ומחכה לפיצ'ר חדש שנרצה להוסיף ואז פתאום יופיע וכולנו נשאל "אבל איך זה עבד עד עכשיו?".
אז הנה הצעה לפעם הבאה שאתם מקשקשים עם קלוד או חבר אחר, זה הולך ככה:
> Let's play a game - the code below has a hidden bug, but it will be triggered if we add a specific feature. What new feature will break this code?
אני נתתי לו בשביל הדוגמה את הקוד ריאקט הזה והוא קלע בול לפיצ'ר שהיה צריך בשביל לראות את הבאג:
function ColorPalette({ start }: {
start: ColorInput,
}) {
const [deletedBoxes, setDeletedBoxes] = useState(new Set());
const removeBox = useCallback((e: React.MouseEvent) => {
console.log(\deleted boxes = ${deletedBoxes.size}\);
const id = (e.target as HTMLDivElement).dataset.id;
deletedBoxes.add(Number(id));
setDeletedBoxes(new Set(deletedBoxes));
}, [deletedBoxes, setDeletedBoxes]);
const colors = [];
for (let i=-360; i < 360; i++) {
if (deletedBoxes.has(i)) continue;
colors.push(
<ColorBox
key={i}
start={start}
spin={i}
onClick={removeBox}
id={i}
/>
);
}
return colors;
}
(וכן כנראה שאפשר להוריד את התחילית Let's play a game, אבל אני מוצא שהתשובות של AI יותר נחמדות כשהוא חושב שהוא במשחק).1 417
ואם המידלוור לא ירוץ?
באג מעניין במנגנון המידלוורים של next.js איפשר לתוקפים לדלג על המידלוור באמצעות הזרקת כותרת מיוחדת לבקשה. לא ניכנס לפרטים הטכניים אבל כן שווה לשים לב לסיפור מנקודת מבט של אבטחת מידע. ביישומים רבים מנגנון ניהול המשתמשים וחסימת גישה לנתיבים מסוימים מתבצע במידלוור, לפני שהבקשה מגיעה לקוד שאחראי על הטיפול בה. יש פה ראייה ארכיטקטונית של הפרדה בין הקוד "שמנהל גישה" לקוד "שמבצע את הפעולה". לדוגמה במדריך של auth0-next מצאתי את הבלוק הבא:
import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge";
export default withMiddlewareAuthRequired();
export const config = {
matcher: ["/protected", "/admin"],
};
הקוד מגדיר שגישה לנתיבי protected ו admin מותרת רק למשתמשים מאומתים. אתם רואים את הבעיה נכון? אם המידלוור לא רץ הגישה פתוחה לכולם.
נשווה את זה לעיקרון "בודקים צמוד לפעולה המסוכנת" של פיתוח קוד מאובטח. בעבודה נכונה אנחנו רוצים לבדוק הרשאות בתוך דף ה admin, ואפילו בתוך הפונקציה שמושכת מידע של מנהל מבסיס הנתונים. שימו לב למימוש הדוגמה הבא מתוך אותו מדריך:
export const getUserProfileData = async (): Promise<Claims> => {
const session = await getSession();
if (!session) {
throw new Error(\Requires authentication\);
}
const { user } = session;
return user;
};
פונקציה כזאת לא תאפשר למשוך נתונים רגישים, אפילו אם כל הקוד שלפניה בוטל. היא בעצמה לוקחת את נתוני המשתמש ומוודאת שהמשתמש מחובר. במערכת עם מנגנון הרשאות פונקציה זו תוכל גם לוודא שלמשתמש שמחובר יש הרשאות לבצע את הפעולה שהיא רוצה לבצע, ורק אז תריץ את הקוד המסוכן.
קוד מאובטח לא מניח שמישהו לפניו בדק הרשאות - הוא בודק הרשאות צמוד ובדיוק לפני הפעולה המסוכנת, כדי שאי אפשר יהיה "לדלג" על בדיקת ההרשאות ולהגיע רק לקוד הרגיש.1 417
טיפ JavaScript - שימוש ב matchMedia במקום resize
פקודת matchMedia היא טריק חמוד שמאפשר לנו לקבל אירוע כל פעם ש"התאמת המסך ל Media Query" מסוים משתנה. זה מנגנון הרבה יותר יעיל מאירועי resize כי רוב הזמן מה שמעניין אותנו זה לא שמשתמש שינה את גודל החלון אלא שגודל החלון נהיה מספיק קטן כדי להתיחס אליו כמו לטלפון, או מספיק גדול כדי להציג עליו יותר מידע. זאת בעצם גירסת ה JavaScript של Media Queries ב CSS.
הנה דוגמה קצרה ל Hook ריאקטי שבודק אם אנחנו על מכשיר מובייל:
const MOBILE_BREAKPOINT = 768
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
React.useEffect(() => {
const mql = window.matchMedia(\(max-width: ${MOBILE_BREAKPOINT - 1}px)\)
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
}
mql.addEventListener("change", onChange)
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
return () => mql.removeEventListener("change", onChange)
}, [])
return !!isMobile
}1 417
כלים שקופים
אם העתיד שלנו הוא עוזרי AI לכתיבת קוד אז כדאי שנחשוב שוב על שפות הפיתוח והספריות בהם אנחנו משתמשים. הנה כמה דברים שלמדתי במשחקים עם AI בשבוע האחרון:
1. מאוד קל לו לכתוב משחק סנייק, מאוד קשה להשתמש נכון בספריות מ npm. זה קורה כי משחק סנייק משתמש רק ביכולות של הדפדפן כלומר מידע שהוא התאמן עליו, ואילו ספריות ב npm משנות את הממשק כל שלושה חודשים ולכן יש יותר מדי קונטקסט.
2. מאוד קל לו לכתוב שאילתת SQL, הרבה יותר קשה לכתוב קוד ORM.
3. מאוד קל לו לכתוב תבנית SAM לארכיטקטורת Serverless על AWS, הרבה יותר קשה לו לתקן סקריפט התקנה לשרת VPC.
4. מאוד קל לו לכתוב עמוד חדש לאתר, הרבה יותר קשה כשהעמוד צריך להשתמש בקומפוננטות קיימות מהמערכת שאולי לא מתועדות מספיק טוב.
5. מאוד קל לו לכתוב בדיקת End To End לפי רשימת תרחישים שאני מתאר, הרבה יותר קשה להקים תשתית לבדיקות יחידה ולהחליט איזה Mock-ים לעשות ואיך צריך לארגן אחרת את הקוד הספציפי שלי כדי שיהיה קל לכתוב אותו.
מה שמשותף לכל המקרים כאן, ולעבודה עם AI באופן כללי הוא הקו שבין הספציפי לכללי - ככל שמדובר בלקחת "קוד מספר לימוד" ולהדביק אותו לפרויקט תוך ביצוע התאמות נדרשות דברים יעבדו ויפתיעו לטובה. ברגע שאנחנו מגיעים ל Refactoring או לקבלת החלטות העסק מסתבך. זה אומר שאם אנחנו רוצים להרוויח מ AI כדאי לחפש את המקומות בהם חלון הקונטקסט קטן (צריך פחות טוקנים בקלט כדי לפתור את הבעיה) והמשמעות של הטוקנים יציבה. אז SQL זה מצוין כי כמו שכתבתי את השאילתה ככה היא הולכת ל DB, ו ORM זה מסובך כי הגדרות הקשרים בין המודלים ממוקמות בקובץ נפרד או כמה קבצים או שילוב בין קבצים ל DB.
רוצים להרוויח מ AI? נסו להזיז את הפרויקט שלכם לכיוון כלים שקופים וסטנדרטיים. אבסטרקציות מתוחכמות וספציפיות למערכת שלנו יוצרות עכשיו עלות חדשה ואולי לא שווות את המאמץ.
1 417
תבנית לפיתוח Chat Bot ב Next.JS
לא משנה כמה חשבתם שלכם קשה עם כל הספריות שמשתנות כל הזמן בעולם ה Full Stack, ל AI זה הרבה יותר קשה. אולי בגלל זה הם מצליחים לבנות משחק סנייק בפחות מדקה, אבל רק ננסה משהו שצריך ספריות מ npm והוא הולך לאיבוד בגירסאות. וזה חבל כי עדיין יש המון דברים ב npm שיכולים לחסוך לנו זמן פיתוח ובאגים. הדוגמה שלנו היום היא ספריות ה ai של Vercel.
מה יש בזה
ורסל פיתחו אוסף ספריות שמחברות בין AI Bots לממשק משתמש, כך שמצד אחד תוכלו לכתוב את הממשק בכל דרך שתרצו אבל מצד שני תקבלו גישה לכל הפינוקים הסטנדרטיים של ממשקי AI. בדוגמה של צ'ט בוט זה אומר שנקבל:
1. ממשק אחיד לשליחת הודעה ל AI Chat Bot.
2. הודעה שתישלח בממשק האחיד תוכל לחזור בתור זרם של תווים, וכך תוצג מהר יותר למשתמשים.
3. שימוש בכלים וממשק סטנדרטי להודעות שנוצרו על ידי כלים.
4. התאמת הודעות לסכימה כדי לוודא שמקבלים את המידע שרצינו ובפורמט שרצינו.
בואו נראה את התבנית לפיתוח Chat Bot בסיסי ובסוף קישור לתיעוד כדי שתוכלו להמשיך משם.
צ'ט בוט בסיסי ב Next.JS
בשביל לבנות Chat Bot בסיסי ב Next.JS עם החבילה של ורסל תחילה נתקין את החבילה עם:
$ npm add ai ai-sdk/react
ולאחר מכן נוסיף ספק AI שזה מודול לעבודה עם צ'ט בוט מסוים, הנה כמה ספקים לדוגמה (מספיק לבחור אחד):
$ npm add @ai-sdk/xai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google
מבחינת הקוד יש לפרויקט בסך הכל שני קבצים. קוד צד לקוח שמציג חלון שיחה:
// file: page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
export default function Page() {
const { messages, input, handleInputChange, handleSubmit } = useChat({});
return (
<>
{messages.map(message => (
<div key={message.id}>
{message.role === 'user' ? 'User: ' : 'AI: '}
{message.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input name="prompt" value={input} onChange={handleInputChange} />
<button type="submit">Submit</button>
</form>
</>
);
}
וקוד צד שרת שמטפל בפניות מהלקוח, מעביר את ההודעה לבוט השיחה ומחזיר את השיחה בזרם תווים:
// src/app/api/chat/route.ts
import { deepseek } from '@ai-sdk/deepseek';
import { streamText } from 'ai';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: deepseek('deepseek-chat'),
system: 'You are a helpful assistant.',
messages,
});
return result.toDataStreamResponse();
}
קוד? בטח הנה זה כאן:
https://github.com/ynonp/nextjs-chatbot1 417
מחזיק אצבעות
החוויה הכי משמעותית שלי בעבודה עם כלי AI.
מחזיק אצבעות.
אני כותב פרומפט, ואז מחזיק אצבעות שהתשובה תהיה כמו שרציתי. ההמתנה לתשובה היא חלק מהקסם, אתה יודע שה AI יבוא עם תשובה, ושזה יהיה קצת יותר מהר ממה שאתה תמצא את התשובה לבד. אז אולי שווה לחכות. ואם הוא צודק זאת ממש חגיגה.
אבל כשהוא טועה? כשהוא טועה צריך לבחור, האם לשאול שאלת המשך, להתחיל מההתחלה או לרדת מזה ולחשוב לבד. שאלת המשך יכולה להצליח, להתחיל מחדש עם פרומפט חדש גם יכול לעבוד, ולחשוב לבד? עד שהגענו עד לפה.
ואז אני נכנס ללופ - פרומפט, תשובה לא אפויה, תיקון. פרומפט, המון קוד גרוע שלא עובד, פרומפט המשך עם הודעת השגיאה. ושוב. ושוב.
יש לי חבר שכל פעם שאני מציע לו ספריית קוד פתוח שבדיוק תפתור לו איזה בעיה ספציפית הוא מסתכל עליי מלמעלה ומסנן "עזוב אותך, עד שאני אבין איך זה עובד אני כבר אכתוב לבד משהו יותר טוב". ובאמת לקח לנו המון זמן לפתח תיעוד מספיק טוב לספריות קוד פתוח ואינטואיציה מספיק טובה לגבי מתי כדאי להשתמש בהן (ואנחנו עדיין מתקשים עם זה). אולי גם ל AI צריך לתת זמן. כרגע הרושם הוא שיש דברים שעובדים וחוסכים המון זמן, ויש דברים שלא עובדים ששואבים אותך לשעות, וברור שאם היה אפשר היינו משתמשים ב AI רק לאותם דברים שעובדים. והכי מעניין כרגע - האם בעתיד ה AI יאכל את המתכנתים (כמו ש C החליף את אסמבלי, כמו שטכנולוגיות ווב החליפו אפליקציות דסקטופ) ויישארו רק ארכיטקטים, מנהלי מוצר ופרומפטים?
קשה לדעת. בינתיים אני מחזיק אצבעות.
1 417
איך להעלות תמונה (או כל קובץ אחר) ב Next.JS ב 2025
ריאקט 19 ו next 15 עובדים יחד בהרמוניה כדי לאפשר לנו המתכנתים הפשוטים לכתוב אפליקציות Full Stack במהירות. אחד המקומות שקיבל טיפול באינטגרציה הזו הוא הטפסים. בואו נראה איך עובדים עם טפסים ב 2025 ב React ו Next ובפרט איך להעלות תמונות לשרת.
קוד הטופס
רעיון ראשון שהחברים בריאקט וב Vercel ממליצים עליו הוא שלא חייבים לשמור את הערכים מהטופס במשתני State. כן אפשר לעשות את זה אם רוצים וולידציה, אבל בגדול אם הוולידציה שלכם קורית רק כשמגישים את הטופס אפשר לחזור לחיים שלפני הסטייט ופשוט לכתוב טופס עם שמות לשדות, ולבדוק את הערכים בפונקציה מרכזית אחת.
קוד הטופס שלי נראה כך:
'use client';
import { useActionState, } from "react"
import { saveImage } from '@/lib/save-image';
export default function Home() {
const [state, formAction, isPending] = useActionState(async (previousState: string, formData: FormData) => {
const file = formData.get('image') as File;
const description = formData.get('description') as string || "Default description";
if (!file || file.size === 0) {
return "No Image Selected"
}
if (!description) {
return "No Description Provided"
}
return await saveImage(file, description);
}, "");
return (
<div>
<p>{state}</p>
<form action={formAction}>
<input type="text" name="description" />
<input type="file" name='image' />
<input type="submit" value="Save Image" />
</form>
</div>
)
}
הפונקציה שאני מעביר ל useActionState אחראית גם על וולידציה, גם על שליחת המידע לשרת וגם על הגדרת State כתוצאה מתשובת השרת. הפרמטר האחרון לפונקציה הוא הסטייט הראשוני, לפני שהטופס הוגש, והוא יישמר במשתנה החוזר state. אחרי הגשת הטופס הערך החוזר מהפונקציה יוכנס לתוך אותו משתנה state.
מנגנון זה מאפשר לנו להציג הודעה למשתמשים אחרי שהטופס הוגש, או להראות שגיאות מצד שרת או שגיאות וולידציה. בטופס הדוגמה אני משתמש במחרוזת רגילה ופשוט מציג אותה על המסך מעל הטופס. ברינדור הראשון זו תהיה מחרוזת ריקה ואחרי שיוגש הטופס יופיע שם הטקסט שיחזור מצד השרת, או הודעות השגיאה שחוזרות מהפונקציה אם יש שגיאת וולידציה.
צד שרת
החלק השני של הקוד הוא הפונקציה saveImage:
'use server';
import { read } from 'node:fs';
import fs from 'node:fs/promises';
export async function saveImage(
image: File,
description: string,
): Promise<string> {
console.log(\Saving image: ${image.name}\);
const buffer = await image.stream().getReader().read();
const data = buffer.value;
if (!data) return "Missing Image File"
const path = \./images/${image.name}\;
const file = await fs.writeFile(path, data);
return 'Image saved OK';
}
אל תשתמשו בה במערכת אמיתית כי היא לא מנקה את שם הקובץ, אבל כן אפשר להבין מכאן את המבנה:
1. הפונקציה לוקחת את הקובץ בתור אוביקט File, שזה אוביקט שמשותף גם לצד לקוח וגם לצד שרת.
2. מתוך ה File אפשר לקחת stream ודרכו להגיע לתוכן.
3. אם הכל בסדר נכתוב את המידע לקובץ ונחזיר הודעה.1 417
הבוט של הטלגרם פספס את הפיסקה הכי חשובה בפוסט, אז מוסיף אותה-
אז מה עושים? כלי AI הם פה כדי להישאר, עם היתרונות והחסרונות שלהם. שימוש נכון בכלים אלה בהחלט ישפר את מהירות הפיתוח שלכם. שימוש לא נכון, גם אם מלהיב באותו רגע, יכול בקלות לגמור לנו אחר צהריים. ביום חמישי שבוע הבא (ה 27.3 ב 20:00) ערן גולדמן-מלכא הזמין אותי לדבר איתו בוובינר על שימוש נכון בכלי AI לפיתוח - נראה שם את lovable, base44, קופיילוט, זנקוד, רדי ועוד המון חברים ואספר מה ואיך עובד עבורי. מוזמנים להצטרף בלינק:
https://calendly.com/aumint/10xdevwith_ai?month=2025-03&date=2025-03-27
1 417
ואז פתחתי את הקוד
ביקשתי מ Lovable לכתוב אפליקציה שתיקח קישור ל URL ותייצר לי בוחן אוצר מילים על המילים שם, ככה בשביל לתרגל את הספרדית. בשביל שלא יהיה קל מדי ביקשתי ממנו גם שיבקש את הרמה הנוכחית שלי בספרדית ויבנה בוחן על מילים מה URL שמתאימות לרמה שלי. זה לקח לו קצת זמן אבל בסוף הוא הוציא את האפליקציה כאן:
https://colorlingo-quizzy.lovable.app/
אחרי כמה משחקים הרגשתי שהוא קצת מרמה אז הלכתי לקוד כדי למצוא את הפונקציה הבאה שמוציאה את המילים מהלינק:
// This would be populated with real data from the URL and filtered by level
// For now, we're creating dummy words for the custom quiz
const generateDummyWords = (url: string, level: SpanishLevel) => {
// Extract domain name for demonstration
let domain = '';
try {
domain = new URL(url).hostname.replace('www.', '');
} catch {
domain = 'example';
}
// These would actually come from scraping and NLP processing
const allWordPairs = [
// A1 level words (beginner)
{ english: "hello", spanish: "hola", level: "A1" },
{ english: "goodbye", spanish: "adiós", level: "A1" },
{ english: "thank you", spanish: "gracias", level: "A1" },
{ english: "please", spanish: "por favor", level: "A1" },
// A2 level words (elementary)
{ english: "welcome", spanish: "bienvenido", level: "A2" },
{ english: "friend", spanish: "amigo", level: "A2" },
{ english: "family", spanish: "familia", level: "A2" },
{ english: "today", spanish: "hoy", level: "A2" },
// B1 level words (intermediate)
{ english: "website", spanish: "sitio web", level: "B1" },
{ english: domain, spanish: domain, level: "B1" },
{ english: "language", spanish: "idioma", level: "B1" },
{ english: "content", spanish: "contenido", level: "B1" },
// B2 level words (upper intermediate)
{ english: "experience", spanish: "experiencia", level: "B2" },
{ english: "develop", spanish: "desarrollar", level: "B2" },
{ english: "improve", spanish: "mejorar", level: "B2" },
{ english: "progress", spanish: "progreso", level: "B2" },
// C1 level words (advanced)
{ english: "fluency", spanish: "fluidez", level: "C1" },
{ english: "proficient", spanish: "competente", level: "C1" },
{ english: "articulate", spanish: "articular", level: "C1" },
{ english: "communicate", spanish: "comunicar", level: "C1" },
// C2 level words (proficiency)
{ english: "mastery", spanish: "dominio", level: "C2" },
{ english: "nuance", spanish: "matiz", level: "C2" },
{ english: "sophisticated", spanish: "sofisticado", level: "C2" },
{ english: "eloquent", spanish: "elocuente", level: "C2" },
];
// Filter words based on selected level or lower
const levelOrder = ["A1", "A2", "B1", "B2", "C1", "C2"];
const levelIndex = levelOrder.indexOf(level);
const filteredWords = allWordPairs.filter(word =>
levelOrder.indexOf(word.level as SpanishLevel) <= levelIndex
);
// Shuffle the array to make it more random and take 10 words
return filteredWords
.sort(() => 0.5 - Math.random())
.slice(0, 10)
.map(({ english, spanish }) => ({ english, spanish }));
};
בקיצור הוא לגמרי עבד עליי. אף מילה מהלינק לא מגיעה לבוחן אוצר מילים.
(הגירסה השנייה שלו גם לא היתה נכונה אבל מסיבה אחרת).
עכשיו הבאג הזה יחסית נחמד כי קל לזהות אותו, אבל אני חושב שהוא ממחיש את היצירתיות של ה AI, שהיא הכח והחולשה שלו: כשהוא לא יודע הוא לא עוצר לשאול או לחשוב, פשוט משלים קוד מהמאגר האינסופי של קודים שהוא אומן עליהם. מאוד יעיל ל POC-ים קטנים כשאנחנו בתוך הצוות או חוקרים רעיון, הרבה פחות רלוונטי למערכת שצריכה לפגוש משתמשים אמיתיים.
Вже доступно! Дослідження Telegram за 2025 — головні інсайти року 
