{"id":23870,"date":"2025-05-28T16:50:32","date_gmt":"2025-05-28T08:50:32","guid":{"rendered":"https:\/\/www.hkmu.edu.hk\/oetools\/?page_id=23870"},"modified":"2026-04-01T15:48:03","modified_gmt":"2026-04-01T07:48:03","slug":"potapp","status":"publish","type":"page","link":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/","title":{"rendered":"potapp"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"23870\" class=\"elementor elementor-23870\" data-elementor-settings=\"[]\">\n\t\t\t\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t<section class=\"has_eae_slider wavo-column-gap-default elementor-section elementor-top-section elementor-element elementor-element-efe51a1 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"efe51a1\" data-element_type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[],&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"has_eae_slider elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-34afcac\" data-id=\"34afcac\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-61d68a3 elementor-widget elementor-widget-html\" data-id=\"61d68a3\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>Pot-app<\/title>\r\n    <link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/swiper@11\/swiper-bundle.min.css\">\r\n    <style>\r\n        :root {--primary-color:#0f2e63;--primary-light:#2a4a8a;--primary-dark:#1a202c;--primary-blue:#6495ED;--accent-color:#0096D6;--accent-green:#7AC143;--light-gray:#edf2f7;--medium-gray:#e0e0e0;--dark-gray:#707070;--success-color:#7AC143;--danger-color:#f44336;--warning-color:#ff9800;--info-color:#0096D6;--primary-red:#e53e3e;--accent-orange:#ed8936;--dark-text:#2d3748;--light-text:#718096;--card-bg:#ffffff;--bg-light:#f7fafc;--shadow:0 4px 12px rgba(0,0,0,0.15);--border-radius:12px;--small-radius:8px;--box-shadow:0 2px 8px rgba(0,0,0,0.1);--box-shadow-hover:0 8px 25px rgba(0,0,0,0.15);--transition:all 0.3s ease;--spacing-xs:8px;--spacing-sm:16px;--spacing-md:24px;--spacing-lg:32px;--spacing-xl:48px;--spacing-xxl:64px;--text-white:#ffffff;--font-serif:'Georgia','Times New Roman',serif;--font-sans:'Segoe UI',-apple-system,BlinkMacSystemFont,'Roboto',sans-serif;--card-light-bg:rgba(255,255,255,0.05);--logo-light-bg:rgba(255,255,255,0.1);}\r\n        * {margin: 0;padding: 0;box-sizing: border-box;font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', sans-serif;}\r\n        body {background-color: var(--bg-light);color: var(--dark-text);font-size: 1rem;line-height: 1.6;scroll-behavior: smooth;}\r\n        #main-container {max-width: 100%;margin: 0 auto;background-color: #0F284A;min-height: 100vh;}\r\n        #content-wrapper {position: relative;}\r\n        main {width: 100%;max-width: 1400px;margin: 0 auto;padding: 0 var(--spacing-sm);}\r\n        .font-btn {background-color: var(--primary-color);color: white;border: none;border-radius: var(--small-radius);padding: 8px 15px;cursor: pointer;font-size: 1rem;transition: var(--transition);}\r\n        .font-btn:hover {background-color: var(--primary-light);transform: translateY(-2px);}\r\n        h1 {font-size: clamp(2rem, 5vw, 3.5rem);color: white;text-align: center;padding: var(--spacing-xl) var(--spacing-sm);margin-bottom: 0;font-weight: 700;text-shadow: 2px 2px 4px rgba(0,0,0,0.3);}\r\n        h2 {font-size: clamp(1.5rem, 4vw, 2.2rem);color: var(--primary-color);margin-bottom: var(--spacing-md);padding-bottom: var(--spacing-sm);border-bottom: 2px solid var(--medium-gray);display: flex;align-items: center;text-shadow: 1px 1px 2px rgba(148, 148, 148, 0.5);}\r\n        h3 {font-size: clamp(1.1rem, 3vw, 1.4rem);margin: var(--spacing-lg) 0 var(--spacing-sm) 0;color: var(--primary-color);}\r\n        p {margin-bottom: var(--spacing-sm);line-height: 1.7;}\r\n        a {text-decoration: none;color: var(--primary-color);transition: var(--transition);}\r\n        a:hover {color: var(--accent-color);}\r\n        section {background-color: white;padding: var(--spacing-xl) var(--spacing-lg);margin-bottom: var(--spacing-md);border-radius: var(--border-radius);box-shadow: var(--box-shadow);transition: var(--transition);position: relative;overflow: hidden;}\r\n        section:hover {box-shadow: var(--box-shadow-hover);transform: translateY(-2px);}\r\n        #educational-scenarios-section {background-color: #0F284A;color: white;}\r\n        #educational-scenarios-section h2,\r\n        #educational-scenarios-section h3,\r\n        #educational-scenarios-section h4,\r\n        #educational-scenarios-section p {color: white;}\r\n        #educational-scenarios-section .scenario {background-color: rgba(255, 255, 255, 0.1);color: white;}\r\n        #educational-scenarios-section .scenario h4 {color: var(--accent-green);}\r\n        #video-demo-section {background-color: #0F284A;color: white;}\r\n        #video-demo-section h2 {color: white;border-bottom-color: rgba(255,255,255,0.3);}\r\n        .faq-section {background-color: #0F284A !important;color: white;}\r\n        .faq-section h2 {color: white;border-bottom-color: rgba(255,255,255,0.3);}\r\n        #key-features-list {margin: var(--spacing-lg) 0;padding: 0;list-style: none;display: grid;gap: var(--spacing-sm);}\r\n        #key-features-list li {background-color: rgba(122, 193, 67, 0.1);padding: var(--spacing-sm);border-radius: var(--small-radius);border-left: 4px solid var(--accent-green);transition: var(--transition);}\r\n        #key-features-list li:hover {background-color: rgba(122, 193, 67, 0.2);transform: translateX(5px);}\r\n        #faq-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));gap: var(--spacing-md);margin-top: var(--spacing-lg);perspective: 1000px;}\r\n        .flip-card {background-color: transparent;height: 140px;perspective: 1000px;}\r\n        .flip-card-inner {position: relative;width: 100%;height: 100%;text-align: center;transition: transform 0.6s;transform-style: preserve-3d;box-shadow: var(--box-shadow);border-radius: var(--border-radius);}\r\n        .flip-card:hover .flip-card-inner {transform: rotateY(180deg);}\r\n        .flip-card-front, .flip-card-back {position: absolute;width: 100%;height: 100%;-webkit-backface-visibility: hidden;backface-visibility: hidden;border-radius: var(--border-radius);display: flex;align-items: center;justify-content: center;padding: var(--spacing-sm);}\r\n        .flip-card:nth-child(odd) .flip-card-front {background: linear-gradient(135deg, #A9D486, #7AC143);color: white;}\r\n        .flip-card:nth-child(even) .flip-card-front {background: linear-gradient(135deg, #5CBABF, #0096D6);color: white;}\r\n        .flip-card:nth-child(odd) .flip-card-back {background: linear-gradient(135deg, #7AC143, #5a8f32);color: white;transform: rotateY(180deg);}\r\n        .flip-card:nth-child(even) .flip-card-back {background: linear-gradient(135deg, #0096D6, #0077B3);color: white;transform: rotateY(180deg);}\r\n        .faq-question, .faq-answer {font-weight: 600;font-size: 1rem;line-height: 1.4;text-align: center;}\r\n        #tool-logo {float: left;width: 100px;height: 100px;margin: 0 var(--spacing-lg) var(--spacing-md) 0;border-radius: var(--border-radius);overflow: hidden;box-shadow: var(--box-shadow);transition: var(--transition);display: flex;align-items: center;justify-content: center;background-color: white;opacity: 1;}\r\n        #tool-logo:hover {transform: scale(1.05);box-shadow: var(--box-shadow-hover);}\r\n        #tool-logo img {max-width: 100%;max-height: 100%;width: auto;height: auto;object-fit: contain;}\r\n        #specifications-list {display: grid;grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));gap: var(--spacing-md);margin-top: var(--spacing-lg);clear: both;}\r\n        .spec-item {background: linear-gradient(135deg, rgba(92, 186, 191, 0.1), rgba(122, 193, 67, 0.1));padding: var(--spacing-md);border-radius: var(--border-radius);border: 1px solid rgba(92, 186, 191, 0.2);transition: var(--transition);}\r\n        .spec-item:hover {background: linear-gradient(135deg, rgba(92, 186, 191, 0.2), rgba(122, 193, 67, 0.2));box-shadow: var(--box-shadow);transform: translateY(-3px);}\r\n        .spec-title {font-weight: 600;margin-bottom: var(--spacing-xs);font-size: 1.1rem;color: var(--primary-color);}\r\n        .spec-value {color: var(--dark-text);font-size: 0.95rem;line-height: 1.5;}\r\n        #video-container {position: relative;height: 350px;max-width: 100%;margin: 0 auto;border-radius: var(--border-radius);box-shadow: var(--box-shadow-hover);overflow: hidden;transition: var(--transition);}\r\n        #video-container:hover {transform: translateY(-5px);box-shadow: 0 12px 30px rgba(0,0,0,0.2);}\r\n        #video-container iframe {position: absolute;top: 0;left: 0;width: 100%;height: 100%;border: none;border-radius: var(--border-radius);}\r\n        #function-carousel {position: relative;width: 100%;display: flex;gap: 0;margin: var(--spacing-lg) 0;background: linear-gradient(135deg, #f9f9f9, #ffffff);border-radius: var(--border-radius);box-shadow: var(--box-shadow);transition: var(--transition);min-height: 300px;overflow: hidden;}\r\n        #carousel-navigation {width: 280px;padding: var(--spacing-md);background: linear-gradient(135deg, var(--light-gray), #e8f4f8);border-radius: var(--border-radius) 0 0 var(--border-radius);overflow-y: auto;}\r\n        .nav-item {padding: var(--spacing-sm) var(--spacing-md);margin-bottom: var(--spacing-sm);cursor: pointer;border-radius: var(--small-radius);transition: var(--transition);font-weight: 500;color: var(--primary-color);background: linear-gradient(135deg, #CAECA8, #b8e293);box-shadow: 0 2px 4px rgba(0,0,0,0.1);border: 1px solid rgba(122, 193, 67, 0.2);}\r\n        .nav-item:hover {background: linear-gradient(135deg, var(--accent-color), #0077B3);color: white;transform: translateX(5px);box-shadow: 0 4px 8px rgba(0,0,0,0.15);}\r\n        .nav-item.active {background: linear-gradient(135deg, var(--accent-color), #0077B3);color: white;font-weight: 600;}\r\n        #carousel-content {flex: 1;padding: var(--spacing-lg);display: flex;flex-direction: column;}\r\n        #carousel-image {width: 100%;height: 100%;border-radius: var(--small-radius);overflow: hidden;box-shadow: var(--box-shadow);background-color: #f5f5f5;display: flex;align-items: center;justify-content: center;position: relative;}\r\n        #carousel-image img {max-width: 100%;max-height: 100%;width: auto;height: auto;object-fit: contain;transition: opacity 0.3s ease;}\r\n        #toggle-container {display: flex;justify-content: center;margin-bottom: var(--spacing-xl);}\r\n        #perspective-toggle {display: flex;background: linear-gradient(135deg, #50A2AC, #0096D6);border-radius: 50px;padding: 6px;box-shadow: var(--box-shadow);position: relative;}\r\n        .toggle-option {padding: var(--spacing-sm) var(--spacing-lg);cursor: pointer;border-radius: 45px;transition: var(--transition);position: relative;z-index: 2;font-weight: 500;color: white;}\r\n        .toggle-option.active {background: linear-gradient(135deg, #8DB47B, #7AC143);color: white;font-weight: 600;box-shadow: 0 2px 8px rgba(0,0,0,0.2);}\r\n        #perspectives-wrapper {position: relative;min-height: 400px;}\r\n        #educators-scenarios, #learners-scenarios {position: absolute;width: 100%;top: 0;transition: transform 0.5s ease, opacity 0.5s ease;opacity: 0;transform: translateX(-20px);pointer-events: none;}\r\n        #educators-scenarios.active, #learners-scenarios.active {opacity: 1;transform: translateX(0);position: relative;pointer-events: auto;}\r\n        .scenario {background: linear-gradient(135deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05));border-radius: var(--border-radius);padding: var(--spacing-lg);margin-bottom: var(--spacing-md);box-shadow: 0 4px 15px rgba(0,0,0,0.1);transition: var(--transition);border: 1px solid rgba(255,255,255,0.1);}\r\n        .scenario:hover {transform: translateY(-3px);box-shadow: 0 8px 25px rgba(0,0,0,0.15);background: linear-gradient(135deg, rgba(255,255,255,0.15), rgba(255,255,255,0.1));}\r\n        .scenario h4 {color: var(--accent-green);margin-bottom: var(--spacing-sm);font-size: 1.2rem;display: flex;align-items: center;}\r\n        .scenario h4:before {content: \"\";display: inline-block;width: 12px;height: 12px;background-color: var(--accent-green);border-radius: 50%;margin-right: var(--spacing-sm);box-shadow: 0 0 0 3px rgba(122, 193, 67, 0.3);}\r\n        @media (max-width: 1200px) {main {padding: 0 var(--spacing-md);}}\r\n        @media (max-width: 1024px) {section {padding: var(--spacing-lg) var(--spacing-md);}#function-carousel {flex-direction: column;    min-height: auto;}#carousel-navigation {width: 100%;    border-radius: var(--border-radius) var(--border-radius) 0 0;    display: flex;    overflow-x: auto;    padding: var(--spacing-md);    gap: var(--spacing-sm);}.nav-item {white-space: nowrap;    margin-bottom: 0;    flex-shrink: 0;}.nav-item:hover {transform: translateY(-3px);}#carousel-content {padding: var(--spacing-md);}#carousel-image {min-height: 300px;}}\r\n        @media (max-width: 768px) {:root {--spacing-xs: 6px;    --spacing-sm: 12px;    --spacing-md: 18px;    --spacing-lg: 24px;    --spacing-xl: 32px;    --spacing-xxl: 48px;}main {padding: 0 var(--spacing-sm);}section {padding: var(--spacing-md) var(--spacing-sm);    margin-bottom: var(--spacing-sm);}h1 {padding: var(--spacing-lg) var(--spacing-sm);}#faq-grid {grid-template-columns: 1fr;    gap: var(--spacing-sm);}.flip-card {height: 120px;}#tool-logo {float: none;    margin: 0 auto var(--spacing-md);    display: block;    width: 80px;    height: 80px;}#specifications-list {grid-template-columns: 1fr;    gap: var(--spacing-sm);    margin-top: var(--spacing-md);}#video-container {width: 100%;    max-width: 500px;    height: 281px;    margin: var(--spacing-md) auto;}#carousel-image {min-height: 200px;}.toggle-option {padding: var(--spacing-sm) var(--spacing-md);    font-size: 0.9rem;}.scenario {padding: var(--spacing-md);}}\r\n        @media (max-width: 480px) {section {padding: var(--spacing-sm);}h1 {font-size: 1.8rem;    padding: var(--spacing-md) var(--spacing-sm);}h2 {font-size: 1.3rem;    margin-bottom: var(--spacing-sm);}#specifications-list {grid-template-columns: 1fr;}#video-container {width: 100%;    height: 225px;}#carousel-image {min-height: 150px;}.flip-card {height: 100px;}.faq-question, .faq-answer {font-size: 0.9rem;}.toggle-option {padding: var(--spacing-xs) var(--spacing-sm);    font-size: 0.8rem;}#carousel-navigation {padding: var(--spacing-sm);}.nav-item {padding: var(--spacing-xs) var(--spacing-sm);    font-size: 0.9rem;}}\r\n        @keyframes fadeInUp {from {opacity: 0;    transform: translateY(30px);}to {opacity: 1;    transform: translateY(0);}}\r\n        @keyframes spin {0% { transform: rotate(0deg);}100% { transform: rotate(360deg);}}\r\n        section {animation: fadeInUp 0.6s ease-out;}\r\n        section:nth-child(1) { animation-delay: 0.1s;}\r\n        section:nth-child(2) { animation-delay: 0.2s;}\r\n        section:nth-child(3) { animation-delay: 0.3s;}\r\n        section:nth-child(4) { animation-delay: 0.4s;}\r\n        section:nth-child(5) { animation-delay: 0.5s;}\r\n        section:nth-child(6) { animation-delay: 0.6s;}\r\n        @media (prefers-reduced-motion: no-preference) {html {scroll-behavior: smooth;}}\r\n        *:focus {outline: 2px solid var(--accent-color);outline-offset: 2px;}\r\n        .tab-container {display: flex;padding: 20px;gap: 20px;}\r\n        .tab-options {width: 30%;background-color: #f5f5f5;border-radius: var(--border-radius);padding: 15px;}\r\n        .tab-option {padding: 12px 15px;margin-bottom: 10px;color: var(--primary-color);background-color: white;border-left: 3px solid var(--accent-color);cursor: pointer;transition: var(--transition);border-radius: 4px;}\r\n        .tab-option:hover {background-color: rgba(0, 150, 214, 0.1);transform: translateX(5px);}\r\n        .tab-option.active {background-color: var(--accent-color);color: white;font-weight: bold;}\r\n        .tab-content {width: 70%;background-color: white;color: var(--primary-color);border-radius: var(--border-radius);padding: 20px;box-shadow: var(--box-shadow);list-style-position: inside;}\r\n        .tab-panel {display: none;}\r\n        .tab-panel.active {display: block;animation: fadeIn 0.5s ease;}\r\n\t\t.tab-content img {max-width: 100%;height: auto;}\r\n        @media (max-width: 768px) {.tab-container {flex-direction: column;}.tab-options, .tab-content {width: 100%;}}\r\n        td {padding-left: 1px;vertical-align: top;min-width: 25px;}\r\n        @keyframes fadeIn {from { opacity: 0;}to { opacity: 1;}}\r\n        .related-tools-section {background-color: #0F284A;color: white;padding: var(--spacing-xl) var(--spacing-lg);margin-bottom: var(--spacing-md);border-radius: var(--border-radius);}\r\n        .related-tools-section h2 {color: white;border-bottom-color: rgba(255,255,255,0.3);margin-bottom: var(--spacing-lg);}\r\n        .related-tools-container {max-width: 100%;margin: 0 auto;position: relative;}\r\n        .related-tools-swiper {padding: 20px 50px;overflow: visible;}\r\n        .related-tool-card {border-radius: 8px;padding: 20px;text-align: center;transition: all 0.2s ease;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.12);position: relative;overflow: hidden;background: rgba(255, 255, 255, 0.05);height: auto;display: flex;flex-direction: column;}\r\n        .related-tool-card::before {content: '';position: absolute;top: 0;left: 0;right: 0;height: 3px;background: linear-gradient(90deg, var(--accent-green), var(--primary-blue));}\r\n        .related-tool-card:hover {transform: translateY(-4px);box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);border-color: rgba(255, 255, 255, 0.25);}\r\n        .related-tool-card:focus-within {outline: 2px solid var(--primary-blue);outline-offset: 2px;}\r\n        .related-tool-logo {width: 100px;height: 100px;margin: 0 auto 20px;border-radius: 20px;overflow: hidden;display: flex;align-items: center;justify-content: center;aspect-ratio: 1 \/ 1;background: rgba(255, 255, 255, 0.1);border: 1px solid rgba(0, 0, 0, 0.05);}\r\n        .related-tool-logo img {width: 85%;height: 85%;aspect-ratio: 1 \/ 1;object-fit: cover;}\r\n        .related-tool-name {font-family: var(--font-serif);font-size: 1.1rem;font-weight: 700;margin-bottom: 12px;color: var(--text-white);letter-spacing: 0.02em;}\r\n        .related-tool-description {font-family: var(--font-sans);font-size: 0.9rem;color: var(--text-white);text-align: center;line-height: 1.6;display: -webkit-box;-webkit-line-clamp: 3;line-clamp: 3;-webkit-box-orient: vertical;overflow: hidden;flex-grow: 1;}\r\n        .related-tool-link {text-decoration: none;color: inherit;display: block;height: 100%;}\r\n        .related-tools-swiper .swiper-button-prev,\r\n        .related-tools-swiper .swiper-button-next {color: white;background: rgba(255, 255, 255, 0.1);width: 40px;height: 40px;border-radius: 50%;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.2);}\r\n        .related-tools-swiper .swiper-button-prev:after,\r\n        .related-tools-swiper .swiper-button-next:after {font-size: 20px;font-weight: bold;}\r\n        .related-tools-swiper .swiper-button-prev:hover,\r\n        .related-tools-swiper .swiper-button-next:hover {background: rgba(255, 255, 255, 0.2);transform: scale(1.1);}\r\n        @media (max-width: 1024px) {.related-tools-swiper {padding: 20px 50px;}}\r\n        @media (max-width: 768px) {.related-tools-swiper {padding: 20px 40px;}.related-tool-card {padding: 15px;}}\r\n        @media (max-width: 480px) {.related-tools-swiper {padding: 20px 30px;}}\r\n    <\/style>\r\n<\/head>\r\n<body>\r\n    <script>\r\n        const GITHUB_ITEMS_URL = 'https:\/\/raw.githubusercontent.com\/oetools\/Free_AI_Tools_Hub_Home_Page\/refs\/heads\/main\/items.json';\r\n        let remoteItemsData = [];\r\n\r\nconst allItems = {\r\n     \"Var_A\": \"pot-app\", \"Var_B\": \"29\",  \"Var_C\": \"Pot-App is an open-source translation and OCR (Optical Character Recognition) tool designed to break language barriers. It supports real-time text translation, screenshot OCR, and text-to-speech features. It integrates different AI models and offers offline functionality. User can compare the translation results from different models and choose the most suitable one.\",  \"Var_D_A\": \"Aggregated Translation Services: The application integrates multiple online translation and dictionary services, including DeepL, Google Translate, and OpenAI. Users can activate their preferred services and view parallel translations from several sources simultaneously in the results window. This allows for a comparative assessment of different translation outputs for a given text selection.\", \"Var_D_B\": \"Integrated OCR and Screenshot Translation: Pot-app incorporates OCR to extract text directly from screen captures for translation. It supports a variety of both online and offline OCR engines, including system-native options for Windows and macOS, as well as plugin-based engines like PaddleOCR. This function is accessible via a configurable hotkey, enabling users to select a screen area and receive an instant translation.\", \"Var_D_C\": \"Extensible Plugin System: The software features a comprehensive plugin system that allows for the addition of new functionalities. Users can install external plugins to add support for new translation services, OCR engines, TTS voices, and word collection interfaces. This modular architecture permits customisation and expansion of the application's core capabilities.\", \"Var_D_D\": \"Hotkey-Driven and Clipboard-Based Operation: Core functions are designed to be triggered by global hotkeys, allowing users to perform selection or screenshot translations from within any application. The tool also features a clipboard listening mode, which automatically translates any text that is copied to the system clipboard. This design facilitates a workflow that does not require constant switching between application windows.\", \"Var_D_E\": \"Cross-Platform and Open-Source: Pot-app is a free and open-source application released under the GPL-3.0 licence. It is available for Windows, macOS, and Linux operating systems, ensuring a consistent user experience across different desktop environments. The open-source nature of the project allows for community contributions and transparency in its development.\", \"Var_D_F\": \"External API Integration: The application provides a local HTTP server that exposes its functionalities through an API. This allows other software and scripts, such as PopClip on macOS or SnipDo on Windows, to integrate with Pot-app for translation and OCR tasks. Users can send requests to perform actions like translating selected text or recognising text from an image file provided externally.\", \"Var_D_G\": \"\", \"Var_D_H\": \"\", \"Var_D_I\": \"\", \"Var_D_J\": \"\",\"Var_E\": \"Pot-App is an open-source tool allows users to customize with different plugins for specialized needs (e.g., manuscript OCR). Different models for translation could be employed and the results are shown together for comparison. Offline OCR and translation ensure operations with limited internet connections.\", \"Var_F_A\": \"Yes\", \"Var_F_B\": \"No\", \"Var_F_C\": \"Yes\", \"Var_F_D\": \"\", \"Var_F_E\": \"Workflow Automation\",\r\n\r\n\t\t\t\/\/ Specifications\r\n\"Var_G_A\": \"https:\/\/pot-app.com\/en\/\",  \"Var_G_B\": \"Community\",  \"Var_G_C\": \"Pylogmon, Ccslykx, xtyuns, Avan, Integral\",  \"Var_G_D\": \"Free\",  \"Var_G_E\": \"Windows, MacOS, Others\",  \"Var_G_F\": \"Chinese, English\",  \"Var_G_G\": \"Yes\",  \"Var_G_H\": \"No\",  \"Var_G_I\": \"Yes\",  \"Var_G_J\": \"Yes\",  \"Var_G_K\": \"https:\/\/www.hkmu.edu.hk\/oetools\/wp-content\/uploads\/sites\/86\/2025\/05\/potapp.jpg\",\r\n\/\/ Classifications\r\n\t\t\t\/\/ Video  URLs\r\n\"Var_I_A\": \"<iframe src='https:\/\/player.vimeo.com\/video\/1172756559?badge=0&autopause=0&player_id=0&app_id=58479&byline=0&portrait=0&title=0&texttrack=en' frameborder='0' allow='autoplay; fullscreen; encrypted-media; web-share' referrerpolicy='strict-origin-when-cross-origin' title='pot-app' style='width: 100%; height: 100%;'><\/iframe>\",\"Var_J_Title_A\": \"\",\"Var_J_Title_B\": \"\", \"Var_J_Title_C\": \"\", \"Var_J_Title_D\": \"\", \"Var_J_Title_E\": \"\", \"Var_J_Title_F\": \"\", \"Var_J_A\": \"\",  \"Var_J_B\": \"\",  \"Var_J_C\": \"\",  \"Var_J_D\": \"\",  \"Var_J_E\": \"\",  \"Var_J_F\": \"\", \r\n\r\n\t\t\t\/\/ Educational Scenarios\r\n\"Var_K_A\": \"Multilingual Lecture Preparation: A professor translates foreign engineering papers using Pot-App, ensuring precise technical term conversion. Translated content is compiled into bilingual lecture slides for non-native students.\", \"Var_K_B\": \"Field Research Documentation: An archaeology team photographs old Chinese inscriptions onsite. Pot-App's offline OCR deciphers weathered text, translating it to English in real time. \", \"Var_K_C\": \"Accessible Course Materials: An instructor converts scanned textbook diagrams into editable text. Translated captions are read aloud via TTS for students.\", \"Var_L_A\": \"Language Lab Practice: During a Chinese reading session, screenshot OCR captures the texts and translate them to English. Foreign students match Chinese texts with English meanings for studying.\", \"Var_L_B\": \"Foreign Technical Manual: Engineering students collaborate on a German technical manual. Pot-App translates sections and provide narrations on difficult wordings.\", \"Var_L_C\": \"Lab Report Source: A graduate student digitizes handwritten lab notes with OCR. The text is further translated to English for lab report preparation. \",\r\n\r\n};\r\n\r\n        async function loadRemoteData() {\r\n            try {\r\n                console.log('Starting to load remote data...');\r\n                \r\n                const CACHE_KEY_ITEMS = 'faith_items_cache';\r\n                try {\r\n                    const cachedItems = localStorage.getItem(CACHE_KEY_ITEMS);\r\n                    if (cachedItems) {\r\n                        remoteItemsData = JSON.parse(cachedItems);\r\n                        console.log('Loaded items from cache:', remoteItemsData.length);\r\n                    }\r\n                } catch (cacheError) {\r\n                    console.warn('Error loading from cache:', cacheError);\r\n                }\r\n                \r\n                const itemsResponse = await fetch(GITHUB_ITEMS_URL);\r\n                if (itemsResponse.ok) {\r\n                    remoteItemsData = await itemsResponse.json();\r\n                    localStorage.setItem(CACHE_KEY_ITEMS, JSON.stringify(remoteItemsData));\r\n                    console.log('Items loaded from network:', remoteItemsData.length);\r\n                } else {\r\n                    console.error('Failed to load items:', itemsResponse.statusText);\r\n                }\r\n            } catch (error) {\r\n                console.error('Error loading remote data:', error);\r\n            }\r\n        }\r\n    <\/script>\r\n    \r\n    <div id=\"main-container\">\r\n        <header>\r\n            <h1 id=\"tool-name\"><\/h1>\r\n        <\/header>\r\n        \r\n        <div id=\"content-wrapper\">\r\n            <main>\r\n                <section id=\"introduction-section\">\r\n                    <div class=\"font-controls\" style=\"margin-top: 0px; text-align: right;\">\r\n                        <h4>Font Size:\r\n                            <button id=\"decrease-font\" class=\"font-btn\" style=\"margin-right: 5px;\" aria-label=\"Decrease font size\">-<\/button>\r\n                            <button id=\"reset-font\" class=\"font-btn\" style=\"margin-right: 5px;\" aria-label=\"Reset font size\">Aa<\/button>\r\n                            <button id=\"increase-font\" class=\"font-btn\" aria-label=\"Increase font size\">+<\/button>\r\n                        <\/h4>\r\n                    <\/div>\r\n                    <h2>Introduction<\/h2>\r\n                    <div id=\"intro-text\"><\/div>\r\n                    <h3>Key Features<\/h3>\r\n                    <ul id=\"key-features-list\"><\/ul>\r\n                    <h3>Uniqueness<\/h3>\r\n                    <div id=\"uniqueness-text\"><\/div>\r\n                <\/section>\r\n\r\n                <section id=\"faq-section\" class=\"faq-section\">\r\n                    <h2>Frequently Asked Questions<\/h2>\r\n                    <div id=\"faq-grid\">\r\n                        <div class=\"flip-card\">\r\n                            <div class=\"flip-card-inner\">\r\n                                <div class=\"flip-card-front\">\r\n                                    <div class=\"faq-question\">Open-Source?<\/div>\r\n                                <\/div>\r\n                                <div class=\"flip-card-back\">\r\n                                    <div class=\"faq-answer\" id=\"faq-answer-1\"><\/div>\r\n                                <\/div>\r\n                            <\/div>\r\n                        <\/div>\r\n                        <div class=\"flip-card\">\r\n                            <div class=\"flip-card-inner\">\r\n                                <div class=\"flip-card-front\">\r\n                                    <div class=\"faq-question\">Registration Needed?<\/div>\r\n                                <\/div>\r\n                                <div class=\"flip-card-back\">\r\n                                    <div class=\"faq-answer\" id=\"faq-answer-2\"><\/div>\r\n                                <\/div>\r\n                            <\/div>\r\n                        <\/div>\r\n                        <div class=\"flip-card\">\r\n                            <div class=\"flip-card-inner\">\r\n                                <div class=\"flip-card-front\">\r\n                                    <div class=\"faq-question\">Installation Required?<\/div>\r\n                                <\/div>\r\n                                <div class=\"flip-card-back\">\r\n                                    <div class=\"faq-answer\" id=\"faq-answer-3\"><\/div>\r\n                                <\/div>\r\n                            <\/div>\r\n                        <\/div>\r\n                        <div class=\"flip-card\">\r\n                            <div class=\"flip-card-inner\">\r\n                                <div class=\"flip-card-front\">\r\n                                    <div class=\"faq-question\">AI-empowered?<\/div>\r\n                                <\/div>\r\n                                <div class=\"flip-card-back\">\r\n                                    <div class=\"faq-answer\" id=\"faq-answer-5\"><\/div>\r\n                                <\/div>\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/section>\r\n                \r\n                <section id=\"specifications-section\">\r\n                    <h2>Specifications<\/h2>\r\n                    <div id=\"tool-logo\"><\/div>\r\n                    <div id=\"specifications-list\">\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">URL: <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-url\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Country or Region: <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-country\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Author(s): <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-authors\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Availability: <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-license\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Operating System(s): <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-os\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Language(s): <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-languages\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Registration Needed: <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-registration\"><\/div>\r\n                        <\/div>\r\n                        <div class=\"spec-item\">\r\n                            <div class=\"spec-title\">Installation Required: <\/div>\r\n                            <div class=\"spec-value\" id=\"spec-installation\"><\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/section>\r\n                <section id=\"video-demo-section\">\r\n                    <h2>Video Demonstration<\/h2>\r\n                    <div id=\"video-container\"><\/div>\r\n                <\/section>\r\n                \r\n                <section id=\"function-list-section\">\r\n                    \r\n    <h2>Function List<\/h2>\r\n    <div id=\"function-carousel\">\r\n        <div id=\"carousel-navigation\">\r\n            <!-- Navigation items will be added dynamically -->\r\n        <\/div>\r\n        <div id=\"carousel-content\">\r\n            <div id=\"carousel-image\"><\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n                <\/section>\r\n \r\n                <section id=\"educational-scenarios-section\">\r\n                    <h2>Educational Scenarios<\/h2>\r\n                    <div id=\"toggle-container\">\r\n                        <div id=\"perspective-toggle\">\r\n                            <div class=\"toggle-option\" id=\"educators-toggle\">Educators' Perspectives<\/div>\r\n                            <div class=\"toggle-option\" id=\"learners-toggle\">Learners' Perspectives<\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                    \r\n                    <div id=\"perspectives-wrapper\">\r\n                        <div id=\"educators-scenarios\" class=\"active\">\r\n                        <\/div>\r\n                        \r\n                        <div id=\"learners-scenarios\">\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/section>\r\n                \r\n                <section id=\"related-tools-section\" class=\"related-tools-section\" style=\"display: none;\">\r\n                    <h2>Related Tools<\/h2>\r\n                    <div class=\"related-tools-container\">\r\n                        <div class=\"swiper related-tools-swiper\">\r\n                            <div class=\"swiper-wrapper\" id=\"relatedToolsWrapper\">\r\n                            <\/div>\r\n                            <div class=\"swiper-button-prev\"><\/div>\r\n                            <div class=\"swiper-button-next\"><\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/section>\r\n            <\/main>\r\n        <\/div>\r\n    <\/div>\r\n    \r\n    <!-- Swiper JS -->\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/swiper@11\/swiper-bundle.min.js\"><\/script>\r\n    \r\n    <script>\r\n        function renderItems() {\r\n            document.getElementById('tool-name').textContent = allItems.Var_A;\r\n            document.getElementById('intro-text').textContent = allItems.Var_C;\r\n            const keyFeaturesList = document.getElementById('key-features-list');\r\n            [\r\n                allItems.Var_D_A, allItems.Var_D_B, allItems.Var_D_C, allItems.Var_D_D,\r\n            ].filter(feature => feature !== \"\").forEach(feature => {\r\n                const li = document.createElement('li');\r\n                if (feature.includes(':')) {\r\n                    const parts = feature.split(':');\r\n                    li.innerHTML = `<strong>${parts[0]}:<\/strong>${parts.slice(1).join(':')}`;\r\n                } else {\r\n                    li.textContent = feature;\r\n                }\r\n                keyFeaturesList.appendChild(li);\r\n            });\r\n            document.getElementById('uniqueness-text').textContent = allItems.Var_E;\r\n            document.getElementById('faq-answer-1').textContent = allItems.Var_F_A;\r\n            document.getElementById('faq-answer-2').textContent = allItems.Var_F_B;\r\n            document.getElementById('faq-answer-3').textContent = allItems.Var_F_C;\r\n            document.getElementById('faq-answer-5').textContent = allItems.Var_F_E;\r\n            const toolLogo = document.getElementById('tool-logo');\r\n            const logoImg = document.createElement('img');\r\n            logoImg.src = allItems.Var_G_K;\r\n            logoImg.alt = `${allItems.Var_A} Logo`;\r\n            logoImg.onload = function() {\r\n                toolLogo.appendChild(logoImg);\r\n            };\r\n            \r\n            logoImg.onerror = function() {\r\n                console.error(\"Failed to load logo image\");\r\n                toolLogo.innerHTML = `<div style=\"color: var(--accent-color); font-size: 3rem; text-align: center;\">\r\n                    ${allItems.Var_A.charAt(0)}\r\n                <\/div>`;\r\n            };\r\n            document.getElementById('spec-url').innerHTML = `<a href=\"${allItems.Var_G_A}\" target=\"_blank\">${allItems.Var_G_A}<\/a>`;\r\n            document.getElementById('spec-country').textContent = allItems.Var_G_B;\r\n            document.getElementById('spec-authors').textContent = allItems.Var_G_C;\r\n            document.getElementById('spec-license').textContent = allItems.Var_G_D;\r\n            document.getElementById('spec-os').textContent = allItems.Var_G_E;\r\n            document.getElementById('spec-languages').textContent = allItems.Var_G_F;\r\n            document.getElementById('spec-registration').textContent = allItems.Var_G_H;\r\n            document.getElementById('spec-installation').textContent = allItems.Var_G_I;\r\n            const videoContainer = document.getElementById('video-container');\r\n            if (allItems.Var_I_A) {\r\n                videoContainer.innerHTML = allItems.Var_I_A;\r\n            }\r\n            const educatorsScenarios = document.getElementById('educators-scenarios');\r\n            [allItems.Var_K_A, allItems.Var_K_B, allItems.Var_K_C].forEach((scenario, index) => {\r\n                if (scenario) {\r\n                    const scenarioDiv = document.createElement('div');\r\n                    scenarioDiv.className = 'scenario';\r\n                    \r\n                    const scenarioTitle = document.createElement('h4');\r\n                    const colonIndex = scenario.indexOf(':');\r\n                    scenarioTitle.textContent = colonIndex !== -1 ? scenario.substring(0, colonIndex) : `Scenario ${index + 1}`;\r\n                    \r\n                    const scenarioContent = document.createElement('p');\r\n                    scenarioContent.textContent = colonIndex !== -1 ? scenario.substring(colonIndex + 1).trim() : scenario;\r\n                    \r\n                    scenarioDiv.appendChild(scenarioTitle);\r\n                    scenarioDiv.appendChild(scenarioContent);\r\n                    educatorsScenarios.appendChild(scenarioDiv);\r\n                }\r\n            });\r\n\r\n            const learnersScenarios = document.getElementById('learners-scenarios');\r\n            [allItems.Var_L_A, allItems.Var_L_B, allItems.Var_L_C].forEach((scenario, index) => {\r\n                if (scenario) {\r\n                    const scenarioDiv = document.createElement('div');\r\n                    scenarioDiv.className = 'scenario';\r\n                    const scenarioTitle = document.createElement('h4');\r\n                    const colonIndex = scenario.indexOf(':');\r\n                    scenarioTitle.textContent = colonIndex !== -1 ? scenario.substring(0, colonIndex) : `Scenario ${index + 1}`;\r\n                    const scenarioContent = document.createElement('p');\r\n                    scenarioContent.textContent = colonIndex !== -1 ? scenario.substring(colonIndex + 1).trim() : scenario;\r\n                    scenarioDiv.appendChild(scenarioTitle);\r\n                    scenarioDiv.appendChild(scenarioContent);\r\n                    learnersScenarios.appendChild(scenarioDiv);\r\n                }\r\n            });\r\n        }\r\n        function findRelatedTools() {\r\n            if (!remoteItemsData || remoteItemsData.length === 0) {\r\n                console.warn('Remote items data not loaded yet');\r\n                return [];\r\n            }\r\n\r\n            const currentToolName = allItems.Var_A;\r\n            const currentTool = remoteItemsData.find(item => item.name === currentToolName);\r\n            if (!currentTool) {\r\n                console.warn(`Current tool \"${currentToolName}\" not found in remote data`);\r\n                return [];\r\n            }\r\n\r\n            const currentCategories = currentTool.categories || [];\r\n            \r\n            if (currentCategories.length === 0) {\r\n                console.warn(`No categories found for current tool \"${currentToolName}\"`);\r\n                return [];\r\n            }\r\n\r\n            const relatedTools = remoteItemsData.filter(item => {\r\n                if (item.name === currentToolName) {\r\n                    return false;\r\n                }\r\n                \r\n                const itemCategories = item.categories || [];\r\n                return itemCategories.some(category => currentCategories.includes(category));\r\n            });\r\n\r\n            console.log(`Found ${relatedTools.length} related tools for \"${currentToolName}\"`);\r\n            return relatedTools;\r\n        }\r\n\r\n        let relatedToolsSwiper = null;\r\n        \r\n        function renderRelatedTools() {\r\n            const relatedTools = findRelatedTools();\r\n            const wrapper = document.getElementById('relatedToolsWrapper');\r\n            \r\n            if (!wrapper) {\r\n                console.error('Related tools wrapper not found');\r\n                return;\r\n            }\r\n\r\n            wrapper.innerHTML = '';\r\n\r\n            if (relatedTools.length === 0) {\r\n                const section = document.getElementById('related-tools-section');\r\n                if (section) {\r\n                    section.style.display = 'none';\r\n                }\r\n                return;\r\n            }\r\n\r\n            const section = document.getElementById('related-tools-section');\r\n            if (section) {\r\n                section.style.display = 'block';\r\n            }\r\n\r\n            relatedTools.forEach(tool => {\r\n                const slide = document.createElement('div');\r\n                slide.className = 'swiper-slide';\r\n\r\n                const card = document.createElement('div');\r\n                card.className = 'related-tool-card';\r\n                const link = document.createElement('a');\r\n                link.href = tool.url || '#';\r\n                link.className = 'related-tool-link';\r\n                link.target = '_blank';\r\n                link.rel = 'noopener noreferrer';\r\n                const logoDiv = document.createElement('div');\r\n                logoDiv.className = 'related-tool-logo';\r\n                logoDiv.innerHTML = tool.logo || '';\r\n                const nameDiv = document.createElement('div');\r\n                nameDiv.className = 'related-tool-name';\r\n                nameDiv.textContent = tool.name || '';\r\n                const descDiv = document.createElement('div');\r\n                descDiv.className = 'related-tool-description';\r\n                descDiv.textContent = tool.description || '';\r\n                link.appendChild(logoDiv);\r\n                link.appendChild(nameDiv);\r\n                link.appendChild(descDiv);\r\n                card.appendChild(link);\r\n                slide.appendChild(card);\r\n                wrapper.appendChild(slide);\r\n            });\r\n\r\n            if (relatedToolsSwiper) {\r\n                relatedToolsSwiper.destroy(true, true);\r\n            }\r\n\r\n            relatedToolsSwiper = new Swiper('.related-tools-swiper', {\r\n                slidesPerView: 4,\r\n                spaceBetween: 24,\r\n                navigation: {\r\n                    nextEl: '.swiper-button-next',\r\n                    prevEl: '.swiper-button-prev',\r\n                },\r\n                breakpoints: {\r\n                    320: {\r\n                        slidesPerView: 1,\r\n                        spaceBetween: 16\r\n                    },\r\n                    480: {\r\n                        slidesPerView: 2,\r\n                        spaceBetween: 16\r\n                    },\r\n                    768: {\r\n                        slidesPerView: 3,\r\n                        spaceBetween: 20\r\n                    },\r\n                    1024: {\r\n                        slidesPerView: 4,\r\n                        spaceBetween: 24\r\n                    }\r\n                }\r\n            });\r\n        }\r\n        \r\n        let tabsInitialized = false;\r\n        function initFunctionTabs() {\r\n            if (tabsInitialized) {\r\n                return;\r\n            }\r\n            const tabOptions = document.querySelectorAll('.tab-option');\r\n            const tabPanels = document.querySelectorAll('.tab-panel');\r\n            \r\n            if (tabOptions.length === 0 || tabPanels.length === 0) {\r\n                return;\r\n            }\r\n            tabOptions.forEach(option => {\r\n                option.addEventListener('click', () => {\r\n                    tabOptions.forEach(opt => opt.classList.remove('active'));\r\n                    tabPanels.forEach(panel => panel.classList.remove('active'));\r\n                    option.classList.add('active');\r\n                    const tabId = option.getAttribute('data-tab');\r\n                    const targetPanel = document.getElementById(tabId);\r\n                    if (targetPanel) {\r\n                        targetPanel.classList.add('active');\r\n                    }\r\n                });\r\n            });\r\n            \r\n            tabsInitialized = true;\r\n        }\r\n\r\n        document.addEventListener('DOMContentLoaded', function() {\r\n            initFunctionTabs();\r\n        });\r\n\r\n        function initializeCarousel() {\r\n            const carouselTitles = [\r\n                allItems.Var_J_Title_A, allItems.Var_J_Title_B, allItems.Var_J_Title_C, allItems.Var_J_Title_D, allItems.Var_J_Title_E, allItems.Var_J_Title_F,\r\n            ].filter(title => title !== \"\");\r\n            \r\n            const carouselImages = [\r\n                allItems.Var_J_A, allItems.Var_J_B, allItems.Var_J_C, allItems.Var_J_D, allItems.Var_J_E, allItems.Var_J_F,\r\n            ].filter(img => img !== \"\");\r\n            \r\n            if (carouselImages.length === 0) {\r\n                document.getElementById('function-list-section').style.display = 'none';\r\n                return;\r\n            }\r\n            let currentSlide = 0;\r\n            const totalSlides = carouselImages.length;\r\n            const navContainer = document.getElementById('carousel-navigation');\r\n            carouselTitles.forEach((title, index) => {\r\n                const navItem = document.createElement('div');\r\n                navItem.className = 'nav-item';\r\n                navItem.textContent = title;\r\n                navItem.addEventListener('click', () => {\r\n                    currentSlide = index;\r\n                    updateCarousel(currentSlide, carouselTitles, carouselImages);\r\n                });\r\n                navContainer.appendChild(navItem);\r\n            });\r\n            updateCarousel(currentSlide, carouselTitles, carouselImages);\r\n            document.addEventListener('keydown', function(e) {\r\n                const rect = document.getElementById('carousel-image').getBoundingClientRect();\r\n                const isInViewport = rect.top < window.innerHeight && rect.bottom >= 0;\r\n                \r\n                if (isInViewport) {\r\n                    if (e.key === 'ArrowUp') {\r\n                        currentSlide = (currentSlide - 1 + totalSlides) % totalSlides;\r\n                        updateCarousel(currentSlide, carouselTitles, carouselImages);\r\n                    } else if (e.key === 'ArrowDown') {\r\n                        currentSlide = (currentSlide + 1) % totalSlides;\r\n                        updateCarousel(currentSlide, carouselTitles, carouselImages);\r\n                    }\r\n                }\r\n            });\r\n        }\r\n\r\n        function updateCarousel(slideIndex, titles, images) {\r\n            const navItems = document.querySelectorAll('.nav-item');\r\n            navItems.forEach((item, index) => {\r\n                if (index === slideIndex) {\r\n                    item.classList.add('active');\r\n                } else {\r\n                    item.classList.remove('active');\r\n                }\r\n            });\r\n            const imageContainer = document.getElementById('carousel-image');\r\n            imageContainer.innerHTML = '<div style=\"width: 50px; height: 50px; border: 5px solid #f3f3f3; border-top: 5px solid var(--accent-color); border-radius: 50%; animation: spin 1s linear infinite;\"><\/div>';\r\n            \r\n            const newImg = new Image();\r\n            newImg.src = images[slideIndex];\r\n            newImg.alt = titles[slideIndex] || \"Function demonstration\";\r\n            newImg.style.opacity = '0';\r\n            newImg.style.transition = 'opacity 0.3s ease';\r\n            \r\n            newImg.onload = function() {\r\n                imageContainer.innerHTML = '';\r\n                imageContainer.appendChild(newImg);\r\n                void newImg.offsetWidth;\r\n                newImg.style.opacity = '1';\r\n            };\r\n            \r\n            newImg.onerror = function() {\r\n                imageContainer.innerHTML = '<div style=\"text-align: center; padding: 20px;\"><p>Image could not be loaded<\/p><\/div>';\r\n            };\r\n        }\r\n\r\n        function setupPerspectiveToggle() {\r\n            const educatorsToggle = document.getElementById('educators-toggle');\r\n            const learnersToggle = document.getElementById('learners-toggle');\r\n            const educatorsScenarios = document.getElementById('educators-scenarios');\r\n            const learnersScenarios = document.getElementById('learners-scenarios');\r\n            educatorsToggle.classList.add('active');\r\n            educatorsScenarios.classList.add('active');\r\n            educatorsToggle.addEventListener('click', function() {\r\n                if (!educatorsToggle.classList.contains('active')) {\r\n                    learnersToggle.classList.remove('active');\r\n                    educatorsToggle.classList.add('active');\r\n                    learnersScenarios.style.transform = 'translateX(20px)';\r\n                    learnersScenarios.style.opacity = '0';\r\n                    \r\n                    setTimeout(() => {\r\n                        learnersScenarios.classList.remove('active');\r\n                        educatorsScenarios.classList.add('active');\r\n\r\n                        setTimeout(() => {\r\n                            educatorsScenarios.style.transform = 'translateX(0)';\r\n                            educatorsScenarios.style.opacity = '1';\r\n                        }, 50);\r\n                    }, 300);\r\n                }\r\n            });\r\n            \r\n            learnersToggle.addEventListener('click', function() {\r\n                if (!learnersToggle.classList.contains('active')) {\r\n                    educatorsToggle.classList.remove('active');\r\n                    learnersToggle.classList.add('active');\r\n                    educatorsScenarios.style.transform = 'translateX(-20px)';\r\n                    educatorsScenarios.style.opacity = '0';\r\n                    setTimeout(() => {\r\n                        educatorsScenarios.classList.remove('active');\r\n                        learnersScenarios.classList.add('active');\r\n                        \r\n                        setTimeout(() => {\r\n                            learnersScenarios.style.transform = 'translateX(0)';\r\n                            learnersScenarios.style.opacity = '1';\r\n                        }, 50);\r\n                    }, 300);\r\n                }\r\n            });\r\n            \r\n            const perspectivesWrapper = document.getElementById('perspectives-wrapper');\r\n            let touchStartX = 0;\r\n            let touchEndX = 0;\r\n            \r\n            perspectivesWrapper.addEventListener('touchstart', function(e) {\r\n                touchStartX = e.changedTouches[0].screenX;\r\n            }, { passive: true });\r\n            \r\n            perspectivesWrapper.addEventListener('touchend', function(e) {\r\n                touchEndX = e.changedTouches[0].screenX;\r\n                handleSwipe();\r\n            }, { passive: true });\r\n            \r\n            document.addEventListener('keydown', function(e) {\r\n                const rect = document.getElementById('educational-scenarios-section').getBoundingClientRect();\r\n                const isInViewport = rect.top < window.innerHeight && rect.bottom >= 0;\r\n                \r\n                if (isInViewport) {\r\n                    if (e.key === 'Tab' && e.shiftKey) {\r\n                        e.preventDefault();\r\n                        \r\n                        if (educatorsToggle.classList.contains('active')) {\r\n                            learnersToggle.click();\r\n                        } else {\r\n                            educatorsToggle.click();\r\n                        }\r\n                    }\r\n                }\r\n            });\r\n            \r\n            function handleSwipe() {\r\n                const swipeThreshold = 50;\r\n                \r\n                if (touchEndX < touchStartX - swipeThreshold) {\r\n                    if (educatorsToggle.classList.contains('active')) {\r\n                        learnersToggle.click();\r\n                    }\r\n                }\r\n                \r\n                if (touchEndX > touchStartX + swipeThreshold) {\r\n                    if (learnersToggle.classList.contains('active')) {\r\n                        educatorsToggle.click();\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        function initFontControls() {\r\n            const decreaseBtn = document.getElementById('decrease-font');\r\n            const resetBtn = document.getElementById('reset-font');\r\n            const increaseBtn = document.getElementById('increase-font');\r\n            let currentFontSize = 16;\r\n            function updateFontSizes() {\r\n                document.body.style.fontSize = `${currentFontSize}px`; \r\n                const elementsToSize = [\r\n                    'p', 'li', 'div', 'span', 'a', 'section', \r\n                    '.spec-value', '.faq-question', '.faq-answer',\r\n                    '.scenario p', '.toggle-option', '.nav-item'\r\n                ];\r\n                \r\n                elementsToSize.forEach(selector => {\r\n                    document.querySelectorAll(selector).forEach(el => {\r\n                        el.style.fontSize = `${currentFontSize}px`;\r\n                    });\r\n                });\r\n            }\r\n            \r\n            decreaseBtn.addEventListener('click', function() {\r\n                if (currentFontSize > 12) {\r\n                    currentFontSize -= 1;\r\n                    updateFontSizes();\r\n                }\r\n            });\r\n            \r\n            resetBtn.addEventListener('click', function() {\r\n                currentFontSize = 16;\r\n                updateFontSizes();\r\n            });\r\n            \r\n            increaseBtn.addEventListener('click', function() {\r\n                if (currentFontSize < 24) {\r\n                    currentFontSize += 1;\r\n                    updateFontSizes();\r\n                }\r\n            });\r\n        }\r\n        \r\n        window.addEventListener('DOMContentLoaded', async function() {\r\n            renderItems();\r\n            setupPerspectiveToggle();\r\n            initFunctionTabs();\r\n            initFontControls();\r\n            await loadRemoteData();\r\n            renderRelatedTools();\r\n            \r\n            document.querySelectorAll('a[href^=\"#\"]').forEach(anchor => {\r\n                anchor.addEventListener('click', function (e) {\r\n                    e.preventDefault();\r\n                    const targetId = this.getAttribute('href').substring(1);\r\n                    const targetElement = document.getElementById(targetId);\r\n                    \r\n                    if (targetElement) {\r\n                        targetElement.scrollIntoView({\r\n                            behavior: 'smooth'\r\n                        });\r\n                    }\r\n                });\r\n            });\r\n        });\r\n\r\n    <\/script>\r\n<\/body>\r\n<\/html>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Pot-app Font Size: - Aa + Introduction Key Features Uniqueness Frequently Asked Questions Open-Source? Registration Needed? Installation Required? AI-empowered? Specifications URL: Country or Region: Author(s): Availability: Operating System(s): Language(s): Registration Needed: Installation Required: Video Demonstration Function List Educational Scenarios Educators' Perspectives Learners' Perspectives Related Tools<\/p>\n","protected":false},"author":749,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"redux-templates_canvas","meta":{"_expiration-date-status":"","_expiration-date":0,"_expiration-date-type":"","_expiration-date-categories":[],"_expiration-date-options":[]},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v17.3 (Yoast SEO v21.2) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Open-Source OCR &amp; Translation Tool with AI Models | Pot-app - Hong Kong Metropolitan University<\/title>\n<meta name=\"description\" content=\"Pot-app is an open-source tool for real-time translation and OCR. Capture text from screenshots, compare results from multiple AI translation models, and use offline. Break language barriers with flexible, privacy-focused options.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"potapp\" \/>\n<meta property=\"og:description\" content=\"Pot-app Font Size: - Aa + Introduction Key Features Uniqueness Frequently Asked Questions Open-Source? Registration Needed? Installation Required?\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/\" \/>\n<meta property=\"og:site_name\" content=\"Hong Kong Metropolitan University\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-01T07:48:03+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Open-Source OCR & Translation Tool with AI Models | Pot-app - Hong Kong Metropolitan University","description":"Pot-app is an open-source tool for real-time translation and OCR. Capture text from screenshots, compare results from multiple AI translation models, and use offline. Break language barriers with flexible, privacy-focused options.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/","og_locale":"en_US","og_type":"article","og_title":"potapp","og_description":"Pot-app Font Size: - Aa + Introduction Key Features Uniqueness Frequently Asked Questions Open-Source? Registration Needed? Installation Required?","og_url":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/","og_site_name":"Hong Kong Metropolitan University","article_modified_time":"2026-04-01T07:48:03+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/","url":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/","name":"Open-Source OCR & Translation Tool with AI Models | Pot-app - Hong Kong Metropolitan University","isPartOf":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website"},"datePublished":"2025-05-28T08:50:32+00:00","dateModified":"2026-04-01T07:48:03+00:00","description":"Pot-app is an open-source tool for real-time translation and OCR. Capture text from screenshots, compare results from multiple AI translation models, and use offline. Break language barriers with flexible, privacy-focused options.","breadcrumb":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/potapp\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Open Educational Tools","item":"\/oetools\/"},{"@type":"ListItem","position":2,"name":"potapp"}]},{"@type":"WebSite","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website","url":"https:\/\/www.hkmu.edu.hk\/oetools\/","name":"Hong Kong Metropolitan University","description":"Open Educational Tools - Hong Kong Metropolitan University","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.hkmu.edu.hk\/oetools\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/23870"}],"collection":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/users\/749"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/comments?post=23870"}],"version-history":[{"count":36,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/23870\/revisions"}],"predecessor-version":[{"id":33437,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/23870\/revisions\/33437"}],"wp:attachment":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/media?parent=23870"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}