Spaces:
Running
Running
File size: 122,647 Bytes
6ece400 7f2f296 6ece400 51086f9 1669e26 6ece400 209a2f8 1669e26 7f2f296 1669e26 6ece400 adfc6f1 209a2f8 51086f9 b4fdf52 6ece400 adfc6f1 209a2f8 b4fdf52 51086f9 b4fdf52 6ece400 064121d 51086f9 7f2f296 51086f9 b4fdf52 209a2f8 51086f9 b4fdf52 51086f9 1669e26 b4fdf52 209a2f8 8be7b60 209a2f8 1669e26 209a2f8 1669e26 209a2f8 51086f9 209a2f8 b4fdf52 209a2f8 b4fdf52 51086f9 b4fdf52 209a2f8 1669e26 209a2f8 1669e26 b4fdf52 1669e26 209a2f8 1669e26 209a2f8 b4fdf52 209a2f8 51086f9 b4fdf52 209a2f8 1669e26 51086f9 1669e26 51086f9 209a2f8 51086f9 209a2f8 1669e26 209a2f8 51086f9 209a2f8 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 1669e26 209a2f8 51086f9 1669e26 51086f9 209a2f8 b4fdf52 51086f9 1669e26 51086f9 1669e26 51086f9 b4fdf52 209a2f8 1669e26 209a2f8 1669e26 b4fdf52 1669e26 209a2f8 1669e26 209a2f8 1669e26 209a2f8 51086f9 1669e26 209a2f8 1669e26 b4fdf52 51086f9 b4fdf52 209a2f8 1669e26 b4fdf52 b9ed7ff 7141429 6ece400 51086f9 3038ff6 51086f9 6ece400 ddc57d5 6ece400 ddc57d5 6ece400 6b0b418 ddc57d5 51086f9 6ece400 ddc57d5 adfc6f1 f1210e5 b7de50a 6ece400 51086f9 1669e26 209a2f8 97409a4 6ece400 51086f9 ddc57d5 51086f9 4c23ad6 a5574a4 1669e26 adfc6f1 1669e26 209a2f8 f296f61 1669e26 209a2f8 f1210e5 d222b1d 6ece400 51086f9 7f2f296 b9ed7ff f296f61 6ece400 adfc6f1 f296f61 3817336 6ece400 adfc6f1 209a2f8 51086f9 209a2f8 b4fdf52 adfc6f1 f296f61 adfc6f1 b4fdf52 adfc6f1 51086f9 adfc6f1 51086f9 6ece400 adfc6f1 51086f9 adfc6f1 f1210e5 adfc6f1 7f2f296 b4fdf52 adfc6f1 f1210e5 064121d 6b0b418 b4fdf52 6b0b418 51086f9 6b0b418 51086f9 b4fdf52 51086f9 6b0b418 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 b4fdf52 51086f9 6b0b418 1669e26 6b0b418 51086f9 6b0b418 b4fdf52 6b0b418 51086f9 6b0b418 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 f296f61 51086f9 b4fdf52 51086f9 b4fdf52 f296f61 6b0b418 b4fdf52 1669e26 51086f9 209a2f8 1669e26 209a2f8 1669e26 51086f9 1669e26 b4fdf52 51086f9 1669e26 b4fdf52 f296f61 51086f9 1669e26 209a2f8 1669e26 b4fdf52 209a2f8 b4fdf52 1669e26 209a2f8 6b0b418 209a2f8 1669e26 6b0b418 1669e26 f296f61 b4fdf52 51086f9 f296f61 51086f9 f296f61 51086f9 b4fdf52 51086f9 b4fdf52 1669e26 209a2f8 1669e26 209a2f8 1669e26 209a2f8 b4fdf52 1669e26 b4fdf52 209a2f8 b4fdf52 209a2f8 8be7b60 b4fdf52 f296f61 b4fdf52 1669e26 6b0b418 209a2f8 6b0b418 1669e26 b4fdf52 1669e26 209a2f8 1669e26 6b0b418 209a2f8 6b0b418 f296f61 51086f9 b4fdf52 51086f9 6b0b418 b4fdf52 6b0b418 f296f61 b4fdf52 6b0b418 209a2f8 51086f9 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 51086f9 b4fdf52 51086f9 b4fdf52 6b0b418 b4fdf52 51086f9 209a2f8 1669e26 b4fdf52 1669e26 6b0b418 209a2f8 6b0b418 51086f9 6b0b418 51086f9 6b0b418 b4fdf52 6b0b418 51086f9 b4fdf52 6b0b418 b4fdf52 6b0b418 51086f9 6b0b418 b4fdf52 6b0b418 b4fdf52 51086f9 b4fdf52 209a2f8 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 51086f9 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 51086f9 b4fdf52 51086f9 6b0b418 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 b4fdf52 6b0b418 51086f9 6b0b418 b4fdf52 f296f61 6b0b418 b4fdf52 51086f9 b4fdf52 51086f9 6b0b418 f296f61 51086f9 b4fdf52 6b0b418 b4fdf52 51086f9 f296f61 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 f296f61 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 51086f9 f296f61 51086f9 f296f61 51086f9 f296f61 b4fdf52 51086f9 b4fdf52 51086f9 6b0b418 b4fdf52 51086f9 6b0b418 51086f9 6b0b418 4559e6a f296f61 4559e6a 51086f9 f296f61 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 1669e26 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 1669e26 f296f61 b4fdf52 1669e26 b4fdf52 51086f9 f296f61 6b0b418 b4fdf52 6b0b418 b4fdf52 f296f61 6b0b418 b4fdf52 1669e26 b4fdf52 4559e6a b4fdf52 f296f61 b4fdf52 f296f61 4559e6a b4fdf52 209a2f8 b4fdf52 1669e26 209a2f8 6b0b418 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 1669e26 b4fdf52 1669e26 b4fdf52 209a2f8 b4fdf52 209a2f8 b4fdf52 209a2f8 b4fdf52 209a2f8 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 209a2f8 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 209a2f8 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 209a2f8 51086f9 209a2f8 b4fdf52 209a2f8 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 209a2f8 b4fdf52 51086f9 209a2f8 51086f9 6b0b418 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 209a2f8 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 209a2f8 6b0b418 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 209a2f8 6b0b418 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 6b0b418 b4fdf52 51086f9 f296f61 51086f9 209a2f8 51086f9 b4fdf52 1669e26 51086f9 6b0b418 b4fdf52 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 51086f9 b4fdf52 51086f9 6b0b418 f296f61 b4fdf52 6b0b418 b4fdf52 209a2f8 b4fdf52 f296f61 51086f9 f296f61 b4fdf52 51086f9 f296f61 b4fdf52 f296f61 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 51086f9 b4fdf52 51086f9 f296f61 51086f9 6b0b418 209a2f8 51086f9 6b0b418 b4fdf52 6b0b418 51086f9 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 1669e26 6b0b418 b4fdf52 51086f9 b4fdf52 6b0b418 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 4559e6a b4fdf52 f296f61 b4fdf52 f296f61 b4fdf52 4559e6a b4fdf52 51086f9 1669e26 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 b4fdf52 51086f9 b4fdf52 1669e26 b4fdf52 1669e26 51086f9 209a2f8 51086f9 b4fdf52 51086f9 209a2f8 f296f61 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 209a2f8 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 209a2f8 b4fdf52 6b0b418 51086f9 b4fdf52 51086f9 b4fdf52 51086f9 b4fdf52 6b0b418 b4fdf52 51086f9 b4fdf52 6b0b418 51086f9 6b0b418 b4fdf52 6b0b418 b4fdf52 6b0b418 7f2f296 51086f9 7f2f296 adfc6f1 7f2f296 51086f9 |
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 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 |
console.log("Script execution started.");
// --- Sidebar Functionality ---
// (Ваш оригинальный код для Sidebar остается без изменений)
let activeFolderElement = null; // DOM element of the currently active folder for new file creation
let activeSidebarFileId = null; // ID of the file whose content is loaded into fileContentForAI
const sidebarFileContents = new Map(); // Stores content of files in sidebar: fileId -> content
let folderIdCounter = 0;
let fileIdCounter = 0;
function initializeSidebar() {
const sidebar = document.getElementById('sidebar');
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebarTitle = document.getElementById('sidebar-title');
const newFolderBtn = document.getElementById('new-folder-btn');
const newFileBtn = document.getElementById('new-file-btn');
if (sidebarToggle) {
sidebarToggle.addEventListener('click', function() {
sidebar.classList.toggle('sidebar-collapsed');
const icon = sidebarToggle.querySelector('.material-icons-round');
if (sidebar.classList.contains('sidebar-collapsed')) {
icon.textContent = 'chevron_right';
if(sidebarTitle) sidebarTitle.style.display = 'none';
// Use more specific selector if possible, or ensure class exists
document.querySelectorAll('.folder-name, .file-name, #sidebar-controls, .folder-toggle-icon, .folder-actions').forEach(el => el.style.display = 'none');
} else {
icon.textContent = 'chevron_left';
if(sidebarTitle) sidebarTitle.style.display = 'inline';
document.querySelectorAll('.folder-name, .file-name, #sidebar-controls, .folder-toggle-icon, .folder-actions').forEach(el => el.style.display = 'inline');
// Ensure expanded state icons are correct
document.querySelectorAll('.folder.expanded .folder-toggle-icon').forEach(icon => icon.textContent = 'expand_less');
document.querySelectorAll('.folder:not(.expanded) .folder-toggle-icon').forEach(icon => icon.textContent = 'expand_more');
}
});
}
// Use event delegation on a stable parent for folder clicks
const sidebarFoldersContainer = document.getElementById('sidebar-folders'); // Assuming you have a container
if (sidebarFoldersContainer) {
sidebarFoldersContainer.addEventListener('click', function(event) {
const header = event.target.closest('.folder-header');
if (header) {
const folder = header.parentElement;
const icon = header.querySelector('.folder-toggle-icon');
const isToggleButton = event.target.closest('.folder-toggle-icon');
const isActionButton = event.target.closest('.folder-actions');
// Toggle expansion only if clicking header itself or toggle icon, but NOT actions
if (!isActionButton) {
folder.classList.toggle('expanded');
if (icon) { // Check if icon exists
icon.textContent = folder.classList.contains('expanded') ? 'expand_less' : 'expand_more';
}
}
// Set active folder ONLY if clicking the header area (excluding toggle/actions)
if (!isToggleButton && !isActionButton) {
if (activeFolderElement && activeFolderElement !== folder) {
activeFolderElement.classList.remove('active-folder');
}
activeFolderElement = folder;
activeFolderElement.classList.add('active-folder');
console.log("Active folder set to:", activeFolderElement.id || activeFolderElement.querySelector('.folder-name')?.textContent || 'Unknown Folder');
}
}
});
} else {
console.warn("Sidebar folders container not found for event delegation.");
// Fallback to original direct binding (less efficient for many folders)
document.querySelectorAll('.folder-header').forEach(header => {
header.addEventListener('click', function(event) {
// Allow clicks on icons within header to not set active folder
if (event.target.closest('.folder-actions') || event.target.closest('.folder-toggle-icon')) return;
const folder = this.parentElement;
folder.classList.toggle('expanded');
const icon = this.querySelector('.folder-toggle-icon');
if (icon) { // Check if icon exists
icon.textContent = folder.classList.contains('expanded') ? 'expand_less' : 'expand_more';
}
// Set active folder
if (activeFolderElement && activeFolderElement !== folder) {
activeFolderElement.classList.remove('active-folder');
}
activeFolderElement = folder;
activeFolderElement.classList.add('active-folder');
console.log("Active folder set to:", activeFolderElement.id || activeFolderElement.querySelector('.folder-name')?.textContent || 'Unknown Folder');
});
});
// Separate handler for toggle icon if needed with fallback
document.querySelectorAll('.folder-toggle-icon').forEach(icon => {
icon.addEventListener('click', function() {
const folder = this.closest('.folder');
if (folder) {
folder.classList.toggle('expanded');
this.textContent = folder.classList.contains('expanded') ? 'expand_less' : 'expand_more';
}
});
});
}
// Initial setup for existing files if any (though typically added dynamically)
document.querySelectorAll('.file').forEach(file => {
attachFileClickListener(file);
});
if (newFolderBtn) {
newFolderBtn.addEventListener('click', createNewFolder);
}
if (newFileBtn) {
newFileBtn.addEventListener('click', createNewFileInActiveFolder);
}
}
function attachFileClickListener(fileElement) {
fileElement.addEventListener('click', function() {
document.querySelectorAll('.file').forEach(f => f.classList.remove('active'));
this.classList.add('active');
activeSidebarFileId = this.dataset.fileId;
fileContentForAI = sidebarFileContents.get(activeSidebarFileId) || "";
// Update UI to reflect active file
const fileName = this.dataset.fileName || 'Selected File';
if(mainInput) {
mainInput.value = `File: ${fileName}\nTask: Summarize this file.`;
mainInput.placeholder = `Task for file: ${fileName}...`;
}
if (fileInfoEl) fileInfoEl.style.display = 'none'; // Hide general upload info
if (urlInfoEl) urlInfoEl.style.display = 'none'; // Hide URL info
console.log(`Active file set: ${activeSidebarFileId}, content loaded for AI.`);
if (fileInput) fileInput.value = null; // Clear file input if a sidebar file is selected
// Ensure the correct tab is active if needed (e.g., switch back from video)
if (videoStreamPanel && videoStreamPanel.classList.contains('active')) {
// Optionally switch back to output or code tab
document.querySelector('.tab-button[data-tab="output"]')?.click(); // Use optional chaining
}
});
}
function createNewFolder() {
const folderName = prompt("Enter new folder name:", "New Folder");
if (!folderName || folderName.trim() === "") return;
folderIdCounter++;
const newFolderId = `folder-dynamic-${folderIdCounter}`;
const sanitizedFolderName = DOMPurify.sanitize(folderName);
const folderDiv = document.createElement('div');
folderDiv.className = 'folder';
folderDiv.id = newFolderId;
const headerDiv = document.createElement('div');
headerDiv.className = 'folder-header';
headerDiv.innerHTML = `
<span class="folder-icon material-icons-round">folder</span>
<span class="folder-name">${sanitizedFolderName}</span>
<span class="folder-toggle-icon material-icons-round">expand_more</span>
<span class="folder-actions" style="/* display:none; */"> <!-- Actions can be visible if needed -->
<!-- Optional: Add folder-specific actions here if needed -->
<!-- <button class="icon-button"><span class="material-icons-round">edit</span></button> -->
<!-- <button class="icon-button"><span class="material-icons-round">delete</span></button> -->
</span>
`;
const contentDiv = document.createElement('div');
contentDiv.className = 'folder-content';
folderDiv.appendChild(headerDiv);
folderDiv.appendChild(contentDiv);
const sidebarFoldersContainer = document.getElementById('sidebar-folders'); // Target specific container
// Find a good place to insert, e.g., before the 'General Uploads' folder or at the end
const generalUploadsFolder = document.getElementById('uploads-dropzone'); // Assuming this is a folder
if (sidebarFoldersContainer) {
if (generalUploadsFolder && generalUploadsFolder.parentElement === sidebarFoldersContainer) {
sidebarFoldersContainer.insertBefore(folderDiv, generalUploadsFolder);
} else {
sidebarFoldersContainer.appendChild(folderDiv); // Append to container if dropzone not found there
}
} else {
// Fallback if specific container not found
const mainSidebar = document.getElementById('sidebar');
if (generalUploadsFolder && mainSidebar) {
mainSidebar.insertBefore(folderDiv, generalUploadsFolder);
} else if (mainSidebar) {
mainSidebar.appendChild(folderDiv);
} else {
console.error("Cannot add folder: Neither #sidebar-folders nor #sidebar found.");
return;
}
}
// Make new folder active and expand it
if (activeFolderElement) activeFolderElement.classList.remove('active-folder');
activeFolderElement = folderDiv;
activeFolderElement.classList.add('active-folder');
activeFolderElement.classList.add('expanded'); // Expand new folder by default
const newIcon = headerDiv.querySelector('.folder-toggle-icon');
if (newIcon) newIcon.textContent = 'expand_less'; // Update icon
console.log(`Folder "${sanitizedFolderName}" created and set as active.`);
// Check sidebar collapse state and hide text if needed
const mainSidebar = document.getElementById('sidebar');
if (mainSidebar && mainSidebar.classList.contains('sidebar-collapsed')) {
document.querySelectorAll(`#${newFolderId} .folder-name, #${newFolderId} .folder-toggle-icon, #${newFolderId} .folder-actions`).forEach(el => el.style.display = 'none');
}
}
function createNewFileInActiveFolder() {
// Enhanced logic to find a folder if none is active
if (!activeFolderElement) {
console.log("No active folder selected. Trying to find a default or first folder.");
activeFolderElement = document.getElementById('uploads-dropzone') || // Prioritize specific dropzone folder
document.querySelector('#sidebar-folders .folder'); // Fallback to the first folder
if (activeFolderElement) {
if (!activeFolderElement.classList.contains('active-folder')) {
if(document.querySelector('.folder.active-folder')) document.querySelector('.folder.active-folder').classList.remove('active-folder');
activeFolderElement.classList.add('active-folder');
}
console.log("Using folder:", activeFolderElement.id || activeFolderElement.querySelector('.folder-name')?.textContent);
} else {
alert("No folder available (active, default, or first found) to add a file to. Please create a folder first.");
return;
}
}
// Ensure the target folder (active or found) is expanded
if (!activeFolderElement.classList.contains('expanded')) {
activeFolderElement.classList.add('expanded');
const icon = activeFolderElement.querySelector('.folder-toggle-icon');
if (icon) icon.textContent = 'expand_less';
console.log("Expanded target folder:", activeFolderElement.id || activeFolderElement.querySelector('.folder-name')?.textContent);
}
const fileName = prompt("Enter new file name (e.g., script.js, notes.txt):", "new_file.txt");
if (!fileName || fileName.trim() === "") return;
fileIdCounter++;
const newFileId = `file-dynamic-${fileIdCounter}`;
const sanitizedFileName = DOMPurify.sanitize(fileName);
const fileDiv = document.createElement('div');
fileDiv.className = 'file';
fileDiv.dataset.fileId = newFileId;
fileDiv.dataset.fileName = sanitizedFileName;
fileDiv.innerHTML = `
<span class="file-icon material-icons-round">description</span>
<span class="file-name">${sanitizedFileName}</span>
`;
const folderContent = activeFolderElement.querySelector('.folder-content');
if (folderContent) {
folderContent.appendChild(fileDiv);
} else {
console.error("Could not find .folder-content in active folder:", activeFolderElement);
// As a fallback, append to activeFolderElement itself if .folder-content is missing (less ideal structure)
activeFolderElement.appendChild(fileDiv);
}
sidebarFileContents.set(newFileId, ""); // New file is empty initially
attachFileClickListener(fileDiv);
// Check sidebar collapse state and hide text if needed
const mainSidebar = document.getElementById('sidebar');
if (mainSidebar && mainSidebar.classList.contains('sidebar-collapsed')) {
fileDiv.querySelector('.file-name').style.display = 'none';
}
// Optionally, make the new file active
fileDiv.click(); // Simulate click to make it active and load its (empty) content
const activeFolderName = activeFolderElement.querySelector('.folder-name')?.textContent || activeFolderElement.id;
console.log(`File "${fileName}" created in folder "${activeFolderName}" with ID ${newFileId}.`);
}
// --- Module Imports ---
// (Ваш оригинальный код импорта остается без изменений)
let GoogleGenerativeAI, marked, DOMPurify;
try {
const gaModule = await import('https://esm.sh/@google/generative-ai');
GoogleGenerativeAI = gaModule.GoogleGenerativeAI;
const markedModule = await import('https://esm.sh/marked');
marked = markedModule.marked;
const domPurifyModule = await import('https://esm.sh/dompurify');
DOMPurify = domPurifyModule.default;
if (!GoogleGenerativeAI || !marked || !DOMPurify || typeof Chart === 'undefined' || typeof pdfjsLib === 'undefined' || typeof Babel === 'undefined') {
let missing = [];
if(!GoogleGenerativeAI) missing.push("GoogleGenerativeAI");
if(!marked) missing.push("marked");
if(!DOMPurify) missing.push("DOMPurify");
if(typeof Chart === 'undefined') missing.push("Chart.js");
if(typeof pdfjsLib === 'undefined') missing.push("pdf.js");
if(typeof Babel === 'undefined') missing.push("Babel");
if (missing.length > 0) throw new Error(`Critical modules failed to load: ${missing.join(', ')}.`);
}
console.log("Core & external libraries seem available.");
} catch (error) {
console.error("Fatal Error: Could not load core dependencies:", error);
const errorMsgEl = document.getElementById('init-error-message') || { textContent: "" };
const sdkErrorOverlayEl = document.getElementById('sdk-error-overlay') || { classList: { add: () => {} }};
errorMsgEl.textContent = `Failed to load critical libraries: ${error.message}. Check network, console, and ensure all script tags are correct.`;
sdkErrorOverlayEl.classList.add('visible');
throw error; // Stop further execution
}
// --- UI Element References ---
// (Ваши оригинальные ссылки на UI элементы остаются без изменений)
const codeInputArea = document.getElementById('code-input-area');
const mainInput = document.getElementById('main-input');
const sendBtn = document.getElementById('send-btn');
const researchBtn = document.getElementById('research-btn');
const stopBtn = document.getElementById('stop-btn');
const uploadBtnLabel = document.getElementById('upload-btn-label');
const fileInput = document.getElementById('file-input');
const fileInfoEl = document.getElementById('file-info'); // General file info, less prominent now
const fileNameEl = document.getElementById('file-name'); // General file name, less prominent
const urlInfoEl = document.getElementById('url-info');
const urlInfoTextEl = document.getElementById('url-info-text');
const fetchUrlBtn = document.getElementById('fetch-url-btn');
const aiOverlay = document.getElementById('ai-overlay');
const aiStatusText = document.getElementById('ai-status-text');
const initStatusText = document.getElementById('init-status-text');
const sdkErrorOverlay = document.getElementById('sdk-error-overlay');
const reloadButton = document.getElementById('reload-button');
const outputFrame = document.getElementById('output-frame');
const fullCodeContent = document.getElementById('full-code-content');
const researchProgressEl = document.getElementById('research-progress');
const languageSelector = document.getElementById('language-selector');
const tabButtons = document.querySelectorAll('.tab-button');
const videoStreamPanel = document.getElementById('video-stream-panel');
const videoElement = document.getElementById('video-element');
const videoCanvas = document.getElementById('video-canvas');
const startStreamBtn = document.getElementById('start-stream-btn');
const stopStreamBtn = document.getElementById('stop-stream-btn');
const aiPlanContainer = document.getElementById('ai-plan-container');
const aiPlanContent = document.getElementById('ai-plan-content');
// --- Application State ---
// (Ваше оригинальное состояние приложения остается без изменений)
let genAI;
let model;
let generationInProgress = false;
let stopGenerationFlag = false;
let generatedCode = '';
let fileContentForAI = ""; // Primarily managed by sidebar file clicks or URL fetch now
let pyodide;
let pyodideLoadingPromise = null;
let currentLanguage = "HTML/CSS/JS (Web Page)";
window.currentAiChart = null;
let screenStream = null;
let currentScreenFrameDataUrl = "";
// --- Configuration Constants ---
// *** ВОССТАНОВЛЕНО: Ваш оригинальный API ключ и Имя модели ***
const API_KEY = "AIzaSyBCURZf72ZWwMN2SHZ7XCQoNgExV4WMX8E";
const MODEL_NAME = "gemini-2.0-flash-thinking-exp-1219"; // Ensure multimodal for screen analysis
// *** КОНЕЦ ВОССТАНОВЛЕНИЯ ***
const CORS_PROXY_URL_PREFIX = "https://api.allorigins.win/raw?url=";
// --- NEW: Solver Hyperparameters ---
const solverParams = {
// Temperatures (Core) - Controls randomness/creativity vs focus
initial_gen_temp_single: 1.0, // Max exploration for Initial Plan/Idea Generation (G)
refine_temp: 0.8, // High exploration for Research/Refinement (R) - Slightly reduced for more focused research
verify_temp: 0.2, // High precision/determinism for Verification/Critique steps (Conceptual) (C)
synthesis_temp: 0.85, // Balanced synthesis for final Code/Analysis/Synthesis (S)
// Sampling - Fine-tune token selection diversity
topP: 0.98, // Wide nucleus for diversity, allows less probable tokens
topK: 80, // Allow reasonably diverse tokens, prevents overly niche choices
// Retry logic (Conceptual - influences prompt design)
max_retries: 3, // Number of internal thought/retry cycles the AI should simulate
// Token Limits (Adjust based on model & typical use cases - Gemini 1.5 handles large contexts well)
// These should be generous for complex tasks, but check model limits if changing MODEL_NAME
max_initial_tokens: 100000, // Max tokens for the Plan generation response
max_critique_tokens: 100000, // Max tokens for critique steps (if implemented separately)
max_refine_tokens: 100000, // Max tokens for refinement/research responses
max_synthesis_tokens: 100000, // Max tokens for the final code/analysis generation // Adjusted slightly down from extreme max
max_reasoning_tokens: 100000, // Max tokens for internal reasoning (e.g., within Synthesis)
// --- PPO-like Prompting Control Parameters (Conceptual - embedded in prompts) ---
// These guide the *style* and *focus* of the generation within the prompts.
// Values represent desired emphasis (0.0 = ignore, 1.0 = maximum focus).
// For Default Mode (Code/Math/Complex Tasks Focus - MAXIMIZED emphasis)
depth_focus_max: 0.95, // Emphasize extreme depth, comprehensive understanding, consider edge cases.
creativity_focus_max: 0.90, // Emphasize high creativity, novel approaches, unique solutions, diverse ideas.
analytical_rigor_max: 0.98, // Demand extreme rigor, correctness, logical consistency, precision, verification.
alternative_exploration_max: 0.95, // Demand wide exploration of alternatives, compare approaches, justify choices.
efficiency_focus_max: 0.90, // Explicit focus on optimal efficiency, performance, conciseness, resourcefulness.
// For Simple Mode (Generic/Simpler Tasks Focus - MODERATE emphasis)
depth_focus_simple: 0.65, // Encourage moderate depth/insight, cover main points.
creativity_focus_simple: 0.70, // Encourage considering alternatives/angles, standard creative elements.
analytical_rigor_simple: 0.75, // Encourage reasonable accuracy/clarity, logical flow.
alternative_exploration_simple: 0.60, // Encourage awareness of options, standard approaches.
efficiency_focus_simple: 0.50, // Mild awareness of efficiency, standard practices.
};
const SUPPORTED_LANGUAGES = [
"HTML/CSS/JS (Web Page)", "JavaScript (Standalone)", "TypeScript",
"React Component", "Next.js Page (Client-Side)", "Python (Pyodide)",
"Java (Code Display)", "Go (Code Display)", "Node.js (Code Display)",
"Flutter (Dart Code Display)", "Unity (C# Script Display)", "Data Analysis (from text/file/URL/screen)"
];
const DATA_ANALYSIS_LANG = "Data Analysis (from text/file/URL/screen)";
// --- Helper Functions ---
// (Ваши оригинальные вспомогательные функции остаются без изменений)
function showError(message) {
console.error("showError called:", message);
const errorMsgEl = document.getElementById('init-error-message');
if (errorMsgEl) errorMsgEl.textContent = message;
if (sdkErrorOverlay) sdkErrorOverlay.classList.add('visible');
else alert("A critical error occurred: " + message);
}
function updateButtonStates(isGenerating) {
if (sendBtn) sendBtn.disabled = isGenerating;
if (researchBtn) researchBtn.disabled = isGenerating;
if (fetchUrlBtn) fetchUrlBtn.disabled = isGenerating;
if (fileInput) fileInput.disabled = isGenerating;
if (uploadBtnLabel) {
uploadBtnLabel.style.cursor = isGenerating ? 'not-allowed' : 'pointer';
uploadBtnLabel.style.opacity = isGenerating ? 0.5 : 1;
}
if (startStreamBtn) startStreamBtn.disabled = isGenerating || screenStream !== null;
if (stopBtn) stopBtn.style.display = isGenerating ? 'block' : 'none';
if (!isGenerating && stopBtn) stopBtn.disabled = false; // Re-enable stop button when not generating
const newFolderBtn = document.getElementById('new-folder-btn');
const newFileBtn = document.getElementById('new-file-btn');
if(newFolderBtn) newFolderBtn.disabled = isGenerating;
if(newFileBtn) newFileBtn.disabled = isGenerating;
}
function showAiOverlay(visible, statusText = "Thinking...") {
if (!aiOverlay || !aiStatusText || !researchProgressEl) {
console.warn("AI Overlay elements not found.");
return;
}
aiStatusText.textContent = statusText;
aiOverlay.classList.toggle('visible', visible);
// Determine if research progress should be shown
const isResearching = statusText.toLowerCase().includes("researching");
// Check for a potential data attribute, defaulting to false if not present or not "true"
const showProgressAttr = researchProgressEl.dataset.showProgress === "true";
const showResearchProgress = visible && isResearching && showProgressAttr;
researchProgressEl.classList.toggle('hidden', !showResearchProgress);
}
function clearInputSourceInfo(preserveActiveFile = false) {
if (fileInfoEl) fileInfoEl.style.display = 'none';
if (fileNameEl) fileNameEl.textContent = '';
if (urlInfoEl) urlInfoEl.style.display = 'none';
if (urlInfoTextEl) urlInfoTextEl.textContent = '';
if (!preserveActiveFile) {
fileContentForAI = "";
activeSidebarFileId = null; // Clear which sidebar file is active for AI
document.querySelectorAll('.file.active').forEach(f => f.classList.remove('active'));
if(mainInput && languageSelector) { // Reset placeholder if not preserving active file
mainInput.placeholder = languageSelector.value === DATA_ANALYSIS_LANG ?
`Describe task for content (file, URL, screen)...` :
`Enter task for ${languageSelector.value.split('(')[0].trim()}...`;
}
}
currentScreenFrameDataUrl = "";
if (fileInput) fileInput.value = null;
}
// --- Initialization ---
// (Ваша оригинальная инициализация остается без изменений)
async function initializeApp() {
console.log("initializeApp started");
try {
initializeSidebar(); // Initialize sidebar first
if(initStatusText) initStatusText.textContent = "Initializing UI...";
setupLanguageSelector();
setupEventListeners();
clearInputSourceInfo(); // Initial clear
if(initStatusText) initStatusText.textContent = "Initializing Gemini...";
if (!GoogleGenerativeAI) throw new Error("GoogleGenerativeAI SDK is not loaded.");
genAI = new GoogleGenerativeAI(API_KEY);
// *** Используем ваше имя модели ***
model = genAI.getGenerativeModel({ model: MODEL_NAME });
console.log(`Gemini model ${MODEL_NAME} initialized.`);
if(codeInputArea) codeInputArea.disabled = false;
if(mainInput) mainInput.disabled = false;
updateButtonStates(false);
if(mainInput) mainInput.placeholder = `Enter task, paste URL, use screen share, upload file, or select a file from workspace...`;
if(codeInputArea) codeInputArea.placeholder = "Results will appear here...";
if(initStatusText) initStatusText.textContent = "Gemini Ready. Loading Pyodide (background)...";
// Use existing Pyodide loading logic
pyodideLoadingPromise = loadPyodideInstance().then(() => {
if(initStatusText) {
initStatusText.textContent = initStatusText.textContent.replace("Loading Pyodide (background)...", "Ready (Gemini, Pyodide)");
console.log("Pyodide loaded successfully in background.");
}
}).catch(err => {
console.warn("Pyodide background load failed:", err);
if(initStatusText) {
initStatusText.textContent = initStatusText.textContent.replace("Loading Pyodide (background)...", "Ready (Gemini; Pyodide failed)");
}
});
} catch (error) {
console.error("Initialization error in initializeApp:", error);
showError(`Failed to initialize application: ${error.message}. Ensure API Key is correct and model name exists.`);
}
}
function setupLanguageSelector() {
if(!languageSelector) return;
// Clear existing options before adding new ones
languageSelector.innerHTML = '';
SUPPORTED_LANGUAGES.forEach(lang => {
const option = document.createElement('option');
option.value = lang; option.textContent = lang;
languageSelector.appendChild(option);
});
// Set default based on your original code or desired default
const defaultLang = DATA_ANALYSIS_LANG; // Or "HTML/CSS/JS (Web Page)" if preferred
languageSelector.value = defaultLang;
currentLanguage = defaultLang;
handleLanguageChange({target: languageSelector}); // Update placeholder initially
}
async function loadPyodideInstance() {
// Check if already loading or loaded
if (pyodideLoadingPromise) {
console.log("Pyodide already loading/loaded (promise exists).");
return pyodideLoadingPromise;
}
if (pyodide) {
console.log("Pyodide instance already available.");
return Promise.resolve(pyodide);
}
if (typeof loadPyodide === "undefined") {
console.error("Pyodide main script (loadPyodide) not loaded.");
throw new Error("Pyodide main script not loaded.");
}
console.log("Attempting to load Pyodide...");
try {
// Store the promise immediately
pyodideLoadingPromise = loadPyodide({
// indexURL: "https://cdn.jsdelivr.net/pyodide/v0.25.0/full/" // Optional: specify version
});
const inst = await pyodideLoadingPromise;
console.log("Pyodide core loaded. Loading micropip...");
await inst.loadPackage(["micropip"]);
console.log("Micropip loaded.");
pyodide = inst;
// No need to clear pyodideLoadingPromise here, it resolves with the instance
console.log("Pyodide instance initialized successfully.");
return pyodide;
} catch (error) {
console.error("Pyodide loading error:", error);
pyodide = null; // Ensure pyodide is null on failure
pyodideLoadingPromise = Promise.reject(error); // Ensure promise rejects
throw error; // Re-throw error
}
}
// --- Event Listeners Setup ---
// (Ваша оригинальная настройка слушателей, но убедитесь, что send/research вызывают *WithPlan* версии)
function setupEventListeners() {
console.log("Setting up event listeners.");
if(reloadButton) reloadButton.addEventListener('click', () => window.location.reload());
if(tabButtons) {
tabButtons.forEach(button => {
button.addEventListener('click', () => {
tabButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
const tabName = button.dataset.tab;
document.getElementById('output-tab')?.classList.toggle('hidden', tabName !== 'output');
document.getElementById('output-tab')?.classList.toggle('active', tabName === 'output');
document.getElementById('code-tab')?.classList.toggle('hidden', tabName !== 'code');
document.getElementById('code-tab')?.classList.toggle('active', tabName === 'code');
const screenSharePanel = document.getElementById('video-stream-panel');
if (screenSharePanel) {
screenSharePanel.classList.toggle('hidden', tabName !== 'video');
screenSharePanel.classList.toggle('active', tabName === 'video');
}
handleLanguageChange({target: languageSelector}); // Update placeholder on tab change
});
});
// Ensure initial state is correct (e.g., 'output' tab is visible)
document.querySelector('.tab-button[data-tab="output"]')?.click();
}
if(fileInput) fileInput.addEventListener('change', handleFileUploadToSidebar); // Uses original handler
if(fetchUrlBtn) fetchUrlBtn.addEventListener('click', handleFetchUrl);
if(languageSelector) languageSelector.addEventListener('change', handleLanguageChange);
// *** Убедитесь, что используются новые обработчики с планированием ***
if(sendBtn) sendBtn.addEventListener('click', handleSendRequestWithPlan);
if(researchBtn) {
researchBtn.addEventListener('click', () => {
const activeFileEl = activeSidebarFileId ? document.querySelector(`.file[data-file-id="${activeSidebarFileId}"]`) : null;
const fileNameHint = activeFileEl ? `Topic related to file: ${activeFileEl.dataset.fileName}` : "";
const query = mainInput.value.trim() || fileNameHint || fileContentForAI.substring(0, 100); // Use file content snippet as last resort topic
if (query) {
// *** Убедитесь, что используется новый обработчик с планированием ***
performResearchWithPlan(query);
} else {
alert("Please enter a topic, select a file, provide content via URL/upload, or use screen share to provide context for research.");
}
});
}
if(mainInput) {
mainInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
const screenSharePanelActive = videoStreamPanel && videoStreamPanel.classList.contains('active');
// Prioritize active file/screen over URL in input, fetch URL only if input looks like URL AND no file/screen active
if (isValidURL(mainInput.value.trim()) && !screenSharePanelActive && !activeSidebarFileId && !fileContentForAI) { // Check fileContent too
handleFetchUrl();
} else {
// *** Убедитесь, что используется новый обработчик с планированием ***
handleSendRequestWithPlan();
}
}
});
}
if(stopBtn) stopBtn.addEventListener('click', handleStopGeneration);
if(startStreamBtn) startStreamBtn.addEventListener('click', startScreenStream);
if(stopStreamBtn) stopStreamBtn.addEventListener('click', stopScreenStream);
}
// --- File, URL Processing ---
// (Ваши оригинальные функции обработки файлов и URL остаются без изменений)
async function handleFileUploadToSidebar(event) {
const file = event.target.files[0];
if (!file) return;
let targetFolderElement = activeFolderElement;
// Enhanced Folder Finding Logic from your original code
if (!targetFolderElement) {
targetFolderElement = document.getElementById('uploads-dropzone') || // Prioritize specific dropzone folder
document.querySelector('#sidebar-folders .folder') || // Fallback to the first folder in container
document.querySelector('.folder'); // Last resort: any folder
if (targetFolderElement) {
if (!targetFolderElement.classList.contains('active-folder')) {
if(document.querySelector('.folder.active-folder')) document.querySelector('.folder.active-folder').classList.remove('active-folder');
targetFolderElement.classList.add('active-folder');
}
activeFolderElement = targetFolderElement; // Set it as active for consistency
console.log("No active folder for upload, using default/first folder:", activeFolderElement.id || activeFolderElement.querySelector('.folder-name')?.textContent);
} else {
alert("Error: No folder available to upload the file to. Please create a folder first.");
clearInputSourceInfo();
if (fileInput) fileInput.value = null;
return;
}
}
// Ensure target folder is expanded
if (!targetFolderElement.classList.contains('expanded')) {
targetFolderElement.classList.add('expanded');
const icon = targetFolderElement.querySelector('.folder-toggle-icon');
if(icon) icon.textContent = 'expand_less';
}
// Use the general file info elements temporarily for status during processing
if(fileNameEl) fileNameEl.textContent = file.name;
if(fileInfoEl) fileInfoEl.style.display = 'flex';
showAiOverlay(true, `Processing ${file.name} for sidebar...`);
try {
let textContent = "";
const fileType = file.name.split('.').pop().toLowerCase();
const MAX_FILE_SIZE_MB = 50; // Example limit: 50MB
if (file.size > MAX_FILE_SIZE_MB * 1024 * 1024) {
throw new Error(`File size (${(file.size / 1024 / 1024).toFixed(1)}MB) exceeds the ${MAX_FILE_SIZE_MB}MB limit.`);
}
// Your original file reading logic
if (fileType === 'pdf') textContent = await readPdfFile(file);
else if (['txt', 'md', 'csv', 'js', 'py', 'html', 'css', 'json', 'xml', 'java', 'c', 'cpp', 'cs', 'go', 'rb', 'php', 'swift', 'kt', 'dart', 'rs'].includes(fileType)) textContent = await readGenericTextFile(file);
else if (fileType === 'docx') {
if (typeof mammoth !== 'undefined') textContent = await readDocxFileWithMammoth(file);
else { textContent = `(mammoth.js not loaded for .docx: ${file.name})`; console.warn("Enable mammoth.js for .docx.");}
} else if (fileType === 'doc') { textContent = `(.doc not supported: ${file.name})`; console.warn(".doc not supported.");
} else { textContent = `(Unsupported readable type .${fileType}: ${file.name}, uploaded as reference)`; console.warn(`Unsupported type .${fileType} for direct reading, file added by name only.`);}
// Truncate reasonably, adjust limit as needed
const MAX_CONTENT_LENGTH = 500000; // Your original limit
if (textContent.length > MAX_CONTENT_LENGTH) {
console.warn(`Truncating file content from ${textContent.length} to ${MAX_CONTENT_LENGTH} characters.`);
textContent = textContent.substring(0, MAX_CONTENT_LENGTH);
}
fileIdCounter++;
const newFileId = `file-uploaded-${fileIdCounter}`;
const sanitizedFileName = DOMPurify.sanitize(file.name);
const fileDiv = document.createElement('div');
fileDiv.className = 'file';
fileDiv.dataset.fileId = newFileId;
fileDiv.dataset.fileName = sanitizedFileName;
fileDiv.innerHTML = `<span class="file-icon material-icons-round">description</span><span class="file-name">${sanitizedFileName}</span>`;
const folderContent = targetFolderElement.querySelector('.folder-content');
if (folderContent) {
folderContent.appendChild(fileDiv);
} else { // Fallback if no .folder-content div
console.warn("Target folder missing '.folder-content' div, appending file directly to folder element.", targetFolderElement);
targetFolderElement.appendChild(fileDiv);
}
sidebarFileContents.set(newFileId, textContent); // Store extracted content
attachFileClickListener(fileDiv);
// Check sidebar collapse state and hide text if needed
const mainSidebar = document.getElementById('sidebar');
if (mainSidebar && mainSidebar.classList.contains('sidebar-collapsed')) {
fileDiv.querySelector('.file-name').style.display = 'none';
}
fileDiv.click(); // Make the newly uploaded file active
// Switch to Data Analysis if not already selected
if(languageSelector && languageSelector.value !== DATA_ANALYSIS_LANG) {
languageSelector.value = DATA_ANALYSIS_LANG;
handleLanguageChange({target: languageSelector}); // Trigger language change effects
}
if(mainInput) { // Updated placeholder and value after click()
mainInput.placeholder = `Task for uploaded file: ${sanitizedFileName}...`;
mainInput.value = `File: ${sanitizedFileName}\nTask: Analyze this uploaded file.`;
mainInput.focus();
}
const activeFolderName = targetFolderElement.querySelector('.folder-name')?.textContent || targetFolderElement.id;
console.log(`File "${file.name}" uploaded to folder "${activeFolderName}" and set as active.`);
} catch (error) {
console.error("File processing error for sidebar:", error);
if(mainInput) mainInput.value = `Error processing ${file.name}: ${error.message}.`;
alert(`Error processing file: ${error.message}`);
clearInputSourceInfo(); // Clear info on error
} finally {
showAiOverlay(false);
if (fileInfoEl) fileInfoEl.style.display = 'none'; // Hide general processing info
if (fileInput) fileInput.value = null; // Clear file input
}
}
function readGenericTextFile(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = event => resolve(event.target.result); reader.onerror = error => reject(error); reader.readAsText(file); }); }
async function readPdfFile(file) {
if (typeof pdfjsLib === 'undefined') throw new Error("pdf.js library is not loaded.");
// Use the worker source defined in your original code if present, or the CDN fallback
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
// Check if pdfjsLib is defined before accessing GlobalWorkerOptions
if (typeof pdfjsLib !== 'undefined') {
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js'; // Your original version
console.log("Set pdf.js worker source to CDN fallback.");
} else {
throw new Error("pdf.js library defined but GlobalWorkerOptions not accessible.");
}
}
const arrayBuffer = await file.arrayBuffer(); const pdf = await pdfjsLib.getDocument({data: arrayBuffer}).promise;
let fullText = ""; for (let i = 1; i <= pdf.numPages; i++) { try {const page = await pdf.getPage(i); const textContent = await page.getTextContent(); fullText += textContent.items.map(item => item.str).join(" ") + "\n";} catch (pageError){ console.error(`Error processing PDF page ${i}:`, pageError); fullText += `\n[Error reading page ${i}]\n`;}} return fullText;
}
async function readDocxFileWithMammoth(file) { if (typeof mammoth === 'undefined') throw new Error("mammoth.js not loaded."); const arrayBuffer = await file.arrayBuffer(); const result = await mammoth.extractRawText({ arrayBuffer: arrayBuffer }); return result.value; }
function isValidURL(string) { try { const url = new URL(string); return url.protocol === "http:" || url.protocol === "https:"; } catch (_) { return false; }}
async function handleFetchUrl() {
const url = mainInput.value.trim(); if (!isValidURL(url)) { alert("Invalid URL. Please enter a full URL including http:// or https://"); return; }
clearInputSourceInfo(); // This will clear activeSidebarFileId
if(urlInfoTextEl) urlInfoTextEl.textContent = url.length > 50 ? url.substring(0,47)+"..." : url; // Increased length slightly
if(urlInfoEl) urlInfoEl.style.display = 'flex';
showAiOverlay(true, `Fetching ${url.substring(0,50)}...`);
try {
const response = await fetch(`${CORS_PROXY_URL_PREFIX}${encodeURIComponent(url)}`);
if (!response.ok) {
const errorText = await response.text();
console.error("Fetch error response:", errorText);
throw new Error(`Fetch failed: ${response.status} ${response.statusText}. Proxy or target server issue. Details: ${errorText.substring(0,150)}`);
}
// Your original HTML parsing logic
const html = await response.text();
const doc = new DOMParser().parseFromString(html, "text/html");
const article = doc.querySelector('article, main, [role="main"], .main, #main, .content, #content, body'); // Your original selector
let text = "";
if (article) {
article.querySelectorAll('script, style, noscript, iframe, header, footer, nav, aside').forEach(el => el.remove());
text = article.textContent || "";
} else {
console.warn("Could not find main content element using selectors, falling back to body text content.");
text = doc.body ? doc.body.textContent || "" : ""; // Fallback to body
}
// Clean up whitespace and limit size using your original limit
const MAX_URL_CONTENT_LENGTH = 500000; // Your original limit
fileContentForAI = text.replace(/\s\s+/g, ' ').trim();
if (fileContentForAI.length > MAX_URL_CONTENT_LENGTH) {
console.warn(`Truncating URL content from ${fileContentForAI.length} to ${MAX_URL_CONTENT_LENGTH} characters.`);
fileContentForAI = fileContentForAI.substring(0, MAX_URL_CONTENT_LENGTH);
}
activeSidebarFileId = null; // Ensure no sidebar file is considered active
document.querySelectorAll('.file.active').forEach(f => f.classList.remove('active'));
if (!fileContentForAI.trim()) console.warn("Extracted text from URL is empty or only whitespace.");
if(languageSelector) languageSelector.value = DATA_ANALYSIS_LANG;
currentLanguage = DATA_ANALYSIS_LANG;
if(mainInput) {
mainInput.placeholder = `Task for URL content...`; mainInput.value = `URL: ${url}\nTask: Summarize.`; mainInput.focus();
}
console.log(`Successfully fetched and processed content from URL: ${url}`);
} catch (error) {
console.error("URL fetch or processing error:", error);
if(mainInput) mainInput.value = `Error fetching ${url}: ${error.message}.`;
fileContentForAI = "";
alert(`URL Error: ${error.message}. This could be due to the CORS proxy, the target website blocking requests, network issues, or errors during content extraction.`);
clearInputSourceInfo(); // Clear info on error
} finally { showAiOverlay(false); }
}
// --- Screen Share Functions ---
// (Ваши оригинальные функции Screen Share остаются без изменений)
async function startScreenStream() {
if (screenStream) {
console.log("Screen stream already active.");
return;
}
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
try {
screenStream = await navigator.mediaDevices.getDisplayMedia({
video: { cursor: "always" }, // Recommend showing cursor
audio: false
});
if(videoElement) {
videoElement.srcObject = screenStream;
videoElement.onloadedmetadata = () => {
// Use promise-based play()
videoElement.play().catch(e => console.error("Video play failed:", e));
};
// Handle potential errors during loading
videoElement.onerror = (e) => {
console.error("Video element error:", e);
alert("Error loading video stream.");
stopScreenStream(false);
};
}
if(startStreamBtn) startStreamBtn.classList.add('hidden');
if(stopStreamBtn) stopStreamBtn.classList.remove('hidden');
console.log("Screen sharing started.");
// Handle stop via browser UI
screenStream.getVideoTracks()[0].onended = () => {
console.log("Screen sharing stopped by user or browser UI.");
stopScreenStream(false); // Don't log again inside stop function
};
updateButtonStates(generationInProgress); // Update buttons now that stream is active
} catch (err) {
console.error("Screen share access error: ", err);
// Provide more specific feedback if possible
if (err.name === 'NotAllowedError') {
alert("Screen share permission denied. Please allow screen sharing in the browser prompt.");
} else if (err.name === 'NotFoundError') {
// This error might mean no screen selected, or sometimes transient issues
alert("No screen/window selected, or screen sharing is unavailable. Please try again.");
} else if (err.name === 'NotReadableError') {
alert("Screen source cannot be read. This might be due to hardware issues or operating system permissions. Please try sharing a different window or screen.");
}
else {
alert("Screen share access error: " + err.message + "\nMake sure you select a screen/window to share.");
}
if (screenStream) stopScreenStream(false); // Ensure cleanup if partially started
updateButtonStates(generationInProgress); // Update buttons after failure
}
} else {
alert("Screen Sharing (getDisplayMedia API) is not supported by your browser!");
updateButtonStates(generationInProgress); // Update buttons if API not supported
}
}
function stopScreenStream(logMessage = true) {
if (screenStream) {
screenStream.getTracks().forEach(track => track.stop());
if(videoElement) videoElement.srcObject = null;
screenStream = null;
if(startStreamBtn) startStreamBtn.classList.remove('hidden');
if(stopStreamBtn) stopStreamBtn.classList.add('hidden');
currentScreenFrameDataUrl = ""; // Clear last captured frame
if (logMessage) console.log("Screen sharing stopped.");
} else {
// Ensure buttons are in the correct state even if called redundantly
if(startStreamBtn) startStreamBtn.classList.remove('hidden');
if(stopStreamBtn) stopStreamBtn.classList.add('hidden');
}
updateButtonStates(generationInProgress); // Always update buttons after stopping attempt
}
async function captureScreenFrameAsDataURL() {
// Added check for readyState >= HAVE_METADATA from your original code
if (!screenStream || !videoElement || !videoElement.srcObject || videoElement.readyState < videoElement.HAVE_METADATA) {
console.warn("Screen stream not active or video element not ready for capture.", {stream: !!screenStream, video: !!videoElement, srcObj: !!videoElement?.srcObject, readyState: videoElement?.readyState});
if (!screenStream) alert("Screen sharing is not active. Please start sharing first.");
else alert("Video stream is not ready yet (readyState < HAVE_METADATA). Please wait a moment and try again.");
return null;
}
// Added check for dimensions from your original code
if (videoElement.videoWidth === 0 || videoElement.videoHeight === 0) {
console.warn("Video element has zero dimensions. Cannot capture frame.");
alert("Cannot capture screen: Video dimensions are zero. Ensure content is visible in the stream and not minimized.");
return null;
}
if (videoCanvas && videoElement) {
videoCanvas.width = videoElement.videoWidth;
videoCanvas.height = videoElement.videoHeight;
const ctx = videoCanvas.getContext('2d');
if (!ctx) {
console.error("Failed to get 2D context from canvas.");
alert("Failed to get drawing context for screen capture.");
return null;
}
try {
ctx.drawImage(videoElement, 0, 0, videoCanvas.width, videoCanvas.height);
// Use JPEG for smaller size, adjust quality as needed
return videoCanvas.toDataURL('image/jpeg', 0.85); // 85% quality JPEG
} catch (e) {
// Check for SecurityError specifically
if (e.name === 'SecurityError') {
console.error("Error converting canvas to Data URL (Canvas Tainted):", e);
alert("Security Error: Could not capture screen frame. The content being shared (e.g., protected video, cross-origin iframe) prevents capture due to security restrictions ('tainted canvas'). Try sharing a different window or application.");
} else {
console.error("Error converting canvas to Data URL:", e);
alert(`Could not capture screen frame due to an unexpected error: ${e.message}`);
}
return null;
}
} else {
console.error("Video canvas or video element not found for capture.");
return null;
}
}
// --- Language/Code Handling ---
// (Ваши оригинальные функции обработки языка/кода остаются без изменений)
function handleLanguageChange(e) {
currentLanguage = e.target.value;
const screenSharePanelActive = videoStreamPanel && videoStreamPanel.classList.contains('active');
if(mainInput) {
// Logic to update placeholder based on context (from your original code)
if (activeSidebarFileId && sidebarFileContents.has(activeSidebarFileId)) {
const activeFileEl = document.querySelector(`.file[data-file-id="${activeSidebarFileId}"]`);
if (activeFileEl) mainInput.placeholder = `Task for file: ${activeFileEl.dataset.fileName}...`;
else mainInput.placeholder = `Task for selected file...`; // Fallback
} else if (screenSharePanelActive) {
mainInput.placeholder = "Describe task for the screen content...";
} else if (currentLanguage === DATA_ANALYSIS_LANG) {
mainInput.placeholder = `Describe task for content (file, URL, screen)...`;
} else {
mainInput.placeholder = `Enter task for ${currentLanguage.split('(')[0].trim()}...`;
}
}
console.log("Language changed to:", currentLanguage);
// Trigger Pyodide load if Python selected and not already loaded/loading (your original logic)
if (currentLanguage === "Python (Pyodide)" && !pyodide && !pyodideLoadingPromise) {
if(initStatusText && !initStatusText.textContent.includes("Pyodide")) {
initStatusText.textContent = initStatusText.textContent.replace("Ready", "Loading Pyodide for Python tasks...");
console.log("Initiating Pyodide load due to language selection.");
loadPyodideInstance().then(() => {
if(initStatusText) initStatusText.textContent = initStatusText.textContent.replace("Loading Pyodide for Python tasks...", "Pyodide Ready.");
}).catch(err => {
if(initStatusText) initStatusText.textContent = initStatusText.textContent.replace("Loading Pyodide for Python tasks...", "Pyodide Failed.");
});
} else if (pyodideLoadingPromise) {
console.log("Pyodide is already loading.");
}
}
}
function handleStopGeneration() { if (generationInProgress) { stopGenerationFlag = true; if(stopBtn) stopBtn.disabled = true; if(aiStatusText) aiStatusText.textContent = "Stopping..."; console.log("Stop generation requested by user.");}}
async function runPythonCode(pythonCode) {
if (!pyodide) {
if(initStatusText && !initStatusText.textContent.includes("Pyodide failed") && !initStatusText.textContent.includes("Pyodide Ready")) initStatusText.textContent += " Waiting for Pyodide...";
console.log("Python execution waiting for Pyodide...");
try {
// Use the existing promise or start loading if needed
await (pyodideLoadingPromise || loadPyodideInstance());
if (!pyodide) throw new Error("Pyodide failed to load previously.");
if(initStatusText && initStatusText.textContent.includes("Waiting for Pyodide")) initStatusText.textContent = initStatusText.textContent.replace(" Waiting for Pyodide...", " Pyodide ready. Retrying Python.");
console.log("Pyodide ready, retrying Python execution.");
} catch (error) {
console.error("Pyodide not available for Python execution:", error);
if(outputFrame) outputFrame.srcdoc = `<div style='color:red;padding:20px;font-family:monospace;white-space:pre-wrap;background:#fff0f0;border:1px solid red;border-radius:8px;'><h3>Pyodide Error</h3><p>Pyodide (Python runtime) is not available or failed to load.</p><p><b>Cannot run Python code.</b></p><p>Error details: ${DOMPurify.sanitize(error.message)}</p></div>`;
return; // Stop execution
}
}
// Ensure output frame exists before proceeding
if (!outputFrame) {
console.error("Output frame not found for Python execution.");
return;
}
outputFrame.srcdoc = `<div style="padding:20px;font-family:monospace;white-space:pre-wrap;background:#f0f0f0;border-radius:8px;">🐍 Running Python via Pyodide...</div>`;
let pyStartTime = performance.now();
try {
console.log("Attempting to load packages from imports...");
await pyodide.loadPackagesFromImports(pythonCode,
{ messageCallback: (msg) => console.log("Pyodide package msg:", msg) , errorCallback: (err) => console.warn("Pyodide package err:", err) } // Add callbacks for visibility
);
console.log("Packages loaded (or none needed). Executing Python code...");
let capturedOutput = "";
// Configure stdout and stderr capture
pyodide.setStdout({ batched: (str) => capturedOutput += str + "\n" });
pyodide.setStderr({ batched: (str) => capturedOutput += `<span style='color:red;'>Error: ${str}</span>\n` }); // Style stderr
// Execute the Python code
await pyodide.runPythonAsync(pythonCode);
let pyEndTime = performance.now();
console.log(`Python execution finished in ${(pyEndTime - pyStartTime).toFixed(2)} ms.`);
// Display the captured output
const outputHTML = capturedOutput.trim() ? DOMPurify.sanitize(capturedOutput) : "<span style='color:grey;'>Python code executed successfully. No explicit print output detected.</span>";
outputFrame.srcdoc = `<div style="padding:20px;font-family:monospace;white-space:pre-wrap;background:#f9f9f9;border:1px solid #eee;border-radius:8px;"><h3>🐍 Python Output</h3>${outputHTML}</div>`;
} catch (error) {
let pyEndTime = performance.now();
console.error("Python execution error:", error);
// Display a detailed error message in the output frame
const errorMessage = error.message ? error.message.replace(/</g, "<").replace(/>/g, ">") : "Unknown Python execution error"; // Basic sanitization for display
outputFrame.srcdoc = `<div style='padding:20px;font-family:monospace;white-space:pre-wrap;color:red;background:#fff0f0;border:1px solid red;border-radius:8px;'><h3>🐍 Python Execution Error</h3><p>Execution failed after ${(pyEndTime - pyStartTime).toFixed(2)} ms.</p><pre>${DOMPurify.sanitize(errorMessage)}</pre></div>`;
} finally {
// Reset stdout/stderr to default if necessary (optional, usually not needed per execution)
// pyodide.setStdout({});
// pyodide.setStderr({});
}
}
function transpileToRunnableJS(code, lang) {
// Your original transpilation logic
let displayLang = lang;
if (lang === "TypeScript" && typeof Babel !== 'undefined') {
try {
const { code: jsCode } = Babel.transform(code, { presets: ['typescript'], filename: 'component.tsx' }); // Use .tsx filename hint for Babel
code = `// Transpiled from TypeScript:\n${jsCode}`;
displayLang = "JavaScript (from TypeScript)";
} catch (e) {
console.warn("TypeScript transpilation failed:", e);
code = `// TypeScript (transpilation failed: ${e.message})\n${code}`;
displayLang = "TypeScript (Transpilation Error)"; // Indicate error
}
} else if ((lang === "React Component" || lang === "Next.js Page (Client-Side)") && typeof Babel !== 'undefined') {
try {
// Ensure 'react' preset is used
const { code: jsCode } = Babel.transform(code, { presets: ['react', 'env'], filename: 'component.jsx' }); // Add 'env' preset for modern JS, use .jsx hint
code = `// Transpiled from JSX (React/Next.js):\n${jsCode}\n\n/* Note: This is a basic transpile for syntax viewing. Full React/Next.js functionality (state, hooks, lifecycle, routing) requires a proper build environment. */`;
displayLang = "JavaScript (from JSX)";
} catch (e) {
console.warn("JSX transpilation failed:", e);
code = `// JSX (transpilation failed: ${e.message})\n${code}`;
displayLang = "JSX (Transpilation Error)"; // Indicate error
}
} else if (lang === "JavaScript (Standalone)") {
displayLang = "JavaScript (Standalone)";
// No notes needed unless you want to add context notes here too
} else {
// Handle other languages for display only
displayLang = lang.replace(' (Code Display)','');
code = `// Displaying code for: ${displayLang}\n// Live execution/transpilation not supported in this environment.\n\n${code}`;
}
// Consistent display wrapper using <pre>
return `<div style="padding:15px; background-color: #282c34; color: #abb2bf; font-family: 'Courier New', Courier, monospace; font-size: 0.9em; border-radius: 8px; overflow:auto; height:100%;box-sizing:border-box;">
<h4 style="color:#61afef; margin-top:0; margin-bottom:10px; border-bottom: 1px solid #3f434a; padding-bottom:5px;">${displayLang} Preview</h4>
<pre style="margin:0; white-space:pre-wrap; word-break:break-all;">${DOMPurify.sanitize(code)}</pre>
</div>`;
}
// --- Main Actions (Send Request, Research) with Planning Step ---
// *** NEW: Enhanced generatePlan function (using new detailed prompts) ***
async function generatePlan(taskDescription, contextSummary, taskType) {
if (!aiPlanContainer || !aiPlanContent) {
console.warn("AI Plan UI elements not found.");
return "PLAN_UI_MISSING"; // Indicate UI missing
}
aiPlanContainer.classList.remove('hidden');
aiPlanContent.innerHTML = "<i>🧠 Generating strategic plan...</i>";
showAiOverlay(true, `Phase 1: Generating Plan for ${taskType}...`);
console.log(`Generating plan for task type: ${taskType}`);
// Determine focus level (simple vs max) - using MAX for planning as default
const focusParams = solverParams; // Use MAX focus for planning
const planPrompt = `
You are an expert AI Strategist and Project Planner. Your goal is to devise a concise, actionable, step-by-step plan to fulfill the user's request effectively and efficiently.
**User's Core Request:**
"${taskDescription}"
**Provided Context Summary:**
${contextSummary ? `"${contextSummary.substring(0, 300)}..."` : "No specific file/URL/screen context provided beyond the task description."}
**Task Type:** ${taskType}
**Your Task:**
Generate a step-by-step plan in Markdown bullet points. Critically analyze the request and context. Outline the key stages required to achieve the user's goal with high quality. Strive for creativity and thoroughness in the plan itself. Consider potential challenges and build steps to address them. Think about how to add extra value or detail based on the request.
**Guiding Principles (Emphasize these in your planning process):**
* **Depth of Analysis (${focusParams.depth_focus_max}):** Thoroughly understand the requirements, nuances, and potential challenges. Go beyond the surface level. Think about edge cases and complexities.
* **Analytical Rigor (${focusParams.analytical_rigor_max}):** Ensure the plan is logical, complete, and addresses the core objectives accurately. Anticipate verification needs.
* **Creative Problem Solving & Detail (${focusParams.creativity_focus_max}):** Consider innovative or highly effective approaches. Plan steps that encourage adding creative details, examples, or richer features as requested by the user. How can the output be made *better* than a basic fulfillment?
* **Exploration of Alternatives (${focusParams.alternative_exploration_max}):** Briefly consider different high-level strategies or structures if relevant, selecting the most promising. Justify complex choices implicitly through the plan steps.
* **Efficiency (${focusParams.efficiency_focus_max}):** Aim for a plan that leads to a solution without unnecessary complexity or redundancy, while still achieving depth and quality.
**Output Format Instructions:**
* Begin the plan *directly* with Markdown bullet points (\`- \` or \`* \`).
* **NO conversational preamble**, introduction, or concluding remarks. Just the plan.
* Keep the plan concise and focused on major steps (typically 4-8 steps, allowing for more detail).
* Each step should be a clear action or analysis phase. Incorporate considerations for detail and creativity within the steps where appropriate.
* Example Structure (more detailed):
* Deeply analyze user request: identify core function, desired style, specific features (e.g., "interactive elements", "data visualization type"), and constraints.
* Outline main structure/sections (e.g., HTML layout, Python class structure, report sections).
* Design and develop core logic/component A (consider alternative implementations briefly). Add detailed comments.
* Implement feature B with creative elements (e.g., unique CSS animations, varied data examples, insightful analysis points).
* Integrate components, ensuring smooth interaction and data flow.
* Refine UI/UX, add polishing details, enhance visual appeal or clarity.
* Perform final verification against requirements, check edge cases, ensure robustness.
* Format output according to specifications (e.g., self-contained HTML, clean Python script, structured Markdown).
**Generate the detailed, creative, and actionable plan now.**
`;
// *** Используем параметры из solverParams ***
const generationConfig = {
temperature: solverParams.initial_gen_temp_single,
topP: solverParams.topP,
topK: solverParams.topK,
maxOutputTokens: solverParams.max_initial_tokens,
};
try {
console.log("Sending plan generation request to Gemini:", { prompt: planPrompt.substring(0, 500) + "...", config: generationConfig });
const result = await model.generateContent(planPrompt, generationConfig); // Pass config as second arg
// Handle potential stop request during generation
if (stopGenerationFlag) {
console.log("Plan generation stopped by user flag.");
throw new Error("Plan generation stopped by user.");
}
const response = result?.response; // Use optional chaining
const planText = response?.text ? response.text() : ""; // Safely access text()
if (!planText.trim()) {
console.error("Received empty plan from AI.", response); // Log the full response for debugging
throw new Error("Received empty plan from AI.");
}
console.log("Plan received:\n", planText);
if (aiPlanContent) {
// Sanitize and render markdown
// Ensure marked is available
if (typeof marked !== 'undefined' && typeof marked.parse === 'function') {
aiPlanContent.innerHTML = DOMPurify.sanitize(marked.parse(planText));
} else {
console.warn("marked library not available for rendering plan.");
// Fallback to preformatted text
aiPlanContent.innerHTML = `<pre style="white-space: pre-wrap; word-wrap: break-word;">${DOMPurify.sanitize(planText)}</pre>`;
}
}
return planText; // Return the raw plan text for the next step
} catch (error) {
console.error(`Error generating plan for ${taskType}:`, error);
if (aiPlanContent) {
aiPlanContent.innerHTML = `<p style="color:red;font-weight:bold;">Error Generating Plan</p><p style="color:red;">${DOMPurify.sanitize(error.message)}</p>`;
}
// Rethrow to be caught by the main handler and stop the process
throw new Error(`Plan generation failed: ${error.message}`);
}
}
// *** NEW: Enhanced handleSendRequestWithPlan function (using new detailed prompts and solverParams) ***
async function handleSendRequestWithPlan() {
const taskDescription = mainInput ? mainInput.value.trim() : "";
let capturedScreenImageDataUrl = null;
const isScreenShareTabActive = videoStreamPanel && videoStreamPanel.classList.contains('active');
let planText = ""; // To store the generated plan
console.log("--- Starting New Request Cycle ---");
console.log("Task:", taskDescription);
console.log("Active File ID:", activeSidebarFileId);
console.log("File Content Snippet:", fileContentForAI.substring(0, 100) + (fileContentForAI.length > 100 ? "..." : ""));
console.log("Screen Share Tab Active:", isScreenShareTabActive);
console.log("Current Language:", currentLanguage);
// 1. Input Validation & Context Determination
if (!taskDescription && !fileContentForAI && !(isScreenShareTabActive && screenStream)) {
alert("Please enter a task, provide content (from sidebar, URL, upload), or activate screen share with a running stream.");
updateButtonStates(false);
if(aiPlanContainer) aiPlanContainer.classList.add('hidden'); // Hide plan container if no action
return;
}
generationInProgress = true;
stopGenerationFlag = false;
updateButtonStates(true);
if(aiPlanContainer) aiPlanContainer.classList.remove('hidden'); // Show plan container
if(aiPlanContent) aiPlanContent.innerHTML = ""; // Clear previous plan view
// Clear previous results
if(codeInputArea) codeInputArea.value = "";
if(outputFrame) outputFrame.srcdoc = `<p style="padding:20px; font-style:italic; color: #555;">Initiating request... waiting for plan.</p>`;
if(fullCodeContent) fullCodeContent.textContent = "";
if (window.currentAiChart) { window.currentAiChart.destroy(); window.currentAiChart = null; }
let currentRequestMode; // "code_generation", "data_analysis", "screen_analysis"
let contextSummaryForPlan = "";
let mainRequestContent; // Will hold the prompt/parts for the main generation call
try {
// 2. Capture Screen (if applicable) BEFORE planning
if (isScreenShareTabActive && screenStream) {
showAiOverlay(true, `Capturing screen frame...`);
capturedScreenImageDataUrl = await captureScreenFrameAsDataURL();
if (!capturedScreenImageDataUrl) {
// Error already shown by capture function if alert was used
throw new Error("Screen capture failed. Cannot proceed.");
}
console.log("Screen frame captured successfully.");
currentRequestMode = "screen_analysis";
contextSummaryForPlan = "Real-time screen capture provided.";
// Ensure Data Analysis mode is selected for screen tasks if language selector exists
if (languageSelector && currentLanguage !== DATA_ANALYSIS_LANG) {
console.log("Switching language to Data Analysis for screen task.");
languageSelector.value = DATA_ANALYSIS_LANG;
currentLanguage = DATA_ANALYSIS_LANG; // Update state immediately
}
} else if (activeSidebarFileId || fileContentForAI || currentLanguage === DATA_ANALYSIS_LANG) {
// Prioritize active file name if available
const activeFileEl = activeSidebarFileId ? document.querySelector(`.file[data-file-id="${activeSidebarFileId}"]`) : null;
const fileNameHint = activeFileEl ? `File: ${activeFileEl.dataset.fileName}` : (fileContentForAI ? "Provided text/data content" : "General data analysis task");
currentRequestMode = "data_analysis";
contextSummaryForPlan = `${fileNameHint} (Content length: ${fileContentForAI.length} chars).`;
// Ensure Data Analysis mode is selected if file content is primary input
if (languageSelector && currentLanguage !== DATA_ANALYSIS_LANG && (activeSidebarFileId || fileContentForAI)) {
console.log("Switching language to Data Analysis for file/text task.");
languageSelector.value = DATA_ANALYSIS_LANG;
currentLanguage = DATA_ANALYSIS_LANG; // Update state immediately
}
} else {
currentRequestMode = "code_generation";
contextSummaryForPlan = `Target Language: ${currentLanguage}. No specific file content provided; focus on the task description.`;
// Ensure language is NOT Data Analysis if it's a pure code gen task
if (languageSelector && currentLanguage === DATA_ANALYSIS_LANG) {
console.warn("Data Analysis language selected, but no data context found. Reverting to default code language.");
// Revert to a sensible default like HTML/JS or the first language in the list
const defaultCodeLang = SUPPORTED_LANGUAGES.find(l => l !== DATA_ANALYSIS_LANG) || "HTML/CSS/JS (Web Page)";
languageSelector.value = defaultCodeLang;
currentLanguage = defaultCodeLang;
contextSummaryForPlan = `Target Language: ${currentLanguage}. No specific file content provided; focus on the task description.`; // Update context summary
}
}
console.log(`Determined Request Mode: ${currentRequestMode}`);
console.log(`Context for Plan: ${contextSummaryForPlan}`);
// 3. Generate Plan
planText = await generatePlan(taskDescription, contextSummaryForPlan, currentRequestMode);
// generatePlan throws on error, so we only proceed if successful.
if (stopGenerationFlag) throw new Error("Stopped by user after plan generation.");
showAiOverlay(true, `Phase 2: Executing Plan for ${currentRequestMode}...`);
if(outputFrame) outputFrame.srcdoc = `<p style="padding:20px; font-style:italic; color: #333;">Plan received. Generating content based on plan...</p>`;
// 4. Prepare and Execute Main Generation Request based on Plan
const focusParams = solverParams; // Use MAX focus for generation by default
const chartCanvasHTML = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:10px;box-sizing:border-box;"><div style="width:95%;max-width:700px;height:95%;max-height:500px;"><canvas id="aiChart"></canvas></div></div>`;
// --- Define Enhanced Prompts ---
let mainPromptText = "";
if (currentRequestMode === "screen_analysis") {
if (!capturedScreenImageDataUrl) throw new Error("Internal Error: Screen analysis mode but no image data captured.");
const base64ImageData = capturedScreenImageDataUrl.split(',')[1];
const mimeType = capturedScreenImageDataUrl.substring(capturedScreenImageDataUrl.indexOf(':') + 1, capturedScreenImageDataUrl.indexOf(';'));
// *** NEW DETAILED SCREEN ANALYSIS PROMPT ***
mainPromptText = `
You are an exceptionally perceptive AI Visual Analyst Assistant. Your purpose is to meticulously analyze the provided screen image and respond to the user's task with insight, detail, and clarity, adhering strictly to the generated plan. Go beyond simple descriptions; infer purpose and relationships where appropriate based on standard UI/UX conventions, but clearly state when making inferences.
**User's Task/Question regarding this screen image:**
"${taskDescription || "Provide a highly detailed, comprehensive description of everything visible on this screen, including potential functions and relationships between elements."}"
**Generated Strategic Plan (Follow this precisely):**
--- PLAN START ---
${planText}
--- PLAN END ---
**Your Core Directives:**
1. **Execute the Plan:** Follow the steps outlined in the plan above meticulously.
2. **Image Focus & Detail:** Base your entire analysis *exclusively* on the visual information present in the provided image. **Do NOT speculate** about content outside the frame, hidden elements, user intentions, or application states not visually confirmed. Describe layout, text content, icons, colors, widgets, and their arrangement with high fidelity.
3. **Analytical Depth & Rigor (${focusParams.depth_focus_max}, ${focusParams.analytical_rigor_max}):** Analyze details intensely. Identify key UI elements (buttons, input fields, menus, text blocks, images, containers). Describe their apparent state (enabled/disabled, selected, etc.). Analyze relationships between components (e.g., this button likely affects that list). Use rigorous observation.
4. **Infer Purpose (Carefully):** Based on standard UI/UX patterns and visual cues, infer the likely purpose or function of elements (e.g., "This appears to be a search bar," "This button likely submits the form"). Clearly distinguish observation ("I see a button labeled 'Save'") from inference ("This button likely saves the current data").
5. **Creativity in Observation (${focusParams.creativity_focus_max}):** Notice subtle details, visual hierarchy, potential usability issues, or interesting design choices visible in the image.
6. **Clarity and Structure:** Present your findings in clear, well-structured Markdown format. Use headings, lists, and bolding logically to create an easy-to-understand analysis. Be detailed but avoid unnecessary jargon.
**Output Format Instructions:**
* Output **ONLY** the final analysis in Markdown format.
* Do **NOT** include any preamble, conversational text, apologies, or self-references like "Based on the image..." or "As an AI...". Start directly with the Markdown content (e.g., a heading like "# Screen Analysis").
* Use appropriate Markdown for structure (headings, lists, bolding, code blocks for text snippets if helpful) to enhance readability.
**Analyze the provided screen image now according to the plan and these directives.**
`;
// Gemini expects content in a specific parts structure for multimodal
// Ensure parts are correctly formatted as an array
mainRequestContent = [
{ text: mainPromptText }, // Text part first
{ inlineData: { mimeType: mimeType, data: base64ImageData } } // Then the image part
];
} else if (currentRequestMode === "data_analysis") {
// *** NEW DETAILED DATA ANALYSIS PROMPT ***
mainPromptText = `
You are a sophisticated AI Data Scientist and Content Analyst, capable of deep insights and creative presentation. Your objective is to analyze the provided text/data context based on the user's task, execute the provided plan with exceptional rigor, and generate the *single most appropriate and insightful* output format: a well-styled raw HTML Table, a detailed raw Chart.js JSON configuration object, or a comprehensive and nuanced Markdown text analysis. Add value beyond the literal request where appropriate.
**User's Task:**
"${taskDescription}"
**Provided Content Context (approx. first ${fileContentForAI.length} chars):**
"""
${fileContentForAI || "No explicit content/data was provided. Analyze the user's task itself. If appropriate, generate illustrative example data conforming to the task's implied domain or explain complex concepts with high clarity."}
"""
**Generated Strategic Plan (Follow this precisely):**
--- PLAN START ---
${planText}
--- PLAN END ---
**Your Core Directives:**
1. **Execute the Plan:** Adhere strictly to the steps in the generated plan, aiming for the highest quality outcome at each stage.
2. **Deep & Insightful Analysis (${focusParams.depth_focus_max}, ${focusParams.analytical_rigor_max}):** Perform a thorough, multi-faceted analysis. Identify not just surface patterns but also underlying trends, correlations, anomalies, or key themes as required by the plan and task. Apply rigorous analytical thinking and statistical concepts if applicable and possible with the given data.
3. **Creative & Optimal Format Selection (${focusParams.creativity_focus_max}, ${focusParams.efficiency_focus_max}):** Based on your deep analysis, determine the **single best format** for conveying the insights most effectively and creatively:
* **HTML Table:** Choose for structured, multi-dimensional data. Make it detailed. **Include inline CSS within `<style>` tags inside the table's parent `<div>` or directly on elements for good presentation** (borders, padding, alignment, perhaps subtle hover effects or striping). Ensure it's well-formed.
* **Chart.js JSON:** Choose for visualizing quantitative relationships. Select the *most effective* chart type (line for trends, bar for comparisons, pie for proportions, scatter for correlations, etc.). Generate a *complete* configuration including appropriate labels, titles, legends, tooltips, and potentially custom styling options within the \`options\` object to make the chart clear and visually appealing.
* **Markdown Text:** Choose for qualitative analysis, summaries, explanations, code analysis, Q&A, or when data is insufficient/unstructured for tables/charts. Make the analysis **rich and insightful**, well-structured with headings/lists, potentially including code snippets (\`\`\`), blockquotes, or bolding for emphasis. Go beyond a simple summary.
4. **Alternative Exploration (${focusParams.alternative_exploration_max}):** Internally consider alternative analysis techniques or visualization types. Justify your final choice implicitly through the quality and appropriateness of the output.
5. **Value Add:** Where appropriate, provide additional context, explain the significance of findings, or suggest next steps for analysis based on the results.
**CRITICAL Output Format Instructions:**
* You **MUST** output **ONLY ONE** type of response: the raw HTML code for a table (potentially wrapped in a div with style), the raw JSON object for a Chart.js configuration, OR the raw Markdown text for analysis.
* **NO PREAMBLE OR EXPLANATIONS:** Do **NOT** include any introductory text, conversational filler, apologies, or explanations about your format choice (e.g., "Here is the HTML table:", "I chose Markdown because...").
* **RAW HTML (Styled):** If outputting a table, start directly with potentially a `<div>` containing `<style>` and then `<table>`, ending with `</table></div>`. Add meaningful inline CSS or CSS in the style block.
* **RAW JSON (Complete):** If outputting Chart.js JSON, start directly with \`{\` and end with \`}\`. It MUST be valid JSON. Use the structure \`{ "type": "chartjs", "config": { "type": "chartType", "data": {...}, "options": {...} } }\`. Ensure 'config' is detailed and well-structured for Chart.js.
* **RAW MARKDOWN (Rich):** If outputting text analysis, start directly with the Markdown content. Structure it well and make it insightful.
* **Insufficient Data:** If the content is truly insufficient for the task, **default to Markdown** and provide a *detailed* explanation of why the task cannot be completed, what specific information is missing, and perhaps how the user could provide better data.
**Generate your single, high-quality response now, following the plan and adhering strictly to these directives.**
`;
// Ensure content is formatted correctly as an array of parts if needed by the model, otherwise just text
mainRequestContent = [{ text: mainPromptText }]; // Assuming text input is sufficient
} else { // code_generation
let languageInstructions = "";
// *** Using NEW DETAILED Language Instructions ***
switch(currentLanguage){
case "HTML/CSS/JS (Web Page)": languageInstructions = `Generate a single, complete, self-contained HTML file. Embed CSS within \`<style>\` tags in the \`<head>\`. Embed JavaScript within \`<script>\` tags before the closing \`</body>\`. Ensure the code is functional, demonstrates the requested features robustly, and includes creative elements. Use modern HTML5, CSS3, and ES6+ JavaScript. Prioritize semantic HTML, accessibility (ARIA attributes, keyboard navigation considerations), and responsive design (using media queries or flexible layouts). Add detailed comments explaining architecture, complex logic, and CSS choices. Make it visually appealing and interactive. Incorporate diverse examples or variations requested by the user.`; break;
case "Python (Pyodide)": languageInstructions = `Generate Python code specifically for execution in a Pyodide environment. Use standard Python 3.9+ features. Leverage built-in libraries (math, random, collections, json, etc.) extensively. If external packages (numpy, pandas, matplotlib) are absolutely necessary and justified, structure the code assuming they *might* be loaded via \`micropip\` or are already present. Use \`print()\` for all user-facing output. Write clean, PEP 8 compliant, idiomatic Python. Include clear docstrings for functions/classes and inline comments for complex sections. Implement error handling (try-except blocks) where appropriate. Structure the code logically with functions or classes for reusability. Provide varied and illustrative examples within the code or comments if applicable.`; break;
case "JavaScript (Standalone)": languageInstructions = `Generate standalone JavaScript code (ES6+/ES2020+). Assume a modern browser environment by default, clearly state if Node.js is targeted or required. Avoid browser-specific APIs (like \`document\`, \`window\`) if aiming for cross-environment compatibility or Node.js. Implement robust error handling. Use modern features like async/await, classes, modules (if appropriate for structure, though output should be a single block). Add detailed JSDoc comments (\`/** ... */\`) for all functions, classes, and complex variables. Ensure code is well-structured, readable, and includes creative or detailed examples as per the task.`; break;
case "TypeScript": languageInstructions = `Generate high-quality TypeScript code (.ts). Utilize strong typing rigorously: provide explicit type annotations for all variables, function parameters, and return values. Define and use interfaces or type aliases for all non-trivial data structures. Leverage advanced TypeScript features like generics, mapped types, conditional types where appropriate to enhance type safety and code clarity. Adhere to TypeScript best practices and common style guides. Include comprehensive TSDoc comments. Implement classes or functions as appropriate. Ensure the code is well-organized, handles potential null/undefined values safely (strict null checks implied), and includes creative details or variations.`; break;
case "React Component": languageInstructions = `Generate a single, well-structured React functional component using JSX syntax (.jsx/.tsx). Import React and hooks (\`useState\`, \`useEffect\`, \`useContext\`, \`useRef\`, \`useCallback\`, \`useMemo\`) correctly and use them idiomatically. Implement state management logically. Handle side effects cleanly within \`useEffect\`. Write reusable and composable component code. Use TypeScript for prop types (define an \`interface Props\`) for robustness. Add detailed comments explaining the component's purpose, state, props, effects, and any complex logic. Incorporate creative UI elements, handle user interactions effectively, and pay attention to accessibility. Make it feature-rich based on the request.`; break;
case "Next.js Page (Client-Side)": languageInstructions = `Generate a Next.js page component, explicitly marked with \`'use client';\` at the top. Use React functional component syntax with Hooks effectively. Include necessary imports from React and Next.js (\`next/link\`, \`next/image\`, \`next/router\`, etc.) where appropriate. Structure the component logically for a page. Manage state and side effects correctly on the client. Add detailed comments and implement TypeScript prop types. Focus on building a complete and creative page section according to the user's task, considering client-side rendering implications. Handle loading and error states gracefully if data fetching is implied.`; break;
default: languageInstructions = `Generate high-quality, idiomatic code for ${currentLanguage.replace(' (Code Display)','').trim()}. Follow language-specific conventions and best practices rigorously. Ensure the code is clear, robust, well-commented (explaining the 'why' not just the 'what'), and thoroughly addresses all aspects of the user's task. Prioritize correctness, maintainability, and security. Include detailed examples or creative variations where applicable. Handle potential errors gracefully.`;
}
// *** NEW DETAILED CODE GENERATION PROMPT ***
mainPromptText = `
You are a World-Class AI Software Engineer and Architect specializing in ${currentLanguage}. Your mission is to generate exceptional, production-quality code that precisely fulfills the user's request, adheres strictly to the generated plan, and embodies the principles of clarity, robustness, efficiency, and creativity. Go significantly beyond a basic implementation; aim for excellence.
**User Task:**
"${taskDescription}"
**Target Language/Framework:** ${currentLanguage}
**Specific Language Instructions & Advanced Best Practices:**
${languageInstructions}
**Generated Strategic Plan (Follow this precisely and meticulously):**
--- PLAN START ---
${planText}
--- PLAN END ---
**Core Directives & Quality Requirements (Apply Maximum Emphasis - Strive for Perfection):**
1. **Execute the Plan Faithfully:** Implement every step of the plan thoroughly. Ensure the final code directly reflects the planned architecture and features.
2. **Code Quality, Correctness & Robustness (${focusParams.analytical_rigor_max}):** Generate code that is not only syntactically perfect but also logically flawless, highly robust, and resilient to errors. Implement comprehensive error handling (e.g., try-catch, validation, handling edge cases, defensive programming). Write code that is demonstrably correct.
3. **Depth, Completeness & Detail (${focusParams.depth_focus_max}):** Create a comprehensive, feature-rich solution. Fully implement all requested and implied functionality. Add numerous creative details, illustrative examples, configuration options, or variations that enhance the solution's value and demonstrate deep understanding. The output should feel complete, not like a mere skeleton.
4. **Creativity, Innovation & "Wow Factor" (${focusParams.creativity_focus_max}):** Where the task allows, incorporate creative algorithms, elegant design patterns, unique user interface elements, or novel approaches that make the solution stand out. Generate diverse ideas and implement them thoughtfully. Add that extra touch that impresses.
5. **Considered Alternatives & Justification (${focusParams.alternative_exploration_max}):** Internally evaluate different algorithms, data structures, or design patterns. Choose the optimal approach, balancing trade-offs (clarity, performance, maintainability). If the choice is non-obvious or particularly interesting, add brief comments explaining the rationale.
6. **Efficiency & Optimization (${focusParams.efficiency_focus_max}):** Write code that is highly efficient in terms of time and space complexity where relevant, following language-specific best practices for performance tuning without sacrificing clarity unnecessarily.
7. **Readability, Maintainability & Documentation:** Produce exceptionally clean, well-formatted code adhering to strict style guides (e.g., PEP 8, Prettier). Use highly descriptive names. Write extensive comments and documentation (e.g., docstrings, JSDoc, TSDoc) that make the code easy to understand, use, and maintain.
**CRITICAL Output Format Instructions:**
* Output **ONLY** the raw code for the specified language (${currentLanguage}).
* **NO MARKDOWN BACKTICKS** (\`\`\`) surrounding the code block whatsoever.
* **NO EXPLANATIONS, PREAMBLE, CHATTY TEXT, OR META-COMMENTS about the code generation process.** Just the code itself, starting from the very first character (e.g., \`<!DOCTYPE html>\`, \`import ...\`, \`package ...\`, \`def ...\`, \`function ...\`, etc.) and ending with the last character of the code.
**Generate the exceptional ${currentLanguage} code now, adhering strictly to the plan and all directives.**
`;
// Ensure content is formatted correctly for the model
mainRequestContent = [{ text: mainPromptText }];
}
// 5. Execute the Main Generation Call
// *** Используем параметры из solverParams ***
const generationConfig = {
temperature: solverParams.synthesis_temp, // Balanced temperature for synthesis
topP: solverParams.topP,
topK: solverParams.topK,
maxOutputTokens: solverParams.max_synthesis_tokens, // Generous token limit for final output
// stopSequences: ["```"] // Optional: Try to force stop if it insists on markdown, but risky
};
console.log(`Sending main ${currentRequestMode} request to Gemini:`, { config: generationConfig, promptSnippet: typeof mainRequestContent === 'string' ? mainRequestContent.substring(0,300) + "..." : mainRequestContent[0].text.substring(0,300) + "..."});
// *** Отправляем запрос модели ***
const result = await model.generateContent(mainRequestContent, generationConfig); // Pass config
// Check for stop flag *after* the async call returns
if (stopGenerationFlag) throw new Error("Stopped by user during content generation.");
const response = result?.response; // Use optional chaining
let rawRspTxt = "";
// Check for response and text() method before calling (more robust check)
if (response && typeof response.text === 'function') {
rawRspTxt = response.text();
} else if (response && response.candidates && response.candidates.length > 0 && response.candidates[0].content && response.candidates[0].content.parts && response.candidates[0].content.parts.length > 0 && typeof response.candidates[0].content.parts[0].text === 'string') {
// Attempt to access candidate text if primary text() fails
rawRspTxt = response.candidates[0].content.parts[0].text;
console.warn("Recovered text from candidate structure.");
} else {
// Log the response structure if it's unusual and text extraction failed
console.error("Unexpected response structure or no text found:", response);
throw new Error("Received no valid text content from Gemini response.");
}
if (!rawRspTxt.trim()) {
console.warn("Received empty or whitespace-only response from AI.");
// Handle potentially empty but non-error responses gracefully
generatedCode = `// AI returned an empty response for the task: ${taskDescription}`;
} else {
console.log("Raw response received (first 500 chars):", rawRspTxt.substring(0, 500));
// More aggressive cleaning - remove potential markdown fences AND optional language hints
generatedCode = rawRspTxt.replace(/^```(?:[a-zA-Z0-9_ .-]+)?\s*\n?([\s\S]*?)\n?```$/gm, '$1').trim();
// Also remove potential leading/trailing whitespace left after cleaning
generatedCode = generatedCode.trim();
}
// 6. Process and Display Results
console.log("Processing and displaying results for mode:", currentRequestMode);
showAiOverlay(true, `Finalizing ${currentRequestMode} results...`); // Update status
// Используем вашу оригинальную логику отображения результатов
if (currentRequestMode === "screen_analysis" || (currentRequestMode === "data_analysis" && !(generatedCode.startsWith("{") && generatedCode.endsWith("}")) && !generatedCode.toLowerCase().includes("<table"))) {
// Default to Markdown rendering for screen analysis or non-chart/table data analysis
let htmlOutput = "";
if (typeof marked !== 'undefined' && typeof marked.parse === 'function') {
htmlOutput = DOMPurify.sanitize(marked.parse(generatedCode));
} else {
console.warn("marked library not available for rendering screen/data analysis.");
htmlOutput = `<pre style="white-space: pre-wrap; word-wrap: break-word;">${DOMPurify.sanitize(generatedCode)}</pre>`; // Fallback
}
if(outputFrame) outputFrame.srcdoc = `<div style="padding:20px;background:white;border-radius:8px;max-height:100%;overflow-y:auto;font-family: Inter, sans-serif; line-height:1.6;">${htmlOutput}</div>`;
if(codeInputArea) codeInputArea.value = generatedCode; // Show raw Markdown/text
if(fullCodeContent) fullCodeContent.textContent = generatedCode;
console.log(`Displayed ${currentRequestMode} results as Markdown.`);
} else if (currentRequestMode === "data_analysis") {
let isChart = false, isTable = false, chartCfg = null;
// Check for Chart.js JSON structure (assuming the AI followed the prompt)
if (generatedCode.startsWith("{") && generatedCode.endsWith("}")) {
try {
const potentialJson = JSON.parse(generatedCode);
// Check for the specific structure requested in the prompt
if(potentialJson.type === "chartjs" && potentialJson.config && typeof potentialJson.config === 'object' && potentialJson.config.type && potentialJson.config.data) {
isChart = true;
chartCfg = potentialJson.config; // Use the 'config' part directly
console.log("Detected Chart.js JSON output.");
} else {
console.warn("Response is JSON but not the expected Chart.js structure. Treating as Markdown.");
}
} catch(e) {
console.warn("Response looked like JSON but failed to parse. Treating as Markdown:", e, generatedCode.substring(0,100));
}
}
// Check for HTML Table (more lenient check)
if (!isChart && generatedCode.toLowerCase().trim().startsWith("<table") && generatedCode.toLowerCase().trim().endsWith("</table>")) {
isTable = true;
console.log("Detected HTML Table output.");
}
// Check for HTML Table with potential wrapper div+style
if (!isChart && !isTable && generatedCode.toLowerCase().trim().startsWith("<div") && generatedCode.includes("<table")) {
isTable = true; // Assume it's the styled table output
console.log("Detected styled HTML Table output (wrapped in div).");
}
if(isChart && outputFrame && chartCfg){
outputFrame.srcdoc = chartCanvasHTML; // Load the canvas container
outputFrame.onload = () => {
// Ensure onload runs only once
if (outputFrame.dataset.chartLoaded === 'true') return;
outputFrame.dataset.chartLoaded = 'true'; // Mark as loaded
try {
const chartCtx = outputFrame.contentWindow.document.getElementById('aiChart');
if(chartCtx) {
if(window.currentAiChart) window.currentAiChart.destroy(); // Destroy previous chart instance
// Check if Chart is already loaded in iframe
if (outputFrame.contentWindow.Chart) {
console.log("Chart.js already loaded in iframe. Rendering chart.");
window.currentAiChart = new outputFrame.contentWindow.Chart(chartCtx, chartCfg);
} else {
// Load Chart.js dynamically into the iframe
console.log("Loading Chart.js into iframe...");
const script = outputFrame.contentWindow.document.createElement('script');
script.src = "https://cdn.jsdelivr.net/npm/chart.js"; // Ensure Chart.js is loaded
script.onload = () => {
console.log("Chart.js loaded. Rendering chart.");
// Ensure Chart constructor is available
if (outputFrame.contentWindow.Chart) {
window.currentAiChart = new outputFrame.contentWindow.Chart(chartCtx, chartCfg);
} else {
console.error("Chart constructor not found on iframe window after script load.");
throw new Error("Chart constructor not found after script load.");
}
};
script.onerror = () => {
console.error("Failed to load Chart.js script into iframe.");
if(outputFrame.contentDocument && outputFrame.contentDocument.body) outputFrame.contentDocument.body.innerHTML=`<div style='padding:10px;color:red'>Chart Error: Failed to load Chart.js library.</div>`;
}
outputFrame.contentWindow.document.head.appendChild(script);
}
} else {
throw new Error("Chart canvas element ('aiChart') not found in iframe after loading.");
}
} catch(e) {
console.error("Chart rendering error:", e);
// Display error and the config JSON in the iframe
if(outputFrame.contentDocument && outputFrame.contentDocument.body) outputFrame.contentDocument.body.innerHTML=`<div style='padding:10px;color:red'>Chart Rendering Error: ${DOMPurify.sanitize(e.message)}</div><h4 style='margin:10px;'>Generated Chart.js Config:</h4><pre style='font-size:0.8em;white-space:pre-wrap;word-break:break-all;background:#eee;padding:10px;border-radius:4px;'>${DOMPurify.sanitize(JSON.stringify(chartCfg,null,2))}</pre>`;
}
// Reset marker after processing
setTimeout(() => { outputFrame.dataset.chartLoaded = 'false'; }, 100);
};
// Store the raw JSON in the code areas
if(codeInputArea) codeInputArea.value = JSON.stringify({ type: "chartjs", config: chartCfg }, null, 2); // Store the full wrapper structure
if(fullCodeContent) fullCodeContent.textContent = codeInputArea.value;
} else if (isTable && outputFrame){
// Sanitize table HTML allowing common table elements AND style tags/attributes
const sanitizedHtml = DOMPurify.sanitize(generatedCode,{
USE_PROFILES: {html: true},
ADD_TAGS: ['style', 'div'], // Allow style tag and wrapper div
ADD_ATTR: ['style'] // Allow inline style attributes
});
outputFrame.srcdoc = `<div style="padding:10px;overflow:auto;background:white;border-radius:8px;">${sanitizedHtml}</div>`;
if(codeInputArea) codeInputArea.value=generatedCode; // Store raw potentially styled HTML
if(fullCodeContent) fullCodeContent.textContent=generatedCode;
console.log("Displayed data analysis results as HTML Table.");
} else { // Fallback for data_analysis (Markdown)
let htmlOutput = "";
if (typeof marked !== 'undefined' && typeof marked.parse === 'function') {
htmlOutput = DOMPurify.sanitize(marked.parse(generatedCode));
} else {
console.warn("marked library not available for rendering data analysis markdown.");
htmlOutput = `<pre style="white-space: pre-wrap; word-wrap: break-word;">${DOMPurify.sanitize(generatedCode)}</pre>`; // Fallback
}
if(outputFrame) outputFrame.srcdoc = `<div style="padding:20px;background:white;border-radius:8px;max-height:100%;overflow-y:auto;font-family: Inter, sans-serif;line-height:1.6;">${htmlOutput}</div>`;
if(codeInputArea) codeInputArea.value=generatedCode; // Store raw Markdown
if(fullCodeContent) fullCodeContent.textContent=generatedCode;
console.log("Displayed data analysis results as Markdown (fallback).");
}
} else { // code_generation
console.log(`Processing code generation result for: ${currentLanguage}`);
if(aiStatusText) aiStatusText.textContent = `Rendering ${currentLanguage} preview...`;
if(outputFrame) {
// Reset onload handler before setting srcdoc for non-chart cases
outputFrame.onload = null;
outputFrame.dataset.chartLoaded = 'false'; // Reset chart loaded flag
switch (currentLanguage) {
case "HTML/CSS/JS (Web Page)":
console.log("Setting outputFrame.srcdoc directly for HTML.");
outputFrame.srcdoc = generatedCode; // Assumes self-contained HTML
break;
case "Python (Pyodide)":
console.log("Running Python code via Pyodide.");
await runPythonCode(generatedCode); // Async function
break;
case "JavaScript (Standalone)":
case "TypeScript":
case "React Component":
case "Next.js Page (Client-Side)":
console.log(`Transpiling and previewing ${currentLanguage}.`);
outputFrame.srcdoc = transpileToRunnableJS(generatedCode, currentLanguage);
break;
default: // Other languages for display only
console.log(`Displaying code for ${currentLanguage} without execution.`);
outputFrame.srcdoc = transpileToRunnableJS(generatedCode, currentLanguage); // Use transpiler function for consistent display
}
}
if(codeInputArea) codeInputArea.value = generatedCode; // Store raw generated code
if(fullCodeContent) fullCodeContent.textContent = generatedCode;
console.log("Displayed code generation results.");
}
} catch (error) {
console.error("--- AI Request/Processing Error ---:", error);
const errTxt = stopGenerationFlag ? "Operation stopped by user." : `Error: ${error.message}`;
// Display error prominently
if(aiPlanContent && planText) { // If plan succeeded but execution failed
aiPlanContent.innerHTML += `<p style="color:red; font-weight:bold; margin-top:15px;">Execution Failed!</p><p style="color:red;">${DOMPurify.sanitize(errTxt)}</p>`;
} else if (aiPlanContent) { // Plan generation itself failed (message already set)
aiPlanContent.innerHTML += `<p style="color:red; font-weight:bold; margin-top:15px;">Content Generation Failed (due to plan error)</p>`;
} else {
// If plan UI itself failed or error happened before plan UI setup
if(outputFrame) outputFrame.srcdoc = `<div style='color:red; padding:20px; font-family: Inter, sans-serif; border: 2px solid red; background: #fff0f0; border-radius: 8px;'><h3>Request Failed</h3><p style="font-weight:bold;">${DOMPurify.sanitize(errTxt)}</p><p>Check the console (F12) for more details.</p></div>`;
}
if(codeInputArea) codeInputArea.value += `\n\n--- ERROR ---\n${DOMPurify.sanitize(errTxt)}`;
if(outputFrame && !outputFrame.srcdoc.includes("Request Failed")) { // Avoid overwriting specific Pyodide errors etc.
outputFrame.srcdoc = `<div style='color:red; padding:20px; font-family: Inter, sans-serif; border: 2px solid red; background: #fff0f0; border-radius: 8px;'><h3>Request Failed</h3><p style="font-weight:bold;">${DOMPurify.sanitize(errTxt)}</p><p>Check the console (F12) for more details. Possible issues include API key problems, network errors, invalid input, or internal AI errors.</p></div>`;
}
// Specific check for multimodal model issues using your original logic
if (error.message && (error.message.toLowerCase().includes("does not support image input") || error.message.toLowerCase().includes("multimodal"))) {
// Check if body exists before appending
if(outputFrame.contentDocument && outputFrame.contentDocument.body) {
outputFrame.contentDocument.body.innerHTML += `<p style='color:orange;padding:0 20px 20px 20px; font-weight:bold;'>Note: The selected model ('${MODEL_NAME}') might not fully support image analysis required for screen sharing tasks. Ensure you are using a multimodal model if required.</p>`;
} else {
outputFrame.srcdoc += `<p style='color:orange;padding:0 20px 20px 20px; font-weight:bold;'>Note: The selected model ('${MODEL_NAME}') might not fully support image analysis required for screen sharing tasks. Ensure you are using a multimodal model if required.</p>`;
}
}
} finally {
generationInProgress = false;
stopGenerationFlag = false; // Reset flag
updateButtonStates(false);
showAiOverlay(false);
currentScreenFrameDataUrl = ""; // Clear captured frame data
// Don't hide aiPlanContainer here, leave plan (and potential error message) visible for reference
console.log("--- Request Cycle Finished ---");
}
}
// *** NEW: Enhanced performResearchWithPlan function (using new detailed prompts and solverParams) ***
async function performResearchWithPlan(topic) {
if (!topic) { alert("Please provide a topic to research."); return; }
if (generationInProgress) { alert("Another AI task is currently in progress. Please wait."); return; }
console.log(`--- Starting New Research Cycle ---`);
console.log(`Research Topic: ${topic}`);
generationInProgress = true;
stopGenerationFlag = false;
updateButtonStates(true);
if(aiPlanContainer) aiPlanContainer.classList.remove('hidden'); // Show plan container
if(aiPlanContent) aiPlanContent.innerHTML = ""; // Clear previous plan view
// Clear previous results
if(codeInputArea) codeInputArea.value = "";
if(outputFrame) outputFrame.srcdoc = `<p style="padding:20px; font-style:italic; color: #555;">Initiating research... waiting for plan.</p>`;
if(fullCodeContent) fullCodeContent.textContent = "";
if (window.currentAiChart) { window.currentAiChart.destroy(); window.currentAiChart = null; }
let planText = "";
try {
// 1. Generate Research Plan
const contextSummaryForPlan = `Research topic: "${topic}". Aim for depth, accuracy, and comprehensive synthesis.`;
planText = await generatePlan(topic, contextSummaryForPlan, "research");
// generatePlan throws on error
if (stopGenerationFlag) throw new Error("Research stopped by user after plan generation.");
showAiOverlay(true, `Phase 2: Executing Research Plan for: ${topic.substring(0, 50)}...`);
if(outputFrame) outputFrame.srcdoc = `<p style="padding:20px; font-style:italic; color: #333;">Plan received. Researching topic: "${DOMPurify.sanitize(topic)}"...</p>`;
// 2. Prepare and Execute Research Request based on Plan
const focusParams = solverParams; // Use MAX focus for research quality
// *** NEW DETAILED RESEARCH PROMPT ***
const researchPrompt = `
You are an expert AI Research Assistant and Knowledge Synthesizer with access to a vast knowledge base. Your task is to conduct meticulous, in-depth research on the given topic, rigorously following the generated plan, and present a comprehensive, accurate, well-structured, and insightful summary of findings in Markdown format.
**Research Topic:**
"${topic}"
**Generated Strategic Plan (Follow this precisely and exhaustively):**
--- PLAN START ---
${planText}
--- PLAN END ---
**Core Directives & Quality Requirements:**
1. **Execute the Plan Faithfully:** Adhere strictly to the research steps outlined in the plan. Ensure all facets mentioned in the plan are covered.
2. **Comprehensiveness, Depth & Nuance (${focusParams.depth_focus_max}):** Gather information from diverse, reliable perspectives. Explore the topic deeply, covering key concepts, definitions, historical context, current state-of-the-art, methodologies, applications, debates, criticisms, and future trends as relevant and guided by the plan. Uncover non-obvious details.
3. **Accuracy, Verification & Rigor (${focusParams.analytical_rigor_max}):** Prioritize factual accuracy above all. Cross-reference information where possible (conceptually). Synthesize information logically, coherently, and critically. Clearly distinguish established facts from theories, opinions, or speculations. Avoid making unsubstantiated claims.
4. **Synthesis, Structure & Clarity (${focusParams.efficiency_focus_max}):** Do not merely list facts. Synthesize the information into a cohesive, well-organized narrative or structured report using clear Markdown (headings, subheadings, nested lists, bolding, italics, blockquotes). Ensure a logical flow and high readability. Define key terms.
5. **Exploration of Perspectives & Creativity (${focusParams.alternative_exploration_max}, ${focusParams.creativity_focus_max}):** If the topic involves multiple viewpoints, theories, controversies, or approaches, represent them accurately and fairly. Present the information in an engaging and insightful manner.
6. **Further Reading (Contextual & General):** Where appropriate, suggest *general areas*, *key concepts*, *influential figures*, or *seminal types of works* for further reading. **Do NOT invent fake URLs, specific book titles, or specific paper citations.** Focus on conceptual pointers that guide further exploration (e.g., "Further reading could explore behavioral economics principles," "Key figures in this field include...").
7. **Acknowledge Limitations & Ambiguities:** If the topic is highly niche, information is genuinely scarce or contradictory, or there are significant unresolved questions, state this explicitly and clearly within the report. Do not invent information.
**CRITICAL Output Format Instructions:**
* Output **ONLY** the final research findings in rich Markdown format.
* **NO PREAMBLE OR CONVERSATIONAL TEXT:** Do not include introductions like "Here is the research on..." or "Based on my knowledge...". Start directly with the Markdown content (e.g., a top-level heading like \`# Research on: ${topic}\`).
* Use Markdown extensively and effectively for structure, emphasis, and readability.
**Conduct the deep research and generate the comprehensive Markdown report now, following the plan and all directives.**
`;
// *** Используем параметры из solverParams ***
const generationConfig = {
temperature: solverParams.refine_temp, // Use refine_temp for research/synthesis
topP: solverParams.topP,
topK: solverParams.topK,
maxOutputTokens: solverParams.max_refine_tokens, // Allow ample space for research findings
};
console.log("Sending research request to Gemini:", { config: generationConfig, promptSnippet: researchPrompt.substring(0, 500) + "..." });
// *** Отправляем запрос модели ***
const result = await model.generateContent(researchPrompt, generationConfig); // Pass config
// Check for stop flag *after* the async call returns
if (stopGenerationFlag) throw new Error("Research stopped by user during generation.");
const response = result?.response; // Use optional chaining
let researchText = "";
if (response && typeof response.text === 'function') {
researchText = response.text();
} else if (response && response.candidates && response.candidates.length > 0 && response.candidates[0].content && response.candidates[0].content.parts && response.candidates[0].content.parts.length > 0 && typeof response.candidates[0].content.parts[0].text === 'string') {
researchText = response.candidates[0].content.parts[0].text;
console.warn("Recovered research text from candidate structure.");
}
else {
console.error("Unexpected response structure or no text found for research:", response);
throw new Error("Received no valid text content from Gemini for research.");
}
if (!researchText.trim()) {
console.warn("Received empty or whitespace-only research response from AI.");
researchText = `*The AI returned an empty response for the research topic: "${topic}". Information might be unavailable, the topic might be too obscure, or the request could not be processed.*`;
}
// Basic cleaning - remove potential ``` markdown fences
const cleanedResearchText = researchText.replace(/^```(?:markdown)?\s*\n?([\s\S]*?)\n?```$/gm, '$1').trim();
console.log("Raw research response received (first 500 chars):", cleanedResearchText.substring(0, 500));
// 3. Display Research Results
if(aiStatusText) aiStatusText.textContent = `Finalizing research results for: ${topic.substring(0, 50)}...`;
// Используем вашу оригинальную логику отображения
if(codeInputArea) codeInputArea.value = `# Research on: ${topic}\n\n${cleanedResearchText}`;
if(fullCodeContent) fullCodeContent.textContent = codeInputArea.value; // Show raw markdown in full code view too
let htmlOutput = "";
if (typeof marked !== 'undefined' && typeof marked.parse === 'function') {
htmlOutput = DOMPurify.sanitize(marked.parse(cleanedResearchText)); // Render Markdown to HTML
} else {
console.warn("marked library not available for rendering research results.");
htmlOutput = `<pre style="white-space: pre-wrap; word-wrap: break-word;">${DOMPurify.sanitize(cleanedResearchText)}</pre>`; // Fallback
}
if(outputFrame) {
// Reset onload handler before setting srcdoc
outputFrame.onload = null;
outputFrame.dataset.chartLoaded = 'false'; // Reset chart loaded flag
outputFrame.srcdoc = `<div style="padding:20px;background:white;border-radius:8px;max-height:100%;overflow-y:auto;font-family: Inter, sans-serif; line-height:1.6;"><h2>Research: ${DOMPurify.sanitize(topic)}</h2>${htmlOutput}</div>`;
}
console.log("Displayed research results as Markdown.");
} catch (error) {
console.error("--- Research Error ---:", error);
const errTxt = stopGenerationFlag ? "Research operation stopped by user." : `Research Error: ${error.message}`;
// Display error prominently
if(aiPlanContent && planText) { // If plan succeeded but execution failed
aiPlanContent.innerHTML += `<p style="color:red; font-weight:bold; margin-top:15px;">Research Execution Failed!</p><p style="color:red;">${DOMPurify.sanitize(errTxt)}</p>`;
} else if (aiPlanContent) { // Plan generation itself failed (message already set)
aiPlanContent.innerHTML += `<p style="color:red; font-weight:bold; margin-top:15px;">Research Failed (due to plan error)</p>`;
} else {
if(outputFrame) outputFrame.srcdoc = `<div style='color:red; padding:20px; font-family: Inter, sans-serif; border: 2px solid red; background: #fff0f0; border-radius: 8px;'><h3>Research Failed</h3><p style="font-weight:bold;">${DOMPurify.sanitize(errTxt)}</p><p>Check the console (F12) for more details.</p></div>`;
}
if(codeInputArea) codeInputArea.value += `\n\n--- RESEARCH ERROR ---\n${DOMPurify.sanitize(errTxt)}`;
if(outputFrame && !outputFrame.srcdoc.includes("Research Failed")) { // Avoid overwriting plan errors if possible
outputFrame.srcdoc = `<div style='color:red; padding:20px; font-family: Inter, sans-serif; border: 2px solid red; background: #fff0f0; border-radius: 8px;'><h3>Research Failed</h3><p style="font-weight:bold;">${DOMPurify.sanitize(errTxt)}</p><p>Check the console (F12) for more details.</p></div>`;
}
} finally {
generationInProgress = false;
stopGenerationFlag = false; // Reset flag
updateButtonStates(false);
showAiOverlay(false);
console.log("--- Research Cycle Finished ---");
}
}
// --- Start Application ---
// (Ваш оригинальный код запуска остается без изменений)
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeApp);
} else {
initializeApp();
}
console.log("Script execution reached end (async ops may continue)."); |