Aiocr_Radar / ui_theme.py
Asem75's picture
Upload 3 files
b055bc3 verified
Raw History Blame Contribute Delete
22.4 kB
# ui_theme.py
# -*- coding: utf-8 -*-
"""مواد واجهة عامة مستخرجة من Divid Teacher UI Raw Kit ومكيّفة مع Gradio.
لا تحتوي أي منطق أعمال أو صوت أو تخزين.
"""
VERSION = "aiocr_radar_ui_theme_v5_voice_studio_routes"
APP_CSS = r"""
:root {
--dt-bg: #f6f8fc;
--dt-surface: #ffffff;
--dt-surface-2: #eef2f7;
--dt-border: #dbe2ea;
--dt-text: #172033;
--dt-muted: #64748b;
--dt-primary: #2563eb;
--dt-primary-soft: rgba(37, 99, 235, .10);
--dt-success: #059669;
--dt-warning: #d97706;
--dt-danger: #dc2626;
--dt-info: #0891b2;
--dt-radius: 14px;
--dt-radius-lg: 18px;
--dt-shadow: 0 8px 28px rgba(15, 23, 42, .10);
}
html[data-dt-theme="dark"] {
--dt-bg: #0b1020;
--dt-surface: #121a2b;
--dt-surface-2: #182238;
--dt-border: #27334b;
--dt-text: #edf2f7;
--dt-muted: #9aa8bd;
--dt-primary: #60a5fa;
--dt-primary-soft: rgba(96,165,250,.12);
--dt-shadow: 0 8px 30px rgba(0,0,0,.28);
}
html[data-dt-theme="contrast"] {
--dt-bg: #000;
--dt-surface: #000;
--dt-surface-2: #111;
--dt-border: #fff;
--dt-text: #fff;
--dt-muted: #e5e5e5;
--dt-primary: #ffd400;
--dt-primary-soft: rgba(255,212,0,.18);
--dt-shadow: none;
}
.gradio-container {
background: var(--dt-bg) !important;
color: var(--dt-text) !important;
max-width: 1320px !important;
margin: 0 auto !important;
}
.gradio-container, .gradio-container * { box-sizing: border-box; }
.gradio-container :focus-visible { outline: 3px solid var(--dt-primary) !important; outline-offset: 2px; }
.dt-hero {
display:flex; justify-content:space-between; gap:18px; align-items:center;
background:var(--dt-surface); border:1px solid var(--dt-border);
border-radius:var(--dt-radius-lg); box-shadow:var(--dt-shadow);
padding:18px 20px; margin:4px 0 16px;
}
.dt-hero h1 { margin:0 0 5px; font-size:1.35rem; color:var(--dt-text); }
.dt-hero p { margin:0; color:var(--dt-muted); }
.dt-manager-chips { display:flex; flex-wrap:wrap; gap:6px; margin-top:10px; }
.dt-chip { border:1px solid var(--dt-border); background:var(--dt-surface-2); color:var(--dt-text); border-radius:999px; padding:5px 9px; font-size:.78rem; }
.dt-theme-picker { display:flex; gap:7px; flex:0 0 auto; }
.dt-theme-btn { width:38px; height:38px; border-radius:50%; border:2px solid var(--dt-border); background:var(--dt-surface-2); color:var(--dt-text); cursor:pointer; font-size:1rem; }
.dt-theme-btn:hover, .dt-theme-btn.active { border-color:var(--dt-primary); box-shadow:0 0 0 2px var(--dt-primary-soft); }
.dt-section-title {
background:var(--dt-surface); border:1px solid var(--dt-border); border-radius:var(--dt-radius);
padding:12px 14px; margin:8px 0 12px; color:var(--dt-text);
}
.dt-section-title strong { color:var(--dt-primary); }
.dt-report-intro {
border:1px solid var(--dt-border); border-right:5px solid var(--dt-primary);
background:var(--dt-surface); color:var(--dt-text); border-radius:var(--dt-radius);
padding:12px 14px; margin-bottom:10px;
}
.dt-report-intro small { color:var(--dt-muted); }
.dt-report-table, .dt-status, .dt-summary {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius) !important;
background:var(--dt-surface) !important;
box-shadow:var(--dt-shadow) !important;
}
.dt-status textarea, .dt-summary textarea { background:var(--dt-surface) !important; color:var(--dt-text) !important; }
.gradio-container button.primary, .gradio-container button.primary:hover {
background:var(--dt-primary) !important; border-color:var(--dt-primary) !important;
}
.gradio-container button { border-radius:12px !important; }
.gradio-container .tab-nav { gap:6px; padding:5px; background:var(--dt-surface); border:1px solid var(--dt-border); border-radius:14px; }
.gradio-container .tab-nav button { border-radius:10px !important; color:var(--dt-text) !important; }
.gradio-container .tab-nav button.selected { background:var(--dt-primary-soft) !important; border-color:var(--dt-primary) !important; }
@media (max-width: 760px) {
.gradio-container { padding-left:8px !important; padding-right:8px !important; }
.dt-hero { align-items:flex-start; flex-direction:column; padding:14px; }
.dt-theme-picker { align-self:flex-end; }
.dt-manager-chips { gap:4px; }
.dt-chip { font-size:.72rem; padding:4px 7px; }
.dt-editor-toolbar { gap:6px !important; padding:8px !important; }
.dt-editor-toolbar > * { min-width:min(100%, 150px) !important; }
.dt-card { padding:9px !important; }
}
/* بطاقات وشريط أدوات مستخلصان من نمط Divid Teacher UI Raw Kit ومكيّفان مع Gradio */
.dt-card {
background:var(--dt-surface) !important;
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
box-shadow:var(--dt-shadow) !important;
padding:12px !important;
margin-bottom:12px !important;
}
.dt-editor-intro {
border:1px solid var(--dt-border);
border-right:5px solid var(--dt-primary);
background:var(--dt-surface);
color:var(--dt-text);
border-radius:var(--dt-radius);
padding:12px 14px;
margin-bottom:12px;
}
.dt-editor-toolbar {
display:flex !important;
flex-wrap:wrap !important;
gap:8px !important;
align-items:end !important;
padding:10px !important;
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
background:var(--dt-surface) !important;
}
.dt-editor-toolbar button {
min-height:42px !important;
}
.dt-editor-grid {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
background:var(--dt-surface) !important;
box-shadow:var(--dt-shadow) !important;
overflow:auto !important;
}
.dt-editor-actions {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
background:var(--dt-surface) !important;
}
.dt-report-table {
overflow:auto !important;
}
.dt-report-table table {
min-width:720px;
}
.dt-editor-grid table {
min-width:680px;
}
/* =========================================================
تحسينات الحقول والاختيارات والمحرر - مستوحاة من UI Raw Kit
========================================================= */
.dt-field {
border-radius:var(--dt-radius) !important;
}
.dt-field label,
.dt-choice label,
.dt-analysis label,
.dt-editor-text label {
font-weight:700 !important;
color:var(--dt-text) !important;
}
.dt-field input,
.dt-field textarea,
.dt-editor-text textarea,
.dt-analysis textarea,
.dt-choice input {
background:var(--dt-surface) !important;
color:var(--dt-text) !important;
border-color:var(--dt-border) !important;
}
.dt-editor-text {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
background:var(--dt-surface) !important;
box-shadow:var(--dt-shadow) !important;
overflow:hidden !important;
}
.dt-editor-text textarea {
min-height:420px !important;
line-height:1.9 !important;
font-size:1.03rem !important;
padding:16px !important;
resize:vertical !important;
}
.dt-editor-text:focus-within,
.dt-field:focus-within,
.dt-choice:focus-within,
.dt-editor-grid:focus-within {
border-color:var(--dt-primary) !important;
box-shadow:0 0 0 3px var(--dt-primary-soft) !important;
}
.dt-analysis {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius) !important;
background:var(--dt-surface-2) !important;
}
.dt-analysis textarea {
background:var(--dt-surface-2) !important;
color:var(--dt-muted) !important;
line-height:1.7 !important;
}
.dt-source-panel {
align-items:stretch !important;
}
.dt-source-panel > div {
background:var(--dt-surface) !important;
border-radius:var(--dt-radius) !important;
}
.dt-quality-toolbar {
border-right:4px solid var(--dt-primary) !important;
}
.dt-tool button,
button.dt-tool {
min-height:42px !important;
border:1px solid var(--dt-border) !important;
background:var(--dt-surface-2) !important;
color:var(--dt-text) !important;
font-weight:700 !important;
transition:transform 140ms ease, border-color 140ms ease, background 140ms ease !important;
}
.dt-tool button:hover,
button.dt-tool:hover {
border-color:var(--dt-primary) !important;
background:var(--dt-primary-soft) !important;
}
.dt-tool button:active,
button.dt-tool:active {
transform:scale(.98);
}
.dt-tool-primary button,
button.dt-tool-primary {
background:var(--dt-primary) !important;
border-color:var(--dt-primary) !important;
color:#fff !important;
}
html[data-dt-theme="contrast"] .dt-tool-primary button,
html[data-dt-theme="contrast"] button.dt-tool-primary {
color:#000 !important;
}
.dt-editor-fields {
display:flex !important;
flex-wrap:wrap !important;
gap:10px !important;
align-items:end !important;
padding:8px 0 !important;
}
.dt-editor-actions {
padding:8px 12px 12px !important;
}
.dt-editor-actions > button {
font-weight:700 !important;
}
.dt-page-nav {
justify-content:space-between !important;
}
.dt-page-nav > * {
flex:1 1 180px !important;
}
.dt-range input[type="range"] {
accent-color:var(--dt-primary);
}
.dt-download {
border:1px dashed var(--dt-border) !important;
border-radius:var(--dt-radius) !important;
background:var(--dt-surface-2) !important;
}
.dt-audio-field {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius) !important;
background:var(--dt-surface-2) !important;
}
/* إبراز الاختيارات الحالية بطريقة قريبة من Multi Selection Pattern */
.gradio-container input[type="radio"]:checked + span,
.gradio-container input[type="checkbox"]:checked + span {
color:var(--dt-primary) !important;
font-weight:700 !important;
}
.gradio-container label:has(input[type="radio"]:checked),
.gradio-container label:has(input[type="checkbox"]:checked) {
border-color:var(--dt-primary) !important;
background:var(--dt-primary-soft) !important;
border-radius:10px !important;
}
/* الحالة المرئية */
.dt-status {
position:relative;
}
.dt-status::before {
content:"";
position:absolute;
top:10px;
right:8px;
width:7px;
height:7px;
border-radius:50%;
background:var(--dt-info);
z-index:2;
}
/* Toast بصري؛ الإعلان الصوتي يستمر عبر aria-live الحالي لمنع التكرار */
.dt-toast-container {
position:fixed;
left:16px;
bottom:16px;
z-index:9999;
display:grid;
gap:8px;
width:min(380px, calc(100vw - 32px));
pointer-events:none;
}
.dt-toast {
padding:12px 14px;
border-radius:var(--dt-radius);
color:#fff;
box-shadow:var(--dt-shadow);
font-weight:700;
opacity:0;
transform:translateY(8px);
animation:dtToastIn .18s ease forwards;
}
.dt-toast.info { background:var(--dt-info); }
.dt-toast.success { background:var(--dt-success); }
.dt-toast.warning { background:var(--dt-warning); }
.dt-toast.error { background:var(--dt-danger); }
@keyframes dtToastIn {
to { opacity:1; transform:none; }
}
@media (max-width:760px) {
.dt-editor-text textarea {
min-height:340px !important;
font-size:1rem !important;
padding:12px !important;
}
.dt-editor-fields > *,
.dt-quality-toolbar > * {
flex:1 1 100% !important;
min-width:0 !important;
}
.dt-page-nav > * {
flex:1 1 calc(50% - 6px) !important;
}
.dt-toast-container {
left:8px;
bottom:8px;
width:calc(100vw - 16px);
}
}
/* وصول قارئات الشاشة */
.dt-sr-only {
position:absolute !important;
width:1px !important;
height:1px !important;
padding:0 !important;
margin:-1px !important;
overflow:hidden !important;
clip:rect(0,0,0,0) !important;
white-space:nowrap !important;
border:0 !important;
}
.dt-status, .dt-summary { scroll-margin-top:12px; }
/* =========================================================
Voice Studio — توليد النصوص مقابل تحويل الكتب
========================================================= */
.dt-route-card {
border:1px solid var(--dt-border);
border-right:5px solid var(--dt-primary);
border-radius:var(--dt-radius-lg);
background:linear-gradient(135deg,var(--dt-surface),var(--dt-surface-2));
box-shadow:var(--dt-shadow);
padding:14px 16px;
margin:8px 0 12px;
}
.dt-generator-card { border-right-color:var(--dt-info); }
.dt-book-convert-card { border-right-color:var(--dt-success); }
.dt-route-kicker { font-weight:800; color:var(--dt-primary); margin-bottom:4px; }
.dt-route-title { font-size:1.06rem; font-weight:800; color:var(--dt-text); }
.dt-route-note { color:var(--dt-muted); margin-top:5px; line-height:1.65; }
.dt-generator-layout,
.dt-book-layout,
.dt-audio-result-card {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
background:var(--dt-surface) !important;
box-shadow:var(--dt-shadow) !important;
padding:12px !important;
gap:12px !important;
}
.dt-engine-options {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius-lg) !important;
background:var(--dt-surface-2) !important;
padding:10px !important;
}
.dt-engine-options > div { margin-bottom:7px !important; }
.dt-clone-panel {
border:1px dashed var(--dt-primary) !important;
border-radius:var(--dt-radius) !important;
background:var(--dt-primary-soft) !important;
padding:9px !important;
}
.dt-action-generate button,
button.dt-action-generate {
min-height:48px !important;
font-weight:800 !important;
font-size:1rem !important;
background:var(--dt-info) !important;
border-color:var(--dt-info) !important;
color:#fff !important;
}
.dt-action-convert button,
button.dt-action-convert {
min-height:48px !important;
font-weight:800 !important;
font-size:1rem !important;
background:var(--dt-success) !important;
border-color:var(--dt-success) !important;
color:#fff !important;
}
.dt-audio-result-card audio {
width:100% !important;
min-height:54px !important;
accent-color:var(--dt-primary);
}
.dt-audio-result-card [class*="waveform"],
.dt-audio-result-card .ui-waveform-host {
border:1px solid var(--dt-border) !important;
border-radius:var(--dt-radius) !important;
background:var(--dt-surface-2) !important;
}
/* Dropdowns: keep the selected value compact; options open only when requested. */
.gradio-container [role="listbox"] { max-height:320px !important; overflow:auto !important; }
.gradio-container select,
.gradio-container input,
.gradio-container textarea { border-radius:10px !important; }
@media (max-width:760px) {
.dt-generator-layout,
.dt-book-layout,
.dt-audio-result-card { padding:8px !important; }
.dt-route-card { padding:11px 12px; }
.dt-action-generate button,
button.dt-action-generate,
.dt-action-convert button,
button.dt-action-convert { width:100% !important; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration:.01ms !important; transition-duration:.01ms !important; }
}
"""
APP_HEAD = r"""
<script>
(function(){
const KEY='aiocr-radar-ui-theme';
const ERROR_WORDS=['خطأ','فشل','فشلت','تعذر','غير موجود','error','failed','failure','exception'];
const SUCCESS_WORDS=['تم ','نجح','جاهز','اكتمل','حُفظ','حفظ','✅','success','completed'];
function debounce(fn,delay){
let timer=null;
return function(){
const args=arguments;
clearTimeout(timer);
timer=setTimeout(function(){fn.apply(null,args);},delay||180);
};
}
function toastType(text){
const lower=(text||'').toLowerCase();
if(ERROR_WORDS.some(function(w){return lower.includes(w.toLowerCase());})) return 'error';
if(SUCCESS_WORDS.some(function(w){return lower.includes(w.toLowerCase());})) return 'success';
if(lower.includes('تحذير') || lower.includes('⚠')) return 'warning';
return 'info';
}
function showToast(message){
const text=(message||'').trim();
if(!text) return;
const container=document.getElementById('dt-toast-container');
if(!container) return;
const node=document.createElement('div');
node.className='dt-toast '+toastType(text);
node.setAttribute('aria-hidden','true');
node.textContent=text.length>220 ? text.slice(0,217)+'…' : text;
container.appendChild(node);
window.setTimeout(function(){node.remove();},3000);
}
function applyTheme(name){
const allowed=['light','dark','contrast'];
if(!allowed.includes(name)) name='light';
document.documentElement.setAttribute('data-dt-theme',name);
try{localStorage.setItem(KEY,name);}catch(e){}
document.querySelectorAll('[data-dt-theme-choice]').forEach(function(btn){
const active=btn.getAttribute('data-dt-theme-choice')===name;
btn.classList.toggle('active',active);
btn.setAttribute('aria-pressed',active ? 'true' : 'false');
});
}
function announce(message, assertive){
const text=(message||'').trim();
if(!text) return;
const id=assertive ? 'dt-live-assertive' : 'dt-live-polite';
const node=document.getElementById(id);
if(!node) return;
node.textContent='';
window.setTimeout(function(){ node.textContent=text; },35);
}
function isError(text){
const lower=(text||'').toLowerCase();
return ERROR_WORDS.some(function(w){return lower.includes(w.toLowerCase());});
}
const lastValues=new WeakMap();
const pollingFields=new WeakSet();
function enhanceBox(box){
if(!box) return;
box.setAttribute('role','status');
box.setAttribute('aria-live','polite');
box.setAttribute('aria-atomic','true');
const field=box.querySelector('textarea, input');
if(!field || pollingFields.has(field)) return;
pollingFields.add(field);
field.setAttribute('aria-readonly','true');
lastValues.set(field,field.value||'');
window.setInterval(function(){
const current=(field.value||'').trim();
const previous=(lastValues.get(field)||'').trim();
if(current!==previous){
lastValues.set(field,current);
if(current){
announce(current,isError(current));
if(box.classList.contains('dt-toast-source')) showToast(current);
}
}
},300);
}
function enhanceAll(root){
if(!root || !root.querySelectorAll) return;
if(root.matches && (root.matches('.dt-status') || root.matches('.dt-summary'))){
enhanceBox(root);
}
root.querySelectorAll('.dt-status, .dt-summary').forEach(enhanceBox);
}
function captureAlerts(root){
if(!root || !root.querySelectorAll) return;
const nodes=[];
if(root.matches && root.matches('[role="alert"]')) nodes.push(root);
root.querySelectorAll('[role="alert"]').forEach(function(x){nodes.push(x);});
nodes.forEach(function(node){
if(node.id==='dt-live-assertive') return;
const text=(node.innerText||node.textContent||'').trim();
if(text) announce(text,true);
});
}
function syncSelectedState(root){
const scope=(root && root.querySelectorAll) ? root : document;
scope.querySelectorAll('.tab-nav button').forEach(function(btn){
btn.setAttribute('aria-selected',btn.classList.contains('selected') ? 'true' : 'false');
});
scope.querySelectorAll('.dt-editor-text textarea').forEach(function(field){
field.setAttribute('spellcheck','false');
field.setAttribute('autocomplete','off');
});
}
const syncSelectedStateDebounced=debounce(function(){syncSelectedState(document);},120);
function initAccessibility(){
enhanceAll(document);
syncSelectedState(document);
const observer=new MutationObserver(function(mutations){
mutations.forEach(function(m){
m.addedNodes.forEach(function(node){
if(node.nodeType!==1) return;
enhanceAll(node);
captureAlerts(node);
});
});
syncSelectedStateDebounced();
});
observer.observe(document.body,{childList:true,subtree:true});
}
try{applyTheme(localStorage.getItem(KEY)||'light');}catch(e){applyTheme('light');}
document.addEventListener('click',function(ev){
const btn=ev.target.closest('[data-dt-theme-choice]');
if(btn){
ev.preventDefault();
applyTheme(btn.getAttribute('data-dt-theme-choice'));
announce('تم تغيير مظهر الواجهة إلى '+(btn.getAttribute('aria-label')||''),false);
}
});
if(document.readyState==='loading'){
document.addEventListener('DOMContentLoaded',initAccessibility);
}else{
initAccessibility();
}
window.setTimeout(function(){
try{applyTheme(localStorage.getItem(KEY)||'light');}catch(e){}
enhanceAll(document);
syncSelectedState(document);
},800);
})();
</script>
"""
APP_HEADER_HTML = r"""
<div id="dt-live-polite" class="dt-sr-only" role="status" aria-live="polite" aria-atomic="true"></div>
<div id="dt-live-assertive" class="dt-sr-only" role="alert" aria-live="assertive" aria-atomic="true"></div>
<div id="dt-toast-container" class="dt-toast-container" aria-hidden="true"></div>
<div class="dt-hero" dir="rtl">
<div>
<h1>🎙️ Aiocr Radar — محطة الصوت والكتب</h1>
<p>توليد النصوص القصيرة وتحويل الكتب في مسارين واضحين، مع إدارة المحركات والصوت والتقارير.</p>
<div class="dt-manager-chips" aria-label="المدراء الفعالون">
<span class="dt-chip">SystemManager</span>
<span class="dt-chip">BookPreparationManager</span>
<span class="dt-chip">SmartEditorManager</span>
<span class="dt-chip">TextQualityManager</span>
<span class="dt-chip">AIManager</span>
<span class="dt-chip">AITaskManager</span>
<span class="dt-chip">VoiceManager</span>
<span class="dt-chip">StorageManager</span>
<span class="dt-chip">ReportManager</span>
</div>
</div>
<div class="dt-theme-picker" role="group" aria-label="مظهر الواجهة">
<button class="dt-theme-btn" type="button" data-dt-theme-choice="light" aria-label="الوضع الفاتح" title="فاتح">☀️</button>
<button class="dt-theme-btn" type="button" data-dt-theme-choice="dark" aria-label="الوضع الداكن" title="داكن">🌙</button>
<button class="dt-theme-btn" type="button" data-dt-theme-choice="contrast" aria-label="تباين عالٍ" title="تباين عالٍ">◐</button>
</div>
</div>
"""