Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Update app.py
Browse files
app.py
CHANGED
@@ -1311,2598 +1311,33 @@ async def root(request: Request, pdf_id: Optional[str] = Query(None)):
|
|
1311 |
return get_html_content()
|
1312 |
|
1313 |
# HTML ๋ฌธ์์ด (AI ๋ฒํผ ๋ฐ ์ฑ๋ด UI ์ถ๊ฐ)
|
1314 |
-
HTML
|
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 |
-
--bg-color: #f8f9fa; /* ๋ฐ์ ๋ฐฐ๊ฒฝ */
|
1342 |
-
--text-color: #495057; /* ๋ถ๋๋ฌ์ด ์ด๋์ด ์ */
|
1343 |
-
--card-bg: #ffffff; /* ์นด๋ ๋ฐฐ๊ฒฝ์ */
|
1344 |
-
--shadow-sm: 0 2px 8px rgba(0,0,0,0.05);
|
1345 |
-
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
|
1346 |
-
--shadow-lg: 0 8px 24px rgba(0,0,0,0.12);
|
1347 |
-
--radius-sm: 8px;
|
1348 |
-
--radius-md: 12px;
|
1349 |
-
--radius-lg: 16px;
|
1350 |
-
--transition: all 0.3s ease;
|
1351 |
-
}
|
1352 |
-
|
1353 |
-
|
1354 |
-
body {
|
1355 |
-
margin: 0;
|
1356 |
-
background: var(--bg-color);
|
1357 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
1358 |
-
color: var(--text-color);
|
1359 |
-
/* ์๋ก์ด ํผํ ๊ณํต ๊ณ ๊ธ์ค๋ฌ์ด ๊ทธ๋ผ๋์์ด์
๋ฐฐ๊ฒฝ */
|
1360 |
-
background-image: linear-gradient(135deg, #2a0845 0%, #6441a5 50%, #c9a8ff 100%);
|
1361 |
-
background-attachment: fixed;
|
1362 |
-
}
|
1363 |
-
|
1364 |
-
/* ๋ทฐ์ด ๋ชจ๋์ผ ๋ ๋ฐฐ๊ฒฝ ๋ณ๊ฒฝ */
|
1365 |
-
.viewer-mode {
|
1366 |
-
background-image: linear-gradient(135deg, #30154e 0%, #6b47ad 50%, #d5b8ff 100%) !important;
|
1367 |
-
}
|
1368 |
-
|
1369 |
-
/* ํค๋ ์ ๋ชฉ ์ ๊ฑฐ ๋ฐ Home ๋ฒํผ ๋ ์ด์ด ์ฒ๋ฆฌ */
|
1370 |
-
.floating-home, .floating-ai {
|
1371 |
-
position: fixed;
|
1372 |
-
top: 20px;
|
1373 |
-
left: 20px;
|
1374 |
-
width: 60px;
|
1375 |
-
height: 60px;
|
1376 |
-
border-radius: 50%;
|
1377 |
-
background: rgba(255, 255, 255, 0.9);
|
1378 |
-
backdrop-filter: blur(10px);
|
1379 |
-
box-shadow: var(--shadow-md);
|
1380 |
-
z-index: 9999;
|
1381 |
-
display: flex;
|
1382 |
-
justify-content: center;
|
1383 |
-
align-items: center;
|
1384 |
-
cursor: pointer;
|
1385 |
-
transition: var(--transition);
|
1386 |
-
overflow: hidden;
|
1387 |
-
}
|
1388 |
-
|
1389 |
-
.floating-ai {
|
1390 |
-
top: 90px; /* Home ๋ฒํผ ์๋์ ์์น */
|
1391 |
-
background: rgba(134, 232, 171, 0.9); /* AI ๋ฒํผ ์์ */
|
1392 |
-
}
|
1393 |
-
|
1394 |
-
.floating-home:hover, .floating-ai:hover {
|
1395 |
-
transform: scale(1.05);
|
1396 |
-
box-shadow: var(--shadow-lg);
|
1397 |
-
}
|
1398 |
-
|
1399 |
-
.floating-home .icon, .floating-ai .icon {
|
1400 |
-
display: flex;
|
1401 |
-
justify-content: center;
|
1402 |
-
align-items: center;
|
1403 |
-
width: 100%;
|
1404 |
-
height: 100%;
|
1405 |
-
font-size: 22px;
|
1406 |
-
color: var(--primary-color);
|
1407 |
-
transition: var(--transition);
|
1408 |
-
}
|
1409 |
-
|
1410 |
-
.floating-ai .icon {
|
1411 |
-
color: white;
|
1412 |
-
}
|
1413 |
-
|
1414 |
-
.floating-home:hover .icon {
|
1415 |
-
color: #8bc5f8;
|
1416 |
-
}
|
1417 |
-
|
1418 |
-
.floating-ai:hover .icon {
|
1419 |
-
color: #ffffff;
|
1420 |
-
}
|
1421 |
-
|
1422 |
-
.floating-home .title, .floating-ai .title {
|
1423 |
-
position: absolute;
|
1424 |
-
left: 70px;
|
1425 |
-
background: rgba(255, 255, 255, 0.95);
|
1426 |
-
padding: 8px 20px;
|
1427 |
-
border-radius: 20px;
|
1428 |
-
box-shadow: var(--shadow-sm);
|
1429 |
-
font-weight: 600;
|
1430 |
-
font-size: 14px;
|
1431 |
-
white-space: nowrap;
|
1432 |
-
pointer-events: none;
|
1433 |
-
opacity: 0;
|
1434 |
-
transform: translateX(-10px);
|
1435 |
-
transition: all 0.3s ease;
|
1436 |
-
}
|
1437 |
-
|
1438 |
-
.floating-home:hover .title, .floating-ai:hover .title {
|
1439 |
-
opacity: 1;
|
1440 |
-
transform: translateX(0);
|
1441 |
-
}
|
1442 |
-
|
1443 |
-
/* ๊ด๋ฆฌ์ ๋ฒํผ ์คํ์ผ */
|
1444 |
-
#adminButton {
|
1445 |
-
position: fixed;
|
1446 |
-
top: 20px;
|
1447 |
-
right: 20px;
|
1448 |
-
background: rgba(255, 255, 255, 0.9);
|
1449 |
-
backdrop-filter: blur(10px);
|
1450 |
-
box-shadow: var(--shadow-md);
|
1451 |
-
border-radius: 30px;
|
1452 |
-
padding: 8px 20px;
|
1453 |
-
display: flex;
|
1454 |
-
align-items: center;
|
1455 |
-
font-weight: 600;
|
1456 |
-
font-size: 14px;
|
1457 |
-
cursor: pointer;
|
1458 |
-
transition: var(--transition);
|
1459 |
-
z-index: 9999;
|
1460 |
-
}
|
1461 |
-
|
1462 |
-
#adminButton i {
|
1463 |
-
margin-right: 8px;
|
1464 |
-
color: var(--accent-color);
|
1465 |
-
}
|
1466 |
-
|
1467 |
-
#adminButton:hover {
|
1468 |
-
transform: translateY(-3px);
|
1469 |
-
box-shadow: var(--shadow-lg);
|
1470 |
-
}
|
1471 |
-
|
1472 |
-
/* ๊ด๋ฆฌ์ ๋ก๊ทธ์ธ ๋ชจ๋ฌ */
|
1473 |
-
.modal {
|
1474 |
-
display: none;
|
1475 |
-
position: fixed;
|
1476 |
-
top: 0;
|
1477 |
-
left: 0;
|
1478 |
-
width: 100%;
|
1479 |
-
height: 100%;
|
1480 |
-
background: rgba(0, 0, 0, 0.5);
|
1481 |
-
backdrop-filter: blur(5px);
|
1482 |
-
z-index: 10000;
|
1483 |
-
align-items: center;
|
1484 |
-
justify-content: center;
|
1485 |
-
}
|
1486 |
-
|
1487 |
-
.modal-content {
|
1488 |
-
background: white;
|
1489 |
-
border-radius: var(--radius-md);
|
1490 |
-
padding: 30px;
|
1491 |
-
width: 90%;
|
1492 |
-
max-width: 400px;
|
1493 |
-
box-shadow: var(--shadow-lg);
|
1494 |
-
text-align: center;
|
1495 |
-
}
|
1496 |
-
|
1497 |
-
.modal-content h2 {
|
1498 |
-
margin-top: 0;
|
1499 |
-
color: var(--accent-color);
|
1500 |
-
margin-bottom: 20px;
|
1501 |
-
}
|
1502 |
-
|
1503 |
-
.modal-content input {
|
1504 |
-
width: 100%;
|
1505 |
-
padding: 12px;
|
1506 |
-
margin-bottom: 20px;
|
1507 |
-
border: 1px solid #ddd;
|
1508 |
-
border-radius: var(--radius-sm);
|
1509 |
-
font-size: 16px;
|
1510 |
-
box-sizing: border-box;
|
1511 |
-
}
|
1512 |
-
|
1513 |
-
.modal-content button {
|
1514 |
-
padding: 10px 20px;
|
1515 |
-
border: none;
|
1516 |
-
border-radius: var(--radius-sm);
|
1517 |
-
background: var(--accent-color);
|
1518 |
-
color: white;
|
1519 |
-
font-weight: 600;
|
1520 |
-
cursor: pointer;
|
1521 |
-
margin: 0 5px;
|
1522 |
-
transition: var(--transition);
|
1523 |
-
}
|
1524 |
-
|
1525 |
-
.modal-content button:hover {
|
1526 |
-
opacity: 0.9;
|
1527 |
-
transform: translateY(-2px);
|
1528 |
-
}
|
1529 |
-
|
1530 |
-
.modal-content #adminLoginClose {
|
1531 |
-
background: #f1f3f5;
|
1532 |
-
color: var(--text-color);
|
1533 |
-
}
|
1534 |
-
|
1535 |
-
#home, #viewerPage, #adminPage {
|
1536 |
-
padding-top: 100px;
|
1537 |
-
max-width: 1200px;
|
1538 |
-
margin: 0 auto;
|
1539 |
-
padding-bottom: 60px;
|
1540 |
-
padding-left: 30px;
|
1541 |
-
padding-right: 30px;
|
1542 |
-
position: relative;
|
1543 |
-
}
|
1544 |
-
|
1545 |
-
/* ์
๋ก๋ ๋ฒํผ ์คํ์ผ */
|
1546 |
-
.upload-container {
|
1547 |
-
display: flex;
|
1548 |
-
margin-bottom: 30px;
|
1549 |
-
justify-content: center;
|
1550 |
-
}
|
1551 |
-
|
1552 |
-
button.upload {
|
1553 |
-
all: unset;
|
1554 |
-
cursor: pointer;
|
1555 |
-
padding: 12px 20px;
|
1556 |
-
border-radius: var(--radius-md);
|
1557 |
-
background: white;
|
1558 |
-
margin: 0 10px;
|
1559 |
-
font-weight: 500;
|
1560 |
-
display: flex;
|
1561 |
-
align-items: center;
|
1562 |
-
|
1563 |
-
|
1564 |
-
box-shadow: var(--shadow-sm);
|
1565 |
-
transition: var(--transition);
|
1566 |
-
position: relative;
|
1567 |
-
overflow: hidden;
|
1568 |
-
}
|
1569 |
-
|
1570 |
-
button.upload::before {
|
1571 |
-
content: '';
|
1572 |
-
position: absolute;
|
1573 |
-
top: 0;
|
1574 |
-
left: 0;
|
1575 |
-
width: 100%;
|
1576 |
-
height: 100%;
|
1577 |
-
background: linear-gradient(120deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
1578 |
-
opacity: 0.08;
|
1579 |
-
z-index: -1;
|
1580 |
-
}
|
1581 |
-
|
1582 |
-
button.upload:hover {
|
1583 |
-
transform: translateY(-3px);
|
1584 |
-
box-shadow: var(--shadow-md);
|
1585 |
-
}
|
1586 |
-
|
1587 |
-
button.upload:hover::before {
|
1588 |
-
opacity: 0.15;
|
1589 |
-
}
|
1590 |
-
|
1591 |
-
button.upload i {
|
1592 |
-
margin-right: 8px;
|
1593 |
-
font-size: 20px;
|
1594 |
-
}
|
1595 |
-
|
1596 |
-
/* ๊ทธ๋ฆฌ๋ ๋ฐ ์นด๋ ์คํ์ผ */
|
1597 |
-
.grid {
|
1598 |
-
display: grid;
|
1599 |
-
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
1600 |
-
gap: 24px;
|
1601 |
-
margin-top: 36px;
|
1602 |
-
}
|
1603 |
-
|
1604 |
-
.card {
|
1605 |
-
background: var(--card-bg);
|
1606 |
-
border-radius: var(--radius-md);
|
1607 |
-
cursor: pointer;
|
1608 |
-
box-shadow: var(--shadow-sm);
|
1609 |
-
width: 100%;
|
1610 |
-
height: 280px;
|
1611 |
-
position: relative;
|
1612 |
-
display: flex;
|
1613 |
-
flex-direction: column;
|
1614 |
-
align-items: center;
|
1615 |
-
justify-content: center;
|
1616 |
-
transition: var(--transition);
|
1617 |
-
overflow: hidden;
|
1618 |
-
}
|
1619 |
-
|
1620 |
-
.card::before {
|
1621 |
-
content: '';
|
1622 |
-
position: absolute;
|
1623 |
-
top: 0;
|
1624 |
-
left: 0;
|
1625 |
-
width: 100%;
|
1626 |
-
height: 100%;
|
1627 |
-
background: linear-gradient(135deg, var(--secondary-color) 0%, var(--primary-color) 100%);
|
1628 |
-
opacity: 0.06;
|
1629 |
-
z-index: 1;
|
1630 |
-
}
|
1631 |
-
|
1632 |
-
.card::after {
|
1633 |
-
content: '';
|
1634 |
-
position: absolute;
|
1635 |
-
top: 0;
|
1636 |
-
left: 0;
|
1637 |
-
width: 100%;
|
1638 |
-
height: 30%;
|
1639 |
-
background: linear-gradient(to bottom, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0) 100%);
|
1640 |
-
z-index: 2;
|
1641 |
-
}
|
1642 |
-
|
1643 |
-
.card img {
|
1644 |
-
width: 65%;
|
1645 |
-
height: auto;
|
1646 |
-
object-fit: contain;
|
1647 |
-
position: absolute;
|
1648 |
-
top: 50%;
|
1649 |
-
left: 50%;
|
1650 |
-
transform: translate(-50%, -65%);
|
1651 |
-
border: 1px solid rgba(0,0,0,0.05);
|
1652 |
-
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
|
1653 |
-
z-index: 3;
|
1654 |
-
transition: var(--transition);
|
1655 |
-
}
|
1656 |
-
|
1657 |
-
.card:hover {
|
1658 |
-
transform: translateY(-5px);
|
1659 |
-
box-shadow: var(--shadow-md);
|
1660 |
-
}
|
1661 |
-
|
1662 |
-
.card:hover img {
|
1663 |
-
transform: translate(-50%, -65%) scale(1.03);
|
1664 |
-
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
1665 |
-
}
|
1666 |
-
|
1667 |
-
.card p {
|
1668 |
-
position: absolute;
|
1669 |
-
bottom: 20px;
|
1670 |
-
left: 50%;
|
1671 |
-
transform: translateX(-50%);
|
1672 |
-
background: rgba(255, 255, 255, 0.9);
|
1673 |
-
padding: 8px 16px;
|
1674 |
-
border-radius: 30px;
|
1675 |
-
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
1676 |
-
width: 80%;
|
1677 |
-
text-align: center;
|
1678 |
-
white-space: nowrap;
|
1679 |
-
overflow: hidden;
|
1680 |
-
text-overflow: ellipsis;
|
1681 |
-
font-size: 14px;
|
1682 |
-
font-weight: 500;
|
1683 |
-
color: var(--text-color);
|
1684 |
-
z-index: 4;
|
1685 |
-
transition: var(--transition);
|
1686 |
-
}
|
1687 |
-
|
1688 |
-
.card:hover p {
|
1689 |
-
background: rgba(255, 255, 255, 0.95);
|
1690 |
-
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
1691 |
-
}
|
1692 |
-
|
1693 |
-
/* ์บ์ ์ํ ๋ฑ์ง */
|
1694 |
-
.cached-status {
|
1695 |
-
position: absolute;
|
1696 |
-
top: 10px;
|
1697 |
-
right: 10px;
|
1698 |
-
background: var(--accent-color);
|
1699 |
-
color: white;
|
1700 |
-
font-size: 11px;
|
1701 |
-
padding: 3px 8px;
|
1702 |
-
border-radius: 12px;
|
1703 |
-
z-index: 5;
|
1704 |
-
box-shadow: var(--shadow-sm);
|
1705 |
-
}
|
1706 |
-
|
1707 |
-
/* ๊ด๋ฆฌ์ ์นด๋ ์ถ๊ฐ ์คํ์ผ */
|
1708 |
-
.admin-card {
|
1709 |
-
height: 300px;
|
1710 |
-
}
|
1711 |
-
|
1712 |
-
.admin-card .card-inner {
|
1713 |
-
width: 100%;
|
1714 |
-
height: 100%;
|
1715 |
-
position: relative;
|
1716 |
-
display: flex;
|
1717 |
-
flex-direction: column;
|
1718 |
-
align-items: center;
|
1719 |
-
}
|
1720 |
-
|
1721 |
-
.delete-btn, .feature-btn, .unfeature-btn {
|
1722 |
-
position: absolute;
|
1723 |
-
bottom: 60px;
|
1724 |
-
left: 50%;
|
1725 |
-
transform: translateX(-50%);
|
1726 |
-
background: #ff7675;
|
1727 |
-
color: white;
|
1728 |
-
border: none;
|
1729 |
-
border-radius: 20px;
|
1730 |
-
padding: 5px 15px;
|
1731 |
-
font-size: 12px;
|
1732 |
-
cursor: pointer;
|
1733 |
-
z-index: 10;
|
1734 |
-
transition: var(--transition);
|
1735 |
-
}
|
1736 |
-
|
1737 |
-
.feature-btn {
|
1738 |
-
bottom: 95px;
|
1739 |
-
background: #74b9ff;
|
1740 |
-
}
|
1741 |
-
|
1742 |
-
.unfeature-btn {
|
1743 |
-
bottom: 95px;
|
1744 |
-
background: #a29bfe;
|
1745 |
-
}
|
1746 |
-
|
1747 |
-
.delete-btn:hover, .feature-btn:hover, .unfeature-btn:hover {
|
1748 |
-
opacity: 0.9;
|
1749 |
-
transform: translateX(-50%) scale(1.05);
|
1750 |
-
}
|
1751 |
-
|
1752 |
-
/* ๋ทฐ์ด ์คํ์ผ */
|
1753 |
-
#viewer {
|
1754 |
-
width: 90%;
|
1755 |
-
height: 90vh;
|
1756 |
-
max-width: 90%;
|
1757 |
-
margin: 0;
|
1758 |
-
background: var(--card-bg);
|
1759 |
-
border: none;
|
1760 |
-
border-radius: var(--radius-lg);
|
1761 |
-
position: fixed;
|
1762 |
-
top: 50%;
|
1763 |
-
left: 50%;
|
1764 |
-
transform: translate(-50%, -50%);
|
1765 |
-
z-index: 1000;
|
1766 |
-
box-shadow: var(--shadow-lg);
|
1767 |
-
max-height: calc(90vh - 40px);
|
1768 |
-
aspect-ratio: auto;
|
1769 |
-
object-fit: contain;
|
1770 |
-
overflow: hidden;
|
1771 |
-
}
|
1772 |
-
|
1773 |
-
/* FlipBook ์ปจํธ๋กค๋ฐ ์คํ์ผ */
|
1774 |
-
.flipbook-container .fb3d-menu-bar {
|
1775 |
-
z-index: 2000 !important;
|
1776 |
-
opacity: 1 !important;
|
1777 |
-
bottom: 0 !important;
|
1778 |
-
background-color: rgba(255,255,255,0.9) !important;
|
1779 |
-
backdrop-filter: blur(10px) !important;
|
1780 |
-
border-radius: 0 0 var(--radius-lg) var(--radius-lg) !important;
|
1781 |
-
padding: 12px 0 !important;
|
1782 |
-
box-shadow: 0 -4px 20px rgba(0,0,0,0.1) !important;
|
1783 |
-
}
|
1784 |
-
|
1785 |
-
.flipbook-container .fb3d-menu-bar > ul > li > img,
|
1786 |
-
.flipbook-container .fb3d-menu-bar > ul > li > div {
|
1787 |
-
opacity: 1 !important;
|
1788 |
-
transform: scale(1.2) !important;
|
1789 |
-
filter: drop-shadow(0 2px 3px rgba(0,0,0,0.1)) !important;
|
1790 |
-
}
|
1791 |
-
|
1792 |
-
.flipbook-container .fb3d-menu-bar > ul > li {
|
1793 |
-
margin: 0 12px !important;
|
1794 |
-
}
|
1795 |
-
|
1796 |
-
/* ๋ฉ๋ด ํดํ ์คํ์ผ */
|
1797 |
-
.flipbook-container .fb3d-menu-bar > ul > li > span {
|
1798 |
-
background-color: rgba(0,0,0,0.7) !important;
|
1799 |
-
color: white !important;
|
1800 |
-
border-radius: var(--radius-sm) !important;
|
1801 |
-
padding: 8px 12px !important;
|
1802 |
-
font-size: 13px !important;
|
1803 |
-
bottom: 55px !important;
|
1804 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
1805 |
-
letter-spacing: 0.3px !important;
|
1806 |
-
}
|
1807 |
-
|
1808 |
-
/* ๋ทฐ์ด ๋ชจ๋์ผ ๋ ๋ฐฐ๊ฒฝ ์ค๋ฒ๋ ์ด */
|
1809 |
-
.viewer-mode {
|
1810 |
-
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
|
1811 |
-
}
|
1812 |
-
|
1813 |
-
/* ๋ทฐ์ด ํ์ด์ง ๋ฐฐ๊ฒฝ */
|
1814 |
-
#viewerPage {
|
1815 |
-
background: transparent;
|
1816 |
-
}
|
1817 |
-
|
1818 |
-
/* ๋ก๋ฉ ์ ๋๋ฉ์ด์
*/
|
1819 |
-
@keyframes spin {
|
1820 |
-
0% { transform: rotate(0deg); }
|
1821 |
-
100% { transform: rotate(360deg); }
|
1822 |
-
}
|
1823 |
-
|
1824 |
-
.loading-spinner {
|
1825 |
-
border: 4px solid rgba(255,255,255,0.3);
|
1826 |
-
border-top: 4px solid var(--primary-color);
|
1827 |
-
border-radius: 50%;
|
1828 |
-
width: 50px;
|
1829 |
-
height: 50px;
|
1830 |
-
margin: 0 auto;
|
1831 |
-
animation: spin 1.5s ease-in-out infinite;
|
1832 |
-
}
|
1833 |
-
|
1834 |
-
.loading-container {
|
1835 |
-
position: absolute;
|
1836 |
-
top: 50%;
|
1837 |
-
left: 50%;
|
1838 |
-
transform: translate(-50%, -50%);
|
1839 |
-
text-align: center;
|
1840 |
-
background: rgba(255, 255, 255, 0.85);
|
1841 |
-
backdrop-filter: blur(10px);
|
1842 |
-
padding: 30px;
|
1843 |
-
border-radius: var(--radius-md);
|
1844 |
-
box-shadow: var(--shadow-md);
|
1845 |
-
z-index: 9999;
|
1846 |
-
}
|
1847 |
-
|
1848 |
-
.loading-text {
|
1849 |
-
margin-top: 20px;
|
1850 |
-
font-size: 16px;
|
1851 |
-
color: var(--text-color);
|
1852 |
-
font-weight: 500;
|
1853 |
-
}
|
1854 |
-
|
1855 |
-
/* ํ์ด์ง ์ ํ ์ ๋๋ฉ์ด์
*/
|
1856 |
-
@keyframes fadeIn {
|
1857 |
-
from { opacity: 0; }
|
1858 |
-
to { opacity: 1; }
|
1859 |
-
}
|
1860 |
-
|
1861 |
-
.fade-in {
|
1862 |
-
animation: fadeIn 0.5s ease-out;
|
1863 |
-
}
|
1864 |
-
|
1865 |
-
/* ์ถ๊ฐ ์คํ์ผ */
|
1866 |
-
.section-title {
|
1867 |
-
font-size: 1.3rem;
|
1868 |
-
font-weight: 600;
|
1869 |
-
margin: 30px 0 15px;
|
1870 |
-
color: var(--text-color);
|
1871 |
-
}
|
1872 |
-
|
1873 |
-
.no-projects {
|
1874 |
-
text-align: center;
|
1875 |
-
margin: 40px 0;
|
1876 |
-
color: var(--text-color);
|
1877 |
-
font-size: 16px;
|
1878 |
-
}
|
1879 |
-
|
1880 |
-
/* ํ๋ก๊ทธ๋ ์ค ๋ฐ */
|
1881 |
-
.progress-bar-container {
|
1882 |
-
width: 100%;
|
1883 |
-
height: 6px;
|
1884 |
-
background-color: rgba(0,0,0,0.1);
|
1885 |
-
border-radius: 3px;
|
1886 |
-
margin-top: 15px;
|
1887 |
-
overflow: hidden;
|
1888 |
-
}
|
1889 |
-
|
1890 |
-
.progress-bar {
|
1891 |
-
height: 100%;
|
1892 |
-
background: linear-gradient(to right, var(--primary-color), var(--accent-color));
|
1893 |
-
border-radius: 3px;
|
1894 |
-
transition: width 0.3s ease;
|
1895 |
-
}
|
1896 |
-
|
1897 |
-
/* ํค๋ ๋ก๊ณ ๋ฐ ํ์ดํ */
|
1898 |
-
.library-header {
|
1899 |
-
position: fixed;
|
1900 |
-
top: 12px;
|
1901 |
-
left: 0;
|
1902 |
-
right: 0;
|
1903 |
-
text-align: center;
|
1904 |
-
z-index: 100;
|
1905 |
-
pointer-events: none;
|
1906 |
-
}
|
1907 |
-
|
1908 |
-
.library-header .title {
|
1909 |
-
display: inline-block;
|
1910 |
-
padding: 8px 24px; /* ํจ๋ฉ ์ถ์ */
|
1911 |
-
background: rgba(255, 255, 255, 0.85);
|
1912 |
-
backdrop-filter: blur(10px);
|
1913 |
-
border-radius: 25px; /* ํ
๋๋ฆฌ ๋ชจ์๋ฆฌ ์ถ์ */
|
1914 |
-
box-shadow: var(--shadow-md);
|
1915 |
-
font-size: 1.25rem; /* ๊ธ์ ํฌ๊ธฐ ์ถ์ (1.5rem์์ 1.25rem์ผ๋ก) */
|
1916 |
-
font-weight: 600;
|
1917 |
-
background-image: linear-gradient(120deg, #8e74eb 0%, #9d66ff 100%); /* ์ ๋ชฉ ์์๋ ๋ฐํํ๋ฉด๊ณผ ์ด์ธ๋ฆฌ๊ฒ ๋ณ๊ฒฝ */
|
1918 |
-
-webkit-background-clip: text;
|
1919 |
-
background-clip: text;
|
1920 |
-
color: transparent;
|
1921 |
-
pointer-events: all;
|
1922 |
-
}
|
1923 |
-
|
1924 |
-
/* ์ ์ง์ ๋ก๋ฉ ํ์ */
|
1925 |
-
.loading-pages {
|
1926 |
-
position: absolute;
|
1927 |
-
bottom: 20px;
|
1928 |
-
left: 50%;
|
1929 |
-
transform: translateX(-50%);
|
1930 |
-
background: rgba(255, 255, 255, 0.9);
|
1931 |
-
padding: 10px 20px;
|
1932 |
-
border-radius: 20px;
|
1933 |
-
box-shadow: var(--shadow-md);
|
1934 |
-
font-size: 14px;
|
1935 |
-
color: var(--text-color);
|
1936 |
-
z-index: 9998;
|
1937 |
-
text-align: center;
|
1938 |
-
}
|
1939 |
-
|
1940 |
-
/* ๊ด๋ฆฌ์ ํ์ด์ง ์คํ์ผ */
|
1941 |
-
#adminPage {
|
1942 |
-
color: white;
|
1943 |
-
max-width: 1400px;
|
1944 |
-
}
|
1945 |
-
|
1946 |
-
#adminPage h1 {
|
1947 |
-
font-size: 2rem;
|
1948 |
-
margin-bottom: 30px;
|
1949 |
-
text-align: center;
|
1950 |
-
background-image: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%);
|
1951 |
-
-webkit-background-clip: text;
|
1952 |
-
background-clip: text;
|
1953 |
-
color: transparent;
|
1954 |
-
}
|
1955 |
-
|
1956 |
-
#adminBackButton {
|
1957 |
-
position: absolute;
|
1958 |
-
top: 20px;
|
1959 |
-
left: 20px;
|
1960 |
-
background: rgba(255, 255, 255, 0.9);
|
1961 |
-
backdrop-filter: blur(10px);
|
1962 |
-
box-shadow: var(--shadow-md);
|
1963 |
-
border: none;
|
1964 |
-
border-radius: 30px;
|
1965 |
-
padding: 8px 20px;
|
1966 |
-
display: flex;
|
1967 |
-
align-items: center;
|
1968 |
-
font-weight: 600;
|
1969 |
-
font-size: 14px;
|
1970 |
-
cursor: pointer;
|
1971 |
-
transition: var(--transition);
|
1972 |
-
}
|
1973 |
-
|
1974 |
-
#adminBackButton:hover {
|
1975 |
-
transform: translateY(-3px);
|
1976 |
-
box-shadow: var(--shadow-lg);
|
1977 |
-
}
|
1978 |
-
|
1979 |
-
/* ๊ด๋ฆฌ์ ๊ทธ๋ฆฌ๋ */
|
1980 |
-
#adminGrid {
|
1981 |
-
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
1982 |
-
}
|
1983 |
-
|
1984 |
-
/* AI ์ฑ๋ด UI ์คํ์ผ */
|
1985 |
-
#aiChatContainer {
|
1986 |
-
display: none;
|
1987 |
-
position: fixed;
|
1988 |
-
top: 0;
|
1989 |
-
right: 0;
|
1990 |
-
width: 400px;
|
1991 |
-
height: 100%;
|
1992 |
-
background: rgba(255, 255, 255, 0.95);
|
1993 |
-
backdrop-filter: blur(10px);
|
1994 |
-
box-shadow: -5px 0 20px rgba(0, 0, 0, 0.1);
|
1995 |
-
z-index: 9999;
|
1996 |
-
transition: all 0.3s ease;
|
1997 |
-
transform: translateX(100%);
|
1998 |
-
padding: 20px;
|
1999 |
-
box-sizing: border-box;
|
2000 |
-
display: flex;
|
2001 |
-
flex-direction: column;
|
2002 |
-
}
|
2003 |
-
|
2004 |
-
#aiChatContainer.active {
|
2005 |
-
transform: translateX(0);
|
2006 |
-
}
|
2007 |
-
|
2008 |
-
#aiChatHeader {
|
2009 |
-
display: flex;
|
2010 |
-
justify-content: space-between;
|
2011 |
-
align-items: center;
|
2012 |
-
margin-bottom: 15px;
|
2013 |
-
padding-bottom: 15px;
|
2014 |
-
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
2015 |
-
}
|
2016 |
-
|
2017 |
-
#aiChatHeader h3 {
|
2018 |
-
margin: 0;
|
2019 |
-
color: #333;
|
2020 |
-
font-size: 18px;
|
2021 |
-
display: flex;
|
2022 |
-
align-items: center;
|
2023 |
-
}
|
2024 |
-
|
2025 |
-
#aiChatHeader h3 i {
|
2026 |
-
margin-right: 10px;
|
2027 |
-
color: var(--ai-color);
|
2028 |
-
}
|
2029 |
-
|
2030 |
-
#aiChatClose {
|
2031 |
-
background: none;
|
2032 |
-
border: none;
|
2033 |
-
cursor: pointer;
|
2034 |
-
font-size: 18px;
|
2035 |
-
color: #666;
|
2036 |
-
transition: var(--transition);
|
2037 |
-
}
|
2038 |
-
|
2039 |
-
#aiChatClose:hover {
|
2040 |
-
color: #333;
|
2041 |
-
transform: scale(1.1);
|
2042 |
-
}
|
2043 |
-
|
2044 |
-
#aiChatMessages {
|
2045 |
-
flex: 1;
|
2046 |
-
overflow-y: auto;
|
2047 |
-
padding: 10px 0;
|
2048 |
-
margin-bottom: 15px;
|
2049 |
-
}
|
2050 |
-
|
2051 |
-
.chat-message {
|
2052 |
-
margin-bottom: 15px;
|
2053 |
-
display: flex;
|
2054 |
-
align-items: flex-start;
|
2055 |
-
}
|
2056 |
-
|
2057 |
-
.chat-message.user {
|
2058 |
-
flex-direction: row-reverse;
|
2059 |
-
}
|
2060 |
-
|
2061 |
-
.chat-avatar {
|
2062 |
-
width: 35px;
|
2063 |
-
height: 35px;
|
2064 |
-
border-radius: 50%;
|
2065 |
-
display: flex;
|
2066 |
-
justify-content: center;
|
2067 |
-
align-items: center;
|
2068 |
-
margin-right: 10px;
|
2069 |
-
flex-shrink: 0;
|
2070 |
-
}
|
2071 |
-
|
2072 |
-
.chat-message.user .chat-avatar {
|
2073 |
-
margin-right: 0;
|
2074 |
-
margin-left: 10px;
|
2075 |
-
background: var(--primary-color);
|
2076 |
-
color: white;
|
2077 |
-
}
|
2078 |
-
|
2079 |
-
.chat-message.ai .chat-avatar {
|
2080 |
-
background: var(--ai-color);
|
2081 |
-
color: white;
|
2082 |
-
}
|
2083 |
-
|
2084 |
-
.chat-content {
|
2085 |
-
background: #f1f1f1;
|
2086 |
-
padding: 12px 15px;
|
2087 |
-
border-radius: 18px;
|
2088 |
-
max-width: 75%;
|
2089 |
-
word-break: break-word;
|
2090 |
-
position: relative;
|
2091 |
-
font-size: 14px;
|
2092 |
-
line-height: 1.4;
|
2093 |
-
}
|
2094 |
-
|
2095 |
-
.chat-message.user .chat-content {
|
2096 |
-
background: var(--primary-color);
|
2097 |
-
color: white;
|
2098 |
-
border-bottom-right-radius: 4px;
|
2099 |
-
}
|
2100 |
-
|
2101 |
-
.chat-message.ai .chat-content {
|
2102 |
-
background: #f1f1f1;
|
2103 |
-
color: #333;
|
2104 |
-
border-bottom-left-radius: 4px;
|
2105 |
-
}
|
2106 |
-
|
2107 |
-
#aiChatForm {
|
2108 |
-
display: flex;
|
2109 |
-
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
2110 |
-
padding-top: 15px;
|
2111 |
-
}
|
2112 |
-
|
2113 |
-
#aiChatInput {
|
2114 |
-
flex: 1;
|
2115 |
-
padding: 12px 15px;
|
2116 |
-
border: 1px solid #ddd;
|
2117 |
-
border-radius: 25px;
|
2118 |
-
font-size: 14px;
|
2119 |
-
outline: none;
|
2120 |
-
transition: var(--transition);
|
2121 |
-
}
|
2122 |
-
|
2123 |
-
#aiChatInput:focus {
|
2124 |
-
border-color: var(--ai-color);
|
2125 |
-
box-shadow: 0 0 0 2px rgba(134, 232, 171, 0.2);
|
2126 |
-
}
|
2127 |
-
|
2128 |
-
#aiChatSubmit {
|
2129 |
-
background: var(--ai-color);
|
2130 |
-
border: none;
|
2131 |
-
color: white;
|
2132 |
-
width: 45px;
|
2133 |
-
height: 45px;
|
2134 |
-
border-radius: 50%;
|
2135 |
-
margin-left: 10px;
|
2136 |
-
display: flex;
|
2137 |
-
justify-content: center;
|
2138 |
-
align-items: center;
|
2139 |
-
cursor: pointer;
|
2140 |
-
transition: var(--transition);
|
2141 |
-
}
|
2142 |
-
|
2143 |
-
#aiChatSubmit:hover {
|
2144 |
-
background: var(--ai-hover);
|
2145 |
-
transform: scale(1.05);
|
2146 |
-
}
|
2147 |
-
|
2148 |
-
#aiChatSubmit:disabled {
|
2149 |
-
background: #ccc;
|
2150 |
-
cursor: not-allowed;
|
2151 |
-
}
|
2152 |
-
|
2153 |
-
.typing-indicator {
|
2154 |
-
display: flex;
|
2155 |
-
align-items: center;
|
2156 |
-
margin-top: 5px;
|
2157 |
-
font-size: 12px;
|
2158 |
-
color: #666;
|
2159 |
-
}
|
2160 |
-
|
2161 |
-
.typing-indicator span {
|
2162 |
-
height: 8px;
|
2163 |
-
width: 8px;
|
2164 |
-
background: var(--ai-color);
|
2165 |
-
border-radius: 50%;
|
2166 |
-
display: inline-block;
|
2167 |
-
margin-right: 3px;
|
2168 |
-
animation: typing 1s infinite;
|
2169 |
-
}
|
2170 |
-
|
2171 |
-
.typing-indicator span:nth-child(2) {
|
2172 |
-
animation-delay: 0.2s;
|
2173 |
-
}
|
2174 |
-
|
2175 |
-
.typing-indicator span:nth-child(3) {
|
2176 |
-
animation-delay: 0.4s;
|
2177 |
-
}
|
2178 |
-
|
2179 |
-
@keyframes typing {
|
2180 |
-
0% { transform: translateY(0); }
|
2181 |
-
50% { transform: translateY(-5px); }
|
2182 |
-
100% { transform: translateY(0); }
|
2183 |
-
}
|
2184 |
-
|
2185 |
-
.chat-time {
|
2186 |
-
font-size: 10px;
|
2187 |
-
color: #999;
|
2188 |
-
margin-top: 5px;
|
2189 |
-
text-align: right;
|
2190 |
-
}
|
2191 |
-
|
2192 |
-
/* ์ฝ๋ ๋ธ๋ก ์คํ์ผ */
|
2193 |
-
.chat-content pre {
|
2194 |
-
background: rgba(0, 0, 0, 0.05);
|
2195 |
-
padding: 10px;
|
2196 |
-
border-radius: 5px;
|
2197 |
-
overflow-x: auto;
|
2198 |
-
font-family: monospace;
|
2199 |
-
font-size: 12px;
|
2200 |
-
margin: 10px 0;
|
2201 |
-
}
|
2202 |
-
|
2203 |
-
/* ๋งํฌ๋ค์ด ์คํ์ผ */
|
2204 |
-
.chat-content strong {
|
2205 |
-
font-weight: bold;
|
2206 |
-
}
|
2207 |
-
|
2208 |
-
.chat-content em {
|
2209 |
-
font-style: italic;
|
2210 |
-
}
|
2211 |
-
|
2212 |
-
.chat-content ul, .chat-content ol {
|
2213 |
-
margin-left: 20px;
|
2214 |
-
margin-top: 5px;
|
2215 |
-
margin-bottom: 5px;
|
2216 |
-
}
|
2217 |
-
|
2218 |
-
/* ๊ณต์ ๋ฒํผ */
|
2219 |
-
#shareChat {
|
2220 |
-
padding: 8px 15px;
|
2221 |
-
background: #f1f1f1;
|
2222 |
-
border: none;
|
2223 |
-
border-radius: 20px;
|
2224 |
-
font-size: 12px;
|
2225 |
-
color: #666;
|
2226 |
-
cursor: pointer;
|
2227 |
-
margin-top: 5px;
|
2228 |
-
transition: var(--transition);
|
2229 |
-
}
|
2230 |
-
|
2231 |
-
#shareChat:hover {
|
2232 |
-
background: #ddd;
|
2233 |
-
}
|
2234 |
-
|
2235 |
-
/* ๋ฐ์ํ ๋์์ธ */
|
2236 |
-
@media (max-width: 768px) {
|
2237 |
-
.grid {
|
2238 |
-
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
2239 |
-
gap: 16px;
|
2240 |
-
}
|
2241 |
-
|
2242 |
-
.card {
|
2243 |
-
height: 240px;
|
2244 |
-
}
|
2245 |
-
|
2246 |
-
.library-header .title {
|
2247 |
-
font-size: 1.25rem;
|
2248 |
-
padding: 10px 20px;
|
2249 |
-
}
|
2250 |
-
|
2251 |
-
.floating-home, .floating-ai {
|
2252 |
-
width: 50px;
|
2253 |
-
height: 50px;
|
2254 |
-
}
|
2255 |
-
|
2256 |
-
.floating-home .icon, .floating-ai .icon {
|
2257 |
-
font-size: 18px;
|
2258 |
-
}
|
2259 |
-
|
2260 |
-
#adminButton {
|
2261 |
-
padding: 6px 15px;
|
2262 |
-
font-size: 12px;
|
2263 |
-
}
|
2264 |
-
|
2265 |
-
#aiChatContainer {
|
2266 |
-
width: 100%;
|
2267 |
-
}
|
2268 |
-
}
|
2269 |
-
</style>
|
2270 |
-
</head>
|
2271 |
-
<body>
|
2272 |
-
<!-- ์ ๋ชฉ์ Home ๋ฒํผ๊ณผ ํจ๊ป ๋ ์ด์ด๋ก ์ฒ๋ฆฌ -->
|
2273 |
-
<div id="homeButton" class="floating-home" style="display:none;">
|
2274 |
-
<div class="icon"><i class="fas fa-home"></i></div>
|
2275 |
-
<div class="title">ํ์ผ๋ก ๋์๊ฐ๊ธฐ</div>
|
2276 |
-
</div>
|
2277 |
-
|
2278 |
-
<!-- AI ๋ฒํผ ์ถ๊ฐ -->
|
2279 |
-
<div id="aiButton" class="floating-ai" style="display:none;">
|
2280 |
-
<div class="icon"><i class="fas fa-robot"></i></div>
|
2281 |
-
<div class="title">AI ์ด์์คํดํธ</div>
|
2282 |
-
</div>
|
2283 |
-
|
2284 |
-
<!-- AI ์ฑ๋ด ์ปจํ
์ด๋ -->
|
2285 |
-
<div id="aiChatContainer">
|
2286 |
-
<div id="aiChatHeader">
|
2287 |
-
<h3><i class="fas fa-robot"></i> AI ์ด์์คํดํธ</h3>
|
2288 |
-
<button id="aiChatClose"><i class="fas fa-times"></i></button>
|
2289 |
-
</div>
|
2290 |
-
<div id="aiChatMessages"></div>
|
2291 |
-
<form id="aiChatForm">
|
2292 |
-
<input type="text" id="aiChatInput" placeholder="PDF์ ๋ํด ์ง๋ฌธํ์ธ์..." autocomplete="off">
|
2293 |
-
<button type="submit" id="aiChatSubmit"><i class="fas fa-paper-plane"></i></button>
|
2294 |
-
</form>
|
2295 |
-
</div>
|
2296 |
-
|
2297 |
-
<!-- ๊ด๋ฆฌ์ ๋ฒํผ -->
|
2298 |
-
<div id="adminButton">
|
2299 |
-
<i class="fas fa-cog"></i> Admin
|
2300 |
-
</div>
|
2301 |
-
|
2302 |
-
<!-- ๊ด๋ฆฌ์ ๋ก๊ทธ์ธ ๋ชจ๋ฌ -->
|
2303 |
-
<div id="adminLoginModal" class="modal">
|
2304 |
-
<div class="modal-content">
|
2305 |
-
<h2>๊ด๋ฆฌ์ ๋ก๊ทธ์ธ</h2>
|
2306 |
-
<input type="password" id="adminPasswordInput" placeholder="๊ด๋ฆฌ์ ๋น๋ฐ๋ฒํธ">
|
2307 |
-
<div>
|
2308 |
-
<button id="adminLoginButton">๋ก๊ทธ์ธ</button>
|
2309 |
-
<button id="adminLoginClose">์ทจ์</button>
|
2310 |
-
</div>
|
2311 |
-
</div>
|
2312 |
-
</div>
|
2313 |
-
|
2314 |
-
<!-- ์ผํฐ ์ ๋ ฌ๋ ํ์ดํ -->
|
2315 |
-
<div class="library-header">
|
2316 |
-
<div class="title">AI FlipBook Maker</div>
|
2317 |
-
</div>
|
2318 |
-
|
2319 |
-
<section id="home" class="fade-in">
|
2320 |
-
<div class="upload-container">
|
2321 |
-
<button class="upload" id="pdfUploadBtn">
|
2322 |
-
<i class="fas fa-file-pdf"></i> PDF Upload
|
2323 |
-
</button>
|
2324 |
-
<button class="upload" id="textToAIBookBtn">
|
2325 |
-
<i class="fas fa-file-alt"></i> Text to AI-Book
|
2326 |
-
</button>
|
2327 |
-
<input id="pdfInput" type="file" accept="application/pdf" style="display:none">
|
2328 |
-
<input id="textInput" type="file" accept=".txt,.docx,.doc" style="display:none">
|
2329 |
-
</div>
|
2330 |
-
|
2331 |
-
<div class="section-title">Projects</div>
|
2332 |
-
<div class="grid" id="grid">
|
2333 |
-
<!-- ์นด๋๊ฐ ์ฌ๊ธฐ์ ๋์ ์ผ๋ก ์ถ๊ฐ๋ฉ๋๋ค -->
|
2334 |
-
</div>
|
2335 |
-
<div id="noProjects" class="no-projects" style="display: none;">
|
2336 |
-
ํ๋ก์ ํธ๊ฐ ์์ต๋๋ค. PDF๋ฅผ ์ถ๊ฐํ์ฌ ์์ํ์ธ์.
|
2337 |
-
</div>
|
2338 |
-
</section>
|
2339 |
-
|
2340 |
-
<section id="viewerPage" style="display:none">
|
2341 |
-
<div id="viewer"></div>
|
2342 |
-
<div id="loadingPages" class="loading-pages" style="display:none;">ํ์ด์ง ๋ก๋ฉ ์ค... <span id="loadingPagesCount">0/0</span></div>
|
2343 |
-
</section>
|
2344 |
-
|
2345 |
-
<!-- ๊ด๋ฆฌ์ ํ์ด์ง -->
|
2346 |
-
<section id="adminPage" style="display:none" class="fade-in">
|
2347 |
-
<h1>๊ด๋ฆฌ์ ํ์ด์ง</h1>
|
2348 |
-
<button id="adminBackButton"><i class="fas fa-arrow-left"></i> ๋ค๋ก ๊ฐ๊ธฐ</button>
|
2349 |
-
|
2350 |
-
<div class="section-title">์ ์ฅ๋ PDF ๋ชฉ๋ก</div>
|
2351 |
-
<div class="grid" id="adminGrid">
|
2352 |
-
<!-- ๊ด๋ฆฌ์ PDF ์นด๋๊ฐ ์ฌ๊ธฐ์ ๋์ ์ผ๋ก ์ถ๊ฐ๋ฉ๋๋ค -->
|
2353 |
-
</div>
|
2354 |
-
<div id="noAdminProjects" class="no-projects" style="display: none;">
|
2355 |
-
์ ์ฅ๋ PDF๊ฐ ์์ต๋๋ค. PDF๋ฅผ ์
๋ก๋ํ์ฌ ์์ํ์ธ์.
|
2356 |
-
</div>
|
2357 |
-
</section>
|
2358 |
-
|
2359 |
-
<script>
|
2360 |
-
let projects=[], fb=null;
|
2361 |
-
const grid=document.getElementById('grid'), viewer=document.getElementById('viewer');
|
2362 |
-
pdfjsLib.GlobalWorkerOptions.workerSrc='/static/pdf.worker.js';
|
2363 |
-
|
2364 |
-
// ์๋ฒ์์ ๋ฏธ๋ฆฌ ๋ก๋๋ PDF ํ๋ก์ ํธ
|
2365 |
-
let serverProjects = [];
|
2366 |
-
|
2367 |
-
// ํ์ฌ ํ์ด์ง ๋ก๋ฉ ์ํ
|
2368 |
-
let currentLoadingPdfPath = null;
|
2369 |
-
let pageLoadingInterval = null;
|
2370 |
-
|
2371 |
-
// ํ์ฌ ์ด๋ฆฐ PDF์ ID
|
2372 |
-
let currentPdfId = null;
|
2373 |
-
|
2374 |
-
// ์ค๋์ค ์ปจํ
์คํธ์ ์ด๊ธฐํ ์ํ ๊ด๋ฆฌ
|
2375 |
-
let audioInitialized = false;
|
2376 |
-
let audioContext = null;
|
2377 |
-
|
2378 |
-
// AI ์ฑ๋ด ๊ด๋ จ ๋ณ์
|
2379 |
-
let isAiChatActive = false;
|
2380 |
-
let isAiProcessing = false;
|
2381 |
-
let hasLoadedSummary = false;
|
2382 |
-
|
2383 |
-
// ์ค๋์ค ์ด๊ธฐํ ํจ์
|
2384 |
-
function initializeAudio() {
|
2385 |
-
if (audioInitialized) return Promise.resolve();
|
2386 |
-
|
2387 |
-
return new Promise((resolve) => {
|
2388 |
-
// ์ค๋์ค ์ปจํ
์คํธ ์์ฑ (์ฌ์ฉ์ ์ํธ์์ฉ์ด ํ์ ์๋ ์ด๊ธฐํ)
|
2389 |
-
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
2390 |
-
|
2391 |
-
// MP3 ๋ก๋ ๋ฐ ์ด๊ธฐํ
|
2392 |
-
const audio = new Audio('/static/turnPage2.mp3');
|
2393 |
-
audio.volume = 0.01; // ์ต์ ๋ณผ๋ฅจ์ผ๋ก ์ค์
|
2394 |
-
|
2395 |
-
// ๋ก๋๋ ์ค๋์ค ์ฌ์ ์๋ (์ฌ์ฉ์ ์ํธ์์ฉ ์๊ตฌ๋ ์ ์์)
|
2396 |
-
const playPromise = audio.play();
|
2397 |
-
|
2398 |
-
if (playPromise !== undefined) {
|
2399 |
-
playPromise
|
2400 |
-
.then(() => {
|
2401 |
-
// ์ฑ๊ณต์ ์ผ๋ก ์ฌ์๋จ - ์ฆ์ ์ผ์์ ์ง
|
2402 |
-
audio.pause();
|
2403 |
-
audioInitialized = true;
|
2404 |
-
console.log('์ค๋์ค ์ด๊ธฐํ ์ฑ๊ณต');
|
2405 |
-
resolve();
|
2406 |
-
})
|
2407 |
-
.catch((error) => {
|
2408 |
-
console.log('์๋ ์ค๋์ค ์ด๊ธฐํ ์คํจ, ์ฌ์ฉ์ ์ํธ์์ฉ ํ์:', error);
|
2409 |
-
|
2410 |
-
// ์ฌ์ฉ์ ์ํธ์์ฉ์ด ํ์ํ ๊ฒฝ์ฐ, ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ถ๊ฐ
|
2411 |
-
const initOnUserAction = function() {
|
2412 |
-
const tempAudio = new Audio('/static/turnPage2.mp3');
|
2413 |
-
tempAudio.volume = 0.01;
|
2414 |
-
tempAudio.play()
|
2415 |
-
.then(() => {
|
2416 |
-
tempAudio.pause();
|
2417 |
-
audioInitialized = true;
|
2418 |
-
console.log('์ฌ์ฉ์ ์ํธ์์ฉ์ผ๋ก ์ค๋์ค ์ด๊ธฐํ ์ฑ๊ณต');
|
2419 |
-
resolve();
|
2420 |
-
|
2421 |
-
// ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ ๊ฑฐ
|
2422 |
-
['click', 'touchstart', 'keydown'].forEach(event => {
|
2423 |
-
document.removeEventListener(event, initOnUserAction, { capture: true });
|
2424 |
-
});
|
2425 |
-
})
|
2426 |
-
.catch(e => console.error('์ค๋์ค ์ด๊ธฐํ ์คํจ:', e));
|
2427 |
-
};
|
2428 |
-
|
2429 |
-
// ์ฌ์ฉ์ ์ํธ์์ฉ ์ด๋ฒคํธ์ ๋ฆฌ์ค๋ ์ถ๊ฐ
|
2430 |
-
['click', 'touchstart', 'keydown'].forEach(event => {
|
2431 |
-
document.addEventListener(event, initOnUserAction, { once: true, capture: true });
|
2432 |
-
});
|
2433 |
-
|
2434 |
-
// ํ์ด์ง ๋ก๋ ์งํ ์ฌ์ฉ์์๊ฒ ์ค๋์ค ํ์ฑํ ์์ฒญ
|
2435 |
-
if (window.location.pathname.startsWith('/view/')) {
|
2436 |
-
// ์ค๋์ค ํ์ฑํ ์๋ด ๋ฉ์์ง ํ์ (๋ฐ๋ก๊ฐ๊ธฐ ๋งํฌ๋ก ์ ์ํ ๊ฒฝ์ฐ)
|
2437 |
-
setTimeout(() => {
|
2438 |
-
const audioPrompt = document.createElement('div');
|
2439 |
-
audioPrompt.style.position = 'fixed';
|
2440 |
-
audioPrompt.style.bottom = '80px';
|
2441 |
-
audioPrompt.style.left = '50%';
|
2442 |
-
audioPrompt.style.transform = 'translateX(-50%)';
|
2443 |
-
audioPrompt.style.backgroundColor = 'rgba(0,0,0,0.7)';
|
2444 |
-
audioPrompt.style.color = 'white';
|
2445 |
-
audioPrompt.style.padding = '10px 20px';
|
2446 |
-
audioPrompt.style.borderRadius = '20px';
|
2447 |
-
audioPrompt.style.zIndex = '10000';
|
2448 |
-
audioPrompt.style.cursor = 'pointer';
|
2449 |
-
audioPrompt.innerHTML = 'ํ์ด์ง ์ด๋๋ ํด๋ฆญํ์ฌ ์๋ฆฌ ํจ๊ณผ ํ์ฑํ <i class="fas fa-volume-up"></i>';
|
2450 |
-
audioPrompt.id = 'audioPrompt';
|
2451 |
-
|
2452 |
-
// ํด๋ฆญ ์ ์๋ฆฌ ํ์ฑํ ๋ฐ ๋ฉ์์ง ์ ๊ฑฐ
|
2453 |
-
audioPrompt.addEventListener('click', function() {
|
2454 |
-
initOnUserAction();
|
2455 |
-
audioPrompt.remove();
|
2456 |
-
});
|
2457 |
-
|
2458 |
-
document.body.appendChild(audioPrompt);
|
2459 |
-
|
2460 |
-
// 10์ด ํ ์๋์ผ๋ก ์จ๊น
|
2461 |
-
setTimeout(() => {
|
2462 |
-
if (document.getElementById('audioPrompt')) {
|
2463 |
-
document.getElementById('audioPrompt').remove();
|
2464 |
-
}
|
2465 |
-
}, 10000);
|
2466 |
-
}, 2000);
|
2467 |
-
}
|
2468 |
-
});
|
2469 |
-
} else {
|
2470 |
-
// ๋ธ๋ผ์ฐ์ ๊ฐ Promise ๊ธฐ๋ฐ ์ฌ์์ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ
|
2471 |
-
audioInitialized = true;
|
2472 |
-
resolve();
|
2473 |
-
}
|
2474 |
-
});
|
2475 |
-
}
|
2476 |
-
|
2477 |
-
/* โโ ์ ํธ โโ */
|
2478 |
-
function $id(id){return document.getElementById(id)}
|
2479 |
-
|
2480 |
-
// ํ์ฌ ์๊ฐ์ ํฌ๋งทํ
ํ๋ ํจ์
|
2481 |
-
function formatTime() {
|
2482 |
-
const now = new Date();
|
2483 |
-
const hours = now.getHours().toString().padStart(2, '0');
|
2484 |
-
const minutes = now.getMinutes().toString().padStart(2, '0');
|
2485 |
-
return `${hours}:${minutes}`;
|
2486 |
-
}
|
2487 |
-
|
2488 |
-
// AI ์ฑ๋ด ๋ฉ์์ง ์ถ๊ฐ ํจ์
|
2489 |
-
function addChatMessage(content, isUser = false) {
|
2490 |
-
const messagesContainer = $id('aiChatMessages');
|
2491 |
-
const messageElement = document.createElement('div');
|
2492 |
-
messageElement.className = `chat-message ${isUser ? 'user' : 'ai'}`;
|
2493 |
-
|
2494 |
-
const currentTime = formatTime();
|
2495 |
-
|
2496 |
-
messageElement.innerHTML = `
|
2497 |
-
<div class="chat-avatar">
|
2498 |
-
<i class="fas ${isUser ? 'fa-user' : 'fa-robot'}"></i>
|
2499 |
-
</div>
|
2500 |
-
<div class="chat-bubble">
|
2501 |
-
<div class="chat-content">${content}</div>
|
2502 |
-
<div class="chat-time">${currentTime}</div>
|
2503 |
-
</div>
|
2504 |
-
`;
|
2505 |
-
|
2506 |
-
messagesContainer.appendChild(messageElement);
|
2507 |
-
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
2508 |
-
return messageElement;
|
2509 |
-
}
|
2510 |
-
|
2511 |
-
// ๋ก๋ฉ ํ์๊ธฐ ์ถ๊ฐ ํจ์
|
2512 |
-
function addTypingIndicator() {
|
2513 |
-
const messagesContainer = $id('aiChatMessages');
|
2514 |
-
const indicatorElement = document.createElement('div');
|
2515 |
-
indicatorElement.className = 'typing-indicator';
|
2516 |
-
indicatorElement.innerHTML = `
|
2517 |
-
<div class="chat-avatar">
|
2518 |
-
<i class="fas fa-robot"></i>
|
2519 |
-
</div>
|
2520 |
-
<div>
|
2521 |
-
<span></span>
|
2522 |
-
<span></span>
|
2523 |
-
<span></span>
|
2524 |
-
</div>
|
2525 |
-
`;
|
2526 |
-
messagesContainer.appendChild(indicatorElement);
|
2527 |
-
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
2528 |
-
return indicatorElement;
|
2529 |
-
}
|
2530 |
-
|
2531 |
-
// AI ์ฑ๋ด ํ ๊ธ ํจ์
|
2532 |
-
function toggleAiChat(show = true) {
|
2533 |
-
const aiChatContainer = $id('aiChatContainer');
|
2534 |
-
|
2535 |
-
if (show) {
|
2536 |
-
// ์ฑ๋ด ํ์
|
2537 |
-
aiChatContainer.style.display = 'flex';
|
2538 |
-
setTimeout(() => {
|
2539 |
-
aiChatContainer.classList.add('active');
|
2540 |
-
}, 10);
|
2541 |
-
isAiChatActive = true;
|
2542 |
-
|
2543 |
-
// ์ฒ์ ์ด ๋ ์๋ ์์ฝ ๋ก๋
|
2544 |
-
if (!hasLoadedSummary && currentPdfId) {
|
2545 |
-
loadPdfSummary();
|
2546 |
-
}
|
2547 |
-
} else {
|
2548 |
-
// ์ฑ๋ด ์จ๊ธฐ๊ธฐ
|
2549 |
-
aiChatContainer.classList.remove('active');
|
2550 |
-
setTimeout(() => {
|
2551 |
-
aiChatContainer.style.display = 'none';
|
2552 |
-
}, 300);
|
2553 |
-
isAiChatActive = false;
|
2554 |
-
}
|
2555 |
-
}
|
2556 |
-
|
2557 |
-
// PDF ์์ฝ ๋ก๋ ํจ์
|
2558 |
-
// PDF ์์ฝ ๋ก๋ ํจ์
|
2559 |
-
async function loadPdfSummary() {
|
2560 |
-
if (!currentPdfId || isAiProcessing || hasLoadedSummary) return;
|
2561 |
-
|
2562 |
-
try {
|
2563 |
-
isAiProcessing = true;
|
2564 |
-
const typingIndicator = addTypingIndicator();
|
2565 |
-
|
2566 |
-
// ์๋ฒ์ ์์ฝ ์์ฒญ
|
2567 |
-
const response = await fetch(`/api/ai/summarize-pdf/${currentPdfId}`);
|
2568 |
-
const data = await response.json();
|
2569 |
-
|
2570 |
-
// ๋ก๋ฉ ํ์๊ธฐ ์ ๊ฑฐ
|
2571 |
-
typingIndicator.remove();
|
2572 |
-
|
2573 |
-
if (data.error) {
|
2574 |
-
// ์ค๋ฅ ๋ฉ์์ง ํ์
|
2575 |
-
addChatMessage(`PDF ์์ฝ์ ์์ฑํ๋ ์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค: ${data.error}<br><br>๊ณ์ ์ง๋ฌธ์ ์
๋ ฅํ์๋ฉด PDF ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณ์ ์๋ํ๊ฒ ์ต๋๋ค.`);
|
2576 |
-
|
2577 |
-
// ์์ฝ์ด ์คํจํด๋ ํน์ ๊ฒฝ์ฐ์๋ ์ฌ์ฉ์์๊ฒ ์๋ฆฌ๊ณ ๊ณ์ ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ์ค์
|
2578 |
-
if (data.summary) {
|
2579 |
-
addChatMessage(`<strong>PDF์์ ์ถ์ถํ ์ ๋ณด:</strong><br>${data.summary}`);
|
2580 |
-
hasLoadedSummary = true;
|
2581 |
-
}
|
2582 |
-
} else {
|
2583 |
-
// ํ์ ๋ฉ์์ง์ ์์ฝ ์ถ๊ฐ
|
2584 |
-
addChatMessage(`์๋
ํ์ธ์! ์ด PDF์ ๋ํด ์ด๋ค ๊ฒ์ด๋ ์ง๋ฌธํด์ฃผ์ธ์. ์ ๊ฐ ๋์๋๋ฆฌ๊ฒ ์ต๋๋ค.<br><br><strong>PDF ์์ฝ:</strong><br>${data.summary}`);
|
2585 |
-
hasLoadedSummary = true;
|
2586 |
-
}
|
2587 |
-
} catch (error) {
|
2588 |
-
console.error("PDF ์์ฝ ๋ก๋ ์ค๋ฅ:", error);
|
2589 |
-
addChatMessage(`PDF ์์ฝ์ ๋ก๋ํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์๋ฒ ์ฐ๊ฒฐ์ ํ์ธํด์ฃผ์ธ์.<br><br>์ด๋ค ์ง๋ฌธ์ด๋ ์
๋ ฅํ์๋ฉด ์ต์ ์ ๋คํด ๋ต๋ณํ๊ฒ ์ต๋๋ค.`);
|
2590 |
-
} finally {
|
2591 |
-
isAiProcessing = false;
|
2592 |
-
}
|
2593 |
-
}
|
2594 |
-
|
2595 |
-
// ์ง๋ฌธ ์ ์ถ ํจ์
|
2596 |
-
async function submitQuestion(question) {
|
2597 |
-
if (!currentPdfId || isAiProcessing || !question.trim()) return;
|
2598 |
-
|
2599 |
-
try {
|
2600 |
-
isAiProcessing = true;
|
2601 |
-
$id('aiChatSubmit').disabled = true;
|
2602 |
-
|
2603 |
-
// ์ฌ์ฉ์ ๋ฉ์์ง ์ถ๊ฐ
|
2604 |
-
addChatMessage(question, true);
|
2605 |
-
|
2606 |
-
// ๋ก๋ฉ ํ์๊ธฐ ์ถ๊ฐ
|
2607 |
-
const typingIndicator = addTypingIndicator();
|
2608 |
-
|
2609 |
-
// ์๋ฒ์ ์ง์ ์์ฒญ
|
2610 |
-
const response = await fetch(`/api/ai/query-pdf/${currentPdfId}`, {
|
2611 |
-
method: 'POST',
|
2612 |
-
headers: {
|
2613 |
-
'Content-Type': 'application/json'
|
2614 |
-
},
|
2615 |
-
body: JSON.stringify({ query: question }),
|
2616 |
-
// ํ์์์ ์ค์ ์ถ๊ฐ
|
2617 |
-
signal: AbortSignal.timeout(60000) // 60์ด ํ์์์
|
2618 |
-
});
|
2619 |
-
|
2620 |
-
const data = await response.json();
|
2621 |
-
|
2622 |
-
// ๋ก๋ฉ ํ์๊ธฐ ์ ๊ฑฐ
|
2623 |
-
typingIndicator.remove();
|
2624 |
-
|
2625 |
-
if (data.error) {
|
2626 |
-
// ์ค๋ฅ ๋ฉ์์ง์ ๋ฐ๋ผ ๋ค๋ฅธ ์น์ ํ ์๋ด ์ ๊ณต
|
2627 |
-
if (data.error.includes("API ํค")) {
|
2628 |
-
addChatMessage("์ฃ์กํฉ๋๋ค. ํ์ฌ AI ์๋น์ค์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค. ์์คํ
๊ด๋ฆฌ์์๊ฒ API ํค ์ค์ ์ ํ์ธํด๋ฌ๋ผ๊ณ ์์ฒญํด์ฃผ์ธ์.");
|
2629 |
-
} else if (data.error.includes("์ฐ๊ฒฐ")) {
|
2630 |
-
addChatMessage("์ฃ์กํฉ๋๋ค. AI ์๋น์ค์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค. ์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํ๊ฑฐ๋ ์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.");
|
2631 |
-
} else {
|
2632 |
-
addChatMessage(`์ฃ์กํฉ๋๋ค. ์ง๋ฌธ์ ๋ต๋ณํ๋ ์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค: ${data.error}`);
|
2633 |
-
}
|
2634 |
-
} else {
|
2635 |
-
// AI ์๋ต ์ถ๊ฐ
|
2636 |
-
addChatMessage(data.answer);
|
2637 |
-
}
|
2638 |
-
} catch (error) {
|
2639 |
-
console.error("์ง๋ฌธ ์ ์ถ ์ค๋ฅ:", error);
|
2640 |
-
if (error.name === 'AbortError') {
|
2641 |
-
addChatMessage("์ฃ์กํฉ๋๋ค. ์๋ต ์๊ฐ์ด ๋๋ฌด ์ค๋ ๊ฑธ๋ ค ์์ฒญ์ด ์ทจ์๋์์ต๋๋ค. ์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํ๊ฑฐ๋ ๋ ์งง์ ์ง๋ฌธ์ผ๋ก ๋ค์ ์๋ํด๋ณด์ธ์.");
|
2642 |
-
} else {
|
2643 |
-
addChatMessage("์ฃ์กํฉ๋๋ค. ์๋ฒ์ ํต์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.");
|
2644 |
-
}
|
2645 |
-
} finally {
|
2646 |
-
isAiProcessing = false;
|
2647 |
-
$id('aiChatSubmit').disabled = false;
|
2648 |
-
$id('aiChatInput').value = '';
|
2649 |
-
$id('aiChatInput').focus();
|
2650 |
-
}
|
2651 |
-
}
|
2652 |
-
|
2653 |
-
|
2654 |
-
// DOM์ด ๋ก๋๋๋ฉด ์คํ
|
2655 |
-
document.addEventListener('DOMContentLoaded', function() {
|
2656 |
-
console.log("DOM ๋ก๋ ์๋ฃ, ์ด๋ฒคํธ ์ค์ ์์");
|
2657 |
-
|
2658 |
-
// ์ค๋์ค ์ด๊ธฐํ ์๋
|
2659 |
-
initializeAudio().catch(e => console.warn('์ค๋์ค ์ด๊ธฐํ ์คํจ:', e));
|
2660 |
-
|
2661 |
-
// PDF ์
๋ก๋ ๋ฒํผ
|
2662 |
-
const pdfBtn = document.getElementById('pdfUploadBtn');
|
2663 |
-
const pdfInput = document.getElementById('pdfInput');
|
2664 |
-
|
2665 |
-
if (pdfBtn && pdfInput) {
|
2666 |
-
console.log("PDF ์
๋ก๋ ๋ฒํผ ์ฐพ์");
|
2667 |
-
|
2668 |
-
// ๋ฒํผ ํด๋ฆญ ์ ํ์ผ ์
๋ ฅ ํธ๋ฆฌ๊ฑฐ
|
2669 |
-
pdfBtn.addEventListener('click', function() {
|
2670 |
-
console.log("PDF ๋ฒํผ ํด๋ฆญ๋จ");
|
2671 |
-
pdfInput.click();
|
2672 |
-
});
|
2673 |
-
|
2674 |
-
// ํ์ผ ์ ํ ์ ์ฒ๋ฆฌ
|
2675 |
-
pdfInput.addEventListener('change', function(e) {
|
2676 |
-
console.log("PDF ํ์ผ ์ ํ๋จ:", e.target.files.length);
|
2677 |
-
const file = e.target.files[0];
|
2678 |
-
if (!file) return;
|
2679 |
-
|
2680 |
-
// ์๋ฒ์ PDF ์
๋ก๋ (์๊ตฌ ์ ์ฅ์์ ์ ์ฅ)
|
2681 |
-
uploadPdfToServer(file);
|
2682 |
-
});
|
2683 |
-
} else {
|
2684 |
-
console.error("PDF ์
๋ก๋ ์์๋ฅผ ์ฐพ์ ์ ์์");
|
2685 |
-
}
|
2686 |
-
|
2687 |
-
// ํ
์คํธ ์
๋ก๋ ๋ฒํผ
|
2688 |
-
const textBtn = document.getElementById('textToAIBookBtn');
|
2689 |
-
const textInput = document.getElementById('textInput');
|
2690 |
-
|
2691 |
-
if (textBtn && textInput) {
|
2692 |
-
// ๋ฒํผ ํด๋ฆญ ์ ํ์ผ ์
๋ ฅ ํธ๋ฆฌ๊ฑฐ
|
2693 |
-
textBtn.addEventListener('click', function() {
|
2694 |
-
textInput.click();
|
2695 |
-
});
|
2696 |
-
|
2697 |
-
// ํ์ผ ์ ํ ์ ์ฒ๋ฆฌ
|
2698 |
-
textInput.addEventListener('change', function(e) {
|
2699 |
-
const file = e.target.files[0];
|
2700 |
-
if (!file) return;
|
2701 |
-
|
2702 |
-
// ์๋ฒ์ ํ
์คํธ ํ์ผ ์
๋ก๋ (์๊ตฌ ์ ์ฅ์์ PDF๋ก ๋ณํํ์ฌ ์ ์ฅ)
|
2703 |
-
uploadTextToServer(file);
|
2704 |
-
});
|
2705 |
-
}
|
2706 |
-
|
2707 |
-
// ์๋ฒ PDF ๋ก๋ ๋ฐ ์บ์ ์ํ ํ์ธ
|
2708 |
-
loadServerPDFs();
|
2709 |
-
|
2710 |
-
// ์บ์ ์ํ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ํ์ธ
|
2711 |
-
setInterval(checkCacheStatus, 3000);
|
2712 |
-
|
2713 |
-
// ๊ด๋ฆฌ์ ๋ฒํผ ์ด๋ฒคํธ ์ค์
|
2714 |
-
setupAdminFunctions();
|
2715 |
-
|
2716 |
-
// ํ ๋ฒํผ ์ด๋ฒคํธ ์ค์
|
2717 |
-
const homeButton = document.getElementById('homeButton');
|
2718 |
-
if (homeButton) {
|
2719 |
-
homeButton.addEventListener('click', function() {
|
2720 |
-
if(fb) {
|
2721 |
-
fb.destroy();
|
2722 |
-
viewer.innerHTML = '';
|
2723 |
-
fb = null;
|
2724 |
-
}
|
2725 |
-
toggle(true);
|
2726 |
-
|
2727 |
-
// ๋ก๋ฉ ์ธ๋์ผ์ดํฐ ์ ๋ฆฌ
|
2728 |
-
if (pageLoadingInterval) {
|
2729 |
-
clearInterval(pageLoadingInterval);
|
2730 |
-
pageLoadingInterval = null;
|
2731 |
-
}
|
2732 |
-
$id('loadingPages').style.display = 'none';
|
2733 |
-
currentLoadingPdfPath = null;
|
2734 |
-
currentPdfId = null;
|
2735 |
-
|
2736 |
-
// AI ์ฑ๋ด ๋ซ๊ธฐ
|
2737 |
-
toggleAiChat(false);
|
2738 |
-
hasLoadedSummary = false; // ์์ฝ ๋ก๋ ์ํ ์ด๊ธฐํ
|
2739 |
-
});
|
2740 |
-
}
|
2741 |
-
|
2742 |
-
// AI ๋ฒํผ ์ด๋ฒคํธ ์ค์
|
2743 |
-
const aiButton = document.getElementById('aiButton');
|
2744 |
-
if (aiButton) {
|
2745 |
-
aiButton.addEventListener('click', function() {
|
2746 |
-
toggleAiChat(!isAiChatActive);
|
2747 |
-
});
|
2748 |
-
}
|
2749 |
-
|
2750 |
-
// AI ์ฑ๋ด ๋ซ๊ธฐ ๋ฒํผ
|
2751 |
-
const aiChatClose = document.getElementById('aiChatClose');
|
2752 |
-
if (aiChatClose) {
|
2753 |
-
aiChatClose.addEventListener('click', function() {
|
2754 |
-
toggleAiChat(false);
|
2755 |
-
});
|
2756 |
-
}
|
2757 |
-
|
2758 |
-
// AI ์ฑ๋ด ํผ ์ ์ถ
|
2759 |
-
const aiChatForm = document.getElementById('aiChatForm');
|
2760 |
-
if (aiChatForm) {
|
2761 |
-
aiChatForm.addEventListener('submit', function(e) {
|
2762 |
-
e.preventDefault();
|
2763 |
-
const inputField = document.getElementById('aiChatInput');
|
2764 |
-
const question = inputField.value.trim();
|
2765 |
-
|
2766 |
-
if (question && !isAiProcessing) {
|
2767 |
-
submitQuestion(question);
|
2768 |
-
}
|
2769 |
-
});
|
2770 |
-
}
|
2771 |
-
});
|
2772 |
-
|
2773 |
-
// ์๋ฒ์ PDF ์
๋ก๋ ํจ์
|
2774 |
-
async function uploadPdfToServer(file) {
|
2775 |
-
try {
|
2776 |
-
showLoading("PDF ์
๋ก๋ ์ค...");
|
2777 |
-
|
2778 |
-
const formData = new FormData();
|
2779 |
-
formData.append('file', file);
|
2780 |
-
|
2781 |
-
const response = await fetch('/api/upload-pdf', {
|
2782 |
-
method: 'POST',
|
2783 |
-
body: formData
|
2784 |
-
});
|
2785 |
-
|
2786 |
-
const result = await response.json();
|
2787 |
-
|
2788 |
-
if (result.success) {
|
2789 |
-
hideLoading();
|
2790 |
-
|
2791 |
-
// ์
๋ก๋ ์ฑ๊ณต ์ ์๋ฒ PDF ๋ฆฌ์คํธ ๋ฆฌ๋ก๋
|
2792 |
-
await loadServerPDFs();
|
2793 |
-
|
2794 |
-
// ์ฑ๊ณต ๋ฉ์์ง
|
2795 |
-
showMessage("PDF๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์
๋ก๋๋์์ต๋๋ค! ๊ณต์ URL: " + result.viewUrl);
|
2796 |
-
} else {
|
2797 |
-
hideLoading();
|
2798 |
-
showError("์
๋ก๋ ์คํจ: " + (result.message || "์ ์ ์๋ ์ค๋ฅ"));
|
2799 |
-
}
|
2800 |
-
} catch (error) {
|
2801 |
-
console.error("PDF ์
๋ก๋ ์ค๋ฅ:", error);
|
2802 |
-
hideLoading();
|
2803 |
-
showError("PDF ์
๋ก๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
2804 |
-
}
|
2805 |
-
}
|
2806 |
-
|
2807 |
-
// ์๋ฒ์ ํ
์คํธ ํ์ผ์ ์
๋ก๋ํ์ฌ PDF๋ก ๋ณํํ๋ ํจ์
|
2808 |
-
async function uploadTextToServer(file) {
|
2809 |
-
try {
|
2810 |
-
showLoading("ํ
์คํธ ๋ถ์ ๋ฐ PDF ๋ณํ ์ค...");
|
2811 |
-
|
2812 |
-
const formData = new FormData();
|
2813 |
-
formData.append('file', file);
|
2814 |
-
|
2815 |
-
const response = await fetch('/api/text-to-pdf', {
|
2816 |
-
method: 'POST',
|
2817 |
-
body: formData
|
2818 |
-
});
|
2819 |
-
|
2820 |
-
const result = await response.json();
|
2821 |
-
|
2822 |
-
if (result.success) {
|
2823 |
-
hideLoading();
|
2824 |
-
|
2825 |
-
// ์
๋ก๋ ์ฑ๊ณต ์ ์๋ฒ PDF ๋ฆฌ์คํธ ๋ฆฌ๋ก๋
|
2826 |
-
await loadServerPDFs();
|
2827 |
-
|
2828 |
-
// ์ฑ๊ณต ๋ฉ์์ง
|
2829 |
-
showMessage("ํ
์คํธ๊ฐ ์ฑ๊ณต์ ์ผ๋ก PDF๋ก ๋ณํ๋์์ต๋๋ค! ๊ณต์ URL: " + result.viewUrl);
|
2830 |
-
} else {
|
2831 |
-
hideLoading();
|
2832 |
-
showError("๋ณํ ์คํจ: " + (result.message || "์ ์ ์๋ ์ค๋ฅ"));
|
2833 |
-
}
|
2834 |
-
} catch (error) {
|
2835 |
-
console.error("ํ
์คํธ ๋ณํ ์ค๋ฅ:", error);
|
2836 |
-
hideLoading();
|
2837 |
-
showError("ํ
์คํธ๋ฅผ PDF๋ก ๋ณํํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
2838 |
-
}
|
2839 |
-
}
|
2840 |
-
|
2841 |
-
function addCard(i, thumb, title, isCached = false, pdfId = null) {
|
2842 |
-
const d = document.createElement('div');
|
2843 |
-
d.className = 'card fade-in';
|
2844 |
-
d.onclick = () => open(i);
|
2845 |
-
|
2846 |
-
// PDF ID๊ฐ ์์ผ๋ฉด ๋ฐ์ดํฐ ์์ฑ์ผ๋ก ์ ์ฅ
|
2847 |
-
if (pdfId) {
|
2848 |
-
d.dataset.pdfId = pdfId;
|
2849 |
-
}
|
2850 |
-
|
2851 |
-
// ์ ๋ชฉ ์ฒ๋ฆฌ
|
2852 |
-
const displayTitle = title ?
|
2853 |
-
(title.length > 15 ? title.substring(0, 15) + '...' : title) :
|
2854 |
-
'ํ๋ก์ ํธ ' + (i+1);
|
2855 |
-
|
2856 |
-
// ์บ์ ์ํ ๋ฑ์ง ์ถ๊ฐ
|
2857 |
-
const cachedBadge = isCached ?
|
2858 |
-
'<div class="cached-status">์บ์๋จ</div>' : '';
|
2859 |
-
|
2860 |
-
// ๋ฐ๋ก๊ฐ๊ธฐ ๋งํฌ ์ถ๊ฐ (PDF ID๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง)
|
2861 |
-
const linkHtml = pdfId ?
|
2862 |
-
`<div style="position: absolute; bottom: 55px; left: 50%; transform: translateX(-50%); z-index:5;">
|
2863 |
-
<a href="/view/${pdfId}" target="_blank" style="color:#4a6ee0; font-size:11px;">Share Link</a>
|
2864 |
-
</div>` : '';
|
2865 |
-
|
2866 |
-
d.innerHTML = `
|
2867 |
-
<div class="card-inner">
|
2868 |
-
${cachedBadge}
|
2869 |
-
<img src="${thumb}" alt="${displayTitle}" loading="lazy">
|
2870 |
-
${linkHtml}
|
2871 |
-
<p title="${title || 'ํ๋ก์ ํธ ' + (i+1)}">${displayTitle}</p>
|
2872 |
-
</div>
|
2873 |
-
`;
|
2874 |
-
grid.appendChild(d);
|
2875 |
-
|
2876 |
-
// ํ๋ก์ ํธ๊ฐ ์์ผ๋ฉด 'ํ๋ก์ ํธ ์์' ๋ฉ์์ง ์จ๊ธฐ๊ธฐ
|
2877 |
-
$id('noProjects').style.display = 'none';
|
2878 |
-
}
|
2879 |
-
|
2880 |
-
/* โโ ํ๋ก์ ํธ ์ ์ฅ โโ */
|
2881 |
-
function save(pages, title, isCached = false, pdfId = null) {
|
2882 |
-
const id = projects.push(pages) - 1;
|
2883 |
-
addCard(id, pages[0].thumb, title, isCached, pdfId);
|
2884 |
-
}
|
2885 |
-
|
2886 |
-
/* โโ ์๋ฒ PDF ๋ก๋ ๋ฐ ์บ์ ์ํ ํ์ธ โโ */
|
2887 |
-
async function loadServerPDFs() {
|
2888 |
-
try {
|
2889 |
-
// ๋ก๋ฉ ํ์ ์ถ๊ฐ
|
2890 |
-
if (document.querySelectorAll('.card').length === 0) {
|
2891 |
-
showLoading("๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก๋ฉ ์ค...");
|
2892 |
-
}
|
2893 |
-
|
2894 |
-
// ๋จผ์ ์บ์ ์ํ ํ์ธ
|
2895 |
-
const cacheStatusRes = await fetch('/api/cache-status');
|
2896 |
-
const cacheStatus = await cacheStatusRes.json();
|
2897 |
-
|
2898 |
-
// PDF ํ๋ก์ ํธ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
|
2899 |
-
const response = await fetch('/api/pdf-projects');
|
2900 |
-
serverProjects = await response.json();
|
2901 |
-
|
2902 |
-
// ๊ธฐ์กด ๊ทธ๋ฆฌ๋ ์ด๊ธฐํ
|
2903 |
-
grid.innerHTML = '';
|
2904 |
-
projects = [];
|
2905 |
-
|
2906 |
-
if (serverProjects.length === 0) {
|
2907 |
-
hideLoading();
|
2908 |
-
$id('noProjects').style.display = 'block';
|
2909 |
-
return;
|
2910 |
-
}
|
2911 |
-
|
2912 |
-
// ์๋ฒ PDF ๋ก๋ ๋ฐ ์ธ๋ค์ผ ์์ฑ (๋ณ๋ ฌ ์ฒ๋ฆฌ๋ก ์ต์ ํ)
|
2913 |
-
const thumbnailPromises = serverProjects.map(async (project, index) => {
|
2914 |
-
updateLoading(`PDF ํ๋ก์ ํธ ๋ก๋ฉ ์ค... (${index+1}/${serverProjects.length})`);
|
2915 |
-
|
2916 |
-
const pdfName = project.name;
|
2917 |
-
const isCached = cacheStatus[pdfName] && cacheStatus[pdfName].status === "completed";
|
2918 |
-
|
2919 |
-
try {
|
2920 |
-
// ์ธ๋ค์ผ ๊ฐ์ ธ์ค๊ธฐ
|
2921 |
-
const response = await fetch(`/api/pdf-thumbnail?path=${encodeURIComponent(project.path)}`);
|
2922 |
-
const data = await response.json();
|
2923 |
-
|
2924 |
-
if(data.thumbnail) {
|
2925 |
-
const pages = [{
|
2926 |
-
src: data.thumbnail,
|
2927 |
-
thumb: data.thumbnail,
|
2928 |
-
path: project.path,
|
2929 |
-
cached: isCached
|
2930 |
-
}];
|
2931 |
-
|
2932 |
-
return {
|
2933 |
-
pages,
|
2934 |
-
name: project.name,
|
2935 |
-
isCached,
|
2936 |
-
id: project.id
|
2937 |
-
};
|
2938 |
-
}
|
2939 |
-
} catch (err) {
|
2940 |
-
console.error(`์ธ๋ค์ผ ๋ก๋ ์ค๋ฅ (${project.name}):`, err);
|
2941 |
-
}
|
2942 |
-
|
2943 |
-
return null;
|
2944 |
-
});
|
2945 |
-
|
2946 |
-
// ๋ชจ๋ ์ธ๋ค์ผ ์์ฒญ ๊ธฐ๋ค๋ฆฌ๊ธฐ
|
2947 |
-
const results = await Promise.all(thumbnailPromises);
|
2948 |
-
|
2949 |
-
// ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์จ ๊ฒฐ๊ณผ๋ง ํ์
|
2950 |
-
results.filter(result => result !== null).forEach(result => {
|
2951 |
-
save(result.pages, result.name, result.isCached, result.id);
|
2952 |
-
});
|
2953 |
-
|
2954 |
-
hideLoading();
|
2955 |
-
|
2956 |
-
// ํ๋ก์ ํธ๊ฐ ์์ ๊ฒฝ์ฐ ๋ฉ์์ง ํ์
|
2957 |
-
if (document.querySelectorAll('.card').length === 0) {
|
2958 |
-
$id('noProjects').style.display = 'block';
|
2959 |
-
}
|
2960 |
-
} catch(error) {
|
2961 |
-
console.error('์๋ฒ PDF ๋ก๋ ์คํจ:', error);
|
2962 |
-
hideLoading();
|
2963 |
-
showError("๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก๋ฉ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
2964 |
-
}
|
2965 |
-
}
|
2966 |
-
|
2967 |
-
/* โโ ์บ์ ์ํ ์ ๊ธฐ์ ์ผ๋ก ํ์ธ โโ */
|
2968 |
-
async function checkCacheStatus() {
|
2969 |
-
try {
|
2970 |
-
const response = await fetch('/api/cache-status');
|
2971 |
-
const cacheStatus = await response.json();
|
2972 |
-
|
2973 |
-
// ํ์ฌ ์นด๋ ์ํ ์
๋ฐ์ดํธ
|
2974 |
-
const cards = document.querySelectorAll('.card');
|
2975 |
-
|
2976 |
-
for(let i = 0; i < cards.length; i++) {
|
2977 |
-
if(projects[i] && projects[i][0] && projects[i][0].path) {
|
2978 |
-
const pdfPath = projects[i][0].path;
|
2979 |
-
const pdfName = pdfPath.split('/').pop().replace('.pdf', '');
|
2980 |
-
|
2981 |
-
// ์บ์ ์ํ ๋ฑ์ง ์
๋ฐ์ดํธ
|
2982 |
-
let badgeEl = cards[i].querySelector('.cached-status');
|
2983 |
-
|
2984 |
-
if(cacheStatus[pdfName] && cacheStatus[pdfName].status === "completed") {
|
2985 |
-
if(!badgeEl) {
|
2986 |
-
badgeEl = document.createElement('div');
|
2987 |
-
badgeEl.className = 'cached-status';
|
2988 |
-
badgeEl.textContent = '์บ์๋จ';
|
2989 |
-
cards[i].querySelector('.card-inner')?.appendChild(badgeEl);
|
2990 |
-
} else if (badgeEl.textContent !== '์บ์๋จ') {
|
2991 |
-
badgeEl.textContent = '์บ์๋จ';
|
2992 |
-
badgeEl.style.background = 'var(--accent-color)';
|
2993 |
-
}
|
2994 |
-
projects[i][0].cached = true;
|
2995 |
-
} else if(cacheStatus[pdfName] && cacheStatus[pdfName].status === "processing") {
|
2996 |
-
if(!badgeEl) {
|
2997 |
-
badgeEl = document.createElement('div');
|
2998 |
-
badgeEl.className = 'cached-status';
|
2999 |
-
cards[i].querySelector('.card-inner')?.appendChild(badgeEl);
|
3000 |
-
}
|
3001 |
-
badgeEl.textContent = `${cacheStatus[pdfName].progress}%`;
|
3002 |
-
badgeEl.style.background = 'var(--secondary-color)';
|
3003 |
-
}
|
3004 |
-
}
|
3005 |
-
}
|
3006 |
-
|
3007 |
-
// ํ์ฌ ๋ก๋ฉ ์ค์ธ PDF๊ฐ ์์ผ๋ฉด ์ํ ํ์ธ
|
3008 |
-
if (currentLoadingPdfPath && pageLoadingInterval) {
|
3009 |
-
const pdfName = currentLoadingPdfPath.split('/').pop().replace('.pdf', '');
|
3010 |
-
|
3011 |
-
if (cacheStatus[pdfName]) {
|
3012 |
-
const status = cacheStatus[pdfName].status;
|
3013 |
-
const progress = cacheStatus[pdfName].progress || 0;
|
3014 |
-
|
3015 |
-
if (status === "completed") {
|
3016 |
-
// ์บ์ฑ ์๋ฃ ์
|
3017 |
-
clearInterval(pageLoadingInterval);
|
3018 |
-
$id('loadingPages').style.display = 'none';
|
3019 |
-
currentLoadingPdfPath = null;
|
3020 |
-
|
3021 |
-
// ์๋ฃ๋ ์บ์๋ก ํ๋ฆฝ๋ถ ๋ค์ ๋ก๋
|
3022 |
-
refreshFlipBook();
|
3023 |
-
} else if (status === "processing") {
|
3024 |
-
// ์งํ ์ค์ผ ๋ ํ์ ์
๋ฐ์ดํธ
|
3025 |
-
$id('loadingPages').style.display = 'block';
|
3026 |
-
$id('loadingPagesCount').textContent = `${progress}%`;
|
3027 |
-
}
|
3028 |
-
}
|
3029 |
-
}
|
3030 |
-
|
3031 |
-
} catch(error) {
|
3032 |
-
console.error('์บ์ ์ํ ํ์ธ ์ค๋ฅ:', error);
|
3033 |
-
}
|
3034 |
-
}
|
3035 |
-
|
3036 |
-
/* โโ PDF ID๋ก PDF ์ด๏ฟฝ๏ฟฝ โโ */
|
3037 |
-
async function openPdfById(pdfId, pdfPath, isCached = false) {
|
3038 |
-
try {
|
3039 |
-
// ์ค๋์ค ์ด๊ธฐํ ์๋
|
3040 |
-
await initializeAudio().catch(e => console.warn('PDF ์ด๊ธฐ ์ ์ค๋์ค ์ด๊ธฐํ ์คํจ:', e));
|
3041 |
-
|
3042 |
-
// ๋จผ์ ํ ํ๋ฉด์์ ์นด๋๋ฅผ ์ฐพ์์ ํด๋ฆญํ๋ ๋ฐฉ๋ฒ ์๋
|
3043 |
-
let foundCard = false;
|
3044 |
-
const cards = document.querySelectorAll('.card');
|
3045 |
-
|
3046 |
-
for (let i = 0; i < cards.length; i++) {
|
3047 |
-
if (cards[i].dataset.pdfId === pdfId) {
|
3048 |
-
cards[i].click();
|
3049 |
-
foundCard = true;
|
3050 |
-
break;
|
3051 |
-
}
|
3052 |
-
}
|
3053 |
-
|
3054 |
-
// ์นด๋๋ฅผ ์ฐพ์ง ๋ชปํ ๊ฒฝ์ฐ ์ง์ ์คํ
|
3055 |
-
if (!foundCard) {
|
3056 |
-
toggle(false);
|
3057 |
-
showLoading("PDF ์ค๋น ์ค...");
|
3058 |
-
|
3059 |
-
let pages = [];
|
3060 |
-
|
3061 |
-
// ์ด๋ฏธ ์บ์๋ ๊ฒฝ์ฐ ์บ์๋ ๋ฐ์ดํฐ ์ฌ์ฉ
|
3062 |
-
if (isCached) {
|
3063 |
-
try {
|
3064 |
-
const response = await fetch(`/api/cached-pdf?path=${encodeURIComponent(pdfPath)}`);
|
3065 |
-
const cachedData = await response.json();
|
3066 |
-
|
3067 |
-
if (cachedData.status === "completed" && cachedData.pages) {
|
3068 |
-
hideLoading();
|
3069 |
-
createFlipBook(cachedData.pages);
|
3070 |
-
// ํ์ฌ ์ด๋ฆฐ PDF์ ID ์ ์ฅ
|
3071 |
-
currentPdfId = pdfId;
|
3072 |
-
// AI ๋ฒํผ ํ์
|
3073 |
-
$id('aiButton').style.display = 'block';
|
3074 |
-
return;
|
3075 |
-
}
|
3076 |
-
} catch (error) {
|
3077 |
-
console.error("์บ์ ๋ฐ์ดํฐ ๋ก๋ ์คํจ:", error);
|
3078 |
-
}
|
3079 |
-
}
|
3080 |
-
|
3081 |
-
// ์ธ๋ค์ผ ๊ฐ์ ธ์ค๊ธฐ
|
3082 |
-
try {
|
3083 |
-
const thumbResponse = await fetch(`/api/pdf-thumbnail?path=${encodeURIComponent(pdfPath)}`);
|
3084 |
-
const thumbData = await thumbResponse.json();
|
3085 |
-
|
3086 |
-
if (thumbData.thumbnail) {
|
3087 |
-
pages = [{
|
3088 |
-
src: thumbData.thumbnail,
|
3089 |
-
thumb: thumbData.thumbnail,
|
3090 |
-
path: pdfPath,
|
3091 |
-
cached: isCached
|
3092 |
-
}];
|
3093 |
-
}
|
3094 |
-
} catch (error) {
|
3095 |
-
console.error("์ธ๋ค์ผ ๋ก๋ ์คํจ:", error);
|
3096 |
-
}
|
3097 |
-
|
3098 |
-
// ์ผ๋จ ๊ธฐ๋ณธ ํ์ด์ง ์ถ๊ฐ
|
3099 |
-
if (pages.length === 0) {
|
3100 |
-
pages = [{
|
3101 |
-
path: pdfPath,
|
3102 |
-
cached: isCached
|
3103 |
-
}];
|
3104 |
-
}
|
3105 |
-
|
3106 |
-
// ํ๋ก์ ํธ์ ์ถ๊ฐํ๊ณ ๋ทฐ์ด ์คํ
|
3107 |
-
const projectId = projects.push(pages) - 1;
|
3108 |
-
hideLoading();
|
3109 |
-
open(projectId);
|
3110 |
-
|
3111 |
-
// ํ์ฌ ์ด๋ฆฐ PDF์ ID ์ ์ฅ
|
3112 |
-
currentPdfId = pdfId;
|
3113 |
-
// AI ๋ฒํผ ํ์
|
3114 |
-
$id('aiButton').style.display = 'block';
|
3115 |
-
}
|
3116 |
-
} catch (error) {
|
3117 |
-
console.error("PDF ID๋ก ์ด๊ธฐ ์คํจ:", error);
|
3118 |
-
hideLoading();
|
3119 |
-
showError("PDF๋ฅผ ์ด ์ ์์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.");
|
3120 |
-
}
|
3121 |
-
}
|
3122 |
-
|
3123 |
-
/* โโ ํ์ฌ PDF์ ๊ณ ์ URL ์์ฑ ๋ฐ ๋ณต์ฌ โโ */
|
3124 |
-
function copyPdfShareUrl() {
|
3125 |
-
if (!currentPdfId) {
|
3126 |
-
showError("๊ณต์ ํ PDF๊ฐ ์์ต๋๋ค.");
|
3127 |
-
return;
|
3128 |
-
}
|
3129 |
-
|
3130 |
-
// ํ์ฌ ๋๋ฉ์ธ ๊ธฐ๋ฐ ์ ์ฒด URL ์์ฑ
|
3131 |
-
const shareUrl = `${window.location.origin}/view/${currentPdfId}`;
|
3132 |
-
|
3133 |
-
// ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
|
3134 |
-
navigator.clipboard.writeText(shareUrl)
|
3135 |
-
.then(() => {
|
3136 |
-
showMessage("PDF ๋งํฌ๊ฐ ๋ณต์ฌ๋์์ต๋๋ค!");
|
3137 |
-
})
|
3138 |
-
.catch(err => {
|
3139 |
-
console.error("ํด๋ฆฝ๋ณด๋ ๋ณต์ฌ ์คํจ:", err);
|
3140 |
-
showError("๋งํฌ ๋ณต์ฌ์ ์คํจํ์ต๋๋ค.");
|
3141 |
-
});
|
3142 |
-
}
|
3143 |
-
|
3144 |
-
/* โโ ์นด๋ โ FlipBook โโ */
|
3145 |
-
async function open(i) {
|
3146 |
-
toggle(false);
|
3147 |
-
const pages = projects[i];
|
3148 |
-
|
3149 |
-
// PDF ID ์ฐพ๊ธฐ ๋ฐ ์ ์ฅ
|
3150 |
-
const card = document.querySelectorAll('.card')[i];
|
3151 |
-
if (card && card.dataset.pdfId) {
|
3152 |
-
currentPdfId = card.dataset.pdfId;
|
3153 |
-
// AI ๋ฒํผ ํ์
|
3154 |
-
$id('aiButton').style.display = 'block';
|
3155 |
-
} else {
|
3156 |
-
currentPdfId = null;
|
3157 |
-
// AI ๋ฒํผ ์จ๊น
|
3158 |
-
$id('aiButton').style.display = 'none';
|
3159 |
-
}
|
3160 |
-
|
3161 |
-
// AI ์ฑ๋ด ์ด๊ธฐํ
|
3162 |
-
toggleAiChat(false);
|
3163 |
-
hasLoadedSummary = false;
|
3164 |
-
$id('aiChatMessages').innerHTML = '';
|
3165 |
-
|
3166 |
-
// ๊ธฐ์กด FlipBook ์ ๋ฆฌ
|
3167 |
-
if(fb) {
|
3168 |
-
fb.destroy();
|
3169 |
-
viewer.innerHTML = '';
|
3170 |
-
}
|
3171 |
-
|
3172 |
-
// ์๋ฒ PDF ๋๋ ๋ก์ปฌ ํ๋ก์ ํธ ์ฒ๋ฆฌ
|
3173 |
-
if(pages[0].path) {
|
3174 |
-
const pdfPath = pages[0].path;
|
3175 |
-
|
3176 |
-
// ์ ์ง์ ๋ก๋ฉ ํ๋๊ทธ ์ด๊ธฐํ
|
3177 |
-
let progressiveLoading = false;
|
3178 |
-
currentLoadingPdfPath = pdfPath;
|
3179 |
-
|
3180 |
-
// ์บ์ ์ฌ๋ถ ํ์ธ
|
3181 |
-
if(pages[0].cached) {
|
3182 |
-
// ์บ์๋ PDF ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
3183 |
-
showLoading("์บ์๋ PDF ๋ก๋ฉ ์ค...");
|
3184 |
-
|
3185 |
-
try {
|
3186 |
-
const response = await fetch(`/api/cached-pdf?path=${encodeURIComponent(pdfPath)}`);
|
3187 |
-
const cachedData = await response.json();
|
3188 |
-
|
3189 |
-
if(cachedData.status === "completed" && cachedData.pages) {
|
3190 |
-
hideLoading();
|
3191 |
-
createFlipBook(cachedData.pages);
|
3192 |
-
currentLoadingPdfPath = null;
|
3193 |
-
return;
|
3194 |
-
} else if(cachedData.status === "processing" && cachedData.pages && cachedData.pages.length > 0) {
|
3195 |
-
// ์ผ๋ถ ํ์ด์ง๊ฐ ์ด๋ฏธ ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ ์ ์ง์ ๋ก๋ฉ ์ฌ์ฉ
|
3196 |
-
hideLoading();
|
3197 |
-
createFlipBook(cachedData.pages);
|
3198 |
-
progressiveLoading = true;
|
3199 |
-
|
3200 |
-
// ์ ์ง์ ๋ก๋ฉ ์ค์์ ํ์
|
3201 |
-
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
3202 |
-
}
|
3203 |
-
} catch(error) {
|
3204 |
-
console.error("์บ์ ๋ฐ์ดํฐ ๋ก๋ ์ค๋ฅ:", error);
|
3205 |
-
// ์บ์ ๋ก๋ฉ ์คํจ ์ ์๋ณธ PDF๋ก ๋์ฒด
|
3206 |
-
}
|
3207 |
-
}
|
3208 |
-
|
3209 |
-
if (!progressiveLoading) {
|
3210 |
-
// ์บ์๊ฐ ์๊ฑฐ๋ ๋ก๋ฉ ์คํจ ์ ์๋ฒ PDF ๋ก๋
|
3211 |
-
showLoading("PDF ์ค๋น ์ค...");
|
3212 |
-
|
3213 |
-
try {
|
3214 |
-
const response = await fetch(`/api/pdf-content?path=${encodeURIComponent(pdfPath)}`);
|
3215 |
-
const data = await response.json();
|
3216 |
-
|
3217 |
-
// ์บ์๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ ๊ฒฝ์ฐ
|
3218 |
-
if(data.redirect) {
|
3219 |
-
const redirectRes = await fetch(data.redirect);
|
3220 |
-
const cachedData = await redirectRes.json();
|
3221 |
-
|
3222 |
-
if(cachedData.status === "completed" && cachedData.pages) {
|
3223 |
-
hideLoading();
|
3224 |
-
createFlipBook(cachedData.pages);
|
3225 |
-
currentLoadingPdfPath = null;
|
3226 |
-
return;
|
3227 |
-
} else if(cachedData.status === "processing" && cachedData.pages && cachedData.pages.length > 0) {
|
3228 |
-
// ์ผ๋ถ ํ์ด์ง๊ฐ ์ด๋ฏธ ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ ์ ์ง์ ๋ก๋ฉ ์ฌ์ฉ
|
3229 |
-
hideLoading();
|
3230 |
-
createFlipBook(cachedData.pages);
|
3231 |
-
|
3232 |
-
// ์ ์ง์ ๋ก๋ฉ ์ค์์ ํ์
|
3233 |
-
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
3234 |
-
return;
|
3235 |
-
}
|
3236 |
-
}
|
3237 |
-
|
3238 |
-
// ์๋ณธ PDF ๋ก๋ (ArrayBuffer ํํ)
|
3239 |
-
const pdfResponse = await fetch(`/api/pdf-content?path=${encodeURIComponent(pdfPath)}`);
|
3240 |
-
|
3241 |
-
// JSON ์๋ต์ธ ๊ฒฝ์ฐ (๋ฆฌ๋ค์ด๋ ํธ ๋ฑ)
|
3242 |
-
try {
|
3243 |
-
const jsonData = await pdfResponse.clone().json();
|
3244 |
-
if (jsonData.redirect) {
|
3245 |
-
const redirectRes = await fetch(jsonData.redirect);
|
3246 |
-
const cachedData = await redirectRes.json();
|
3247 |
-
|
3248 |
-
if(cachedData.pages && cachedData.pages.length > 0) {
|
3249 |
-
hideLoading();
|
3250 |
-
createFlipBook(cachedData.pages);
|
3251 |
-
|
3252 |
-
if(cachedData.status === "processing") {
|
3253 |
-
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
3254 |
-
} else {
|
3255 |
-
currentLoadingPdfPath = null;
|
3256 |
-
}
|
3257 |
-
return;
|
3258 |
-
}
|
3259 |
-
}
|
3260 |
-
} catch (e) {
|
3261 |
-
// JSON ํ์ฑ ์คํจ ์ ์๋ณธ PDF ๋ฐ์ดํฐ๋ก ์ฒ๋ฆฌ
|
3262 |
-
}
|
3263 |
-
|
3264 |
-
// ArrayBuffer ํํ์ PDF ๋ฐ์ดํฐ
|
3265 |
-
const pdfData = await pdfResponse.arrayBuffer();
|
3266 |
-
|
3267 |
-
// PDF ๋ก๋ ๋ฐ ํ์ด์ง ๋ ๋๋ง
|
3268 |
-
const pdf = await pdfjsLib.getDocument({data: pdfData}).promise;
|
3269 |
-
const pdfPages = [];
|
3270 |
-
|
3271 |
-
for(let p = 1; p <= pdf.numPages; p++) {
|
3272 |
-
updateLoading(`ํ์ด์ง ์ค๋น ์ค... (${p}/${pdf.numPages})`);
|
3273 |
-
|
3274 |
-
const pg = await pdf.getPage(p);
|
3275 |
-
const vp = pg.getViewport({scale: 1});
|
3276 |
-
const c = document.createElement('canvas');
|
3277 |
-
c.width = vp.width;
|
3278 |
-
c.height = vp.height;
|
3279 |
-
|
3280 |
-
await pg.render({canvasContext: c.getContext('2d'), viewport: vp}).promise;
|
3281 |
-
pdfPages.push({src: c.toDataURL(), thumb: c.toDataURL()});
|
3282 |
-
}
|
3283 |
-
|
3284 |
-
hideLoading();
|
3285 |
-
createFlipBook(pdfPages);
|
3286 |
-
currentLoadingPdfPath = null;
|
3287 |
-
|
3288 |
-
} catch(error) {
|
3289 |
-
console.error('PDF ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์:', error);
|
3290 |
-
hideLoading();
|
3291 |
-
showError("PDF๋ฅผ ๋ก๋ํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3292 |
-
currentLoadingPdfPath = null;
|
3293 |
-
}
|
3294 |
-
}
|
3295 |
-
} else {
|
3296 |
-
// ๋ก์ปฌ ์
๋ก๋๋ ํ๋ก์ ํธ ์คํ
|
3297 |
-
createFlipBook(pages);
|
3298 |
-
currentLoadingPdfPath = null;
|
3299 |
-
}
|
3300 |
-
}
|
3301 |
-
|
3302 |
-
/* โโ ์ ์ง์ ๋ก๋ฉ ์ธ๋์ผ์ดํฐ ์์ โโ */
|
3303 |
-
function startProgressiveLoadingIndicator(progress, totalPages) {
|
3304 |
-
// ์งํ ์ํ ํ์ ํ์ฑํ
|
3305 |
-
$id('loadingPages').style.display = 'block';
|
3306 |
-
$id('loadingPagesCount').textContent = `${progress}%`;
|
3307 |
-
|
3308 |
-
// ๊ธฐ์กด ์ธํฐ๋ฒ ์ ๊ฑฐ
|
3309 |
-
if (pageLoadingInterval) {
|
3310 |
-
clearInterval(pageLoadingInterval);
|
3311 |
-
}
|
3312 |
-
|
3313 |
-
// ์ฃผ๊ธฐ์ ์ผ๋ก ์บ์ ์ํ ํ์ธ (2์ด๋ง๋ค)
|
3314 |
-
pageLoadingInterval = setInterval(async () => {
|
3315 |
-
if (!currentLoadingPdfPath) {
|
3316 |
-
clearInterval(pageLoadingInterval);
|
3317 |
-
$id('loadingPages').style.display = 'none';
|
3318 |
-
return;
|
3319 |
-
}
|
3320 |
-
|
3321 |
-
try {
|
3322 |
-
const response = await fetch(`/api/cache-status?path=${encodeURIComponent(currentLoadingPdfPath)}`);
|
3323 |
-
const status = await response.json();
|
3324 |
-
|
3325 |
-
// ์ํ ์
๋ฐ์ดํธ
|
3326 |
-
if (status.status === "completed") {
|
3327 |
-
clearInterval(pageLoadingInterval);
|
3328 |
-
$id('loadingPages').style.display = 'none';
|
3329 |
-
refreshFlipBook(); // ์๋ฃ๋ ๋ฐ์ดํฐ๋ก ์๋ก๊ณ ์นจ
|
3330 |
-
currentLoadingPdfPath = null;
|
3331 |
-
} else if (status.status === "processing") {
|
3332 |
-
$id('loadingPagesCount').textContent = `${status.progress}%`;
|
3333 |
-
}
|
3334 |
-
} catch (e) {
|
3335 |
-
console.error("์บ์ ์ํ ํ์ธ ์ค๋ฅ:", e);
|
3336 |
-
}
|
3337 |
-
}, 1000);
|
3338 |
-
}
|
3339 |
-
|
3340 |
-
/* โโ ํ๋ฆฝ๋ถ ์๋ก๊ณ ์นจ โโ */
|
3341 |
-
async function refreshFlipBook() {
|
3342 |
-
if (!currentLoadingPdfPath || !fb) return;
|
3343 |
-
|
3344 |
-
try {
|
3345 |
-
const response = await fetch(`/api/cached-pdf?path=${encodeURIComponent(currentLoadingPdfPath)}`);
|
3346 |
-
const cachedData = await response.json();
|
3347 |
-
|
3348 |
-
if(cachedData.status === "completed" && cachedData.pages) {
|
3349 |
-
// ๊ธฐ์กด ํ๋ฆฝ๋ถ ์ ๋ฆฌ
|
3350 |
-
fb.destroy();
|
3351 |
-
viewer.innerHTML = '';
|
3352 |
-
|
3353 |
-
// ์ ๋ฐ์ดํฐ๋ก ์ฌ์์ฑ
|
3354 |
-
createFlipBook(cachedData.pages);
|
3355 |
-
currentLoadingPdfPath = null;
|
3356 |
-
}
|
3357 |
-
} catch (e) {
|
3358 |
-
console.error("ํ๋ฆฝ๋ถ ์๋ก๊ณ ์นจ ์ค๋ฅ:", e);
|
3359 |
-
}
|
3360 |
-
}
|
3361 |
-
|
3362 |
-
function createFlipBook(pages) {
|
3363 |
-
console.log('FlipBook ์์ฑ ์์. ํ์ด์ง ์:', pages.length);
|
3364 |
-
|
3365 |
-
try {
|
3366 |
-
// ํ๋ฉด ๋น์จ ๊ณ์ฐ
|
3367 |
-
const calculateAspectRatio = () => {
|
3368 |
-
const windowWidth = window.innerWidth;
|
3369 |
-
const windowHeight = window.innerHeight;
|
3370 |
-
const aspectRatio = windowWidth / windowHeight;
|
3371 |
-
|
3372 |
-
// ๋๋น ๋๋ ๋์ด ๊ธฐ์ค์ผ๋ก ์ต๋ 90% ์ ํ
|
3373 |
-
let width, height;
|
3374 |
-
if (aspectRatio > 1) { // ๊ฐ๋ก ํ๋ฉด
|
3375 |
-
height = Math.min(windowHeight * 0.9, windowHeight - 40);
|
3376 |
-
width = height * aspectRatio * 0.8; // ๊ฐ๋ก ํ๋ฉด์์๋ ์ฝ๊ฐ ์ค์
|
3377 |
-
if (width > windowWidth * 0.9) {
|
3378 |
-
width = windowWidth * 0.9;
|
3379 |
-
height = width / (aspectRatio * 0.8);
|
3380 |
-
}
|
3381 |
-
} else { // ์ธ๋ก ํ๋ฉด
|
3382 |
-
width = Math.min(windowWidth * 0.9, windowWidth - 40);
|
3383 |
-
height = width / aspectRatio * 0.9; // ์ธ๋ก ํ๋ฉด์์๋ ์ฝ๊ฐ ๋๋ฆผ
|
3384 |
-
if (height > windowHeight * 0.9) {
|
3385 |
-
height = windowHeight * 0.9;
|
3386 |
-
width = height * aspectRatio * 0.9;
|
3387 |
-
}
|
3388 |
-
}
|
3389 |
-
|
3390 |
-
// ์ต์ ์ฌ์ด์ฆ ๋ฐํ
|
3391 |
-
return {
|
3392 |
-
width: Math.round(width),
|
3393 |
-
height: Math.round(height)
|
3394 |
-
};
|
3395 |
-
};
|
3396 |
-
|
3397 |
-
// ์ด๊ธฐ ํ๋ฉด ๋น์จ ๊ณ์ฐ
|
3398 |
-
const size = calculateAspectRatio();
|
3399 |
-
viewer.style.width = size.width + 'px';
|
3400 |
-
viewer.style.height = size.height + 'px';
|
3401 |
-
|
3402 |
-
// ์ฌ์ด๋ ์ด๊ธฐํ ์ฌ๋ถ ํ์ธ
|
3403 |
-
if (!audioInitialized) {
|
3404 |
-
initializeAudio()
|
3405 |
-
.then(() => console.log('FlipBook ์์ฑ ์ ์ค๋์ค ์ด๊ธฐํ ์๋ฃ'))
|
3406 |
-
.catch(e => console.warn('FlipBook ์์ฑ ์ ์ค๋์ค ์ด๊ธฐํ ์คํจ:', e));
|
3407 |
-
}
|
3408 |
-
|
3409 |
-
// ํ์ด์ง ๋ฐ์ดํฐ ์ ์ (๋น ํ์ด์ง ์ฒ๋ฆฌ)
|
3410 |
-
const validPages = pages.map(page => {
|
3411 |
-
// src๊ฐ ์๋ ํ์ด์ง๋ ๋ก๋ฉ ์ค ์ด๋ฏธ์ง๋ก ๋์ฒด
|
3412 |
-
if (!page || !page.src) {
|
3413 |
-
return {
|
3414 |
-
src: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjVmNWY1Ii8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iIGZpbGw9IiM1NTUiPkxvYWRpbmcuLi48L3RleHQ+PC9zdmc+',
|
3415 |
-
thumb: page && page.thumb ? page.thumb : ''
|
3416 |
-
};
|
3417 |
-
}
|
3418 |
-
return page;
|
3419 |
-
});
|
3420 |
-
|
3421 |
-
fb = new FlipBook(viewer, {
|
3422 |
-
pages: validPages,
|
3423 |
-
viewMode: 'webgl',
|
3424 |
-
autoSize: true,
|
3425 |
-
flipDuration: 800,
|
3426 |
-
backgroundColor: '#fff',
|
3427 |
-
/* ๐ ๋ด์ฅ ์ฌ์ด๋ */
|
3428 |
-
sound: true,
|
3429 |
-
assets: {flipMp3: '/static/turnPage2.mp3', hardFlipMp3: '/static/turnPage2.mp3'}, // ์ ๋ ๊ฒฝ๋ก๋ก ์์
|
3430 |
-
controlsProps: {
|
3431 |
-
enableFullscreen: true,
|
3432 |
-
enableToc: true,
|
3433 |
-
enableDownload: false,
|
3434 |
-
enablePrint: false,
|
3435 |
-
enableZoom: true,
|
3436 |
-
enableShare: true, // ๊ณต์ ๋ฒํผ ํ์ฑํ
|
3437 |
-
enableSearch: true,
|
3438 |
-
enableAutoPlay: true,
|
3439 |
-
enableAnnotation: false,
|
3440 |
-
enableSound: true,
|
3441 |
-
enableLightbox: false,
|
3442 |
-
layout: 10, // ๋ ์ด์์ ์ต์
|
3443 |
-
skin: 'light', // ์คํจ ์คํ์ผ
|
3444 |
-
autoNavigationTime: 3600, // ์๋ ๋๊น ์๊ฐ(์ด)
|
3445 |
-
hideControls: false, // ์ปจํธ๋กค ์จ๊น ๋นํ์ฑํ
|
3446 |
-
paddingTop: 10, // ์๋จ ํจ๋ฉ
|
3447 |
-
paddingLeft: 10, // ์ข์ธก ํจ๋ฉ
|
3448 |
-
paddingRight: 10, // ์ฐ์ธก ํจ๋ฉ
|
3449 |
-
paddingBottom: 10, // ํ๋จ ํจ๋ฉ
|
3450 |
-
pageTextureSize: 1024, // ํ์ด์ง ํ
์ค์ฒ ํฌ๊ธฐ
|
3451 |
-
thumbnails: true, // ์ฌ๋ค์ผ ํ์ฑํ
|
3452 |
-
autoHideControls: false, // ์๋ ์จ๊น ๋นํ์ฑํ
|
3453 |
-
controlsTimeout: 8000, // ์ปจํธ๋กค ํ์ ์๊ฐ ์ฐ์ฅ
|
3454 |
-
shareHandler: copyPdfShareUrl // ๊ณต์ ํธ๋ค๋ฌ ์ค์
|
3455 |
-
}
|
3456 |
-
});
|
3457 |
-
|
3458 |
-
// ํ๋ฉด ํฌ๊ธฐ ๋ณ๊ฒฝ ์ FlipBook ํฌ๊ธฐ ์กฐ์
|
3459 |
-
window.addEventListener('resize', () => {
|
3460 |
-
if (fb) {
|
3461 |
-
const newSize = calculateAspectRatio();
|
3462 |
-
viewer.style.width = newSize.width + 'px';
|
3463 |
-
viewer.style.height = newSize.height + 'px';
|
3464 |
-
fb.resize();
|
3465 |
-
}
|
3466 |
-
});
|
3467 |
-
|
3468 |
-
// FlipBook ์์ฑ ํ ์ปจํธ๋กค๋ฐ ๊ฐ์ ํ์
|
3469 |
-
setTimeout(() => {
|
3470 |
-
try {
|
3471 |
-
// ์ปจํธ๋กค๋ฐ ๊ด๋ จ ์์ ์ฐพ๊ธฐ ๋ฐ ์คํ์ผ ์ ์ฉ
|
3472 |
-
const menuBars = document.querySelectorAll('.flipbook-container .fb3d-menu-bar');
|
3473 |
-
if (menuBars && menuBars.length > 0) {
|
3474 |
-
menuBars.forEach(menuBar => {
|
3475 |
-
menuBar.style.display = 'block';
|
3476 |
-
menuBar.style.opacity = '1';
|
3477 |
-
menuBar.style.visibility = 'visible';
|
3478 |
-
menuBar.style.zIndex = '9999';
|
3479 |
-
});
|
3480 |
-
}
|
3481 |
-
} catch (e) {
|
3482 |
-
console.warn('์ปจํธ๋กค๋ฐ ์คํ์ผ ์ ์ฉ ์ค ์ค๋ฅ:', e);
|
3483 |
-
}
|
3484 |
-
}, 1000);
|
3485 |
-
|
3486 |
-
console.log('FlipBook ์์ฑ ์๋ฃ');
|
3487 |
-
} catch (error) {
|
3488 |
-
console.error('FlipBook ์์ฑ ์ค ์ค๋ฅ ๋ฐ์:', error);
|
3489 |
-
showError("FlipBook์ ์์ฑํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3490 |
-
}
|
3491 |
-
}
|
3492 |
-
|
3493 |
-
/* โโ ๋ค๋น๊ฒ์ด์
โโ */
|
3494 |
-
function toggle(showHome){
|
3495 |
-
$id('home').style.display=showHome?'block':'none';
|
3496 |
-
$id('viewerPage').style.display=showHome?'none':'block';
|
3497 |
-
$id('homeButton').style.display=showHome?'none':'block';
|
3498 |
-
$id('adminPage').style.display='none';
|
3499 |
-
|
3500 |
-
// AI ๋ฒํผ ๊ด๋ฆฌ
|
3501 |
-
$id('aiButton').style.display = (!showHome && currentPdfId) ? 'block' : 'none';
|
3502 |
-
|
3503 |
-
// AI ์ฑ๋ด์ด ์ด๋ ค์์ผ๋ฉด ๋ซ๊ธฐ
|
3504 |
-
if (isAiChatActive) {
|
3505 |
-
toggleAiChat(false);
|
3506 |
-
}
|
3507 |
-
|
3508 |
-
// ๋ทฐ์ด ๋ชจ๋์ผ ๋ ์คํ์ผ ๋ณ๊ฒฝ
|
3509 |
-
if(!showHome) {
|
3510 |
-
document.body.classList.add('viewer-mode');
|
3511 |
-
} else {
|
3512 |
-
document.body.classList.remove('viewer-mode');
|
3513 |
-
}
|
3514 |
-
}
|
3515 |
-
|
3516 |
-
/* -- ๊ด๋ฆฌ์ ๊ธฐ๋ฅ -- */
|
3517 |
-
function setupAdminFunctions() {
|
3518 |
-
// ๊ด๋ฆฌ์ ๋ฒํผ ํด๋ฆญ - ๋ชจ๋ฌ ํ์
|
3519 |
-
const adminButton = document.getElementById('adminButton');
|
3520 |
-
const adminLoginModal = document.getElementById('adminLoginModal');
|
3521 |
-
const adminLoginClose = document.getElementById('adminLoginClose');
|
3522 |
-
const adminLoginButton = document.getElementById('adminLoginButton');
|
3523 |
-
const adminPasswordInput = document.getElementById('adminPasswordInput');
|
3524 |
-
const adminBackButton = document.getElementById('adminBackButton');
|
3525 |
-
|
3526 |
-
if (adminButton) {
|
3527 |
-
adminButton.addEventListener('click', function() {
|
3528 |
-
if (adminLoginModal) {
|
3529 |
-
adminLoginModal.style.display = 'flex';
|
3530 |
-
if (adminPasswordInput) {
|
3531 |
-
adminPasswordInput.value = '';
|
3532 |
-
adminPasswordInput.focus();
|
3533 |
-
}
|
3534 |
-
}
|
3535 |
-
});
|
3536 |
-
}
|
3537 |
-
|
3538 |
-
// ๋ชจ๋ฌ ๋ซ๊ธฐ ๋ฒํผ
|
3539 |
-
if (adminLoginClose) {
|
3540 |
-
adminLoginClose.addEventListener('click', function() {
|
3541 |
-
if (adminLoginModal) {
|
3542 |
-
adminLoginModal.style.display = 'none';
|
3543 |
-
}
|
3544 |
-
});
|
3545 |
-
}
|
3546 |
-
|
3547 |
-
// ์ํฐ ํค๋ก ๋ก๊ทธ์ธ
|
3548 |
-
if (adminPasswordInput) {
|
3549 |
-
adminPasswordInput.addEventListener('keyup', function(e) {
|
3550 |
-
if (e.key === 'Enter' && adminLoginButton) {
|
3551 |
-
adminLoginButton.click();
|
3552 |
-
}
|
3553 |
-
});
|
3554 |
-
}
|
3555 |
-
|
3556 |
-
// ๋ก๊ทธ์ธ ๋ฒํผ
|
3557 |
-
if (adminLoginButton) {
|
3558 |
-
adminLoginButton.addEventListener('click', async function() {
|
3559 |
-
if (!adminPasswordInput) return;
|
3560 |
-
|
3561 |
-
const password = adminPasswordInput.value;
|
3562 |
-
|
3563 |
-
try {
|
3564 |
-
showLoading("๋ก๊ทธ์ธ ์ค...");
|
3565 |
-
|
3566 |
-
const formData = new FormData();
|
3567 |
-
formData.append('password', password);
|
3568 |
-
|
3569 |
-
const response = await fetch('/api/admin-login', {
|
3570 |
-
method: 'POST',
|
3571 |
-
body: formData
|
3572 |
-
});
|
3573 |
-
|
3574 |
-
const data = await response.json();
|
3575 |
-
|
3576 |
-
hideLoading();
|
3577 |
-
|
3578 |
-
if (data.success) {
|
3579 |
-
// ๋ก๊ทธ์ธ ์ฑ๊ณต - ๊ด๋ฆฌ์ ํ์ด์ง ํ์
|
3580 |
-
if (adminLoginModal) {
|
3581 |
-
adminLoginModal.style.display = 'none';
|
3582 |
-
}
|
3583 |
-
showAdminPage();
|
3584 |
-
} else {
|
3585 |
-
// ๋ก๊ทธ์ธ ์คํจ
|
3586 |
-
showError("๊ด๋ฆฌ์ ์ธ์ฆ ์คํจ: ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.");
|
3587 |
-
}
|
3588 |
-
} catch (error) {
|
3589 |
-
console.error("๊ด๋ฆฌ์ ๋ก๊ทธ์ธ ์ค๋ฅ:", error);
|
3590 |
-
hideLoading();
|
3591 |
-
showError("๋ก๊ทธ์ธ ์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3592 |
-
}
|
3593 |
-
});
|
3594 |
-
}
|
3595 |
-
|
3596 |
-
// ๊ด๋ฆฌ์ ํ์ด์ง ๋ค๋ก๊ฐ๊ธฐ
|
3597 |
-
if (adminBackButton) {
|
3598 |
-
adminBackButton.addEventListener('click', function() {
|
3599 |
-
document.getElementById('adminPage').style.display = 'none';
|
3600 |
-
document.getElementById('home').style.display = 'block';
|
3601 |
-
});
|
3602 |
-
}
|
3603 |
-
}
|
3604 |
-
|
3605 |
-
// ๊ด๋ฆฌ์ ํ์ด์ง ํ์
|
3606 |
-
async function showAdminPage() {
|
3607 |
-
showLoading("๊ด๋ฆฌ์ ํ์ด์ง ๋ก๋ฉ ์ค...");
|
3608 |
-
|
3609 |
-
// ๋ค๋ฅธ ํ์ด์ง ์จ๊ธฐ๊ธฐ
|
3610 |
-
$id('home').style.display = 'none';
|
3611 |
-
$id('viewerPage').style.display = 'none';
|
3612 |
-
|
3613 |
-
// ๊ด๋ฆฌ์ ํ์ด์ง์ PDF ๋ชฉ๋ก ๋ก๋
|
3614 |
-
try {
|
3615 |
-
const response = await fetch('/api/permanent-pdf-projects');
|
3616 |
-
const data = await response.json();
|
3617 |
-
|
3618 |
-
const adminGrid = $id('adminGrid');
|
3619 |
-
adminGrid.innerHTML = ''; // ๊ธฐ์กด ๋ด์ฉ ์ง์ฐ๊ธฐ
|
3620 |
-
|
3621 |
-
if (data.length === 0) {
|
3622 |
-
$id('noAdminProjects').style.display = 'block';
|
3623 |
-
} else {
|
3624 |
-
$id('noAdminProjects').style.display = 'none';
|
3625 |
-
|
3626 |
-
// ๊ฐ PDF ํ์ผ์ ๋ํ ์นด๋ ์์ฑ
|
3627 |
-
const thumbnailPromises = data.map(async (pdf) => {
|
3628 |
-
try {
|
3629 |
-
// ์ธ๋ค์ผ ๊ฐ์ ธ์ค๊ธฐ
|
3630 |
-
const thumbResponse = await fetch(`/api/pdf-thumbnail?path=${encodeURIComponent(pdf.path)}`);
|
3631 |
-
const thumbData = await thumbResponse.json();
|
3632 |
-
|
3633 |
-
// ํ์ ์ฌ๋ถ ํ์ธ (๋ฉ์ธ ํ์ด์ง์ ํ์๋๋์ง)
|
3634 |
-
const mainPdfPath = pdf.path.split('/').pop();
|
3635 |
-
const isMainDisplayed = serverProjects.some(p => p.path.includes(mainPdfPath));
|
3636 |
-
|
3637 |
-
// ๊ด๋ฆฌ์ ์นด๋ ์์ฑ
|
3638 |
-
const card = document.createElement('div');
|
3639 |
-
card.className = 'admin-card card fade-in';
|
3640 |
-
|
3641 |
-
// ๊ณ ์ URL ์์ฑ
|
3642 |
-
const viewUrl = `${window.location.origin}/view/${pdf.id}`;
|
3643 |
-
|
3644 |
-
// ์ธ๋ค์ผ ๋ฐ ์ ๋ณด
|
3645 |
-
card.innerHTML = `
|
3646 |
-
<div class="card-inner">
|
3647 |
-
${pdf.cached ? '<div class="cached-status">์บ์๋จ</div>' : ''}
|
3648 |
-
<img src="${thumbData.thumbnail || ''}" alt="${pdf.name}" loading="lazy">
|
3649 |
-
<p title="${pdf.name}">${pdf.name.length > 15 ? pdf.name.substring(0, 15) + '...' : pdf.name}</p>
|
3650 |
-
<div style="position: absolute; bottom: 130px; left: 50%; transform: translateX(-50%); z-index:10;">
|
3651 |
-
<a href="${viewUrl}" target="_blank" style="color:#4a6ee0; font-size:12px;">๋ฐ๋ก๊ฐ๊ธฐ ๋งํฌ</a>
|
3652 |
-
</div>
|
3653 |
-
${isMainDisplayed ?
|
3654 |
-
`<button class="unfeature-btn" data-path="${pdf.path}">๋ฉ์ธ์์ ์ ๊ฑฐ</button>` :
|
3655 |
-
`<button class="feature-btn" data-path="${pdf.path}">๋ฉ์ธ์ ํ์</button>`}
|
3656 |
-
<button class="delete-btn" data-path="${pdf.path}">์ญ์ </button>
|
3657 |
-
</div>
|
3658 |
-
`;
|
3659 |
-
|
3660 |
-
adminGrid.appendChild(card);
|
3661 |
-
|
3662 |
-
// ์ญ์ ๋ฒํผ ์ด๋ฒคํธ
|
3663 |
-
const deleteBtn = card.querySelector('.delete-btn');
|
3664 |
-
if (deleteBtn) {
|
3665 |
-
deleteBtn.addEventListener('click', async function(e) {
|
3666 |
-
e.stopPropagation(); // ์นด๋ ํด๋ฆญ ์ด๋ฒคํธ ์ ํ ๋ฐฉ์ง
|
3667 |
-
|
3668 |
-
if (confirm(`์ ๋ง "${pdf.name}" PDF๋ฅผ ์ญ์ ํ์๊ฒ ์ต๋๊น?`)) {
|
3669 |
-
try {
|
3670 |
-
showLoading("PDF ์ญ์ ์ค...");
|
3671 |
-
|
3672 |
-
const response = await fetch(`/api/admin/delete-pdf?path=${encodeURIComponent(pdf.path)}`, {
|
3673 |
-
method: 'DELETE'
|
3674 |
-
});
|
3675 |
-
|
3676 |
-
const result = await response.json();
|
3677 |
-
|
3678 |
-
hideLoading();
|
3679 |
-
|
3680 |
-
if (result.success) {
|
3681 |
-
card.remove();
|
3682 |
-
showMessage("PDF๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ญ์ ๋์์ต๋๋ค.");
|
3683 |
-
|
3684 |
-
// ๋ฉ์ธ PDF ๋ชฉ๋ก ์๋ก๊ณ ์นจ
|
3685 |
-
loadServerPDFs();
|
3686 |
-
} else {
|
3687 |
-
showError("์ญ์ ์คํจ: " + (result.message || "์ ์ ์๋ ์ค๋ฅ"));
|
3688 |
-
}
|
3689 |
-
} catch (error) {
|
3690 |
-
console.error("PDF ์ญ์ ์ค๋ฅ:", error);
|
3691 |
-
hideLoading();
|
3692 |
-
showError("PDF ์ญ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3693 |
-
}
|
3694 |
-
}
|
3695 |
-
});
|
3696 |
-
}
|
3697 |
-
|
3698 |
-
// ๋ฉ์ธ์ ํ์ ๋ฒํผ ์ด๋ฒคํธ
|
3699 |
-
const featureBtn = card.querySelector('.feature-btn');
|
3700 |
-
if (featureBtn) {
|
3701 |
-
featureBtn.addEventListener('click', async function(e) {
|
3702 |
-
e.stopPropagation(); // ์นด๋ ํด๋ฆญ ์ด๋ฒคํธ ์ ํ ๋ฐฉ์ง
|
3703 |
-
|
3704 |
-
try {
|
3705 |
-
showLoading("์ฒ๋ฆฌ ์ค...");
|
3706 |
-
|
3707 |
-
const response = await fetch(`/api/admin/feature-pdf?path=${encodeURIComponent(pdf.path)}`, {
|
3708 |
-
method: 'POST'
|
3709 |
-
});
|
3710 |
-
|
3711 |
-
const result = await response.json();
|
3712 |
-
|
3713 |
-
hideLoading();
|
3714 |
-
|
3715 |
-
if (result.success) {
|
3716 |
-
showMessage("PDF๊ฐ ๋ฉ์ธ ํ์ด์ง์ ํ์๋ฉ๋๋ค.");
|
3717 |
-
// ๊ด๋ฆฌ์ ํ์ด์ง ์๋ก๊ณ ์นจ
|
3718 |
-
showAdminPage();
|
3719 |
-
// ๋ฉ์ธ PDF ๋ชฉ๋ก ์๋ก๊ณ ์นจ
|
3720 |
-
loadServerPDFs();
|
3721 |
-
} else {
|
3722 |
-
showError("์ฒ๋ฆฌ ์คํจ: " + (result.message || "์ ์ ์๋ ์ค๋ฅ"));
|
3723 |
-
}
|
3724 |
-
} catch (error) {
|
3725 |
-
console.error("PDF ํ์ ์ค์ ์ค๋ฅ:", error);
|
3726 |
-
hideLoading();
|
3727 |
-
showError("์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3728 |
-
}
|
3729 |
-
});
|
3730 |
-
}
|
3731 |
-
|
3732 |
-
// ๋ฉ์ธ์์ ์ ๊ฑฐ ๋ฒํผ ์ด๋ฒคํธ
|
3733 |
-
const unfeatureBtn = card.querySelector('.unfeature-btn');
|
3734 |
-
if (unfeatureBtn) {
|
3735 |
-
unfeatureBtn.addEventListener('click', async function(e) {
|
3736 |
-
e.stopPropagation(); // ์นด๋ ํด๋ฆญ ์ด๋ฒคํธ ์ ํ ๋ฐฉ์ง
|
3737 |
-
|
3738 |
-
try {
|
3739 |
-
showLoading("์ฒ๋ฆฌ ์ค...");
|
3740 |
-
|
3741 |
-
const response = await fetch(`/api/admin/unfeature-pdf?path=${encodeURIComponent(pdf.path)}`, {
|
3742 |
-
method: 'DELETE'
|
3743 |
-
});
|
3744 |
-
|
3745 |
-
const result = await response.json();
|
3746 |
-
|
3747 |
-
hideLoading();
|
3748 |
-
|
3749 |
-
if (result.success) {
|
3750 |
-
showMessage("PDF๊ฐ ๋ฉ์ธ ํ์ด์ง์์ ์ ๊ฑฐ๋์์ต๋๋ค.");
|
3751 |
-
// ๊ด๋ฆฌ์ ํ์ด์ง ์๋ก๊ณ ์นจ
|
3752 |
-
showAdminPage();
|
3753 |
-
// ๋ฉ์ธ PDF ๋ชฉ๋ก ์๋ก๊ณ ์นจ
|
3754 |
-
loadServerPDFs();
|
3755 |
-
} else {
|
3756 |
-
showError("์ฒ๋ฆฌ ์คํจ: " + (result.message || "์ ์ ์๋ ์ค๋ฅ"));
|
3757 |
-
}
|
3758 |
-
} catch (error) {
|
3759 |
-
console.error("PDF ํ์ ํด์ ์ค๋ฅ:", error);
|
3760 |
-
hideLoading();
|
3761 |
-
showError("์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3762 |
-
}
|
3763 |
-
});
|
3764 |
-
}
|
3765 |
-
} catch (error) {
|
3766 |
-
console.error(`PDF ${pdf.name} ์ฒ๋ฆฌ ์ค๋ฅ:`, error);
|
3767 |
-
}
|
3768 |
-
});
|
3769 |
-
|
3770 |
-
await Promise.all(thumbnailPromises);
|
3771 |
-
}
|
3772 |
-
|
3773 |
-
// ๊ด๋ฆฌ์ ํ์ด์ง ํ์
|
3774 |
-
hideLoading();
|
3775 |
-
$id('adminPage').style.display = 'block';
|
3776 |
-
|
3777 |
-
} catch (error) {
|
3778 |
-
console.error("๊ด๋ฆฌ์ ํ์ด์ง ๋ก๋ ์ค๋ฅ:", error);
|
3779 |
-
hideLoading();
|
3780 |
-
showError("๊ด๋ฆฌ์ ํ์ด์ง ๋ก๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
3781 |
-
$id('home').style.display = 'block'; // ์ค๋ฅ ์ ํ์ผ๋ก ๋ณต๊ท
|
3782 |
-
}
|
3783 |
-
}
|
3784 |
-
|
3785 |
-
/* -- ๋ก๋ฉ ๋ฐ ์ค๋ฅ ํ์ -- */
|
3786 |
-
function showLoading(message, progress = -1) {
|
3787 |
-
// ๊ธฐ์กด ๋ก๋ฉ ์ปจํ
์ด๋๊ฐ ์๋ค๋ฉด ์ ๊ฑฐ
|
3788 |
-
hideLoading();
|
3789 |
-
|
3790 |
-
const loadingContainer = document.createElement('div');
|
3791 |
-
loadingContainer.className = 'loading-container fade-in';
|
3792 |
-
loadingContainer.id = 'loadingContainer';
|
3793 |
-
|
3794 |
-
let progressBarHtml = '';
|
3795 |
-
if (progress >= 0) {
|
3796 |
-
progressBarHtml = `
|
3797 |
-
<div class="progress-bar-container">
|
3798 |
-
<div id="progressBar" class="progress-bar" style="width: ${progress}%;"></div>
|
3799 |
-
</div>
|
3800 |
-
`;
|
3801 |
-
}
|
3802 |
-
|
3803 |
-
loadingContainer.innerHTML = `
|
3804 |
-
<div class="loading-spinner"></div>
|
3805 |
-
<p class="loading-text" id="loadingText">${message || '๋ก๋ฉ ์ค...'}</p>
|
3806 |
-
${progressBarHtml}
|
3807 |
-
`;
|
3808 |
-
|
3809 |
-
document.body.appendChild(loadingContainer);
|
3810 |
-
}
|
3811 |
-
|
3812 |
-
function updateLoading(message, progress = -1) {
|
3813 |
-
const loadingText = $id('loadingText');
|
3814 |
-
if (loadingText) {
|
3815 |
-
loadingText.textContent = message;
|
3816 |
-
}
|
3817 |
-
|
3818 |
-
if (progress >= 0) {
|
3819 |
-
let progressBar = $id('progressBar');
|
3820 |
-
|
3821 |
-
if (!progressBar) {
|
3822 |
-
const loadingContainer = $id('loadingContainer');
|
3823 |
-
if (loadingContainer) {
|
3824 |
-
const progressContainer = document.createElement('div');
|
3825 |
-
progressContainer.className = 'progress-bar-container';
|
3826 |
-
progressContainer.innerHTML = `<div id="progressBar" class="progress-bar" style="width: ${progress}%;"></div>`;
|
3827 |
-
loadingContainer.appendChild(progressContainer);
|
3828 |
-
progressBar = $id('progressBar');
|
3829 |
-
}
|
3830 |
-
} else {
|
3831 |
-
progressBar.style.width = `${progress}%`;
|
3832 |
-
}
|
3833 |
-
}
|
3834 |
-
}
|
3835 |
-
|
3836 |
-
function hideLoading() {
|
3837 |
-
const loadingContainer = $id('loadingContainer');
|
3838 |
-
if (loadingContainer) {
|
3839 |
-
loadingContainer.remove();
|
3840 |
-
}
|
3841 |
-
}
|
3842 |
-
|
3843 |
-
function showError(message) {
|
3844 |
-
// ๊ธฐ์กด ์ค๋ฅ ๋ฉ์์ง๊ฐ ์๋ค๋ฉด ์ ๊ฑฐ
|
3845 |
-
const existingError = $id('errorContainer');
|
3846 |
-
if (existingError) {
|
3847 |
-
existingError.remove();
|
3848 |
-
}
|
3849 |
-
|
3850 |
-
const errorContainer = document.createElement('div');
|
3851 |
-
errorContainer.className = 'loading-container fade-in';
|
3852 |
-
errorContainer.id = 'errorContainer';
|
3853 |
-
errorContainer.innerHTML = `
|
3854 |
-
<p class="loading-text" style="color: #e74c3c;">${message}</p>
|
3855 |
-
<button id="errorCloseBtn" style="margin-top: 15px; padding: 8px 16px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer;">ํ์ธ</button>
|
3856 |
-
`;
|
3857 |
-
|
3858 |
-
document.body.appendChild(errorContainer);
|
3859 |
-
|
3860 |
-
// ํ์ธ ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ
|
3861 |
-
$id('errorCloseBtn').onclick = () => {
|
3862 |
-
errorContainer.remove();
|
3863 |
-
};
|
3864 |
-
|
3865 |
-
// 5์ด ํ ์๋์ผ๋ก ๋ซ๊ธฐ
|
3866 |
-
setTimeout(() => {
|
3867 |
-
if ($id('errorContainer')) {
|
3868 |
-
$id('errorContainer').remove();
|
3869 |
-
}
|
3870 |
-
}, 5000);
|
3871 |
-
}
|
3872 |
-
|
3873 |
-
function showMessage(message) {
|
3874 |
-
// ๊ธฐ์กด ๋ฉ์์ง๊ฐ ์๋ค๋ฉด ์ ๊ฑฐ
|
3875 |
-
const existingMessage = $id('messageContainer');
|
3876 |
-
if (existingMessage) {
|
3877 |
-
existingMessage.remove();
|
3878 |
-
}
|
3879 |
-
|
3880 |
-
const messageContainer = document.createElement('div');
|
3881 |
-
messageContainer.className = 'loading-container fade-in';
|
3882 |
-
messageContainer.id = 'messageContainer';
|
3883 |
-
messageContainer.innerHTML = `
|
3884 |
-
<p class="loading-text" style="color: #2ecc71;">${message}</p>
|
3885 |
-
<button id="messageCloseBtn" style="margin-top: 15px; padding: 8px 16px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer;">ํ์ธ</button>
|
3886 |
-
`;
|
3887 |
-
|
3888 |
-
document.body.appendChild(messageContainer);
|
3889 |
-
|
3890 |
-
// ํ์ธ ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ
|
3891 |
-
$id('messageCloseBtn').onclick = () => {
|
3892 |
-
messageContainer.remove();
|
3893 |
-
};
|
3894 |
-
|
3895 |
-
// 3์ด ํ ์๋์ผ๋ก ๋ซ๊ธฐ
|
3896 |
-
setTimeout(() => {
|
3897 |
-
if ($id('messageContainer')) {
|
3898 |
-
$id('messageContainer').remove();
|
3899 |
-
}
|
3900 |
-
}, 3000);
|
3901 |
-
}
|
3902 |
-
</script>
|
3903 |
-
</body>
|
3904 |
-
</html>
|
3905 |
-
"""
|
3906 |
|
3907 |
if __name__ == "__main__":
|
3908 |
uvicorn.run("app:app", host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
|
|
|
1311 |
return get_html_content()
|
1312 |
|
1313 |
# HTML ๋ฌธ์์ด (AI ๋ฒํผ ๋ฐ ์ฑ๋ด UI ์ถ๊ฐ)
|
1314 |
+
# HTML ๋ฌธ์์ด (AI ๋ฒํผ ๋ฐ ์ฑ๋ด UI ์ถ๊ฐ)
|
1315 |
+
import os
|
1316 |
+
|
1317 |
+
# Hugging Face Space์ secret์์ HTML ํ
ํ๋ฆฟ ๋ก๋
|
1318 |
+
HTML = os.getenv("HTML_TEMPLATE", "")
|
1319 |
+
|
1320 |
+
# HTML์ด ๋น์ด์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ HTML ์ฌ์ฉ (fallback)
|
1321 |
+
if not HTML:
|
1322 |
+
logger.warning("HTML_TEMPLATE secret์ด ์ค์ ๋์ด ์์ง ์์ต๋๋ค. ๊ธฐ๋ณธ HTML์ ์ฌ์ฉํฉ๋๋ค.")
|
1323 |
+
HTML = """
|
1324 |
+
<!doctype html>
|
1325 |
+
<html lang="ko">
|
1326 |
+
<head>
|
1327 |
+
<meta charset="utf-8">
|
1328 |
+
<title>FlipBook Space</title>
|
1329 |
+
<style>
|
1330 |
+
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
|
1331 |
+
.error { color: red; }
|
1332 |
+
</style>
|
1333 |
+
</head>
|
1334 |
+
<body>
|
1335 |
+
<h1>HTML ํ
ํ๋ฆฟ์ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค</h1>
|
1336 |
+
<p class="error">HTML_TEMPLATE secret์ด ์ค์ ๋์ด ์์ง ์์ต๋๋ค.</p>
|
1337 |
+
<p>Hugging Face Space์ secret ์์ญ์ HTML_TEMPLATE์ ์ค์ ํด์ฃผ์ธ์.</p>
|
1338 |
+
</body>
|
1339 |
+
</html>
|
1340 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1341 |
|
1342 |
if __name__ == "__main__":
|
1343 |
uvicorn.run("app:app", host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
|