Spaces:
Sleeping
Sleeping
File size: 28,039 Bytes
e2ce00f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
// این کد، فایل script.js بهروزرسانی شده نهایی است که دکمه جستجو را زمانی فعال میکند که هم دادهها بارگذاری شده باشند و هم متنی در کادر جستجو وارد شده باشد.
// ****** تعریف متغیرهای عناصر HTML در بالاترین اسکوپ ******
let searchButton;
let questionInput;
let resultsDiv;
let loadingStatusParagraph;
let selectionErrorParagraph;
let selectAllCheckbox;
let bookCheckboxes; // NodeList از تمام چک باکس های کتاب ها (به جز انتخاب همه)
let resultsPerPageSelect; // المان select برای تعداد نتایج
// ****** تعریف URL سرور پایتون برای دریافت Embedding سوال ******
const EMBEDDING_SERVER_URL = 'https://montaghem630-khatere-khan.hf.space/get_embedding';
// ****** متغیر برای نگهداری دادههای ترکیب شده از کتابهای انتخاب شده ******
let memoirsWithEmbeddings = [];
// ****** نگاشت نام فایل JSON به نام کامل کتاب (برای نمایش در نتایج) ******
// این لیست باید با مقادیر value چک باکس ها و نام های نمایشی در HTML مطابقت داشته باشد
const bookInfo = {
'jabe_siah.json': 'جعبه سیاه (منتخب خاطرات اسدالله علم)',
// اگر کتاب های دیگری دارید، اینجا اضافه کنید
// 'ketab_dovom.json': 'نام کتاب دوم',
// 'ketab_sevom.json': 'نام کتاب سوم',
};
// *****************************************************************
// تابع کمکی برای نمایش پیام وضعیت بارگذاری/پردازش
function updateStatus(message, isError = false) {
if (loadingStatusParagraph) {
loadingStatusParagraph.textContent = message;
loadingStatusParagraph.style.color = isError ? 'red' : '#666';
} else {
console.log("Status:", message); // لاگ برای توسعه
}
}
// تابع کمکی برای نمایش خطای انتخاب کتاب
function updateSelectionError(message) {
if (selectionErrorParagraph) {
selectionErrorParagraph.textContent = message;
selectionErrorParagraph.style.color = 'red';
} else {
console.error("Selection Error:", message); // لاگ برای توسعه
}
}
// تابع کمکی برای فعال/غیرفعال کردن دکمه جستجو
function setButtonEnabled(enabled) {
if (searchButton) {
searchButton.disabled = !enabled;
}
}
// ****** تابع جدید برای بررسی وضعیت و فعال/غیرفعال کردن دکمه جستجو ******
// این تابع بررسی می کند که آیا داده ها بارگذاری شده اند و آیا کادر سوال خالی نیست
function checkAndEnableSearchButton() {
const isDataLoaded = memoirsWithEmbeddings.length > 0;
const isQueryNotEmpty = questionInput && questionInput.value.trim() !== ''; // چک کردن وجود questionInput قبل از دسترسی به value
// دکمه فقط زمانی فعال می شود که هم داده بارگذاری شده باشد و هم متن سوال خالی نباشد
setButtonEnabled(isDataLoaded && isQueryNotEmpty);
console.log(`Check Button State: Data Loaded = ${isDataLoaded}, Query Not Empty = ${isQueryNotEmpty}, Button Enabled = ${isDataLoaded && isQueryNotEmpty}`); // لاگ برای توسعه
}
// ****** تابع اصلی برای بارگذاری دادهها از فایلهای JSON کتابهای انتخاب شده ******
// این تابع هر زمان که انتخاب کتاب ها تغییر می کند، داده ها را بارگذاری مجدد می کند
async function updateSelectedBooksData() {
console.log("Updating selected books data..."); // لاگ برای توسعه
updateStatus("در حال بارگذاری دادهها..."); // پیام برای کاربر
updateSelectionError(""); // پاک کردن پیام خطای قبلی
// setButtonEnabled(false); // غیرفعال کردن دکمه جستجو حین بارگذاری (توسط checkAndEnableSearchButton انجام می شود)
checkAndEnableSearchButton(); // در ابتدای بارگذاری، دکمه غیرفعال خواهد شد اگر هنوز متن نیست یا داده خالی می شود
// پیدا کردن چک باکس های کتاب ها که انتخاب شده اند
const selectedBookFiles = Array.from(bookCheckboxes)
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value); // value چک باکس ها نام فایل JSON است
console.log("Selected book files:", selectedBookFiles); // لاگ برای توسعه
// ****** چک کردن اینکه حداقل یک کتاب انتخاب شده باشد ******
if (selectedBookFiles.length === 0) {
updateStatus(""); // پاک کردن پیام وضعیت
updateSelectionError("لطفاً حداقل یک کتاب برای جستجو انتخاب کنید."); // پیام خطا برای کاربر
console.warn("No books selected. Cannot load data."); // لاگ برای توسعه
memoirsWithEmbeddings = []; // پاک کردن داده های قبلی
resultsDiv.innerHTML = '<p>پس از انتخاب کتابها و وارد کردن سوال، نتایج اینجا نمایش داده میشوند.</p>'; // بازگرداندن پیام اولیه
checkAndEnableSearchButton(); // مطمئن می شویم دکمه غیرفعال شود
return; // توقف فرآیند اگر هیچ کتابی انتخاب نشده است
}
// ********************************************************
memoirsWithEmbeddings = []; // پاک کردن دادههای قبلی قبل از بارگذاری جدید
try {
// بارگذاری همزمان تمام فایلهای JSON انتخاب شده
const fetchPromises = selectedBookFiles.map(filename => {
const filePath = `./${filename}`;
console.log(`Attempting to fetch: ${filePath}`); // لاگ برای توسعه
return fetch(filePath).then(response => {
if (!response.ok) {
throw new Error(`Error fetching file: ${filename} (Status: ${response.status})`);
}
return response.json();
})
.catch(error => {
console.error(`Failed to fetch or parse file ${filename}:`, error); // لاگ خطا برای توسعه
throw new Error(`Failed to load data for book file "${filename}".`);
});
});
const booksData = await Promise.all(fetchPromises); // انتظار برای دانلود و تجزیه همه فایل ها
// ترکیب دادهها از تمام فایلهای JSON بارگذاری شده
booksData.forEach(data => {
if (Array.isArray(data)) {
memoirsWithEmbeddings = memoirsWithEmbeddings.concat(data);
} else {
console.error("Fetched data is not an array:", data); // لاگ برای توسعه
}
});
const loadedBooksCount = selectedBookFiles.length;
const totalPassagesLoaded = memoirsWithEmbeddings.length;
console.log(`Successfully loaded data from ${loadedBooksCount} book(s). Total passages loaded: ${totalPassagesLoaded}`); // لاگ برای توسعه
updateStatus(`دادهها از ${loadedBooksCount} کتاب با موفقیت بارگذاری شد. مجموع خاطرات: ${totalPassagesLoaded}. آماده جستجو هستید.`); // پیام برای کاربر
// setButtonEnabled(true); // فعال کردن دکمه جستجو پس از بارگذاری موفقیت آمیز داده (توسط checkAndEnableSearchButton انجام می شود)
checkAndEnableSearchButton(); // بررسی و فعال کردن دکمه پس از بارگذاری داده
// نتایج قبلی را پاک نمی کنیم، فقط پیام اولیه را پاک میکنیم اگر هنوز نمایش داده می شود
if (resultsDiv && resultsDiv.innerHTML === '<p>پس از انتخاب کتابها و وارد کردن سوال، نتایج اینجا نمایش داده میشوند.</p>') {
resultsDiv.innerHTML = '';
}
} catch (error) {
console.error("Error loading selected books data:", error); // لاگ برای توسعه
updateStatus("خطا در بارگذاری دادهها.", true); // پیام برای کاربر
updateSelectionError(`خطا در بارگذاری داده از کتابهای انتخاب شده: ${error.message || 'خطای نامشخص'}. جزئیات بیشتر در کنسول مرورگر.`); // پیام خطا برای کاربر
memoirsWithEmbeddings = []; // اطمینان از خالی بودن داده در صورت خطا
// setButtonEnabled(false); // غیرفعال نگه داشتن دکمه جستجو (توسط checkAndEnableSearchButton انجام می شود)
checkAndEnableSearchButton(); // مطمئن می شویم دکمه غیرفعال بماند
resultsDiv.innerHTML = '<p>پس از انتخاب کتابها و وارد کردن سوال، نتایج اینجا نمایش داده میشوند.</p>'; // بازگرداندن پیام اولیه
}
}
// تابع کمکی برای محاسبه شباهت کسینوسی بین دو بردار (بدون تغییر)
function cosineSimilarity(vecA, vecB) {
if (!vecA || !vecB || vecA.length !== vecB.length || vecA.length === 0) {
console.error("Cosine Similarity Error: Invalid vectors.", {vecA_length: vecA ? vecA.length : 'null', vecB_length: vecB ? vecB.length : 'null'});
return 0;
}
let dotProduct = 0;
let magnitudeA = 0;
let magnitudeB = 0;
for (let i = 0; i < vecA.length; i++) {
dotProduct += vecA[i] * vecB[i];
magnitudeA += vecA[i] * vecA[i];
magnitudeB += vecB[i] * vecB[i];
}
magnitudeA = Math.sqrt(magnitudeA);
magnitudeB = Math.sqrt(magnitudeB);
if (magnitudeA === 0 || magnitudeB === 0) {
return 0;
}
const similarity = dotProduct / (magnitudeA * magnitudeB);
return similarity;
}
// تابع کمکی برای حذف بخش کلمات کلیدی از متن Passage (همانند قبل)
function cleanPassageTextForDisplay(passage) {
const startDelimiter = ' <کلیدواژه ها: ';
const startIndex = passage.indexOf(startDelimiter);
if (startIndex === -1) {
return passage;
}
let cleanText = passage.substring(0, startIndex);
return cleanText.trim();
}
// ****** تابع اصلی جستجو که هنگام کلیک دکمه یا فشردن Enter اجرا میشود ******
async function searchMemoirs() {
console.log("Search triggered - executing search"); // لاگ برای توسعه (مشخص نیست دکمه یا Enter)
console.log(`Data loaded state (passages count): ${memoirsWithEmbeddings.length}`); // لاگ برای توسعه
// ****** چک کردن اینکه داده ها (از کتاب های انتخاب شده) بارگذاری شده باشند ******
// این چک در checkAndEnableSearchButton هم هست، اما اینجا برای اطمینان بیشتر است
if (memoirsWithEmbeddings.length === 0) {
console.warn("No memoir data loaded. Cannot search."); // لاگ برای توسعه
updateSelectionError("لطفاً ابتدا کتابهای مورد نظر برای جستجو را انتخاب کرده و منتظر بارگذاری دادهها بمانید."); // پیام خطا برای کاربر
return;
}
// **************************************************************************
const query = questionInput.value.trim();
console.log(`Query text is: "${query}"`); // لاگ برای توسعه
if (!query) {
if (resultsDiv) {
resultsDiv.innerHTML = `<p>لطفاً عبارت مورد نظر برای جستجو را وارد کنید.</p>`; // پیام برای کاربر
}
console.warn("Search query is empty."); // لاگ برای توسعه
return;
}
updateStatus("در حال جستجو..."); // بهروزرسانی پیام وضعیت به جستجو برای کاربر
resultsDiv.innerHTML = ''; // پاک کردن نتایج قبلی یا پیام اولیه
try {
console.log("Requesting query embedding from Python server..."); // لاگ برای توسعه
const serverResponse = await fetch(EMBEDDING_SERVER_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: query })
});
if (!serverResponse.ok) {
const errorBody = await serverResponse.text(); // بخوان به صورت متن برای اطلاعات بیشتر
console.error(`Server responded with status ${serverResponse.status}: ${errorBody}`); // لاگ خطا برای توسعه
throw new Error(`خطا از سرور (${serverResponse.status}). جزئیات بیشتر در کنسول مرورگر.`); // پیام خطا برای کاربر
}
const serverData = await serverResponse.json();
const queryEmbeddingArray = serverData.embedding;
if (!queryEmbeddingArray || !Array.isArray(queryEmbeddingArray) || queryEmbeddingArray.length === 0) {
console.error("Server returned an invalid or empty embedding:", serverData); // لاگ خطا برای توسعه
throw new Error("سرور بردار جستجو را به درستی برنگرداند. جزئیات در کنسول مرورگر."); // پیام خطا برای کاربر
}
console.log("Query embedding received from server successfully."); // لاگ برای توسعه
console.log("Calculating similarities in browser..."); // لاگ برای توسعه
const searchResults = [];
// محاسبه شباهت با تمام قطعات خاطره ای که از کتاب های انتخاب شده بارگذاری شده اند
for (const memoir of memoirsWithEmbeddings) {
// اطمینان از وجود و صحت بردار embedding در آیتم خاطره
if (memoir.embedding && Array.isArray(memoir.embedding) && memoir.embedding.length === queryEmbeddingArray.length) {
const similarity = cosineSimilarity(queryEmbeddingArray, memoir.embedding);
// اضافه کردن تمام فیلدهای اصلی خاطره و امتیاز شباهت به نتیجه
searchResults.push({ ...memoir, similarity: similarity });
} else {
// هشدار برای آیتم های بدون بردار یا با ابعاد نامعتبر (فقط در کنسول)
console.warn(`Skipping memoir due to missing or invalid embedding: ${memoir.book_title || 'Unknown Book'} - ${memoir.reference || 'Unknown Reference'}`);
}
}
console.log(`Similarity calculation complete. Found ${searchResults.length} results with valid embeddings.`); // لاگ برای توسعه
console.log("Sorting results by similarity..."); // لاگ برای توسعه
searchResults.sort((a, b) => b.similarity - a.similarity);
console.log("Results sorted."); // لاگ برای توسعه
// ****** انتخاب تعداد نتایج برتر بر اساس انتخاب کاربر ******
const resultsPerPage = parseInt(resultsPerPageSelect.value, 10); // خواندن مقدار انتخاب شده و تبدیل به عدد صحیح
const topResults = searchResults.slice(0, resultsPerPage); // انتخاب فقط N نتیجه برتر
console.log(`Displaying top ${topResults.length} results based on user selection.`); // لاگ برای توسعه
// ***********************************************************
// ****** منطق نمایش نتایج ******
if (resultsDiv) {
// نتایج قبلی را پاک کرده ایم
if (topResults.length === 0) { // اگر هیچ نتیجه ای یافت نشد
resultsDiv.innerHTML = `<p>نتیجه مرتبطی یافت نشد.</p>`; // پیام برای کاربر
console.log("No relevant results found."); // لاگ برای توسعه
} else { // اگر نتایجی یافت شد
console.log("Results found, updating DOM."); // لاگ برای توسعه
const resultsList = document.createElement('div');
resultsList.classList.add('results-list');
topResults.forEach(result => {
const resultItem = document.createElement('div');
resultItem.classList.add('result-item');
// حذف نمایش امتیاز شباهت
// const similarityElement = document.createElement('p');
// similarityElement.classList.add('result-similarity');
// similarityElement.textContent = `شباهت: ${result.similarity.toFixed(4)}`;
// نمایش نام کتاب
const bookTitleElement = document.createElement('p');
bookTitleElement.classList.add('result-book-title');
bookTitleElement.textContent = `از کتاب: ${result.book_title || 'نامشخص'}`;
// نمایش مرجع خاطره
const referenceElement = document.createElement('p');
referenceElement.classList.add('result-reference');
referenceElement.innerHTML = `<strong>مرجع:</strong> ${result.reference || 'نامشخص'}`;
// نمایش متن خاطره (با حذف کلمات کلیدی)
const passageElement = document.createElement('p');
passageElement.classList.add('result-passage');
passageElement.textContent = cleanPassageTextForDisplay(result.passage || '');
// اضافه کردن عناصر به آیتم نتیجه با ترتیب جدید (متن -> مرجع -> کتاب)
resultItem.appendChild(passageElement);
resultItem.appendChild(referenceElement);
resultItem.appendChild(bookTitleElement);
resultsList.appendChild(resultItem);
});
resultsDiv.appendChild(resultsList);
console.log("DOM updated with results."); // لاگ برای توسعه
// لاگ کردن نتایج برای توسعه
console.log(`Top ${topResults.length} results displayed (reference, book, and similarity):`);
topResults.forEach(result => {
console.log(` Book: ${result.book_title || 'Unknown'}, Ref: ${result.reference || 'N/A'}, Sim: ${result.similarity.toFixed(4)}`); // امتیاز را در لاگ نگه می داریم
});
}
updateStatus(`جستجو به پایان رسید. ${topResults.length} نتیجه برتر نمایش داده شد.`); // بهروزرسانی پیام وضعیت پس از جستجو
} else {
console.error("Could not find resultsDiv to display results."); // لاگ برای توسعه
updateStatus("جستجو با خطا مواجه شد.", true); // پیام برای کاربر
}
} catch (error) {
console.error("Error during search:", error); // لاگ برای توسعه
if (resultsDiv) {
// نمایش پیام خطای عمومی به کاربر
resultsDiv.innerHTML = `<p style="color: red;">هنگام جستجو خطایی رخ داد: ${error.message || 'خطای نامشخص'}. جزئیات بیشتر در کنسول مرورگر موجود است.</p>`;
}
updateStatus("جستجو با خطا مواجه شد.", true); // پیام برای کاربر
} finally {
// در نهایت (چه موفقیت آمیز چه با خطا)، دکمه را دوباره بررسی و تنظیم وضعیت می کنیم
checkAndEnableSearchButton();
}
}
// ****** Event Listeners و مقداردهی اولیه در زمان بارگذاری صفحه ******
document.addEventListener('DOMContentLoaded', () => {
console.log("DOMContentLoaded fired. Attaching event listeners and initializing."); // لاگ برای توسعه
// پیدا کردن عناصر HTML با ID یا Class
searchButton = document.getElementById('searchButton');
questionInput = document.getElementById('userQuestion');
resultsDiv = document.getElementById('searchResults');
loadingStatusParagraph = document.getElementById('loadingStatus');
selectionErrorParagraph = document.getElementById('selectionError');
selectAllCheckbox = document.getElementById('select_all_books');
bookCheckboxes = document.querySelectorAll('.book-checkbox'); // انتخاب تمام چک باکس های با کلاس .book-checkbox
resultsPerPageSelect = document.getElementById('resultsPerPage'); // پیدا کردن المان select
// لاگ برای تأیید پیدا شدن عناصر HTML (برای توسعه)
console.log("Search Button found:", !!searchButton);
console.log("Question Input found:", !!questionInput);
console.log("Results Div found:", !!resultsDiv);
console.log("Loading Status found:", !!loadingStatusParagraph);
console.log("Selection Error found:", !!selectionErrorParagraph);
console.log("Select All Checkbox found:", !!selectAllCheckbox);
console.log(`Book Checkboxes found: ${bookCheckboxes.length}`);
console.log("Results Per Page Select found:", !!resultsPerPageSelect);
// اگر تمام عناصر مورد نیاز پیدا شدند، Event Listeners را اضافه کرده و مقداردهی اولیه را انجام دهید
if (searchButton && questionInput && resultsDiv && loadingStatusParagraph && selectionErrorParagraph && selectAllCheckbox && bookCheckboxes.length > 0 && resultsPerPageSelect) {
// ****** اضافه کردن Event Listener به دکمه جستجو ******
searchButton.addEventListener('click', searchMemoirs);
console.log("Search button event listener attached."); // لاگ برای توسعه
// ****** اضافه کردن Event Listener برای تغییر متن در کادر سوال ******
// از event 'input' استفاده می کنیم که با هر تغییری (تایپ، پیست و...) اجرا می شود
questionInput.addEventListener('input', () => {
checkAndEnableSearchButton(); // هر بار که متن تغییر کرد، وضعیت دکمه را بررسی کن
});
// Event listener برای keypress (Enter) برای اجرای جستجو باقی می ماند
questionInput.addEventListener('keypress', (event) => {
if (event.key === 'Enter' || event.keyCode === 13) {
event.preventDefault();
// فقط در صورتی جستجو را اجرا کن که دکمه فعال است (یعنی داده بارگذاری شده و متن خالی نیست)
if (!searchButton.disabled) {
searchMemoirs();
} else {
console.warn("Attempted to search with Enter, but button is disabled (data not loaded or query empty)."); // لاگ برای توسعه
}
}
});
console.log("Input change and keypress event listeners attached to question input."); // لاگ برای توسعه
// ****** منطق چک باکس 'انتخاب همه' ******
selectAllCheckbox.addEventListener('change', () => {
const isChecked = selectAllCheckbox.checked;
bookCheckboxes.forEach(checkbox => {
checkbox.checked = isChecked;
});
// بارگذاری مجدد داده ها پس از تغییر انتخاب همه (با تاخیر کم برای جلوگیری از فشردگی)
setTimeout(updateSelectedBooksData, 50); // تاخیر 50 میلی ثانیه
});
// ****** اضافه کردن Event Listeners به چک باکس های کتاب ها ******
bookCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', () => {
// اگر یکی از چک باکس های کتاب از حالت انتخاب خارج شد، 'انتخاب همه' را هم از حالت انتخاب خارج کن
if (!checkbox.checked) {
selectAllCheckbox.checked = false;
}
// اگر تمام چک باکس های کتاب انتخاب شدند، 'انتخاب همه' را هم انتخاب کن
else {
const allBooksSelected = Array.from(bookCheckboxes).every(cb => cb.checked);
if (allBooksSelected) {
selectAllCheckbox.checked = true;
}
}
// بارگذاری مجدد داده ها پس از تغییر انتخاب کتاب (با تاخیر کم)
setTimeout(updateSelectedBooksData, 50); // تاخیر 50 میلی ثانیه
});
});
// ****** اضافه کردن Event Listener به انتخابگر تعداد نتایج ******
resultsPerPageSelect.addEventListener('change', () => {
console.log("Results per page changed to:", resultsPerPageSelect.value); // لاگ برای توسعه
});
// ****** بارگذاری اولیه داده ها بر اساس انتخاب های پیش فرض هنگام بارگذاری صفحه ******
// این تابع بر اساس چک باکس های پیش فرض (که در HTML تیک خورده اند) داده ها را بارگذاری می کند
updateSelectedBooksData(); // این فراخوانی در نهایت checkAndEnableSearchButton را صدا می زند
} else {
// اگر عناصر مورد نیاز پیدا نشدند، پیام خطا در کنسول و روی صفحه نمایش داده میشود
const errorMessage = "خطا: عناصر لازم صفحه پیدا نشدند. شناسههای HTML و نام کلاسها را در index.html بررسی کنید."; // پیام خطا برای کاربر
console.error(errorMessage); // لاگ برای توسعه
if (resultsDiv) {
resultsDiv.innerHTML = `<p style="color: red;">${errorMessage}</p>`;
}
updateStatus("راهاندازی اولیه با خطا مواجه شد.", true); // پیام برای کاربر
}
}); |