Includes fixes found while reviewing the app: - Results: open each result by a type+agent+id key, so a video and a material draft with the same id no longer open each other's popup - AuthGate: clear the query cache during render, before pages build their queries, and remount pages on organization switch (pages no longer stay on skeletons for up to a minute after sign-in or refresh) - Instruction: keep only failed rules after a partial approve, so retrying does not create the successful schedules twice; drop the monthly note once count or frequency is edited - Parser/schedules: never schedule a count below one - Audio: ignore the end event of a cancelled utterance when changing speed - Downloads: revoke the object URL later so Safari/Firefox can read it - Results: refetch SCORM content when any part's revision changes; show a failed video as failed instead of "still rendering" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
616 lines
133 KiB
HTML
616 lines
133 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="id">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<meta name="description" content="Learning Lab Express by Bawana. Prototipe pembuatan modul pelatihan dari materi yang sudah ada.">
|
||
<title>Learning Lab Express · by Bawana</title>
|
||
<style>:root{--blue:#2563eb;--blue-dark:#194dcc;--navy:#142d50;--ink:#1b2c46;--muted:#768399;--line:#e6ebf2;--bg:#f7f9fc;--light:#eef5ff;--green:#14866b;--radius:14px;font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:var(--ink);font-size:14px}
|
||
.admin-menu{margin-top:26px;border-top:1px solid var(--line);padding-top:16px}.admin-menu summary{list-style:none;display:flex;align-items:center;justify-content:space-between;padding:12px;color:var(--muted);font-size:11px;cursor:pointer}.admin-menu summary::-webkit-details-marker{display:none}.admin-menu summary svg{width:14px}.admin-menu[open] summary svg{transform:rotate(180deg)}.auto-hero-note{display:flex;align-items:center;gap:7px;font-size:10px;color:#6c83a3;margin-top:18px}.auto-hero-note svg{width:14px;height:14px;color:var(--blue)}.auto-flow{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:22px 0 18px;padding:0 3px;color:#607594;font-size:11px}.auto-flow>span{display:flex;align-items:center;gap:9px}.auto-flow b{color:var(--blue);background:#eaf2ff;border-radius:50%;display:grid;place-items:center;width:27px;height:27px;font-size:10px}.auto-flow>svg{width:15px;color:#9eafc7}.upload-box.upload-compact{display:flex;align-items:center;gap:18px;text-align:left;padding:25px 28px;margin-bottom:25px}.upload-compact .upload-symbol{margin:0;width:45px;height:45px;flex-shrink:0}.upload-compact>div{flex:1}.upload-compact h3{font-size:14px}.upload-compact p{font-size:11px;margin:6px 0}.upload-compact small{font-size:9px}.upload-compact .btn{flex-shrink:0}
|
||
@media(max-width:600px){.auto-flow{flex-direction:column;align-items:flex-start;gap:12px;margin:18px 0}.auto-flow>svg{display:none}.upload-box.upload-compact{flex-wrap:wrap;padding:20px;gap:13px}.upload-compact>div{flex-basis:calc(100% - 60px)}.upload-compact .btn{width:100%}.auto-hero-note{line-height:1.6}.admin-menu{margin-top:0;padding-top:0;border:0}.admin-menu summary{padding:10px 6px;font-size:10px}}
|
||
*{box-sizing:border-box}body{margin:0;background:var(--bg)}button,input,select,textarea{font:inherit}button,a,input,select,textarea{outline-offset:4px}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.48}a{color:inherit;text-decoration:none}button{color:inherit}button:focus-visible,a:focus-visible{outline:3px solid #9dbfff}svg{width:20px;height:20px;flex-shrink:0;vertical-align:middle}p{line-height:1.7;margin:0;color:var(--muted)}h1,h2,h3,h4{margin:0;letter-spacing:-.035em}h1{font-size:29px;line-height:1.25}h2{font-size:23px}h3{font-size:16px;letter-spacing:-.02em}h4{font-size:14px}small{font-size:11px;color:var(--muted)}[hidden]{display:none!important}
|
||
.brand{display:flex;align-items:center;gap:10px;line-height:1.15;font-size:16px;font-weight:750;letter-spacing:-.5px;white-space:nowrap}.brand b{font-weight:500;display:block;font-size:14px;margin-top:4px;letter-spacing:.1px}.brand small{display:block;font-size:9px;letter-spacing:1.5px;text-transform:uppercase;margin-top:7px;font-weight:600;color:#8493aa}.brand-mark{width:40px;height:42px;background:var(--blue);border-radius:11px;display:flex;align-items:center;justify-content:center;color:#fff;box-shadow:0 5px 10px #2563eb16}.brand-mark svg{width:24px;height:24px}
|
||
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:42px;padding:10px 17px;border-radius:8px;border:1px solid #dce3ee;background:white;font-size:12px;font-weight:650;white-space:nowrap;transition:background .15s,transform .15s}.btn:hover{background:#f5f8fd}.btn.primary{color:#fff;background:var(--blue);border-color:var(--blue);box-shadow:0 3px 6px #2563eb12}.btn.primary:hover{background:var(--blue-dark)}.btn.light{background:var(--light);border-color:#dfebff;color:var(--blue)}.btn.small{min-height:34px;padding:7px 11px;font-size:11px}.btn svg{width:16px;height:16px}.btn.danger{color:#b64747}.wide{width:100%}.icon-btn{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:0;border-radius:7px;padding:7px;color:#8290a5}.icon-btn:hover{background:var(--light);color:var(--blue)}.icon-btn svg{width:18px;height:18px}.text-btn{border:0;background:transparent;padding:0;color:var(--blue);font-size:12px;font-weight:600;display:inline-flex;align-items:center;gap:6px}.text-btn svg{width:15px;height:15px}.badge,.status{display:inline-flex;align-items:center;gap:5px;border-radius:5px;padding:5px 8px;font-size:10px;font-weight:650;line-height:1;white-space:nowrap}.badge.neutral{background:#eff3f8;color:#718098}.badge.blue{color:#2563eb;background:#edf4ff}.badge.purple{color:#8063bb;background:#f3effb}.badge.amber{color:#a78126;background:#fff7df}.status.ready{color:var(--green);background:#e9f7f2}.status.draft{color:#9a7a37;background:#fff6df}.dot{width:5px;height:5px;background:currentColor;border-radius:50%}
|
||
label{display:block;font-size:12px;font-weight:650;margin:0 0 8px}input:not([type=checkbox]):not([type=radio]):not([type=file]),select,textarea{width:100%;border:1px solid #dce3ed;padding:12px 13px;border-radius:8px;background:white;min-height:43px;color:var(--ink);transition:border-color .15s;outline-color:var(--blue)}input:focus,select:focus,textarea:focus{border-color:var(--blue)}input::placeholder,textarea::placeholder{color:#a3adbc}textarea{resize:vertical;line-height:1.65}input[type=checkbox]{accent-color:var(--blue);width:15px;height:15px;flex-shrink:0;cursor:pointer}.check-label{display:flex;align-items:center;gap:7px;font-weight:500;margin:0;line-height:1.6}.field{margin-bottom:22px}.field small{display:block;line-height:1.6;margin-top:7px}.field-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 20px}.error{color:#c35252;font-size:12px;margin-top:10px}.inline-note{display:flex;gap:9px;padding:13px 15px;background:#f5f8fc;border-radius:8px;font-size:11px;line-height:1.75;color:var(--muted)}.inline-note svg{width:16px;height:16px;flex-shrink:0;margin-top:2px;color:var(--blue)}
|
||
.login-layout{min-height:100vh;display:grid;grid-template-columns:55% 45%;background:white}.login-story{padding:44px 8%;background:#f0f6ff;display:flex;flex-direction:column;position:relative;overflow:hidden}.login-story:after{content:"";position:absolute;right:-260px;bottom:-370px;width:720px;height:720px;border:1px solid #dce9fc;border-radius:50%;pointer-events:none}.login-copy{margin:auto 0;padding:54px 0 45px;position:relative;z-index:1}.eyebrow{font-size:9px;font-weight:750;color:var(--blue);letter-spacing:1.6px}.login-copy h1{font-size:42px;line-height:1.22;letter-spacing:-1.6px;margin:22px 0}.login-copy>p{font-size:14px;max-width:420px;color:#74849b}.login-diagram{margin:35px 0 28px;width:390px;max-width:100%;position:relative}.mini-source,.mini-output{display:flex;align-items:center;gap:13px;padding:16px 18px;background:white;border:1px solid #e3ebf8;border-radius:11px;box-shadow:0 5px 10px #24457604;font-weight:650;font-size:12px}.mini-source{margin-right:30px}.mini-output{margin-left:30px}.mini-source small,.mini-output small{display:block;margin-top:5px;font-size:10px;font-weight:450}.mini-check{margin-left:auto;color:#31a287;background:#ecf8f3;width:22px;height:22px;text-align:center;border-radius:50%;line-height:22px}.connector{height:43px;display:flex;align-items:center;justify-content:center;gap:12px;color:var(--blue)}.connector i{display:block;width:1px;height:11px;background:#b3c8ee}.connector svg{width:24px;height:24px}.output-icon{background:#ebf3ff;border-radius:8px;padding:10px;color:var(--blue)}.mini-output .status{margin-left:auto}.login-benefits{display:flex;flex-direction:column;gap:13px;color:#607594;font-size:11px}.login-benefits>span{display:flex;align-items:center;gap:9px}.login-benefits svg{width:16px;height:16px;color:#6b8cc4}.login-footer{position:relative;z-index:1;font-size:8px;color:#8ba0bc;letter-spacing:1px;display:flex;justify-content:space-between;gap:12px}.login-footer span{letter-spacing:0}.login-panel{display:flex;align-items:center;justify-content:center;padding:45px}.login-form-wrap{width:360px;max-width:100%}.login-form-wrap h2{font-size:26px;margin-top:22px;line-height:1.3}.login-form-wrap>p{font-size:12px;margin:12px 0 30px}.login-form-wrap form>label{margin-top:20px}.password-field{position:relative}.password-field input{padding-right:45px!important}.password-field button{position:absolute;right:5px;top:6px;background:none;border:0;padding:6px;color:#929eaf}.password-field svg{width:18px;height:18px}.login-small{display:flex;align-items:center;justify-content:space-between;font-size:10px;color:#8e9aad;margin:18px 0 26px}.login-small label{font-size:10px;font-weight:500}.login-small input{width:13px;height:13px}.demo-login{margin-top:26px;display:flex;gap:10px;border:1px solid var(--line);border-radius:9px;padding:14px}.demo-login>svg{color:var(--blue);width:17px;height:17px;flex-shrink:0;margin-top:2px}.demo-login p{font-size:10px;line-height:1.8;color:#8390a2}.login-form-wrap .login-bottom{margin-top:46px;font-size:10px;text-align:center;color:#a3adbc}.login-bottom span{margin-left:6px;color:#6c86ad}
|
||
.app{display:flex;min-height:100vh}.sidebar{width:238px;flex-shrink:0;background:white;position:fixed;inset:0 auto 0 0;border-right:1px solid var(--line);padding:29px 18px 14px;display:flex;flex-direction:column;z-index:5}.sidebar>.brand{margin:0 13px 26px;font-size:14px}.sidebar .brand b{font-size:12px}.sidebar .brand-mark{width:35px;height:38px;border-radius:9px}.sidebar .brand small{font-size:8px}.workspace{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:9px;padding:11px 10px;margin-bottom:30px;font-size:11px;font-weight:650}.workspace-icon{display:flex;align-items:center;justify-content:center;background:#eef3fa;border-radius:7px;width:29px;height:29px;color:#7086a6}.workspace small{display:block;font-weight:450;font-size:9px;margin-top:3px}.workspace>svg{width:13px;height:13px;margin-left:auto;color:#94a1b4}.nav-label{font-size:8px;font-weight:700;color:#a0aabc;letter-spacing:1.5px;margin:0 12px 14px}nav{display:flex;flex-direction:column;gap:5px}nav button{display:flex;align-items:center;gap:11px;padding:13px 13px;border:0;background:none;border-radius:8px;color:#7b879b;font-size:12px;text-align:left;font-weight:550;width:100%}nav button svg{width:18px;height:18px;color:#8995a9}nav button:hover{background:#f6f9fe}nav button.active{background:#edf4ff;color:#2563eb;font-weight:650}nav button.active svg{color:#2563eb}.nav-count{font-size:9px;background:#eef2f7;padding:3px 6px;border-radius:4px;margin-left:auto;color:#8895a9}.active .nav-count{background:#dceaff;color:#2563eb}.sidebar-help{margin: auto 0 16px;border:1px solid #e5edf9;background:#f8fbff;border-radius:10px;padding:16px 13px}.sidebar-help>svg{color:#7091c2;width:18px;height:18px;display:block;margin-bottom:11px}.sidebar-help b{font-size:11px;font-weight:650}.sidebar-help p{font-size:10px;line-height:1.7;margin:8px 0 12px}.sidebar-help button{display:flex;align-items:center;gap:7px;color:var(--blue);font-size:10px;font-weight:650;border:0;padding:0;background:none}.sidebar-help button svg{width:13px;height:13px}.profile{display:flex;gap:9px;align-items:center;padding:14px 5px 0;border-top:1px solid var(--line)}.avatar{height:32px;width:32px;background:#edf3ff;color:#527fc8;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:650}.profile b{font-size:11px;font-weight:650}.profile small{display:block;font-size:9px;margin-top:4px}.profile>.icon-btn{margin-left:auto}
|
||
.workspace-main{margin-left:238px;width:calc(100% - 238px);min-height:100vh;display:flex;flex-direction:column}.topbar{height:72px;display:flex;justify-content:space-between;align-items:center;background:white;border-bottom:1px solid var(--line);padding:0 34px;gap:15px}.breadcrumb{font-size:11px;color:#94a0b1;white-space:nowrap}.breadcrumb span{margin:0 10px;color:#c1c9d5}.breadcrumb b{color:#64738a;font-weight:550}.topbar-right{display:flex;align-items:center;gap:19px;font-size:10px;color:#8b97a9}.demo-indicator{display:flex;align-items:center;gap:6px}.demo-indicator i{width:5px;height:5px;border-radius:50%;background:#d6a34b}.demo-strip{display:flex;align-items:center;gap:9px;margin:23px 34px 0;padding:10px 13px;border:1px solid #e1ebfb;background:#f0f6ff;border-radius:7px;font-size:10px;color:#7b91b2;line-height:1.5}.demo-strip svg{width:14px;height:14px;color:#7193c6}.demo-strip-tag{margin-left:auto;font-size:8px;letter-spacing:1px;font-weight:700;color:#7b96be}.app main{padding:28px 34px 30px;width:100%;max-width:1500px;align-self:center}.page-head{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;margin-bottom:25px}.page-head p{font-size:12px;margin-top:8px}.head-actions{display:flex;gap:9px;align-items:center}.app-footer{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin: auto 34px 0;padding:18px 0;border-top:1px solid var(--line);font-size:9px;color:#99a4b5}.app-footer>span:first-child{color:#7493c3}.app-footer>span:last-child{margin-left:auto;font-size:8px}.card{background:white;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.card-head{padding:21px 23px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;gap:10px}.card-head h3{font-size:14px}.card-head p{font-size:10px;margin-top:5px}.card-body{padding:23px}.hero{position:relative;display:grid;grid-template-columns:1.1fr 1fr;background:#edf4ff;border:1px solid #dfeaff;border-radius:14px;padding:30px 30px 29px;align-items:center;gap:15px;min-height:245px;margin-bottom:23px}.hero .eyebrow{font-size:8px;letter-spacing:1.4px}.hero h2{font-size:31px;line-height:1.23;letter-spacing:-1px;margin:15px 0 10px}.hero p{font-size:11px;max-width:375px;color:#7e91ad}.hero .head-actions{margin-top:21px}.hero-art{position:relative;justify-self:center;width:310px;max-width:100%}.hero-art .mini-source{font-size:10px;padding:13px;max-width:230px;transform:rotate(-3deg);margin-right:0}.hero-art .mini-source+.mini-source{position:relative;margin-top:8px;margin-left:25px;transform:rotate(1deg)}.hero-art .mini-source small{font-size:8px}.hero-art .file-icon{width:29px;height:34px;font-size:8px}.hero-art .connector{height:30px;margin-left:80px}.hero-art .mini-output{margin-left:45px;padding:13px;font-size:10px;position:relative;z-index:2;box-shadow:0 7px 15px #2563eb08}.hero-art .mini-output small{font-size:8px}.hero-art .output-icon{padding:8px}.hero-art .output-icon svg{width:17px;height:17px}.hero-art:before{content:"";position:absolute;border:1px dashed #c3d6f4;border-radius:50%;height:245px;width:245px;top:-10px;right:10px}
|
||
.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-bottom:23px}.metric{background:white;border:1px solid var(--line);border-radius:11px;padding:19px 19px 16px;position:relative}.metric>span{font-size:10px;color:#8c98aa}.metric strong{display:block;font-size:27px;letter-spacing:-1px;font-weight:650;margin:10px 0 8px}.metric small{font-size:9px;color:#9aa5b5}.metric i{position:absolute;right:16px;top:16px;background:#f2f6fc;border-radius:8px;padding:8px;color:#7f9bc5}.metric i svg{width:16px;height:16px}.setup-row{padding:17px 21px;display:flex;align-items:center;gap:24px;margin-bottom:23px}.setup-row>b{font-size:11px;white-space:nowrap}.steps{display:flex;justify-content:space-between;width:100%;gap:12px}.step{display:flex;align-items:center;gap:9px;font-size:10px;color:#8a98ac;background:none;border:0;padding:0;text-align:left}.step>i{height:25px;width:25px;display:flex;align-items:center;justify-content:center;border:1px solid #dfe7f3;border-radius:50%;font-size:9px;font-style:normal;color:#8b9cb6;background:#fafcff}.step.done>i{background:#ebf8f1;color:#3b9a77;border-color:#d9efdf}.step small{font-size:8px;display:block;margin-top:4px}.step-arrow{color:#c2cddd;font-size:15px}.dashboard-grid{display:grid;grid-template-columns:1.6fr 1fr;gap:21px;margin-bottom:23px}.empty{display:flex;flex-direction:column;align-items:center;text-align:center;padding:36px 25px;color:#8090a6}.empty-icon{color:#8da9d6;background:#f2f7fe;width:48px;height:48px;border-radius:12px;display:flex;align-items:center;justify-content:center;margin-bottom:14px}.empty-icon svg{width:24px;height:24px}.empty h3{font-size:13px;color:#596d8b;margin-bottom:8px}.empty p{font-size:10px;max-width:310px;margin-bottom:18px}.schedule-mini{padding:22px}.schedule-mini .clock-circle{background:#edf4ff;color:#7499ce;display:inline-flex;padding:11px;border-radius:50%;margin-bottom:17px}.schedule-mini h3{font-size:14px;margin-bottom:8px}.schedule-mini p{font-size:10px;line-height:1.75}.schedule-mini>.btn{margin-top:18px}.schedule-meta{border-top:1px solid var(--line);margin-top:17px;padding-top:15px;display:flex;justify-content:space-between;font-size:10px;color:#8491a5}.schedule-meta b{font-size:10px;color:#566e91;font-weight:600}.section-heading{display:flex;justify-content:space-between;align-items:center;margin:26px 0 15px;gap:12px}.section-heading p{font-size:10px;margin-top:6px}.section-heading h3{font-size:14px}.match-grid{display:grid;grid-template-columns:1fr 1fr;gap:15px}.match-card{padding:18px}.match-card .pair-names{display:flex;flex-direction:column;gap:10px;font-size:11px;line-height:1.5;font-weight:600}.pair-names span{display:flex;align-items:center;gap:8px}.pair-names svg{width:15px;height:15px;color:#93a7c5}.match-tags{display:flex;gap:5px;flex-wrap:wrap;margin:15px 0}.tag{display:inline-block;font-size:9px;color:#8493a8;background:#f4f6fa;border-radius:4px;padding:4px 6px}.match-card .match-footer{display:flex;justify-content:space-between;align-items:center;gap:8px;border-top:1px solid var(--line);padding-top:13px}.match-footer small{font-size:9px}
|
||
.file-icon{width:34px;height:40px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;border-radius:6px;background:#eef2f7;font-size:9px;font-weight:700;color:#8290a4;border:1px solid #e6ebf3}.file-icon.pdf{color:#d47c7c;background:#fff2f2;border-color:#fae8e8}.file-icon.pptx{color:#c58e5d;background:#fff5ea;border-color:#f8eadb}.file-icon.docx{color:#6e94d6;background:#f0f5ff;border-color:#e2eafb}.file-icon.txt,.file-icon.md{color:#689d86;background:#eef9f3;border-color:#def0e5}.upload-box{border:1.5px dashed #c9d9ef;border-radius:12px;background:#f9fbff;text-align:center;padding:31px 24px;margin-bottom:18px;transition:background .15s}.upload-box.drag{background:#eaf2ff;border-color:var(--blue)}.upload-symbol{display:inline-flex;padding:12px;background:#eaf2ff;border:1px solid #deebff;color:#7299d5;border-radius:12px;margin-bottom:14px}.upload-box h3{font-size:14px;margin-bottom:8px}.upload-box p{font-size:10px;margin-bottom:17px}.upload-box .upload-limit{font-size:9px;margin:11px 0 0}.toolbar{padding:17px 21px;display:flex;justify-content:space-between;gap:15px;align-items:center;border-bottom:1px solid var(--line)}.toolbar h3{font-size:13px}.toolbar .toolbar-controls{display:flex;gap:10px;max-width:520px;width:55%}.search{position:relative;width:100%;max-width:280px}.search>svg{position:absolute;left:11px;top:11px;width:15px;height:15px;color:#9aa7ba}.search input{padding:9px 12px 9px 34px!important;font-size:10px;min-height:35px!important}.toolbar select{padding:8px 11px;font-size:10px;min-height:35px;width:155px}.table-wrap{overflow:auto}table{border-collapse:collapse;width:100%;text-align:left}th{font-size:9px;font-weight:650;color:#9aa6b7;background:#fbfcfe;padding:13px 21px;border-bottom:1px solid var(--line);white-space:nowrap}td{padding:17px 21px;border-bottom:1px solid #eff2f7;font-size:10px;color:#8390a4;vertical-align:middle}tbody tr:last-child td{border-bottom:0}.file-name{display:flex;gap:12px;align-items:center;min-width:200px}.file-name b{font-size:11px;display:block;font-weight:600;color:#455a78;line-height:1.5;max-width:280px;overflow-wrap:anywhere}.file-name small{font-size:9px;display:block;margin-top:5px}.table-actions{white-space:nowrap;text-align:right}.table-note{border-top:1px solid var(--line);font-size:9px;color:#9aa7ba;padding:13px 21px}.notice-row{display:flex;align-items:center;gap:10px;font-size:10px;color:#8396b2;background:#edf4ff;border:1px solid #e0eafb;border-radius:8px;padding:12px 15px;margin-bottom:20px}.notice-row svg{width:15px;height:15px;flex-shrink:0}.notice-row button{margin-left:auto;white-space:nowrap;font-size:10px}.two-column{display:grid;grid-template-columns:1.55fr 1fr;align-items:start;gap:23px}.form-card .card-body{padding:25px}.form-section-title{font-size:11px;font-weight:700;color:#4c6280;margin-bottom:21px}.form-footer{border-top:1px solid var(--line);padding:17px 25px;display:flex;justify-content:space-between;align-items:center;gap:15px}.form-footer small{font-size:9px;line-height:1.6}.side-card{padding:23px}.side-card h3{font-size:13px;margin-bottom:12px}.side-card p{font-size:11px;margin-bottom:18px}.side-list{margin:0;padding:0;list-style:none;counter-reset:steps}.side-list li{counter-increment:steps;display:flex;align-items:flex-start;gap:11px;font-size:11px;color:#7c8da5;padding:12px 0;border-bottom:1px solid #f0f3f8;line-height:1.7}.side-list li:before{content:counter(steps);font-size:9px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:#edf4ff;color:#7b9ac7;flex-shrink:0;width:22px;height:22px}.side-list li:last-child{border-bottom:0}.mode-box{display:flex;gap:12px;background:#f7faff;border:1px solid #e3ecfa;border-radius:10px;padding:16px;margin-bottom:26px}.mode-box>span{background:#e9f2ff;color:#6d95d1;border-radius:8px;padding:9px;height:38px}.mode-box b{font-size:11px}.mode-box p{font-size:10px;line-height:1.7;margin-top:5px}.mode-box .badge{margin-left:auto;height:22px;font-size:8px}.output-checks{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:16px;border:1px solid var(--line);border-radius:8px}.output-checks label{font-size:11px}.switch-row{display:flex;justify-content:space-between;align-items:center;padding:18px;background:#f8fbff;border:1px solid #e6eefb;border-radius:9px;margin-bottom:25px}.switch-row b{font-size:12px}.switch-row p{font-size:10px;margin-top:5px}.switch{width:35px;height:20px;position:relative;display:inline-flex}.switch input{width:100%;height:100%;opacity:0;position:absolute;z-index:1;margin:0}.switch i{position:absolute;inset:0;background:#d7dfeb;border-radius:20px;transition:background .2s}.switch i:after{content:"";height:14px;width:14px;background:white;border-radius:50%;position:absolute;left:3px;top:3px;transition:transform .2s}.switch input:checked+i{background:var(--blue)}.switch input:checked+i:after{transform:translateX(15px)}.next-run{margin:19px 0;padding:16px;border:1px solid #dfebfb;background:#f4f8ff;border-radius:9px}.next-run small{font-size:9px}.next-run strong{display:block;font-size:12px;line-height:1.7;color:#5a78a2;margin-top:7px}.schedule-tip{padding:16px;background:#fafbfd;border:1px solid var(--line);border-radius:8px;font-size:10px;line-height:1.8;color:#8b98ab}
|
||
.results-summary{display:flex;gap:7px;margin-top:13px}.result-list{padding:0 21px}.result-row{display:flex;align-items:center;gap:15px;padding:21px 0;border-bottom:1px solid var(--line)}.result-row:last-child{border:0}.result-cover{width:46px;height:52px;background:#edf5ff;border:1px solid #e3edfc;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#7297ce}.result-main{flex:1;min-width:0}.result-title{font-size:12px;color:#455a79;font-weight:650;background:none;border:0;padding:0;text-align:left;line-height:1.55}.result-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:7px;color:#98a4b5;font-size:9px}.result-meta .tag{padding:3px 5px;font-size:8px}.result-actions{display:flex;gap:7px;align-items:center}.mini-result{display:flex;align-items:center;gap:11px;padding:18px 22px;border-bottom:1px solid #f0f3f8}.mini-result:last-child{border:0}.mini-result .result-cover{width:35px;height:40px}.mini-result .result-cover svg{width:18px;height:18px}.mini-result button{border:0;background:transparent;font-size:11px;text-align:left;padding:0;font-weight:600;line-height:1.5;color:#526986}.mini-result small{font-size:9px;display:block;margin-top:5px}.mini-result .status{margin-left:auto;font-size:8px}.mini-result>div:nth-child(2){min-width:0}
|
||
dialog{padding:0;border:1px solid #e6ebf2;border-radius:15px;box-shadow:0 28px 95px #142d5038;width:740px;max-width:calc(100vw - 30px);max-height:92vh;color:var(--ink);overflow:hidden;background:white}dialog::backdrop{background:#142d5070;backdrop-filter:blur(3px)}dialog.preview{width:1030px}.modal-head{padding:23px 25px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;gap:15px;align-items:flex-start}.modal-head h2{font-size:20px;letter-spacing:-.5px}.modal-head p{font-size:10px;line-height:1.7;margin-top:7px}.modal-body{padding:24px 25px;max-height:65vh;overflow:auto}.modal-foot{display:flex;align-items:center;justify-content:flex-end;gap:9px;border-top:1px solid var(--line);padding:16px 25px;background:#fff}.modal-foot>small{margin-right:auto;font-size:9px;line-height:1.5}.source-choices{display:flex;flex-direction:column;gap:8px;margin:14px 0 20px}.source-choice{display:flex;align-items:center;gap:11px;padding:13px;border:1px solid var(--line);border-radius:8px;margin:0;font-weight:500;cursor:pointer}.source-choice:has(input:checked){border-color:#a6c4f5;background:#f8fbff}.source-choice.disabled{opacity:.5;cursor:default}.source-choice>div{flex:1;font-size:11px;line-height:1.7}.source-choice b{font-weight:600;display:block}.source-choice small{font-size:9px}.source-choice .file-icon{width:28px;height:33px;font-size:8px}.generate-summary{display:flex;align-items:center;gap:18px;padding:14px 15px;border-radius:8px;background:#f7faff;color:#91a0b7;font-size:10px}.generate-summary span{display:flex;align-items:center;gap:6px}.generate-summary svg{width:15px;height:15px}.progress-state{padding:55px 30px 45px;text-align:center}.progress-orb{display:inline-flex;width:64px;height:64px;align-items:center;justify-content:center;color:var(--blue);background:#edf5ff;border-radius:18px;margin-bottom:22px}.progress-orb svg{width:30px;height:30px;animation:pulse 1.3s ease-in-out infinite}.progress-state h3{font-size:19px;margin-bottom:12px}.progress-state p{font-size:11px}.progress-track{width:290px;max-width:90%;height:5px;background:#edf1f8;border-radius:5px;overflow:hidden;margin:25px auto 16px}.progress-track i{display:block;background:var(--blue);height:100%;width:5%;transition:width .4s;border-radius:5px}.progress-state small{font-size:9px}@keyframes pulse{50%{transform:scale(.87);opacity:.6}}
|
||
.preview-top{display:flex;align-items:center;gap:7px;font-size:10px;color:#96a2b4;margin-bottom:12px}.preview-layout{display:grid;grid-template-columns:1fr 226px;gap:27px}.prose h1{font-size:25px;margin-bottom:18px;line-height:1.35}.prose h2{font-size:15px;margin:25px 0 10px;letter-spacing:-.2px}.prose p,.prose li{font-size:12px;color:#6f819b;line-height:1.9;white-space:pre-line;overflow-wrap:anywhere}.prose ul,.prose ol{padding-left:19px}.prose .ref-line{font-size:9px;color:#a3afbe;margin-top:8px}.quiz-item{border:1px solid var(--line);padding:14px;border-radius:8px;margin:12px 0}.quiz-item strong{font-size:11px;display:block;font-weight:600;line-height:1.7}.quiz-item ol{list-style:upper-alpha;font-size:11px}.quiz-item li{font-size:11px}.quiz-item details{font-size:10px;color:#668e7e;margin-top:8px}.preview-side{padding:17px;background:#f8faff;border:1px solid #e4ecf9;border-radius:10px;height:max-content;position:sticky;top:0}.preview-side h3{font-size:11px;margin-bottom:15px}.preview-side .detail-label{font-size:8px;color:#a0adbd;margin:17px 0 5px;letter-spacing:.5px;text-transform:uppercase}.preview-side b{font-size:10px;font-weight:600;display:block;line-height:1.7;overflow-wrap:anywhere}.preview-source{padding:9px 0;border-bottom:1px solid #e8eef8;font-size:10px;line-height:1.7;color:#7f93af}.preview-source:last-child{border:0}.preview-source svg{width:13px;height:13px;margin-right:5px}.preview-tabs{display:flex;gap:17px;border-bottom:1px solid var(--line);margin-bottom:21px}.preview-tabs button{padding:0 0 11px;background:none;border:0;font-size:11px;color:#8e9bb0;border-bottom:2px solid transparent}.preview-tabs button.active{color:var(--blue);border-color:var(--blue);font-weight:650}.edit-area{font-size:12px!important;min-height:360px}.help-steps{display:flex;flex-direction:column;gap:22px}.help-step{display:flex;gap:14px}.help-step>span{background:#edf4ff;color:#5787d1;width:29px;height:29px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:12px}.help-step h3{font-size:12px;margin-bottom:5px}.help-step p{font-size:11px}.toast{position:fixed;bottom:25px;left:50%;transform:translate(-50%,20px);opacity:0;pointer-events:none;z-index:30;background:#203b62;color:white;box-shadow:0 7px 25px #142d5020;border-radius:9px;padding:14px 19px;max-width:calc(100vw - 35px);font-size:12px;line-height:1.6;transition:opacity .18s,transform .18s}.toast.show{opacity:1;transform:translate(-50%,0)}.toast.error{background:#9f4747}.muted{color:var(--muted)}.spacer{height:20px}.source-text{white-space:pre-wrap;font-size:12px;line-height:1.9;color:#6a7d98;background:#f8faff;padding:19px;border:1px solid #e4ecf9;border-radius:8px;max-height:350px;overflow:auto;overflow-wrap:anywhere}
|
||
@media(min-width:1600px){.app main{padding-top:36px}.hero{min-height:280px}.hero h2{font-size:35px}.metrics{gap:20px}}
|
||
@media(max-width:1120px){.sidebar{width:210px;padding-left:13px;padding-right:13px}.workspace-main{margin-left:210px;width:calc(100% - 210px)}.topbar{padding:0 24px}.demo-strip{margin-left:24px;margin-right:24px}.app main{padding-left:24px;padding-right:24px}.hero{padding:25px;grid-template-columns:1.2fr 1fr}.hero h2{font-size:27px}.hero-art{width:260px}.hero-art .mini-output{margin-left:20px}.hero .btn{padding:9px 11px;font-size:10px}.metric{padding:17px 14px}.metric i{right:11px;top:13px;padding:6px}.metrics{gap:11px}.setup-row{gap:14px;padding:16px}.step{font-size:9px}.step small{font-size:8px}.dashboard-grid{grid-template-columns:1.3fr 1fr;gap:16px}.two-column{grid-template-columns:1.5fr 1fr;gap:16px}.card-body,.form-card .card-body{padding:21px}.toolbar .toolbar-controls{width:65%}.page-head h1{font-size:26px}.login-copy h1{font-size:36px}.login-panel{padding:35px}.preview-layout{grid-template-columns:1fr 200px}}
|
||
@media(max-width:900px){.hero-art{display:none}.hero{display:block}.hero p{max-width:470px}.metrics{grid-template-columns:1fr 1fr}.dashboard-grid,.two-column{grid-template-columns:1fr}.setup-row{align-items:flex-start;flex-direction:column}.setup-row>b{margin-bottom:7px}.schedule-mini .btn{width:100%}.match-grid{grid-template-columns:1fr}.topbar-right>#header-date{display:none}.preview-layout{grid-template-columns:1fr}.preview-side{position:static;display:none}.login-layout{grid-template-columns:1fr 1fr}.login-copy h1{font-size:30px}.login-story{padding:35px 30px}.login-footer span{display:none}.login-copy>p{font-size:12px}.login-benefits{font-size:10px}}
|
||
@media(max-width:700px){.sidebar{position:static;width:100%;padding:15px 17px 0;display:block;border-right:0;border-bottom:1px solid var(--line)}.sidebar>.brand{margin:0 0 16px;font-size:14px}.sidebar .brand b{display:inline;margin-left:3px}.sidebar .brand small{margin-top:4px;letter-spacing:1px}.sidebar .brand-mark{width:31px;height:34px}.workspace,.nav-label,.sidebar-help,.profile{display:none}.sidebar nav{flex-direction:row;overflow:auto;gap:4px;margin:0 -3px;padding-bottom:9px}.sidebar nav button{font-size:9px;gap:5px;padding:10px;white-space:nowrap;width:auto}.sidebar nav svg{width:14px;height:14px}.nav-count{display:none}.app{display:block}.workspace-main{margin:0;width:100%}.topbar{height:50px;padding:0 19px}.breadcrumb{font-size:10px}.topbar-right{gap:10px;font-size:9px}.demo-strip{margin:17px 17px 0;font-size:9px;padding:9px 10px}.demo-strip-tag{display:none}.app main{padding:23px 17px}.page-head{flex-direction:column;gap:15px;margin-bottom:21px}.page-head h1{font-size:26px}.page-head p{font-size:11px}.page-head .head-actions{width:100%}.page-head .head-actions .btn{flex:1}.hero{padding:23px}.hero h2{font-size:27px}.hero p{font-size:10px}.metric strong{font-size:25px}.metric small{font-size:8px}.setup-row{padding:16px}.step-arrow{display:none}.steps{gap:8px}.step{align-items:flex-start;gap:6px}.step>i{width:21px;height:21px;flex-shrink:0}.step small{font-size:7px}.app-footer{margin-left:17px;margin-right:17px;font-size:8px}.app-footer>span:last-child{display:none}.toolbar{padding:16px;flex-direction:column;align-items:stretch;gap:12px}.toolbar .toolbar-controls{width:100%;max-width:none}.search{max-width:none}.result-list{padding:0 16px}.result-row{gap:10px;flex-wrap:wrap}.result-main{min-width:200px}.result-actions{margin-left:56px}.field-grid{grid-template-columns:1fr}.output-checks{grid-template-columns:1fr}.form-footer{padding:16px 20px;align-items:flex-start;flex-direction:column}.form-footer .btn{width:100%}.modal-head{padding:20px}.modal-head h2{font-size:19px}.modal-body{padding:20px;max-height:63vh}.modal-foot{padding:15px 20px;flex-wrap:wrap}.modal-foot>small{width:100%;margin-bottom:5px}.modal-foot .btn{flex:1;font-size:10px;padding:9px}.generate-summary{gap:10px;flex-wrap:wrap;font-size:9px}.login-layout{display:flex;flex-direction:column}.login-story{padding:22px 25px;background:#f3f7ff}.login-copy{padding:26px 0 16px}.login-copy h1{font-size:28px;margin:13px 0}.login-copy h1 br:last-child{display:none}.login-copy>p{font-size:11px}.login-diagram,.login-benefits,.login-footer{display:none}.login-story .brand b{display:inline;font-size:14px;margin-left:3px}.login-story .brand small{font-size:8px}.login-story .brand-mark{width:33px;height:36px}.eyebrow{font-size:7px}.login-panel{padding:30px 25px}.login-form-wrap{width:100%;max-width:400px}.login-form-wrap h2{font-size:23px;margin-top:16px}.login-form-wrap>p{margin-bottom:22px}.login-form-wrap .login-bottom{margin-top:30px}}
|
||
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
|
||
@media(max-width:700px){.sidebar nav{flex-wrap:wrap;overflow:visible;align-items:flex-start}.sidebar nav>button{flex:1;justify-content:center}.sidebar nav .admin-menu{width:100%;margin:3px 0 0;padding:4px 0 0;border-top:1px solid var(--line)}.sidebar nav .admin-menu summary{padding:7px 10px;justify-content:flex-start;gap:8px;font-size:9px}.sidebar nav .admin-menu button{display:inline-flex;width:auto;padding:8px 10px;font-size:9px}.sidebar nav .admin-menu summary svg{width:12px;height:12px}}
|
||
/* Content library and format-specific previews. */
|
||
@media(max-width:600px){.overview-banner{display:grid!important;grid-template-columns:42px minmax(0,1fr);align-items:start!important}.overview-banner>div{min-width:0}.overview-banner>.status{grid-column:2;margin:0!important;justify-self:start}}
|
||
.overview-banner{display:flex;align-items:center;gap:20px;background:#edf4ff;border:1px solid #dfeaff;border-radius:14px;padding:26px 28px;margin-bottom:24px}.overview-symbol{display:grid;place-items:center;width:55px;height:55px;border-radius:15px;color:var(--blue);background:white;flex-shrink:0}.overview-symbol svg{width:27px;height:27px}.overview-banner h2{font-size:23px;margin:9px 0 6px}.overview-banner p{font-size:11px}.overview-banner>.status{margin-left:auto}.metric strong.date-metric{font-size:21px;letter-spacing:-.7px;margin-top:16px}.summary-metrics{margin-bottom:26px}.format-summary{display:flex;justify-content:center;flex-wrap:wrap;gap:28px;padding:22px 0 4px;font-size:10px;color:var(--muted)}.format-summary>span{display:flex;align-items:center;gap:8px}.format-summary b{color:var(--ink);margin-left:4px}.type-badge{display:inline-flex;align-items:center;gap:5px;padding:5px 7px;border-radius:5px;font-size:9px;font-weight:650;white-space:nowrap;background:#edf4ff;color:#4872bd}.type-badge svg{width:12px;height:12px}.type-badge.pdf{background:#fff0ee;color:#c77b72}.type-badge.scorm{background:#f1edfc;color:#8a6dc0}.type-badge.audio{background:#eaf6f3;color:#518e80}.content-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:18px;padding:22px}.content-card{border:1px solid var(--line);border-radius:11px;overflow:hidden;min-width:0;background:white}.content-cover{height:122px;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;background:#ecf3ff;color:#5d85c5}.content-cover>svg{width:30px;height:30px;stroke-width:1.3}.content-cover>span{font-size:11px;font-weight:650}.content-cover>small{position:absolute;top:11px;left:12px;font-size:7px;letter-spacing:1.2px;color:inherit;opacity:.65}.content-cover.pdf{background:#fbf1ef;color:#bd867d}.content-cover.audio{background:#edf7f4;color:#679b8d}.content-cover.scorm{background:#f2effa;color:#9a82bb}.content-card-body{padding:15px}.content-card-top{display:flex;align-items:center;justify-content:space-between;gap:5px;margin-bottom:12px}.content-card-top .status{font-size:8px;padding:5px 6px}.content-title{display:block;text-align:left;border:0;padding:0;background:none;font-weight:650;font-size:13px;line-height:1.55;letter-spacing:-.2px;min-height:61px;color:var(--ink);overflow-wrap:anywhere}.content-title:hover{color:var(--blue)}.content-card-body>p{font-size:9px;margin:9px 0 15px}.content-card-foot{border-top:1px solid var(--line);padding-top:13px;display:flex;align-items:center;justify-content:space-between;gap:8px}.content-card-foot>span{font-size:8px;color:#a0aabd}.content-card-foot .text-btn{font-size:10px}.content-card-foot svg{width:12px;height:12px}.supported-formats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:20px}.supported-formats>div{display:flex;align-items:center;gap:12px;border:1px solid var(--line);border-radius:10px;padding:17px;background:white}.supported-formats b{font-size:12px}.supported-formats small{display:block;font-size:9px;margin-top:6px}.format-icon{display:grid;place-items:center;width:38px;height:40px;border-radius:8px;background:#edf4ff;color:#5d85c5;flex-shrink:0}.format-icon.scorm{color:#8a6dc0;background:#f1edfc}.format-icon.audio{color:#518e80;background:#eaf6f3}.format-icon.pdf{color:#c77b72;background:#fff0ee}.file-icon.scorm{font-size:7px;background:#f1edfc;color:#8a6dc0}.file-icon.mp4,.file-icon.webm,.file-icon.mov,.file-icon.m4v{color:#5d85c5;background:#edf4ff}.file-icon.mp3,.file-icon.wav,.file-icon.m4a,.file-icon.ogg,.file-icon.aac,.file-icon.flac{background:#eaf6f3;color:#518e80}.preview-notice{display:flex;gap:9px;padding:12px 14px;background:#eef5ff;border-radius:7px;color:#6d86a8;font-size:11px;line-height:1.7;margin-bottom:18px}.preview-notice.concept{background:#fff8e9;color:#a88c55}.preview-notice svg{width:16px;height:16px;margin-top:2px}.storyboard-screen{padding:25px;background:#eef4ff;border:1px solid #dae7fa;border-radius:12px;min-height:290px;display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:center;color:#3f659c}.storyboard-screen>span{font-size:8px;letter-spacing:1.6px}.storyboard-screen>div{margin:21px 0 13px}.storyboard-screen svg{width:38px;height:38px;stroke-width:1.3}.storyboard-screen h2{font-size:22px}.storyboard-screen p{font-size:11px;max-width:460px;margin:13px 0;color:#7c92b1}.storyboard-screen>small{font-size:8px;margin-top:16px}.scene-list{display:flex;gap:8px;overflow:auto;padding:14px 0 6px}.scene-list button{padding:11px;text-align:left;flex:0 0 128px;font-size:9px;line-height:1.6;background:white;border:1px solid var(--line);border-radius:7px;color:#8b98ab}.scene-list button b{display:block;margin-bottom:6px;font-size:8px}.scene-list button.active{border-color:#a7c4fa;background:#f3f7ff;color:var(--blue)}.script-details>summary{font-size:12px;color:var(--blue);cursor:pointer;margin-bottom:20px}.audio-preview{text-align:center;padding:26px;background:#f1f8f6;border-radius:12px;border:1px solid #e0efea}.audio-orb{display:grid;place-items:center;background:white;color:#619d89;border-radius:50%;height:58px;width:58px;margin:0 auto 18px}.audio-orb svg{width:27px;height:27px}.waveform{display:flex;justify-content:center;gap:4px;height:60px;align-items:center;margin:0 auto 20px;max-width:100%;overflow:hidden}.waveform i{width:3px;border-radius:4px;flex-shrink:0;background:#9bc7b8}.audio-preview h2{font-size:20px}.audio-preview p{font-size:11px;margin:12px auto 20px}.audio-preview .head-actions{justify-content:center}.audio-preview>small{display:block;font-size:9px;margin-top:15px}.scorm-preview-head{display:flex;gap:11px;align-items:center;padding:15px;background:#f3effb;color:#9377b8;border-radius:8px;margin-bottom:23px;font-size:12px}.scorm-preview-head svg{width:26px;height:26px}.scorm-preview-head small{display:block;font-size:9px;margin-top:5px}.scorm-preview>h2{font-size:23px}.scorm-preview>p{font-size:11px;margin:10px 0 20px}.chapter{border:1px solid var(--line);border-radius:8px;margin:10px 0;overflow:hidden}.chapter summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:11px;padding:15px;font-size:12px;font-weight:600;background:#fafbfe}.chapter summary>span{font-size:9px;background:#f0edf8;color:#9a83ba;padding:7px;border-radius:5px}.chapter>p{font-size:12px;padding:17px;white-space:pre-wrap}.source-player{display:block;width:100%;max-height:350px;margin:0 auto 20px;border-radius:8px}.upload-issues{font-size:12px;line-height:1.9;color:#b47a6a}
|
||
@media(max-width:1250px){.content-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.content-title{min-height:42px}.supported-formats{gap:10px}.supported-formats>div{padding:12px;gap:8px}.supported-formats small{font-size:8px}.overview-banner h2{font-size:20px}}
|
||
@media(max-width:900px){.overview-banner{align-items:flex-start;flex-wrap:wrap;padding:22px}.overview-banner>.status{margin:0 0 0 75px}.supported-formats{grid-template-columns:repeat(2,1fr)}.toolbar-controls{flex-wrap:wrap!important;gap:8px!important}.toolbar-controls>.search{flex-basis:100%}.toolbar-controls>select{flex:1}.format-summary{gap:14px}}
|
||
@media(max-width:600px){.overview-banner{gap:14px;padding:19px}.overview-symbol{width:42px;height:44px}.overview-banner>div{flex:1}.overview-banner h2{font-size:19px;line-height:1.4}.overview-banner .eyebrow{font-size:6px;letter-spacing:1px}.overview-banner p{font-size:9px}.overview-banner>.status{margin:0 0 0 56px;font-size:9px}.content-grid{grid-template-columns:1fr;padding:16px;gap:17px}.content-cover{height:130px}.content-title{min-height:0;font-size:14px}.content-card-top .status{font-size:9px}.content-card-body{padding:17px}.format-summary{justify-content:flex-start}.format-summary>span{width:calc(50% - 14px);font-size:9px}.supported-formats>div{padding:12px}.supported-formats small{font-size:8px}.metric strong.date-metric{font-size:17px}.audio-preview{padding:22px 16px}.audio-preview .head-actions{flex-wrap:wrap}.audio-preview h2{font-size:19px}.storyboard-screen{min-height:290px;padding:24px 18px}.storyboard-screen h2{font-size:20px}.waveform{gap:3px}.upload-limit{line-height:1.9!important}.card-head{flex-wrap:wrap}.content-grid .text-btn{font-size:11px}}
|
||
/* Brain and the L&D assistant workflow. */
|
||
.journey-strip{display:flex;gap:15px;justify-content:space-between;flex-wrap:wrap;margin:0 0 24px;padding:0 3px;font-size:11px;color:#7a8ba5}.journey-strip>span{display:flex;align-items:center;gap:9px}.journey-strip b{display:grid;place-items:center;width:24px;height:24px;border-radius:50%;background:#eaf2ff;color:var(--blue);font-size:9px}.brain-banner{display:flex;align-items:center;gap:23px;padding:29px;background:#edf4ff;border:1px solid #dfeaff;border-radius:14px;margin-bottom:20px}.brain-symbol{height:76px;width:76px;border-radius:20px;background:white;color:var(--blue);display:grid;place-items:center;flex-shrink:0}.brain-symbol svg{width:42px;height:42px;stroke-width:1.25}.brain-banner h2{font-size:25px;margin:11px 0 9px}.brain-banner p{font-size:11px}.brain-formats{display:flex;gap:7px;margin-top:15px}.brain-context{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:22px 0}.brain-context>div{display:flex;align-items:center;gap:12px;padding:5px 3px;color:#7192be}.brain-context b{display:block;font-size:12px;color:#4a6285}.brain-context small{display:block;font-size:10px;margin-top:5px;line-height:1.7}.brain-selection{padding:13px 23px;background:#fbfcfe;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:15px;font-size:10px;color:var(--muted)}.brain-selection label{font-size:11px}.brain-topics{margin:26px 0 3px}.brain-topics h3{font-size:14px}.brain-topics p{font-size:10px;margin:7px 0 14px}.brain-topics>div{display:flex;flex-wrap:wrap;gap:7px}.upload-success{display:flex;align-items:center;gap:17px;padding:23px;margin-bottom:20px}.upload-success>span{padding:10px;background:#eaf6f2;color:#559a84;border-radius:50%}.upload-success h3{font-size:14px}.upload-success p{font-size:11px;margin-top:7px}.upload-success .btn{margin-left:auto}.ai-workspace{display:grid;grid-template-columns:1.05fr 1fr;gap:23px;align-items:start}.assistant-title{display:flex;gap:12px;align-items:center}.assistant-title>span{display:grid;place-items:center;width:38px;height:40px;border-radius:9px;background:#edf4ff;color:var(--blue)}.ai-messages{padding:24px 22px;max-height:450px;overflow:auto;min-height:215px}.ai-message{display:flex;align-items:flex-start;gap:10px;margin-bottom:18px}.ai-message:last-child{margin-bottom:0}.ai-message>span{display:grid;place-items:center;flex-shrink:0;width:25px;height:27px;border-radius:7px;background:#eaf2ff;color:#6f91c5}.ai-message svg{width:14px;height:14px}.ai-message p{background:#f5f8fd;border-radius:0 10px 10px;padding:14px;font-size:11px;line-height:1.85;color:#788da9;white-space:pre-wrap}.ai-message.user{flex-direction:row-reverse}.ai-message.user p{border-radius:10px 0 10px 10px;background:#eef4ff;color:#587cb2}.prompt-chips{display:flex;gap:7px;flex-wrap:wrap;padding:0 22px 20px}.prompt-chips button{font-size:9px;padding:8px 10px;border:1px solid #dfe9f8;border-radius:20px;background:white;color:#7390b7}.prompt-chips button:hover{background:#f0f6ff;color:var(--blue)}#ai-request-form{padding:20px 22px;border-top:1px solid var(--line);background:#fcfdff}#ai-request-form textarea{font-size:12px;min-height:110px}.ai-compose-footer{display:flex;gap:15px;align-items:center;justify-content:space-between;margin-top:14px}.ai-compose-footer small{font-size:9px;line-height:1.7}.ai-compose-footer .btn{font-size:11px;flex-shrink:0}.ai-disclosure{display:flex;align-items:flex-start;gap:8px;padding:17px 22px;font-size:9px;line-height:1.8;color:#9ba8bb;border-top:1px solid var(--line)}.ai-disclosure svg{width:13px;height:13px;margin-top:3px;flex-shrink:0}.ai-plan .card-body{padding:23px}.ai-plan label{font-size:11px}.ai-plan .field{margin-bottom:20px}.ai-plan .field-grid{gap:0 14px}.plan-source{display:flex;align-items:center;gap:10px;padding:14px;background:#f1f6ff;border-radius:8px;margin-bottom:21px}.plan-source>span{display:grid;place-items:center;width:33px;height:34px;border-radius:8px;background:white;color:#7c9bcc}.plan-source b{font-size:11px}.plan-source small{display:block;font-size:8px;line-height:1.6;margin-top:4px}.plan-source .text-btn{margin-left:auto;font-size:10px}.plan-actions{display:flex;gap:9px}.plan-actions .btn{flex:1;padding:10px;font-size:11px}.ai-plan .switch-row{padding:16px 0 20px;margin-top:0}.result-tabs{display:flex;gap:7px;border-bottom:1px solid #e5ebf4;margin-bottom:20px}.result-tabs button{background:none;border:0;border-bottom:2px solid transparent;font-size:12px;padding:11px 17px 14px;color:#92a0b6}.result-tabs button.active{color:var(--blue);border-color:var(--blue)}.result-tabs b{font-size:9px;background:#edf3ff;color:#85a0c7;padding:3px 5px;border-radius:4px;margin-left:5px}.research-list{padding:0 22px}.research-row{display:flex;align-items:center;gap:16px;padding:22px 0;border-bottom:1px solid var(--line)}.research-row:last-child{border-bottom:0}.research-row>span{display:grid;place-items:center;height:43px;width:43px;background:#f0f5fd;color:#819cc4;border-radius:9px;flex-shrink:0}.research-row h3{font-size:13px;margin-top:10px;line-height:1.6;overflow-wrap:anywhere}.research-row p{font-size:9px;margin-top:7px}.research-row>.btn{margin-left:auto;flex-shrink:0}.content-cover.text{background:#edf3fa;color:#849bb7}.type-badge.text{background:#edf3fa;color:#7993b7}.format-icon.text{background:#edf3fa;color:#849bb7}
|
||
@media(max-width:1100px){.ai-workspace{grid-template-columns:1fr}.ai-messages{min-height:150px}.ai-plan .field-grid{grid-template-columns:1fr 1fr}.brain-banner h2{font-size:22px}}
|
||
@media(max-width:700px){.sidebar nav{gap:4px}.sidebar nav>button{flex:1 0 auto;padding:9px;font-size:9px}.ai-workspace{gap:20px}.brain-context{grid-template-columns:1fr;gap:12px}.brain-selection{align-items:flex-start;flex-direction:column;gap:8px;padding:13px 17px}.brain-banner{padding:22px;gap:17px;align-items:flex-start}.brain-symbol{width:48px;height:50px;border-radius:12px}.brain-symbol svg{width:29px;height:29px}.brain-banner h2{font-size:21px;line-height:1.4}.brain-banner p{font-size:10px}.brain-banner .eyebrow{font-size:6px;letter-spacing:1.1px}.brain-formats{flex-wrap:wrap;gap:5px}.brain-banner>div{min-width:0}.journey-strip{display:grid;grid-template-columns:1fr 1fr;gap:14px;font-size:10px}.upload-success{flex-wrap:wrap;gap:13px;padding:20px}.upload-success>div{flex:1;min-width:180px}.upload-success .btn{width:100%;margin-left:0}.ai-plan .field-grid{grid-template-columns:1fr}.ai-messages{padding:20px 17px;max-height:340px}.ai-message p{font-size:11px;padding:12px}.prompt-chips{padding:0 17px 18px}#ai-request-form{padding:20px 17px}.ai-compose-footer{align-items:flex-start;flex-direction:column;gap:12px}.ai-compose-footer .btn{width:100%}.ai-disclosure{padding:17px}.plan-actions{flex-direction:column}.plan-actions .btn{width:100%}.ai-plan .card-body{padding:20px}.research-row{align-items:flex-start;flex-wrap:wrap;gap:12px}.research-row>div{flex:1;min-width:170px}.research-row>.btn{margin-left:55px}.research-list{padding:0 17px}.research-row h3{font-size:12px}.plan-source{gap:8px}.plan-source small{font-size:8px}.assistant-title{gap:8px}.assistant-title h3{font-size:13px}}
|
||
/* Three simple screens: upload, the combined Brain and AI workspace, results. */
|
||
.unified-workspace{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr);gap:22px;align-items:start}.compact-brain .card-head,.simple-ai .card-head{padding:19px 20px}.compact-brain .card-head>svg{color:#8ca6cd}.compact-brain .badge{margin-left:5px}.compact-brain-search{padding:17px 19px}.compact-brain-search .search{width:100%;max-width:none}.compact-brain .brain-selection{padding:12px 19px;font-size:9px}.compact-brain .brain-selection label{font-size:10px}.compact-source-list{max-height:450px;overflow:auto;padding:0 17px}.compact-source-row{display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--line);padding:15px 0;min-height:74px}.compact-source-row:last-child{border-bottom:0}.compact-source-row>input{margin:0;width:14px;height:14px}.compact-source-row .file-icon{width:30px;height:35px;font-size:7px;flex-shrink:0}.compact-source-name{min-width:0;flex:1}.compact-source-name b{display:block;font-size:11px;line-height:1.5;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.compact-source-name small{display:block;font-size:8px;margin-top:5px}.compact-source-row .icon-btn{padding:4px}.compact-source-row .icon-btn svg{width:14px;height:14px}.simple-ai #ai-request-form{padding:19px 20px;background:white;border:0}.simple-ai #ai-request-form textarea{min-height:85px;font-size:12px}.simple-ai .ai-compose-footer{margin-top:12px;gap:10px}.simple-ai .prompt-chips{padding:0;gap:6px}.simple-ai .prompt-chips button{padding:6px 9px;font-size:8px}.simple-ai .ai-compose-footer>.btn{min-height:32px;padding:7px 14px;font-size:10px}.compact-ai-reply{display:flex;gap:9px;padding:12px 15px;margin:0 20px 17px;background:#f3f7ff;border-radius:8px;color:#849cc0}.compact-ai-reply svg{width:14px;height:14px;margin-top:3px}.compact-ai-reply p{font-size:10px;line-height:1.8;color:#8195b3}.simple-plan{padding:17px 20px 18px;border-top:1px solid var(--line)}.simple-plan-row{display:grid;grid-template-columns:minmax(0,1fr) 100px;gap:13px}.simple-plan .field{margin-bottom:14px}.simple-plan label{font-size:10px}.simple-plan input:not([type=checkbox]),.simple-plan select,.simple-plan textarea{font-size:11px;min-height:38px;padding:10px 11px}.simple-plan #ai-count-help{font-size:8px;line-height:1.7;display:block;margin-top:-4px}.simple-options{margin:14px 0 0}.simple-options>summary{font-size:10px;color:#8a9cb7;cursor:pointer;padding:5px 0 11px}.simple-options[open]>summary{margin-bottom:7px}.simple-options small{font-size:8px}.simple-schedule-toggle{display:flex;justify-content:space-between;align-items:center;padding:13px 0 17px;border-top:1px solid #eff2f7;margin-top:6px}.simple-schedule-toggle label{font-size:11px;color:#647f9f}.simple-schedule-toggle>span{font-size:8px;color:#a3afbf}.simple-plan #schedule-fields{padding:0 0 7px}.simple-plan .field-grid{gap:13px}.simple-plan .plan-actions{gap:9px}.simple-plan .plan-actions .btn{font-size:11px;min-height:39px}.simple-saved-status{font-size:8px;color:#9aaac0;line-height:1.8;margin-top:12px}.simple-ai>.table-note,.compact-brain>.table-note{font-size:8px;line-height:1.8;padding:13px 19px}.sidebar .profile{margin-top:auto}
|
||
@media(max-width:1000px){.unified-workspace{grid-template-columns:1fr}.compact-source-list{max-height:280px}.compact-source-row{min-height:65px;padding:12px 0}.simple-ai .ai-compose-footer{flex-direction:row;align-items:center}.simple-plan-row{grid-template-columns:minmax(0,1fr) 110px}.simple-plan .field-grid{grid-template-columns:1fr 1fr}}
|
||
@media(max-width:700px){.sidebar nav>button{flex:1 0 0;justify-content:center}.unified-workspace{gap:18px}.compact-brain .card-head,.simple-ai .card-head{padding:17px}.compact-brain .brain-selection{flex-direction:row;gap:9px;padding:11px 17px}.compact-source-row{gap:8px}.compact-source-row .file-icon{width:27px;height:32px;font-size:6px}.compact-source-name b{font-size:10px}.compact-source-name small{font-size:7px}.simple-ai #ai-request-form{padding:17px}.simple-plan{padding:17px}.simple-ai .ai-compose-footer{align-items:flex-start;gap:8px}.simple-ai .ai-compose-footer>.btn{width:auto;flex-shrink:0}.simple-ai .prompt-chips{flex-direction:column;align-items:flex-start}.simple-plan-row{grid-template-columns:minmax(0,1fr) 90px}.simple-plan .plan-actions{flex-direction:row}.simple-plan .plan-actions .btn{font-size:10px;padding:9px 8px}.compact-ai-reply{margin-left:17px;margin-right:17px}.compact-ai-reply p{font-size:9px}.simple-plan .field-grid{grid-template-columns:1fr 1fr}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<section id="login-view" class="login-layout">
|
||
<div class="login-story">
|
||
<a class="brand" href="#" aria-label="Learning Lab Express"><span class="brand-mark" data-icon="book"></span><span>Learning Lab <b>Express</b><small>by Bawana</small></span></a>
|
||
<div class="login-copy"><span class="eyebrow">BELAJAR BARU, TANPA MULAI DARI NOL</span><h1>Pengetahuan tim.<br>Ide baru.<br>Pembelajaran tanpa henti.</h1><p>Satukan materi dalam Brain, lalu minta AI mengembangkannya menjadi banyak konten pelatihan dan rencana riset.</p>
|
||
<div class="login-diagram" aria-hidden="true"><div class="mini-source"><span class="file-icon pdf">PDF</span><div>Materi pelatihan<small>Knowledge base · Brain</small></div><span class="mini-check">✓</span></div><div class="connector"><i></i><span data-icon="spark"></span><i></i></div><div class="mini-output"><span class="output-icon" data-icon="book"></span><div>Konten pembelajaran baru<small>Terstruktur · Relevan · Siap ditinjau</small></div><span class="status ready">Baru</span></div></div>
|
||
<div class="login-benefits"><span><i data-icon="upload"></i> Text, video, audio, dan SCORM</span><span><i data-icon="clock"></i> Jadwalkan konten & riset</span><span><i data-icon="download"></i> Buka dan download hasilnya</span></div>
|
||
</div>
|
||
<div class="login-footer">LEARNING LAB EXPRESS <span>Pengetahuan Anda. Kemungkinan baru.</span></div>
|
||
</div>
|
||
<div class="login-panel"><div class="login-form-wrap"><span class="badge neutral">Prototipe interaktif</span><h2>Selamat datang kembali</h2><p>Masuk untuk mengelola materi dan membuat modul pelatihan.</p>
|
||
<form id="login-form"><label for="login-email">Email</label><input id="login-email" type="email" autocomplete="username" value="wulan@perusahaan.id" required>
|
||
<label for="login-password">Password</label><div class="password-field"><input id="login-password" type="password" autocomplete="off" value="demo12345" minlength="4" required><button type="button" data-action="toggle-password" aria-label="Tampilkan password"><span data-icon="eye"></span></button></div>
|
||
<div class="login-small"><label class="check-label"><input type="checkbox" checked> Ingat sesi demo ini</label><span>Mode demo</span></div>
|
||
<button class="btn primary wide" type="submit">Masuk ke Learning Lab <span data-icon="arrow-right"></span></button><p id="login-error" class="error" role="alert"></p></form>
|
||
<div class="demo-login"><span data-icon="info"></span><p>Akun contoh sudah terisi. Klik <b>Masuk</b> untuk mencoba alurnya. Login dan proses AI pada prototipe ini berupa simulasi.</p></div>
|
||
<p class="login-bottom">Learning Lab Express <span>by Bawana</span></p>
|
||
</div></div>
|
||
</section>
|
||
<div id="app" class="app" hidden>
|
||
<aside class="sidebar">
|
||
<a class="brand" href="#" data-nav="sources"><span class="brand-mark" data-icon="book"></span><span>Learning Lab <b>Express</b><small>by Bawana</small></span></a>
|
||
<div class="workspace"><span class="workspace-icon">L</span><div>Ruang belajar<small>Workspace perusahaan</small></div><span data-icon="chevron"></span></div>
|
||
<div class="nav-label">RUANG KERJA</div>
|
||
<nav aria-label="Navigasi utama">
|
||
<button data-nav="sources"><span data-icon="upload"></span>Upload materi</button>
|
||
<button data-nav="brain"><span data-icon="brain"></span>Brain & AI<span class="nav-count" id="source-count">0</span></button>
|
||
<button data-nav="results"><span data-icon="book"></span>Hasil<span class="nav-count" id="module-count">0</span></button>
|
||
</nav>
|
||
<div class="profile"><span class="avatar">W</span><div><b id="profile-name">Wulan</b><small>Ruang belajar · Demo</small></div><button data-action="logout" class="icon-btn" aria-label="Keluar"><span data-icon="logout"></span></button></div>
|
||
</aside>
|
||
<div class="workspace-main">
|
||
<header class="topbar"><div class="breadcrumb">Ruang kerja <span>/</span> <b id="breadcrumb">Upload materi</b></div><div class="topbar-right"><span class="demo-indicator"><i></i> Mode demo</span><span id="header-date"></span><button class="icon-btn" data-action="help" aria-label="Panduan"><span data-icon="help"></span></button></div></header>
|
||
<div class="demo-strip"><span data-icon="info"></span><span>Anda sedang mencoba prototipe. Proses AI dan jadwal disimulasikan; data tersimpan di browser ini.</span><span class="demo-strip-tag">DEMO</span></div>
|
||
<main id="view-content" tabindex="-1"></main>
|
||
<footer class="app-footer">Learning Lab Express <span>by Bawana</span><span>Ruang kerja untuk pengetahuan yang terus berkembang.</span></footer>
|
||
</div>
|
||
</div>
|
||
<dialog id="modal" aria-labelledby="modal-title"><div id="modal-content"></div></dialog>
|
||
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
||
<input id="file-input" type="file" accept=".zip,.mp4,.webm,.mov,.m4v,.mp3,.wav,.m4a,.ogg,.aac,.flac,.pdf,.docx,.pptx,.txt,.md" multiple hidden>
|
||
<script>(function(root){
|
||
const Core = {};
|
||
Core.escape=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||
const stop=new Set('yang dan untuk dengan dalam pada adalah ini itu dari atau sebagai dapat agar akan oleh ke di kita anda harus setiap materi panduan pelatihan modul proses suatu lebih melalui serta saat secara semua tersebut menjadi juga tentang the and with for this that from into your you are dalam menggunakan gunakan membantu memiliki merupakan sesuai berdasarkan'.split(' '));
|
||
function words(s){return [...new Set((String(s).toLowerCase().match(/[a-zà-ž]{4,}/g)||[]).filter(w=>!stop.has(w)))];}
|
||
Core.matchSources=function(sources){
|
||
const readable=sources.filter(s=>s.text?.trim()); const pairs=[];
|
||
for(let i=0;i<readable.length;i++)for(let j=i+1;j<readable.length;j++){
|
||
const a=readable[i],b=readable[j],aw=new Set(words(a.title+' '+a.text)),shared=words(b.title+' '+b.text).filter(w=>aw.has(w));
|
||
const tags=(a.tags||[]).filter(t=>(b.tags||[]).includes(t));
|
||
if(shared.length||tags.length)pairs.push({a:a.id,b:b.id,shared:[...new Set([...tags,...shared])],score:shared.length+tags.length*3});
|
||
}
|
||
return pairs.sort((a,b)=>b.score-a.score);
|
||
};
|
||
Core.autoSources=function(sources,preferredIDs=[],iteration=0){
|
||
const readable=sources.filter(s=>s.text?.trim());
|
||
const actual=readable.filter(s=>!s.sample),pool=actual.length?actual:readable;
|
||
if(!pool.length)return [];
|
||
const preferred=pool.filter(s=>preferredIDs.includes(s.id));
|
||
const pairs=Core.matchSources(pool).filter(p=>!preferred.length||preferredIDs.includes(p.a)||preferredIDs.includes(p.b));
|
||
if(pairs.length){const pair=pairs[iteration%pairs.length];return pool.filter(s=>s.id===pair.a||s.id===pair.b);}
|
||
return [preferred[0]||pool[iteration%pool.length]];
|
||
};
|
||
Core.contentType=function(source){
|
||
const name=typeof source==='string'?source:source.originalName||source.format||'';
|
||
const ext=name.toLowerCase().split('.').pop();
|
||
if(['zip','scorm'].includes(ext))return 'scorm';
|
||
if(['mp4','webm','mov','m4v','video'].includes(ext))return 'video';
|
||
if(['mp3','wav','m4a','ogg','aac','flac','audio'].includes(ext))return 'audio';
|
||
if(['pdf','docx','pptx','txt','md'].includes(ext))return 'pdf';
|
||
return null;
|
||
};
|
||
Core.contentJobs=function(sources,preferredIDs=[],iteration=0){
|
||
const actual=sources.filter(s=>!s.sample),pool=actual.length?actual:sources;
|
||
const groups=new Map();
|
||
pool.forEach(s=>{const type=Core.contentType(s);if(type){if(!groups.has(type))groups.set(type,[]);groups.get(type).push(s);}});
|
||
return [...groups].filter(([,group])=>!preferredIDs.length||group.some(s=>preferredIDs.includes(s.id))).map(([type,group])=>{
|
||
const preferred=group.filter(s=>preferredIDs.includes(s.id));
|
||
const anchor=preferred[0]||group[iteration%group.length];
|
||
const selected=anchor.text?.trim()?Core.autoSources(group.filter(s=>s.text?.trim()),[anchor.id],iteration):[anchor];
|
||
return {type,sources:selected};
|
||
});
|
||
};
|
||
Core.createContent=function(sources,settings={},iteration=0){
|
||
if(!sources.length)throw new Error('Tambahkan materi terlebih dahulu.');
|
||
const outputType=Core.contentType(sources[0]);
|
||
if(!outputType)throw new Error('Format materi belum didukung.');
|
||
const basis=sources.every(s=>s.text?.trim())?'text':'filename';
|
||
const prepared=sources.map(s=>s.text?.trim()?s:{...s,text:`Konsep pembelajaran untuk “${s.title}”. Ini contoh kerangka berdasarkan nama berkas; isi sumber belum dibaca. Tentukan tujuan belajar, rangkum konsep dari materi asli, lalu lengkapi contoh penerapan dan pertanyaan evaluasi setelah isi sumber tersedia.`});
|
||
const m=Core.createModule(prepared,settings,iteration);
|
||
return {...m,outputType,basis,notice:basis==='filename'?'Konsep demo berdasarkan nama berkas. Isi sumber belum dibaca atau dianalisis.':'Hasil simulasi dari teks materi. Tinjau sebelum digunakan.'};
|
||
};
|
||
Core.brainSources=function(sources){const own=sources.filter(s=>!s.sample);return own.length?own:sources;};
|
||
Core.planBrainBatch=function(sources,options={},iteration=0){
|
||
const count=Number(options.count??20);
|
||
if(!Number.isInteger(count)||count<1||count>50)throw new Error('Jumlah konten harus 1–50 per proses demo.');
|
||
const pool=Core.brainSources(sources).filter(s=>Core.contentType(s)&&(!options.sourceIDs?.length||options.sourceIDs.includes(s.id)));
|
||
if(!pool.length)throw new Error('Pilih materi yang masih tersedia di Brain.');
|
||
const formats=[...new Set(pool.map(s=>Core.contentType(s)==='pdf'?'text':Core.contentType(s)))];
|
||
const angles=['Panduan pemula','Studi kasus kerja','Checklist penerapan','Latihan pemahaman','Kesalahan yang perlu dihindari','Simulasi situasi kerja','Ringkasan konsep','Pertanyaan refleksi','Panduan diskusi tim','Tantangan praktik'];
|
||
return Array.from({length:count},(_,i)=>{const n=iteration+i,angle=angles[n%angles.length];return {type:formats[n%formats.length],sources:pool,angle,title:(options.topic||'Pembelajaran dari Brain')+': '+angle+' · '+(n+1)};});
|
||
};
|
||
Core.createBrainContent=function(job,settings={},iteration=0){
|
||
const m=Core.createContent(job.sources,{...settings,title:job.title},iteration);
|
||
m.outputType=job.type;m.angle=job.angle;
|
||
m.sections.push({title:'Fokus pengembangan: '+job.angle,body:'Gunakan konsep dari sumber-sumber di Brain untuk menyusun '+job.angle.toLowerCase()+'. Catat langkah, contoh, dan pertanyaan yang perlu ditinjau bersama pemilik materi. Kerangka ini merupakan simulasi dan perlu dikembangkan lebih lanjut.',sourceIds:m.sources.map(s=>s.id)});
|
||
if(m.basis==='filename')m.notice='Konsep demo dari Brain. Sebagian atau seluruh isi berkas belum dibaca; daftar sumber menunjukkan materi yang dipilih, bukan bukti analisis isi.';
|
||
return m;
|
||
};
|
||
Core.parseAIRequest=function(prompt,defaults={}){
|
||
const p=String(prompt).toLowerCase(),number=p.match(/(\d+)\s*(?:konten|materi|topik|ide|modul|draft)/),time=p.match(/(?:pukul|jam|at)\s*(\d{1,2})[:.](\d{2})/);
|
||
const research=/riset|research/.test(p),generate=/buat|generate|susun/.test(p);
|
||
const days=['minggu','senin','selasa','rabu','kamis','jumat','sabtu'];
|
||
const day=days.findIndex(d=>new RegExp(d==='minggu'?'setiap hari minggu':'setiap (?:hari )?'+d).test(p));
|
||
const frequency=/bulan/.test(p)?'monthly':/hari kerja/.test(p)?'workdays':day>=0||/mingguan|setiap minggu/.test(p)?'weekly':'daily';
|
||
return {...defaults,scope:/seluruh|semua/.test(p)?'all':defaults.scope||'all',prompt:String(prompt).trim(),count:number?Number(number[1]):/sebanyak/.test(p)?50:Number(defaults.count)||20,mode:research?(generate?'both':'research'):'generate',enabled:/setiap|jadwal|otomatis/.test(p),frequency,weekday:day>=0?day:Number(defaults.weekday??1),time:time?time[1].padStart(2,'0')+':'+time[2]:defaults.time||'09:00'};
|
||
};
|
||
Core.researchBrief=function(options,sources,iteration=0){
|
||
const topic=String(options.topic||'Pengembangan materi pelatihan'),angles=['Kebutuhan belajar','Praktik dan contoh penerapan','Perkembangan topik','Pertanyaan yang belum terjawab','Perbandingan pendekatan'];
|
||
const urls=String(options.researchURLs||'').split(/\s+/).filter(Boolean).filter(s=>{try{return ['https:','http:'].includes(new URL(s).protocol);}catch{return false;}});
|
||
return {id:'research-'+Date.now().toString(36)+'-'+iteration,title:topic+': '+angles[iteration%angles.length]+' · '+(iteration+1),topic,prompt:options.prompt||'',status:'planned',verified:false,findings:[],sources:sources.map(s=>({id:s.id,title:s.title})),queries:[topic+' '+angles[iteration%angles.length].toLowerCase(),topic+' studi kasus pelatihan'],requestedURLs:urls,notice:'Rencana riset demo. Pencarian web belum dijalankan dan belum ada temuan atau referensi terverifikasi.',createdAt:new Date().toISOString()};
|
||
};
|
||
Core.isSCORM=function(bytes){
|
||
const b=bytes instanceof Uint8Array?bytes:new Uint8Array(bytes),v=new DataView(b.buffer,b.byteOffset,b.byteLength);
|
||
for(let i=b.length-22;i>=Math.max(0,b.length-65557);i--){
|
||
if(v.getUint32(i,true)!==0x06054b50)continue;
|
||
let offset=v.getUint32(i+16,true);const count=v.getUint16(i+10,true);
|
||
for(let n=0;n<count;n++){
|
||
if(offset+46>b.length||v.getUint32(offset,true)!==0x02014b50)return false;
|
||
const length=v.getUint16(offset+28,true),extra=v.getUint16(offset+30,true),comment=v.getUint16(offset+32,true);
|
||
if(offset+46+length>b.length)return false;
|
||
if(new TextDecoder().decode(b.subarray(offset+46,offset+46+length)).toLowerCase()==='imsmanifest.xml')return true;
|
||
offset+=46+length+extra+comment;
|
||
}return false;
|
||
}return false;
|
||
};
|
||
Core.nextRun=function(schedule,now=new Date()){
|
||
const local=new Date(now.getTime()+7*3600000), y=local.getUTCFullYear(),m=local.getUTCMonth(),d=local.getUTCDate();
|
||
const [h,min]=(/^\d\d:\d\d$/.test(schedule.time||'')?schedule.time:'09:00').split(':').map(Number);
|
||
let out=new Date(Date.UTC(y,m,d,h-7,min));
|
||
const addDay=()=>out=new Date(out.getTime()+86400000);
|
||
if(schedule.frequency==='weekly'){
|
||
const weekday=Number(schedule.weekday??1),today=local.getUTCDay();
|
||
out=new Date(out.getTime()+((weekday-today+7)%7)*86400000);if(out<=now)out=new Date(out.getTime()+7*86400000);
|
||
}else if(schedule.frequency==='monthly'){
|
||
const day=Math.min(28,Math.max(1,Number(schedule.monthday)||1));out=new Date(Date.UTC(y,m,day,h-7,min));
|
||
if(out<=now)out=new Date(Date.UTC(y,m+1,day,h-7,min));
|
||
}else{
|
||
if(out<=now)addDay();
|
||
if(schedule.frequency==='workdays')while([0,6].includes(new Date(out.getTime()+7*3600000).getUTCDay()))addDay();
|
||
}
|
||
return out;
|
||
};
|
||
Core.createModule=function(sources,settings={},iteration=0){
|
||
const usable=sources.filter(s=>s.text?.trim());if(!usable.length)throw new Error('Tambahkan materi yang teksnya tersedia.');
|
||
const pair=Core.matchSources(usable)[0],shared=pair?.shared||[];
|
||
const topic=String(settings.topic||usable[0].title||shared[0]||'Pembelajaran terpadu').trim();
|
||
const single=usable.length===1;
|
||
const angles=['Panduan praktis','Dari konsep ke penerapan','Belajar melalui studi kasus'];
|
||
const title=String(settings.title||`${topic}: ${angles[iteration%angles.length]}`);
|
||
const audience=settings.audience||'Seluruh karyawan';
|
||
const refs=usable.map(s=>({id:s.id,title:s.title,format:s.format||'TXT',text:s.text.trim()}));
|
||
const sections=[{title:'Pengantar',body:`Modul ini disusun untuk ${audience.toLowerCase()} dengan ${single?'mengembangkan pembelajaran dari':'menghubungkan materi'} ${refs.map(r=>'“'+r.title+'”').join(' dan ')}. Fokus pembelajaran: ${topic.toLowerCase()}. Baca tiap bagian, pahami konsepnya, lalu gunakan latihan untuk merumuskan penerapan.`,sourceIds:refs.map(r=>r.id)}];
|
||
refs.forEach((r,i)=>sections.push({title:`${i+1}. ${r.title}`,body:r.text.slice(0,1600),sourceIds:[r.id]}));
|
||
sections.push({title:single?'Dari materi ke praktik':'Menghubungkan materi',body:single?`Pilih satu konsep penting dari “${refs[0].title}”. Jelaskan kembali dengan bahasa Anda sendiri, berikan contoh situasi kerja, lalu uraikan langkah penerapannya. Gunakan isi sumber sebagai acuan dan catat hal yang perlu dikonfirmasi kepada pemilik materi.`:shared.length?`Topik yang menghubungkan sumber ini adalah ${shared.slice(0,4).join(', ')}. Bandingkan konsep dari tiap sumber. Susun satu alur tindakan yang mengambil langkah relevan dari sumber-sumber tersebut, lalu catat informasi yang perlu dikonfirmasi kepada pemilik materi.`:'Bandingkan ruang lingkup tiap sumber. Catat hubungan yang dapat digunakan bersama dan konfirmasikan perbedaan kepada pemilik materi sebelum diterapkan.',sourceIds:refs.map(r=>r.id)});
|
||
if(settings.exercise!==false)sections.push({title:'Latihan penerapan',body:`Pilih satu situasi kerja yang berkaitan dengan ${topic.toLowerCase()}.\n1. Jelaskan masalah dan kebutuhan pihak yang terlibat.\n2. Ambil minimal satu prinsip dari setiap materi sumber.\n3. Susun tiga langkah tindakan dan alasan pemilihannya.\n4. Jelaskan cara mengevaluasi hasil tindakan tersebut.`,sourceIds:refs.map(r=>r.id)});
|
||
sections.push({title:'Ringkasan & refleksi',body:`Tuliskan tiga hal yang Anda pelajari dari ${topic.toLowerCase()}. Tentukan satu tindakan yang akan dicoba di pekerjaan dan sumber yang mendasarinya. Bila ada perbedaan informasi, gunakan catatan sumber sebagai bahan review bersama pemilik materi.`,sourceIds:refs.map(r=>r.id)});
|
||
const q1=refs[0].text.split(/(?<=[.!?])\s+/)[0];
|
||
const quiz=settings.quiz===false?[]:[
|
||
{q:`Pernyataan mana yang sesuai dengan materi “${refs[0].title}”?`,options:[q1,'Mengambil keputusan tanpa acuan materi.','Mengabaikan kebutuhan pihak terkait.','Mengikuti kebiasaan tanpa evaluasi.'],answer:0},
|
||
{q:single?'Apa tujuan mengembangkan materi menjadi modul pelatihan?':'Apa tujuan menggabungkan materi sumber dalam modul ini?',options:['Menghilangkan seluruh informasi asal.','Menghubungkan konsep untuk membantu penerapannya.','Mengganti ketentuan perusahaan secara otomatis.','Membuat semua sumber memiliki isi yang sama.'],answer:1},
|
||
{q:'Apa yang perlu dilakukan saat menemukan informasi yang belum jelas?',options:['Mengabaikan informasi tersebut.','Memilih informasi secara acak.','Mencatat dan mengonfirmasi kepada pemilik materi.','Menganggap seluruh informasi tidak berlaku.'],answer:2}
|
||
];
|
||
return {id:'mod-'+Date.now().toString(36)+'-'+iteration,title,topic,audience,duration:Number(settings.duration)||10,language:settings.language||'Bahasa Indonesia',tone:settings.tone||'Profesional & mudah dipahami',objectives:[`Menjelaskan konsep utama ${topic.toLowerCase()} dari materi sumber.`,`Menghubungkan prinsip dari ${refs.length} materi untuk kebutuhan ${audience.toLowerCase()}.`,'Menyusun langkah penerapan dan mengevaluasi hasilnya.'],sections,quiz,sources:refs.map(({text,...r})=>r),status:'draft',createdAt:new Date().toISOString(),origin:'manual',editedText:''};
|
||
};
|
||
Core.moduleText=function(m){
|
||
if(m.editedText)return m.title+'\n\n'+m.editedText;
|
||
const parts=[m.title,`Audiens: ${m.audience} | Estimasi: ${m.duration} menit`,`Tujuan pembelajaran\n${m.objectives.map((x,i)=>(i+1)+'. '+x).join('\n')}`,...m.sections.map(s=>s.title+'\n'+s.body)];
|
||
if(m.quiz.length)parts.push('Kuis pemahaman\n'+m.quiz.map((q,i)=>`${i+1}. ${q.q}\n${q.options.map((o,j)=>'ABCD'[j]+'. '+o).join('\n')}\nKunci: ${'ABCD'[q.answer]}`).join('\n\n'));
|
||
parts.push('Sumber materi\n'+m.sources.map((s,i)=>(i+1)+'. '+s.title).join('\n'),'Modul simulasi Learning Lab Express. Tinjau kembali sebelum digunakan.');return parts.join('\n\n');
|
||
};
|
||
Core.makeSCORM=function(m){
|
||
const E=Core.escape;
|
||
const manifest=`<?xml version="1.0" encoding="UTF-8"?><manifest identifier="LLE-DEMO" version="1.0" xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2"><metadata><schema>ADL SCORM</schema><schemaversion>1.2</schemaversion></metadata><organizations default="ORG"><organization identifier="ORG"><title>${E(m.title)}</title><item identifier="ITEM" identifierref="RES"><title>${E(m.title)}</title></item></organization></organizations><resources><resource identifier="RES" type="webcontent" adlcp:scormtype="sco" href="index.html"><file href="index.html"/></resource></resources></manifest>`;
|
||
const content='<!doctype html><html lang="id"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>'+E(m.title)+'</title><style>body{font:16px/1.8 system-ui;background:#f5f8fd;color:#20334e;margin:0}main{max-width:800px;margin:40px auto;padding:32px;background:white;border-radius:16px}h1,h2{line-height:1.3}p{white-space:pre-wrap}.note{padding:16px;background:#eef4ff;font-size:13px}button{padding:14px 22px;background:#2563eb;color:white;border:0;border-radius:8px;cursor:pointer}</style><main><small>LEARNING LAB EXPRESS · SCORM 1.2 · DEMO</small><h1>'+E(m.title)+'</h1><p class="note">'+E(m.notice||'Modul simulasi. Tinjau sebelum digunakan.')+'</p><h2>Tujuan belajar</h2><ol>'+m.objectives.map(o=>'<li>'+E(o)+'</li>').join('')+'</ol>'+(m.editedText?'<p>'+E(m.editedText)+'</p>':m.sections.map(s=>'<section><h2>'+E(s.title)+'</h2><p>'+E(s.body)+'</p></section>').join(''))+'<h2>Refleksi</h2><p>Tuliskan satu penerapan yang akan Anda coba setelah mempelajari materi ini.</p><h2>Sumber</h2><ul>'+m.sources.map(s=>'<li>'+E(s.title)+'</li>').join('')+'</ul><button id="finish">Selesaikan pembelajaran</button><p id="completion" role="status"></p></main><script>let api=null;try{let w=window;for(let i=0;i<10;i++){if(w.API){api=w.API;break}if(w.parent===w)break;w=w.parent}if(!api&&window.opener)api=window.opener.API;if(api){api.LMSInitialize("");if(["not attempted",""].includes(api.LMSGetValue("cmi.core.lesson_status")))api.LMSSetValue("cmi.core.lesson_status","incomplete")}}catch(e){}document.getElementById("finish").onclick=function(){try{if(api){api.LMSSetValue("cmi.core.lesson_status","completed");api.LMSCommit("")}}catch(e){}this.disabled=true;document.getElementById("completion").textContent="Pembelajaran selesai."};addEventListener("pagehide",()=>{try{if(api)api.LMSFinish("")}catch(e){}});<\/script></html>';
|
||
const files=[['imsmanifest.xml',manifest],['index.html',content]],enc=new TextEncoder(),parts=[],central=[];let offset=0;
|
||
const crc=data=>{let c=0xffffffff;for(const x of data){c^=x;for(let k=0;k<8;k++)c=(c>>>1)^((c&1)?0xedb88320:0);}return (c^0xffffffff)>>>0;};
|
||
for(const [name,text] of files){
|
||
const n=enc.encode(name),data=enc.encode(text),checksum=crc(data),header=new Uint8Array(30+n.length),h=new DataView(header.buffer);
|
||
h.setUint32(0,0x04034b50,true);h.setUint16(4,20,true);h.setUint16(6,0x800,true);h.setUint32(14,checksum,true);h.setUint32(18,data.length,true);h.setUint32(22,data.length,true);h.setUint16(26,n.length,true);header.set(n,30);
|
||
const c=new Uint8Array(46+n.length),v=new DataView(c.buffer);v.setUint32(0,0x02014b50,true);v.setUint16(4,20,true);v.setUint16(6,20,true);v.setUint16(8,0x800,true);v.setUint32(16,checksum,true);v.setUint32(20,data.length,true);v.setUint32(24,data.length,true);v.setUint16(28,n.length,true);v.setUint32(42,offset,true);c.set(n,46);
|
||
parts.push(header,data);central.push(c);offset+=header.length+data.length;
|
||
}
|
||
const end=new Uint8Array(22),ev=new DataView(end.buffer),size=central.reduce((n,p)=>n+p.length,0);ev.setUint32(0,0x06054b50,true);ev.setUint16(8,files.length,true);ev.setUint16(10,files.length,true);ev.setUint32(12,size,true);ev.setUint32(16,offset,true);
|
||
const out=new Uint8Array(offset+size+22);let cursor=0;for(const part of [...parts,...central,end]){out.set(part,cursor);cursor+=part.length;}return out;
|
||
};
|
||
Core.makePDF=function(text){
|
||
const clean=String(text).replace(/[“”]/g,'"').replace(/[‘’]/g,"'").replace(/[–—]/g,'-').replace(/•/g,'-').normalize('NFKD').replace(/[\u0300-\u036f]/g,'').replace(/[^\x20-\x7E\n\r\t]/g,'?');
|
||
const lines=[];const cw=c=>('ilI.,:;!|\'`'.includes(c)?2.5:('mwMW@'.includes(c)?9.2:(' '.includes(c)?3.05:5.7)));
|
||
for(const paragraph of clean.split('\n')){
|
||
if(!paragraph.trim()){lines.push('');continue;}let current='';
|
||
for(const word of paragraph.split(/\s+/)){
|
||
const pieces=word.match(/.{1,48}/g)||[''];for(const piece of pieces){const next=current?current+' '+piece:piece;
|
||
if([...next].reduce((a,c)=>a+cw(c),0)>485&¤t){lines.push(current);current=piece;}else current=next;
|
||
}
|
||
}if(current)lines.push(current);
|
||
}
|
||
const pages=[];for(let i=0;i<lines.length;i+=46)pages.push(lines.slice(i,i+46));if(!pages.length)pages.push(['']);
|
||
const objects=[];objects[1]='<< /Type /Catalog /Pages 2 0 R >>';objects[3]='<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>';
|
||
const kids=[];const esc=s=>s.replace(/([\\()])/g,'\\$1');
|
||
pages.forEach((page,i)=>{const pageID=4+i*2,streamID=pageID+1;kids.push(pageID+' 0 R');
|
||
const commands=['BT /F1 11 Tf 15 TL 52 790 Td',...page.flatMap((s,j)=>[(j?'T* ':'')+'('+esc(s)+') Tj']),'ET',`BT /F1 8 Tf 52 35 Td (Learning Lab Express - Modul simulasi | ${i+1}/${pages.length}) Tj ET`].join('\n');
|
||
objects[pageID]=`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595.28 841.89] /Resources << /Font << /F1 3 0 R >> >> /Contents ${streamID} 0 R >>`;
|
||
objects[streamID]=`<< /Length ${commands.length} >>\nstream\n${commands}\nendstream`;
|
||
});
|
||
objects[2]=`<< /Type /Pages /Count ${pages.length} /Kids [${kids.join(' ')}] >>`;
|
||
let result='%PDF-1.4\n';const offsets=[0];for(let i=1;i<objects.length;i++){offsets[i]=result.length;result+=i+' 0 obj\n'+objects[i]+'\nendobj\n';}
|
||
const xref=result.length;result+=`xref\n0 ${objects.length}\n0000000000 65535 f \n`;
|
||
for(let i=1;i<objects.length;i++)result+=String(offsets[i]).padStart(10,'0')+' 00000 n \n';
|
||
result+=`trailer\n<< /Size ${objects.length} /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF`;
|
||
return Uint8Array.from(result,c=>c.charCodeAt(0));
|
||
};
|
||
if(typeof module!=='undefined' && module.exports) module.exports=Core;
|
||
root.ContentCore=Core;
|
||
})(typeof globalThis!=='undefined'?globalThis:this);
|
||
</script>
|
||
<script>(function(){
|
||
'use strict';
|
||
const C=window.ContentCore,E=C.escape,$=s=>document.querySelector(s),KEY='learning-lab-express-v1';
|
||
const icons={
|
||
book:'<path d="M3 5c3-1 6 0 9 2 3-2 6-3 9-2v14c-3-1-6 0-9 2-3-2-6-3-9-2z"/><path d="M12 7v14M6 9l3 1M15 10l3-1"/>',
|
||
home:'<path d="m3 10 9-7 9 7v10H3z"/><path d="M9 20v-7h6v7"/>',
|
||
folder:'<path d="M3 6h6l2 2h10v12H3z"/><path d="M3 6V4h6l2 2h9v2"/>',
|
||
spark:'<path d="m12 3 2.5 6.5L21 12l-6.5 2.5L12 21l-2.5-6.5L3 12l6.5-2.5z"/><path d="m20 2 .5 1.5L22 4l-1.5.5L20 6l-.5-1.5L18 4l1.5-.5z"/>',
|
||
calendar:'<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M7 3v4M17 3v4M3 11h18M7 15h3M14 15h3"/>',
|
||
clock:'<circle cx="12" cy="12" r="9"/><path d="M12 7v6l4 2"/>',
|
||
layers:'<path d="m12 3 10 5-10 5L2 8zM2 12l10 5 10-5M2 16l10 5 10-5"/>',
|
||
download:'<path d="M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5"/>',
|
||
upload:'<path d="M12 16V4m-5 5 5-5 5 5M4 16v5h16v-5"/>',
|
||
file:'<path d="M5 3h9l5 5v13H5zM14 3v6h5M8 13h8M8 17h6"/>',
|
||
'arrow-right':'<path d="M4 12h16m-6-6 6 6-6 6"/>',
|
||
chevron:'<path d="m7 10 5 5 5-5"/>',
|
||
search:'<circle cx="10" cy="10" r="6"/><path d="m15 15 5 5"/>',
|
||
eye:'<path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/>',
|
||
trash:'<path d="M3 6h18M9 3h6l1 3M6 6l1 15h10l1-15M10 10v7M14 10v7"/>',
|
||
close:'<path d="m6 6 12 12M18 6 6 18"/>',
|
||
info:'<circle cx="12" cy="12" r="9"/><path d="M12 11v6M12 7v.2"/>',
|
||
help:'<circle cx="12" cy="12" r="9"/><path d="M9 9a3 3 0 0 1 6 0c0 2-3 2-3 4M12 17v.2"/>',
|
||
check:'<path d="m5 12 4 4L19 6"/>',
|
||
edit:'<path d="m15 4 5 5M4 20l5-1L21 7l-4-4L5 15z"/>',
|
||
logout:'<path d="M10 4H4v16h6M10 12h11m-4-4 4 4-4 4"/>',
|
||
plus:'<path d="M12 4v16M4 12h16"/>'
|
||
};
|
||
icons.video='<rect x="3" y="5" width="18" height="14" rx="3"/><path d="m10 9 5 3-5 3z"/>';
|
||
icons.audio='<path d="M4 13v-2a8 8 0 0 1 16 0v2"/><rect x="3" y="11" width="4" height="8" rx="2"/><rect x="17" y="11" width="4" height="8" rx="2"/>';
|
||
icons.scorm='<path d="m12 3 9 5-9 5-9-5zM3 8v9l9 5 9-5V8M12 13v9"/>';
|
||
const types={pdf:{label:'PDF',icon:'file',hint:'Dokumen pembelajaran',file:'PDF'},video:{label:'Video',icon:'video',hint:'Video pembelajaran',file:'MP4 / WEBM'},audio:{label:'Audio',icon:'audio',hint:'Audio pembelajaran',file:'MP3 / WAV'},scorm:{label:'SCORM',icon:'scorm',hint:'Modul interaktif',file:'ZIP'}};
|
||
types.text={label:'Text',icon:'file',hint:'Artikel, modul & panduan',file:'PDF / DOCX / PPTX / TXT'};
|
||
const visibleTypes=['text','video','audio','scorm'];
|
||
icons.brain='<path d="M12 5c-3-4-8-1-7 3-4 2-3 7 0 8-1 4 5 6 7 2M12 5c3-4 8-1 7 3 4 2 3 7 0 8 1 4-5 6-7 2M12 5v15M7 9l3 2M17 9l-3 2M6 15l3-1M18 15l-3-1"/>';
|
||
const sourceURLs=new Map();
|
||
function I(name){return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'+(icons[name]||icons.file)+'</svg>';}
|
||
function fillIcons(root=document){root.querySelectorAll('[data-icon]').forEach(el=>el.innerHTML=I(el.dataset.icon));}
|
||
const now=()=>new Date().toISOString();
|
||
function sampleSources(){
|
||
return [
|
||
{id:'sample-service',title:'Panduan Layanan Pelanggan',format:'PDF',tags:['layanan','pelanggan','empati'],sample:true,createdAt:now(),text:'Mulai layanan dengan menyapa pelanggan secara ramah dan memperkenalkan diri. Dengarkan kebutuhan pelanggan tanpa menyela, lalu konfirmasikan kembali kebutuhan tersebut dengan bahasa yang jelas.\n\nGunakan empati untuk memahami sudut pandang pelanggan. Jelaskan pilihan solusi secara terbuka dan hindari menjanjikan sesuatu di luar kewenangan. Jika perlu eskalasi, sampaikan pihak yang akan membantu dan perkiraan tindak lanjut sesuai prosedur perusahaan.\n\nTutup interaksi dengan merangkum solusi dan memastikan pelanggan memahami langkah berikutnya. Dokumentasikan kebutuhan, solusi, serta tindak lanjut agar kualitas layanan tetap konsisten.'},
|
||
{id:'sample-communication',title:'Komunikasi Efektif di Tempat Kerja',format:'MP4',tags:['komunikasi','pelanggan','empati'],sample:true,createdAt:now(),text:'Komunikasi efektif dimulai dengan mendengarkan secara aktif. Fokus pada pesan yang disampaikan dan ajukan pertanyaan terbuka untuk memperjelas kebutuhan pelanggan maupun rekan kerja.\n\nGunakan bahasa yang ringkas, jelas, dan sesuai dengan penerima pesan. Tunjukkan empati melalui respons yang menghargai perasaan lawan bicara. Hindari jargon dan pastikan pesan penting dipahami dengan meminta konfirmasi.\n\nDalam komunikasi tertulis, jelaskan tujuan, konteks, tindakan yang dibutuhkan, dan batas waktu secara berurutan. Setelah diskusi, rangkum kesepakatan dan pemilik tindak lanjut agar tidak terjadi perbedaan pemahaman.'},
|
||
{id:'sample-complaints',title:'Menangani Keluhan dengan Empati',format:'MP3',tags:['keluhan','pelanggan','empati'],sample:true,createdAt:now(),text:'Saat menerima keluhan pelanggan, dengarkan hingga selesai dan jaga sikap tenang. Sampaikan bahwa Anda memahami ketidaknyamanan yang dialami, lalu klarifikasi fakta tanpa menyalahkan pihak lain.\n\nCatat kebutuhan pelanggan, dampak masalah, dan solusi yang diharapkan. Periksa informasi yang tersedia sebelum menawarkan solusi. Komunikasi harus jelas dan berempati; jangan menjanjikan keputusan yang berada di luar kewenangan Anda.\n\nJika masalah membutuhkan penanganan lanjutan, eskalasikan sesuai prosedur perusahaan dan berikan pembaruan secara berkala. Dokumentasikan penyelesaian serta pembelajaran dari keluhan untuk memperbaiki kualitas layanan.'},
|
||
{id:'sample-scorm',title:'Dasar Layanan Prima',format:'SCORM',tags:['layanan','pelanggan'],sample:true,createdAt:now(),text:'Kenali kebutuhan pelanggan sebelum memberikan solusi. Dengarkan secara aktif dan gunakan bahasa yang jelas. Konfirmasikan tindak lanjut dan dokumentasikan hasil layanan. Lengkapi pembelajaran dengan latihan situasi kerja dan evaluasi pemahaman.'},
|
||
{id:'sample-omnichannel',title:'Studi Kasus Layanan Omnichannel',format:'TXT',tags:['layanan','pelanggan','komunikasi'],sample:true,createdAt:now(),text:'Pastikan informasi pelanggan konsisten saat berpindah dari kanal chat ke telepon. Rangkum kebutuhan dan tindak lanjut agar pelanggan tidak perlu mengulang penjelasan. Gunakan komunikasi yang jelas, empati, dan dokumentasi layanan untuk koordinasi antar tim.'}
|
||
];
|
||
}
|
||
function fresh(){return {schema:4,user:null,remember:true,sources:sampleSources(),modules:[],settings:{topic:'',audience:'Seluruh karyawan',duration:10,tone:'Profesional & mudah dipahami',language:'Bahasa Indonesia',exercise:true,quiz:true,saved:false},automation:{count:20,mode:'generate',topic:'',prompt:'',researchURLs:'',scope:'all',sourceIDs:[]},chat:[],research:[],lastUploadIDs:[],schedule:{enabled:false,frequency:'daily',time:'09:00',weekday:1,monthday:1,nextRun:null,lastRun:null}};}
|
||
let state;try{const saved=JSON.parse(localStorage.getItem(KEY)||'null');state=saved&&Array.isArray(saved.sources)&&Array.isArray(saved.modules)?Object.assign(fresh(),saved):fresh();if(saved)state.schema=saved.schema||1;}catch{state=fresh();}
|
||
if(state.schema<2){state.schedule=fresh().schedule;if(!state.settings.saved)state.settings.topic='';}
|
||
if(state.schema<3){sampleSources().forEach(sample=>{const old=state.sources.find(s=>s.id===sample.id&&s.sample);if(old)old.format=sample.format;else if(!state.sources.some(s=>s.id===sample.id))state.sources.push(sample);});state.schema=3;}
|
||
if(state.schema<4){sampleSources().forEach(sample=>{if(!state.sources.some(s=>s.id===sample.id))state.sources.push(sample);});state.schema=4;}
|
||
state.automation=Object.assign(fresh().automation,state.automation||{});state.chat=state.chat||[];state.research=state.research||[];
|
||
state.modules.forEach(m=>{m.outputType=m.outputType==='pdf'||!m.outputType?'text':m.outputType;m.basis=m.basis||'text';});
|
||
if(state.schedule.enabled&&!state.schedule.nextRun)state.schedule.nextRun=C.nextRun(state.schedule).toISOString();
|
||
let view='sources',sourceQuery='',resultQuery='',resultStatus='all',resultType='all',resultTab='content',selectedBrain=new Set(state.automation.scope==='selected'?state.automation.sourceIDs:[]),aiDraft=null,busy=false,previewID=null,toastTimer;
|
||
function persist(){try{localStorage.setItem(KEY,JSON.stringify(Object.assign({},state,{user:state.remember?state.user:null})));}catch{toast('Penyimpanan browser penuh. Perubahan tersedia selama halaman ini terbuka.',true);}}
|
||
function toast(message,error=false){const t=$('#toast');t.textContent=message;t.className='toast show'+(error?' error':'');clearTimeout(toastTimer);toastTimer=setTimeout(()=>t.classList.remove('show'),4200);}
|
||
const names={sources:'Upload materi',brain:'Brain & AI',results:'Hasil'};
|
||
function typeOf(m){const t=m.outputType||C.contentType(m)||'text';return t==='pdf'?'text':t;}
|
||
function brainSources(){return C.brainSources(state.sources);}
|
||
function typeBadge(type){return '<span class="type-badge '+type+'">'+I(types[type].icon)+types[type].label+'</span>';}
|
||
function readable(){return state.sources.filter(s=>s.text&&s.text.trim());}
|
||
function dt(value,long=false){try{return new Intl.DateTimeFormat('id-ID',{dateStyle:long?'full':'medium',...(long?{timeStyle:'short'}:{}),timeZone:'Asia/Jakarta'}).format(new Date(value));}catch{return '-';}}
|
||
function bytes(n){return n>1048576?(n/1048576).toFixed(1)+' MB':Math.max(1,Math.round(n/1024))+' KB';}
|
||
function fileIcon(s){return '<span class="file-icon '+E(s.format.toLowerCase())+'">'+E(s.format)+'</span>';}
|
||
function status(m){return '<span class="status '+(m.status==='ready'?'ready':'draft')+'"><i class="dot"></i>'+(m.status==='ready'?'Sudah ditinjau':m.basis==='filename'?'Konsep demo':'Draft')+'</span>';}
|
||
function button(text,action,style='primary',icon='plus',extra=''){return '<button type="button" class="btn '+style+'" data-action="'+action+'" '+extra+'>'+I(icon)+text+'</button>';}
|
||
function head(title,description,actions=''){return '<div class="page-head"><div><h1>'+title+'</h1><p>'+description+'</p></div><div class="head-actions">'+actions+'</div></div>';}
|
||
function empty(title,text,action='',label='Buat modul baru'){return '<div class="empty"><span class="empty-icon">'+I('book')+'</span><h3>'+title+'</h3><p>'+text+'</p>'+(action?button(label,action,'light','plus'):'')+'</div>';}
|
||
function render(){
|
||
$('#login-view').hidden=!!state.user;$('#app').hidden=!state.user;fillIcons();
|
||
if(!state.user)return;
|
||
$('#profile-name').textContent=state.user.name||'Wulan';
|
||
$('#header-date').textContent=dt(new Date());
|
||
$('#breadcrumb').textContent=names[view];
|
||
$('#source-count').textContent=brainSources().length;$('#module-count').textContent=state.modules.length;
|
||
document.querySelectorAll('nav [data-nav]').forEach(b=>{b.classList.toggle('active',b.dataset.nav===view);b.setAttribute('aria-current',b.dataset.nav===view?'page':'false');});
|
||
$('#view-content').innerHTML=({sources:sourcesView,brain:brainView,results:resultsView}[view])();
|
||
fillIcons($('#view-content'));bindView();
|
||
}
|
||
function go(next){stopSpeech();view=['sources','brain','results'].includes(next)?next:'brain';render();window.scrollTo({top:0,behavior:'instant'});}
|
||
function heroArt(){return '<div class="hero-art" aria-hidden="true"><div class="mini-source"><span class="file-icon pdf">PDF</span><div>Materi layanan pelanggan<small>Perpustakaan materi</small></div></div><div class="mini-source"><span class="file-icon pptx">PPT</span><div>Komunikasi efektif<small>Topik yang saling berkaitan</small></div></div><div class="connector">'+I('spark')+'</div><div class="mini-output"><span class="output-icon">'+I('book')+'</span><div>Modul pelatihan baru<small>Lengkap dengan latihan & kuis</small></div><span class="status ready">Draft</span></div></div>';}
|
||
function scheduleLabel(){return ({daily:'Setiap hari',workdays:'Setiap hari kerja',weekly:'Setiap minggu',monthly:'Setiap bulan'}[state.schedule.frequency]||'Setiap hari')+' · '+state.schedule.time.replace(':','.')+' WIB';}
|
||
function pairCard(pair){
|
||
const a=state.sources.find(s=>s.id===pair.a),b=state.sources.find(s=>s.id===pair.b);
|
||
return '<div class="card match-card"><div class="pair-names"><span>'+I('file')+E(a.title)+'</span><span>'+I('file')+E(b.title)+'</span></div><div class="match-tags">'+pair.shared.slice(0,3).map(t=>'<span class="tag">'+E(t)+'</span>').join('')+'</div><div class="match-footer"><small>Topik berkaitan · 2 sumber</small><button class="text-btn" data-action="pair" data-pair="'+E(pair.a+','+pair.b)+'">Buat modul '+I('arrow-right')+'</button></div></div>';
|
||
}
|
||
function contentCard(m){
|
||
const type=typeOf(m);
|
||
return '<article class="content-card"><div class="content-cover '+type+'">'+I(types[type].icon)+'<span>'+types[type].label+'</span><small>'+ (m.sample?'CONTOH':m.basis==='filename'?'KONSEP DEMO':'DRAFT BARU')+'</small></div><div class="content-card-body"><div class="content-card-top">'+typeBadge(type)+status(m)+'</div><button class="content-title" data-action="preview" data-id="'+E(m.id)+'">'+E(m.title)+'</button><p>'+m.sources.length+' materi sumber · '+E(dt(m.createdAt))+'</p><div class="content-card-foot"><span>'+(m.origin==='scheduled'?'Terjadwal':m.origin==='sample'?'Contoh':'Permintaan AI')+'</span><button class="text-btn" data-action="preview" data-id="'+E(m.id)+'">Buka konten '+I('arrow-right')+'</button></div></div></article>';
|
||
}
|
||
function sourceRows(){
|
||
const list=brainSources().filter(s=>(s.title+' '+(s.tags||[]).join(' ')).toLowerCase().includes(sourceQuery.toLowerCase()));
|
||
if(!list.length)return empty('Belum ada materi yang cocok','Coba kata kunci lain atau tambahkan materi.');
|
||
return list.map(s=>'<div class="compact-source-row"><input class="brain-source-check" type="checkbox" aria-label="Pilih '+E(s.title)+'" value="'+E(s.id)+'" '+(selectedBrain.has(s.id)?'checked':'')+'>'+fileIcon(s)+'<div class="compact-source-name"><b title="'+E(s.title)+'">'+E(s.title)+'</b><small>'+E(s.format)+' · '+(s.sample?'Contoh':s.text?'Teks tersedia':'Belum dianalisis')+'</small></div><button class="icon-btn" data-action="source-preview" data-id="'+E(s.id)+'" aria-label="Lihat '+E(s.title)+'">'+I('eye')+'</button><button class="icon-btn" data-action="source-delete" data-id="'+E(s.id)+'" aria-label="Hapus '+E(s.title)+'">'+I('trash')+'</button></div>').join('');
|
||
}
|
||
function sourcesView(){
|
||
const last=(state.lastUploadIDs||[]).map(id=>state.sources.find(s=>s.id===id)).filter(Boolean);
|
||
return head('Upload materi','Tambahkan pengetahuan ke Brain. Anda bisa mengupload beberapa berkas sekaligus.')+
|
||
'<div id="drop-zone" class="upload-box"><span class="upload-symbol">'+I('upload')+'</span><h3>Mulai dari materi yang sudah Anda miliki</h3><p>Upload satu atau beberapa materi. Semuanya akan tersimpan di Brain.</p>'+button('Pilih berkas','upload','primary','plus')+'<p class="upload-limit">PDF, DOCX, PPTX, TXT, MD · MP4, WEBM, MOV · MP3, WAV, M4A, OGG, AAC, FLAC · SCORM ZIP<br>Maks. 200 MB per berkas · SCORM ZIP memiliki imsmanifest.xml</p></div>'+
|
||
(last.length?'<section class="card upload-success"><span>'+I('check')+'</span><div><h3>'+last.length+' materi ditambahkan ke Brain</h3><p>Materi tersimpan sebagai sumber pengetahuan. Pembuatan konten dimulai saat Anda meminta AI.</p></div><button class="btn primary" data-nav="brain">Lihat Brain '+I('arrow-right')+'</button></section>':'')+
|
||
'<div class="notice-row">'+I('info')+'<span>Demo membaca TXT/MD. Berkas lainnya terdaftar di Brain, tetapi isinya belum dianalisis.</span><button class="text-btn" data-action="load-brain-demo">Coba Brain dengan 5 contoh</button></div>';
|
||
}
|
||
function brainView(){
|
||
const pool=brainSources();selectedBrain=new Set([...selectedBrain].filter(id=>pool.some(s=>s.id===id)));
|
||
if(!aiDraft)aiDraft={...state.automation,...state.schedule,scope:selectedBrain.size?'selected':'all',sourceIDs:[...selectedBrain]};
|
||
if(aiDraft.scope==='all')selectedBrain.clear();else{aiDraft.sourceIDs=[...selectedBrain];if(!selectedBrain.size)aiDraft.scope='all';}
|
||
const count=selectedBrain.size||pool.length;
|
||
return head('Brain & AI','Pilih materi, tulis kebutuhan, lalu buat konten atau atur jadwal di sini.')+
|
||
'<div class="unified-workspace"><section class="card compact-brain"><div class="card-head"><div><h3>Brain <span class="badge neutral">'+pool.length+' materi</span></h3><p>Knowledge base tim Anda.</p></div>'+I('brain')+'</div><div class="compact-brain-search"><div class="search">'+I('search')+'<input id="source-search" aria-label="Cari materi di Brain" placeholder="Cari materi..." value="'+E(sourceQuery)+'"></div></div><div class="brain-selection"><label class="check-label"><input id="brain-select-all" type="checkbox" '+(pool.length&&pool.every(s=>selectedBrain.has(s.id))?'checked':'')+'> Pilih semua</label><span id="brain-selection-label">'+(selectedBrain.size?selectedBrain.size+' dipilih':'Seluruh Brain digunakan')+'</span></div><div id="source-rows" class="compact-source-list">'+sourceRows()+'</div><div class="table-note">'+(pool.some(s=>!s.sample)?'Sumber pilihan menjadi acuan AI.':'Anda sedang memakai materi contoh.')+' Kosongkan pilihan untuk seluruh Brain.</div></section>'+assistantView(count)+'</div>';
|
||
}
|
||
function assistantView(count){
|
||
const o=aiDraft||{...state.automation,...state.schedule},saved=state.schedule.plan||state.automation;
|
||
const message=[...state.chat].reverse().find(m=>m.role==='assistant');
|
||
return '<section class="card simple-ai"><div class="card-head"><div class="assistant-title"><span>'+I('spark')+'</span><div><h3>AI & jadwal</h3><p id="plan-source-label">'+count+' materi menjadi sumber AI</p></div></div><span class="badge blue">Demo</span></div><form id="ai-request-form"><label for="ai-prompt">Apa yang ingin dibuat?</label><textarea id="ai-prompt" rows="3" maxlength="1600" required placeholder="Buat 20 konten dari materi ini setiap hari pukul 09.00...">'+E(o.prompt||'')+'</textarea><div class="ai-compose-footer"><div class="prompt-chips"><button type="button" data-action="prompt-example" data-prompt="Buat 20 konten dari seluruh Brain sekarang">20 konten</button><button type="button" data-action="prompt-example" data-prompt="Riset 5 topik setiap Senin pukul 08.00">Riset mingguan</button></div><button class="btn light" type="submit">Terapkan</button></div></form>'+
|
||
(message?'<div class="compact-ai-reply">'+I('spark')+'<p>'+E(message.text)+'</p></div>':'')+
|
||
'<form id="schedule-form" class="simple-plan"><select id="ai-scope" hidden><option value="all" '+(o.scope!=='selected'?'selected':'')+'>Seluruh Brain</option><option value="selected" '+(o.scope==='selected'?'selected':'')+'>Materi terpilih</option></select><div class="simple-plan-row"><div class="field"><label for="ai-mode">Aktivitas</label><select id="ai-mode">'+[['generate','Buat konten'],['research','Riset topik'],['both','Konten + riset']].map(([v,l])=>'<option value="'+v+'" '+(o.mode===v?'selected':'')+'>'+l+'</option>').join('')+'</select></div><div class="field"><label for="ai-count">Jumlah</label><input id="ai-count" type="number" min="1" max="50" step="1" required value="'+o.count+'"></div></div><small id="ai-count-help">1–50 per proses demo. '+(o.mode==='both'?'Untuk konten dan brief masing-masing.':'')+'</small><details class="simple-options"><summary>Opsi tambahan</summary><div class="field"><label for="ai-topic">Fokus topik (opsional)</label><input id="ai-topic" maxlength="100" value="'+E(o.topic||'')+'" placeholder="Contoh: layanan pelanggan"></div><div id="research-options" '+(o.mode==='generate'?'hidden':'')+'><div class="field"><label for="research-urls">Referensi riset (opsional)</label><textarea id="research-urls" rows="2" maxlength="1200" placeholder="https://...">'+E(o.researchURLs||'')+'</textarea><small>Tautan belum diakses dalam demo.</small></div></div></details><div class="simple-schedule-toggle"><label class="check-label"><input id="schedule-enabled" type="checkbox" aria-label="Jadwalkan otomatis" '+(o.enabled?'checked':'')+'> Jadwalkan otomatis</label><span>WIB</span></div><div id="schedule-fields" '+(o.enabled?'':'hidden')+'><div class="field-grid"><div class="field"><label for="schedule-frequency">Frekuensi</label><select id="schedule-frequency">'+[['daily','Setiap hari'],['workdays','Hari kerja'],['weekly','Setiap minggu'],['monthly','Setiap bulan']].map(([v,l])=>'<option value="'+v+'" '+(o.frequency===v?'selected':'')+'>'+l+'</option>').join('')+'</select></div><div class="field"><label for="schedule-time">Waktu</label><input id="schedule-time" type="time" value="'+E(o.time)+'" required></div></div><div class="field" id="weekday-field" '+(o.frequency==='weekly'?'':'hidden')+'><label for="schedule-weekday">Hari</label><select id="schedule-weekday">'+['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'].map((d,i)=>'<option value="'+i+'" '+(Number(o.weekday)===i?'selected':'')+'>'+d+'</option>').join('')+'</select></div><div class="field" id="monthday-field" '+(o.frequency==='monthly'?'':'hidden')+'><label for="schedule-monthday">Tanggal</label><select id="schedule-monthday">'+Array.from({length:28},(_,i)=>'<option '+(Number(o.monthday)===i+1?'selected':'')+'>'+(i+1)+'</option>').join('')+'</select></div></div><p id="plan-error" class="error" role="alert"></p><div class="plan-actions"><button class="btn primary" type="submit" data-intent="run">'+I('spark')+'Buat sekarang</button><button class="btn" type="submit" data-intent="save">'+I('calendar')+'Simpan jadwal</button></div><div class="simple-saved-status">'+(state.schedule.enabled?'Jadwal aktif: '+saved.count+' '+(saved.mode==='research'?'brief riset':'draft')+' · '+E(scheduleLabel()):'Belum ada jadwal aktif.')+'</div></form><div class="table-note">AI dan riset disimulasikan. Video/audio berupa konsep dan naskah. Jadwal berjalan selama halaman terbuka.</div></section>';
|
||
}
|
||
function syncBrainSelection(){
|
||
if(view!=='brain'||!$('#ai-scope'))return;
|
||
let o;try{o=readAIPlan();}catch{o=aiDraft||{...state.automation,...state.schedule};}
|
||
aiDraft={...o,scope:selectedBrain.size?'selected':'all',sourceIDs:[...selectedBrain]};
|
||
$('#ai-scope').value=aiDraft.scope;
|
||
$('#plan-source-label').textContent=(selectedBrain.size||brainSources().length)+' materi menjadi sumber AI';
|
||
$('#brain-selection-label').textContent=selectedBrain.size?selectedBrain.size+' dipilih':'Seluruh Brain digunakan';
|
||
$('#brain-select-all').checked=brainSources().length>0&&brainSources().every(s=>selectedBrain.has(s.id));
|
||
document.querySelectorAll('.brain-source-check').forEach(c=>{c.checked=selectedBrain.has(c.value);});
|
||
}
|
||
function readAIPlan(){
|
||
const base=aiDraft||{...state.automation,...state.schedule};
|
||
const o={...base,count:Number($('#ai-count').value),mode:$('#ai-mode').value,topic:$('#ai-topic').value.trim(),researchURLs:$('#research-urls').value.trim(),scope:$('#ai-scope').value,sourceIDs:base.sourceIDs||[],enabled:$('#schedule-enabled').checked,frequency:$('#schedule-frequency').value,time:$('#schedule-time').value,weekday:Number($('#schedule-weekday').value),monthday:Number($('#schedule-monthday').value)};
|
||
if(!Number.isInteger(o.count)||o.count<1||o.count>50)throw new Error('Jumlah harus 1–50 per proses demo.');
|
||
if(o.scope==='selected'&&!brainSources().some(s=>o.sourceIDs.includes(s.id)))throw new Error('Pilih materi pada daftar Brain terlebih dahulu.');
|
||
const urls=o.researchURLs.split(/\s+/).filter(Boolean);if(urls.some(s=>{try{return !['http:','https:'].includes(new URL(s).protocol);}catch{return true;}}))throw new Error('Gunakan tautan http:// atau https:// untuk referensi riset.');
|
||
return o;
|
||
}
|
||
function resultRows(){
|
||
if(resultTab==='research')return researchRows();
|
||
const filtered=state.modules.filter(m=>m.title.toLowerCase().includes(resultQuery.toLowerCase())&&(resultStatus==='all'||m.status===resultStatus)&&(resultType==='all'||typeOf(m)===resultType));
|
||
if(!filtered.length)return empty(state.modules.length?'Tidak ada konten yang cocok':'Konten baru Anda akan muncul di sini',state.modules.length?'Coba kata kunci, tipe, atau status lain.':'Tambahkan materi melalui halaman Upload materi untuk mulai.');
|
||
return '<div class="content-grid">'+filtered.map(contentCard).join('')+'</div>';
|
||
}
|
||
function resultsView(){
|
||
return head('Hasil','Konten dan brief riset yang sudah dibuat.')+'<div class="result-tabs"><button class="'+(resultTab==='content'?'active':'')+'" data-action="result-tab" data-tab="content">Konten <b>'+state.modules.length+'</b></button><button class="'+(resultTab==='research'?'active':'')+'" data-action="result-tab" data-tab="research">Riset <b>'+state.research.length+'</b></button></div>'+
|
||
'<section class="card"><div class="toolbar"><h3>'+(resultTab==='research'?'Brief riset':'Semua konten')+' <span class="badge neutral">'+(resultTab==='research'?state.research.length:state.modules.length)+'</span></h3><div class="toolbar-controls"><div class="search">'+I('search')+'<input id="result-search" aria-label="Cari konten" placeholder="Cari judul konten..." value="'+E(resultQuery)+'"></div><select id="result-type" aria-label="Filter tipe konten"><option value="all">Semua tipe</option>'+visibleTypes.map(k=>{const t=types[k];return '<option value="'+k+'" '+(resultType===k?'selected':'')+'>'+t.label+'</option>';}).join('')+'</select><select id="result-status" aria-label="Filter status">'+[['all','Semua status'],['draft','Draft / konsep'],['ready','Sudah ditinjau']].map(x=>'<option value="'+x[0]+'" '+(resultStatus===x[0]?'selected':'')+'>'+x[1]+'</option>').join('')+'</select></div></div><div id="result-rows">'+resultRows()+'</div><div class="table-note">Text dapat diunduh sebagai TXT/PDF, SCORM sebagai ZIP demo. Video/audio berupa konsep dan naskah. Brief riset belum berisi temuan terverifikasi.</div></section>';
|
||
}
|
||
function modalHead(title,sub=''){return '<div class="modal-head"><div><h2 id="modal-title">'+title+'</h2>'+(sub?'<p>'+sub+'</p>':'')+'</div><button class="icon-btn" data-action="close" aria-label="Tutup dialog">'+I('close')+'</button></div>';}
|
||
function openModal(content,preview=false){const modal=$('#modal');modal.classList.toggle('preview',preview);$('#modal-content').innerHTML=content;fillIcons(modal);if(!modal.open)modal.showModal();}
|
||
function openGenerate(ids){
|
||
if(busy){toast('Pembuatan modul sebelumnya masih berlangsung.');return;}
|
||
const pairs=C.matchSources(state.sources);const selected=ids||(pairs[0]?[pairs[0].a,pairs[0].b]:readable().slice(0,2).map(s=>s.id));
|
||
openModal(modalHead('Buat modul baru','Opsional: pilih sendiri materi untuk modul tambahan.')+'<form id="generate-form"><div class="modal-body"><div class="field"><label for="generate-title">Judul modul <span class="muted">(opsional)</span></label><input id="generate-title" placeholder="Biarkan kosong untuk judul otomatis" maxlength="140"></div><label>Materi yang dipadukan <span class="muted">· minimal 1</span></label><div class="source-choices">'+state.sources.map(s=>'<label class="source-choice '+(!s.text?'disabled':'')+'"><input type="checkbox" name="generate-sources" value="'+E(s.id)+'" '+(selected.includes(s.id)&&s.text?'checked':'')+' '+(!s.text?'disabled':'')+'>'+fileIcon(s)+'<div><b>'+E(s.title)+'</b><small>'+(s.text?(s.sample?'Materi contoh · ':'')+'Teks tersedia':'Isi belum dibaca dalam demo')+'</small></div></label>').join('')+'</div><div class="generate-summary"><span>'+I('layers')+'Gabungkan materi terpilih</span><span>'+I('clock')+'Target '+state.settings.duration+' menit</span><span>'+I('book')+'Modul & '+(state.settings.quiz?'kuis':'ringkasan')+'</span></div><p id="generate-error" class="error" role="alert"></p>'+(readable().length<1?'<div class="spacer"></div><div class="inline-note">'+I('info')+'Upload satu materi TXT/MD, atau coba materi contoh.</div>':'')+'</div><div class="modal-foot"><small>Mode demo · Hasil akan disimpan sebagai draft.</small><button class="btn" type="button" data-action="close">Batal</button><button class="btn primary" type="submit" '+(readable().length<1?'disabled':'')+'>'+I('spark')+'Generate modul</button></div></form>');
|
||
}
|
||
const delay=ms=>new Promise(r=>setTimeout(r,ms));
|
||
async function generate(ids=[],origin='manual',customTitle='',options=null){
|
||
if(busy)return;
|
||
const o={...state.automation,...(options||{})},pool=origin==='sample'?state.sources.filter(s=>s.sample):brainSources();
|
||
const chosen=ids.length?ids:o.scope==='selected'?o.sourceIDs:[];
|
||
if(o.scope==='selected'&&!pool.some(s=>(chosen||[]).includes(s.id))){toast('Sumber pilihan tidak lagi tersedia. Pilih ulang materi di Brain.',true);return;}
|
||
let jobs=[];
|
||
try{if(o.mode!=='research')jobs=C.planBrainBatch(pool,{count:o.count,sourceIDs:chosen,topic:o.topic},state.modules.length);if(!Number.isInteger(Number(o.count))||o.count<1||o.count>50)throw new Error('Jumlah harus 1–50.');}catch(e){toast(e.message,true);return;}
|
||
const refs=pool.filter(s=>!chosen.length||chosen.includes(s.id));
|
||
if(o.mode==='research'&&!refs.length&&!o.topic){toast('Tambahkan materi ke Brain atau isi fokus topik untuk riset.',true);return;}
|
||
busy=true;
|
||
openModal(modalHead('Mengembangkan pengetahuan dari Brain','Menyusun banyak draft dengan sumber yang dapat ditelusuri.')+'<div class="progress-state"><span class="progress-orb">'+I('brain')+'</span><h3 id="progress-label">Menyiapkan '+refs.length+' sumber pengetahuan</h3><p>'+Number(o.count)+' '+(o.mode==='both'?'draft konten + '+o.count+' brief riset':o.mode==='research'?'brief riset':'draft konten')+' dalam proses demo ini.</p><div class="progress-track"><i id="progress-bar"></i></div><small>Simulasi AI · Tidak ada pencarian web atau produksi media asli</small></div>');
|
||
try{
|
||
await delay(350);if($('#progress-bar'))$('#progress-bar').style.width='40%';if($('#progress-label'))$('#progress-label').textContent='Memetakan sudut pembelajaran dan format hasil';
|
||
await delay(450);if($('#progress-bar'))$('#progress-bar').style.width='80%';await delay(350);
|
||
const made=jobs.map((job,i)=>{const m=C.createBrainContent(job,state.settings,state.modules.length+i);m.origin=origin;m.sample=refs.every(s=>s.sample);return m;});
|
||
const briefs=o.mode==='generate'?[]:Array.from({length:Number(o.count)},(_,i)=>({...C.researchBrief(o,refs,state.research.length+i),origin}));
|
||
state.modules.unshift(...made);state.research.unshift(...briefs);
|
||
if(origin==='scheduled'){state.schedule.lastRun=now();state.schedule.nextRun=C.nextRun(state.schedule).toISOString();}
|
||
state.lastBatch={contents:made.length,research:briefs.length,sources:refs.length,createdAt:now()};
|
||
if(origin==='manual')state.chat.push({role:'assistant',text:'Proses demo selesai: '+made.length+' draft konten dan '+briefs.length+' brief riset dari '+refs.length+' sumber Brain. Hasil dapat dibuka di Hasil konten. Draft dan brief masih perlu ditinjau; riset web belum dijalankan.'});
|
||
persist();view='results';resultTab=made.length?'content':'research';resultType='all';resultQuery='';resultStatus='all';render();$('#modal').close();
|
||
toast((made.length?made.length+' draft konten':'')+(made.length&&briefs.length?' dan ':'')+(briefs.length?briefs.length+' brief riset demo':'')+' tersedia.');
|
||
}catch(e){$('#modal').close();toast(e.message||'Proses belum berhasil.',true);}finally{busy=false;}
|
||
}
|
||
function researchRows(){
|
||
const list=state.research.filter(r=>r.title.toLowerCase().includes(resultQuery.toLowerCase()));
|
||
if(!list.length)return empty('Belum ada rencana riset','Minta AI merencanakan riset melalui AI & jadwal.');
|
||
return '<div class="research-list">'+list.map(r=>'<article class="research-row"><span>'+I('search')+'</span><div><span class="badge amber">Rencana riset · Demo</span><h3>'+E(r.title)+'</h3><p>'+r.sources.length+' sumber Brain · '+E(dt(r.createdAt))+' · Belum ada temuan terverifikasi</p></div><button class="btn small" data-action="research-preview" data-id="'+E(r.id)+'">Lihat brief</button></article>').join('')+'</div>';
|
||
}
|
||
function researchPreview(id){
|
||
const r=state.research.find(x=>x.id===id);if(!r)return;
|
||
openModal(modalHead('Brief riset','Rencana pencarian, belum merupakan hasil riset.')+'<div class="modal-body"><div class="preview-notice concept">'+I('info')+'<span>'+E(r.notice)+'</span></div><div class="prose"><h1>'+E(r.title)+'</h1><h2>Permintaan</h2><p>'+E(r.prompt||r.topic)+'</p><h2>Kata kunci pencarian yang disarankan</h2><ul>'+r.queries.map(q=>'<li>'+E(q)+'</li>').join('')+'</ul><h2>Referensi yang diminta</h2><ul>'+(r.requestedURLs.length?r.requestedURLs.map(u=>'<li>'+E(u)+' · belum diakses</li>').join(''):'<li>Belum ditentukan.</li>')+'</ul><h2>Sumber internal Brain</h2><ul>'+r.sources.map(s=>'<li>'+E(s.title)+'</li>').join('')+'</ul><h2>Temuan</h2><p>Belum tersedia. Pencarian web dan verifikasi sumber memerlukan koneksi layanan riset pada sistem produksi.</p></div></div><div class="modal-foot">'+button('Unduh brief TXT','download-research','primary','download','data-id="'+E(id)+'"')+'</div>');
|
||
}
|
||
function prose(m){
|
||
if(m.editedText)return '<div class="prose"><h1>'+E(m.title)+'</h1><p>'+E(m.editedText)+'</p></div>';
|
||
return '<article class="prose"><h1>'+E(m.title)+'</h1><h2>Tujuan pembelajaran</h2><ol>'+m.objectives.map(o=>'<li>'+E(o)+'</li>').join('')+'</ol>'+
|
||
m.sections.map(s=>'<h2>'+E(s.title)+'</h2><p>'+E(s.body)+'</p><p class="ref-line">Sumber: '+s.sourceIds.map(id=>E(m.sources.find(r=>r.id===id)?.title||'Materi sumber')).join(' · ')+'</p>').join('')+
|
||
(m.quiz.length?'<h2>Kuis pemahaman</h2>'+m.quiz.map((q,i)=>'<div class="quiz-item"><strong>'+(i+1)+'. '+E(q.q)+'</strong><ol>'+q.options.map(o=>'<li>'+E(o)+'</li>').join('')+'</ol><details><summary>Lihat kunci jawaban</summary><p>Jawaban: '+E('ABCD'[q.answer])+'. '+E(q.options[q.answer])+'</p></details></div>').join(''):'')+'</article>';
|
||
}
|
||
function contentSections(m){return m.editedText?[{title:'Naskah revisi',body:m.editedText,sourceIds:m.sources.map(s=>s.id)}]:m.sections;}
|
||
function typePreview(m){
|
||
const type=typeOf(m),sections=contentSections(m);
|
||
if(type==='video')return '<div class="video-preview"><div class="storyboard-screen" id="storyboard-screen"><span>STORYBOARD · ADEGAN 1</span><div>'+I('video')+'</div><h2>'+E(sections[0].title)+'</h2><p>'+E(sections[0].body.slice(0,240))+'</p><small>Pratinjau adegan · Belum berupa file video</small></div><div class="scene-list">'+sections.map((s,i)=>'<button class="'+(i===0?'active':'')+'" data-action="scene" data-index="'+i+'"><b>'+String(i+1).padStart(2,'0')+'</b>'+E(s.title)+'</button>').join('')+'</div></div><div class="spacer"></div><details class="script-details"><summary>Lihat naskah lengkap</summary>'+prose(m)+'</details>';
|
||
if(type==='audio')return '<div class="audio-preview"><span class="audio-orb">'+I('audio')+'</span><div class="waveform" aria-hidden="true">'+Array.from({length:36},(_,i)=>'<i style="height:'+([16,29,45,22,37,56,31,19,43][i%9])+'px"></i>').join('')+'</div><h2>'+E(m.title)+'</h2><p>Pratinjau naskah dengan suara perangkat. Belum berupa file audio.</p><div class="head-actions">'+button('Dengarkan naskah','speak','primary','audio')+button('Hentikan','stop-speech','','close')+'</div><small id="speech-status" role="status">Tekan dengarkan untuk memulai.</small></div><div class="spacer"></div>'+prose(m);
|
||
if(type==='scorm')return '<div class="scorm-preview"><div class="scorm-preview-head">'+I('scorm')+'<div><b>Modul interaktif</b><small>SCORM 1.2 · '+sections.length+' bagian · Paket ZIP demo</small></div></div><h2>'+E(m.title)+'</h2><p>Pilih bagian untuk membuka materi.</p>'+sections.map((s,i)=>'<details class="chapter" '+(i===0?'open':'')+'><summary><span>'+String(i+1).padStart(2,'0')+'</span>'+E(s.title)+'</summary><p>'+E(s.body)+'</p></details>').join('')+'</div>';
|
||
return prose(m);
|
||
}
|
||
function stopSpeech(){if('speechSynthesis' in window)window.speechSynthesis.cancel();}
|
||
function preview(id,editing=false){
|
||
stopSpeech();
|
||
const m=state.modules.find(x=>x.id===id);if(!m)return;previewID=id;
|
||
const type=typeOf(m),media=type==='video'||type==='audio';
|
||
const side='<aside class="preview-side"><h3>Informasi konten</h3>'+typeBadge(type)+'<div class="detail-label">Status</div>'+status(m)+'<div class="detail-label">Audiens</div><b>'+E(m.audience)+'</b><div class="detail-label">Target durasi</div><b>'+m.duration+' menit</b><div class="detail-label">Pembuatan</div><b>'+(m.origin==='scheduled'?'Sesuai jadwal':m.sample?'Materi contoh':'Setelah upload')+'</b><div class="detail-label">Materi sumber</div>'+m.sources.map(s=>'<div class="preview-source">'+I('file')+E(s.title)+'</div>').join('')+'</aside>';
|
||
const body=editing?'<form id="edit-form"><div class="field"><label for="edit-title">Judul konten</label><input id="edit-title" maxlength="140" required value="'+E(m.title)+'"></div><div class="field"><label for="edit-text">Isi / naskah konten</label><textarea id="edit-text" class="edit-area" required>'+E(m.editedText||C.moduleText(m).split('\n').slice(1).join('\n').trim())+'</textarea></div><button class="btn primary" type="submit">'+I('check')+'Simpan revisi</button></form>':typePreview(m);
|
||
const exportAction=media?button('Unduh naskah TXT','download-txt','','download','data-id="'+E(id)+'"'):button(type==='scorm'?'Unduh SCORM ZIP':'Unduh PDF',type==='scorm'?'download-scorm':'download-pdf','primary','download','data-id="'+E(id)+'"');
|
||
openModal(modalHead('Pratinjau '+types[type].label,'Format hasil mengikuti jenis materi sumber.')+'<div class="modal-body"><div class="preview-notice '+(m.basis==='filename'?'concept':'')+'">'+I('info')+'<span>'+E(m.notice||'Hasil simulasi. Tinjau sebelum digunakan.')+(media?' Ekspor file '+types[type].label.toLowerCase()+' asli belum tersedia dalam demo.':'')+'</span></div><div class="preview-tabs"><button data-action="preview-tab" data-editing="no" class="'+(!editing?'active':'')+'">Pratinjau '+types[type].label+'</button><button data-action="preview-tab" data-editing="yes" class="'+(editing?'active':'')+'">Sunting '+(media?'naskah':'isi')+'</button></div><div class="preview-layout"><div>'+body+'</div>'+side+'</div></div><div class="modal-foot"><small>'+(media?'Pratinjau konsep · Ekspor media pada versi produksi':'File demo · Tinjau sebelum digunakan')+'</small>'+(type==='text'?button('Unduh Text TXT','download-txt','','download','data-id="'+E(id)+'"'):'')+exportAction+button(m.status==='ready'?'Sudah ditinjau':'Tandai sudah ditinjau','approve','','check','data-id="'+E(id)+'" '+(m.status==='ready'?'disabled':''))+'</div>',true);
|
||
}
|
||
function download(id,type){
|
||
const m=state.modules.find(x=>x.id===id);if(!m)return;
|
||
const text=C.moduleText(m),data=type==='scorm'?C.makeSCORM(m):type==='pdf'?C.makePDF(text):text,extension=type==='scorm'?'zip':type;
|
||
const blob=new Blob([data],{type:type==='scorm'?'application/zip':type==='pdf'?'application/pdf':'text/plain;charset=utf-8'}),url=URL.createObjectURL(blob),a=document.createElement('a');
|
||
a.href=url;a.download=m.title.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'')+'.'+extension;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),1500);toast('Unduhan '+type.toUpperCase()+' disiapkan.');
|
||
}
|
||
function showHelp(){
|
||
const steps=[['Upload materi','Setelah login, tambahkan dokumen, video, audio, atau SCORM. Materi masuk ke Brain.'],['Brain & AI','Daftar sumber, permintaan AI, jumlah draft, dan jadwal ada dalam satu halaman. Pilih materi atau kosongkan pilihan untuk seluruh Brain. Tulis permintaan, terapkan rencana, lalu buat sekarang atau simpan jadwal.'],['Hasil','Buka draft konten dan brief riset. Text dan SCORM demo dapat diunduh. Video/audio berupa konsep dan naskah; riset belum berisi temuan terverifikasi.']];
|
||
openModal(modalHead('Alur Learning Lab Express','Upload materi → Brain & AI → Hasil.')+'<div class="modal-body"><div class="help-steps">'+steps.map((x,i)=>'<div class="help-step"><span>'+(i+1)+'</span><div><h3>'+x[0]+'</h3><p>'+x[1]+'</p></div></div>').join('')+'</div><div class="spacer"></div><div class="inline-note">'+I('info')+'Proses AI dan jadwal adalah simulasi. Demo membaca TXT/MD; format lain belum dianalisis. Jadwal hanya berjalan selama halaman terbuka. Jumlah demo 1–50 draft/brief per proses.</div></div><div class="modal-foot">'+button('Mulai mencoba','close','primary','arrow-right')+'</div>');
|
||
}
|
||
async function uploadFiles(files){
|
||
if(busy){toast('Konten sedang disiapkan. Tambahkan materi lagi setelah proses selesai.');return;}
|
||
if(!files.length)return;
|
||
busy=true;
|
||
const issues=[],addedIDs=[];
|
||
try{
|
||
for(const f of files){
|
||
const ext=f.name.split('.').pop().toLowerCase(),type=C.contentType(f.name);
|
||
if(!type){issues.push(f.name+': format belum didukung');continue;}
|
||
if(f.size>200*1024*1024){issues.push(f.name+': melebihi 200 MB');continue;}
|
||
if(!f.size){issues.push(f.name+': berkas kosong');continue;}
|
||
if(state.sources.some(s=>s.originalName===f.name&&s.bytes===f.size)){issues.push(f.name+': sudah terdaftar');continue;}
|
||
if(state.sources.length>=100){issues.push('Maksimal 100 materi untuk demo ini.');break;}
|
||
if(type==='scorm'&&!C.isSCORM(new Uint8Array(await f.arrayBuffer()))){issues.push(f.name+': ZIP harus berisi imsmanifest.xml di folder utama');continue;}
|
||
let text='';if(ext==='txt'||ext==='md')text=(await f.text()).slice(0,25000).trim();
|
||
if((ext==='txt'||ext==='md')&&!text){issues.push(f.name+': isi teks kosong');continue;}
|
||
const id='src-'+(crypto.randomUUID?crypto.randomUUID():Date.now()+'-'+Math.random().toString(36).slice(2));
|
||
state.sources.unshift({id,title:f.name.replace(/\.[^.]+$/,''),originalName:f.name,format:type==='scorm'?'SCORM':ext.toUpperCase(),tags:[],text,bytes:f.size,createdAt:now(),sample:false});
|
||
if(type==='video'||type==='audio')sourceURLs.set(id,URL.createObjectURL(f));
|
||
addedIDs.push(id);
|
||
}
|
||
}catch(e){issues.push('Berkas belum dapat dibaca: '+e.message);}
|
||
finally{busy=false;$('#file-input').value='';}
|
||
state.lastUploadIDs=addedIDs;selectedBrain.clear();aiDraft={...state.automation,...state.schedule,scope:'all',sourceIDs:[]};persist();view='sources';render();
|
||
if(issues.length)openModal(modalHead('Sebagian materi belum ditambahkan')+'<div class="modal-body"><ul class="upload-issues">'+issues.map(x=>'<li>'+E(x)+'</li>').join('')+'</ul><p>Materi lain yang berhasil ditambahkan tetap tersimpan.</p></div><div class="modal-foot">'+(addedIDs.length?button('Lihat '+addedIDs.length+' materi di Brain','continue-upload','primary','spark','data-ids="'+E(addedIDs.join(','))+'"'):button('Tutup','close','','close'))+'</div>');
|
||
else if(addedIDs.length){go('brain');toast(addedIDs.length+' materi ditambahkan ke Brain. Minta AI untuk mulai membuat konten.');}
|
||
}
|
||
function bindView(){
|
||
const messages=$('.ai-messages');if(messages)messages.scrollTop=messages.scrollHeight;
|
||
const sourceSearch=$('#source-search');if(sourceSearch)sourceSearch.addEventListener('input',e=>{sourceQuery=e.target.value;$('#source-rows').innerHTML=sourceRows();});
|
||
const resultSearch=$('#result-search');if(resultSearch)resultSearch.addEventListener('input',e=>{resultQuery=e.target.value;$('#result-rows').innerHTML=resultRows();});
|
||
const filter=$('#result-status');if(filter)filter.addEventListener('change',e=>{resultStatus=e.target.value;$('#result-rows').innerHTML=resultRows();});
|
||
const typeFilter=$('#result-type');if(typeFilter)typeFilter.addEventListener('change',e=>{resultType=e.target.value;$('#result-rows').innerHTML=resultRows();});
|
||
const mode=$('#ai-mode');if(mode)mode.addEventListener('change',()=>{$('#research-options').hidden=mode.value==='generate';$('#ai-count-help').textContent='1–50 per proses demo. '+(mode.value==='both'?'Jumlah berlaku untuk konten dan brief masing-masing.':'');});
|
||
if(view==='results'&&resultTab==='research'){$('#result-type').hidden=true;$('#result-status').hidden=true;}
|
||
const frequency=$('#schedule-frequency');if(frequency)frequency.addEventListener('change',e=>{$('#weekday-field').hidden=e.target.value!=='weekly';$('#monthday-field').hidden=e.target.value!=='monthly';});
|
||
const drop=$('#drop-zone');if(drop){
|
||
['dragenter','dragover'].forEach(t=>drop.addEventListener(t,e=>{e.preventDefault();drop.classList.add('drag');}));
|
||
['dragleave','drop'].forEach(t=>drop.addEventListener(t,e=>{e.preventDefault();drop.classList.remove('drag');}));
|
||
drop.addEventListener('drop',e=>uploadFiles([...e.dataTransfer.files]));
|
||
}
|
||
}
|
||
document.addEventListener('click',e=>{
|
||
const nav=e.target.closest('[data-nav]');if(nav){e.preventDefault();go(nav.dataset.nav);return;}
|
||
const el=e.target.closest('[data-action]');if(!el||el.disabled)return;e.preventDefault();
|
||
const action=el.dataset.action,id=el.dataset.id;
|
||
if(action==='toggle-password'){const input=$('#login-password');input.type=input.type==='password'?'text':'password';el.setAttribute('aria-label',input.type==='password'?'Tampilkan password':'Sembunyikan password');}
|
||
else if(action==='logout'){stopSpeech();view='sources';state.user=null;persist();render();}
|
||
else if(action==='help')showHelp();
|
||
else if(action==='close'){stopSpeech();$('#modal').close();}
|
||
else if(action==='to-sources')go('sources');
|
||
else if(action==='to-schedule')go('brain');
|
||
else if(action==='upload'){if(view!=='sources')go('sources');$('#file-input').click();}
|
||
else if(action==='generate')go('brain');
|
||
else if(action==='brain-to-ai')go('brain');
|
||
else if(action==='load-brain-demo'){sampleSources().forEach(s=>{if(!state.sources.some(x=>x.id===s.id))state.sources.push(s);});persist();go('brain');}
|
||
else if(action==='prompt-example'){$('#ai-prompt').value=el.dataset.prompt;$('#ai-prompt').focus();}
|
||
else if(action==='result-tab'){resultTab=el.dataset.tab;resultQuery='';render();}
|
||
else if(action==='research-preview')researchPreview(id);
|
||
else if(action==='download-research'){const r=state.research.find(x=>x.id===id);if(r){const text=[r.title,r.notice,r.prompt,...r.queries,...r.requestedURLs,...r.sources.map(s=>s.title)].join('\n\n'),url=URL.createObjectURL(new Blob([text],{type:'text/plain;charset=utf-8'})),a=document.createElement('a');a.href=url;a.download='brief-riset-'+r.id+'.txt';a.click();setTimeout(()=>URL.revokeObjectURL(url),1500);}}
|
||
else if(action==='pair')openGenerate(el.dataset.pair.split(','));
|
||
else if(action==='preview')preview(id);
|
||
else if(action==='preview-tab')preview(previewID,el.dataset.editing==='yes');
|
||
else if(action==='download-pdf')download(id,'pdf');
|
||
else if(action==='download-txt')download(id,'txt');
|
||
else if(action==='download-scorm')download(id,'scorm');
|
||
else if(action==='continue-upload'){$('#modal').close();go('brain');}
|
||
else if(action==='scene'){
|
||
const m=state.modules.find(x=>x.id===previewID),index=Number(el.dataset.index),scene=m?contentSections(m)[index]:null;if(scene){$('#storyboard-screen').innerHTML='<span>STORYBOARD · ADEGAN '+(index+1)+'</span><div>'+I('video')+'</div><h2>'+E(scene.title)+'</h2><p>'+E(scene.body.slice(0,240))+'</p><small>Pratinjau adegan · Belum berupa file video</small>';document.querySelectorAll('.scene-list button').forEach(b=>b.classList.toggle('active',b===el));}
|
||
}
|
||
else if(action==='speak'){
|
||
const m=state.modules.find(x=>x.id===previewID);if(!m)return;
|
||
if(!('speechSynthesis' in window)){toast('Perangkat ini belum mendukung pratinjau suara. Naskah tetap dapat dibaca dan diunduh.',true);return;}
|
||
stopSpeech();const utterance=new SpeechSynthesisUtterance(m.editedText||m.sections.map(s=>s.title+'. '+s.body).join('\n'));
|
||
utterance.lang='id-ID';const voice=speechSynthesis.getVoices().find(v=>v.lang.startsWith('id'));if(voice)utterance.voice=voice;
|
||
utterance.onstart=()=>{if($('#speech-status'))$('#speech-status').textContent='Membacakan naskah dengan suara perangkat…';};utterance.onend=()=>{if($('#speech-status'))$('#speech-status').textContent='Pratinjau selesai.';};utterance.onerror=()=>{if($('#speech-status'))$('#speech-status').textContent='Pratinjau suara tidak tersedia. Anda tetap bisa mengunduh naskah.';};speechSynthesis.speak(utterance);
|
||
}
|
||
else if(action==='stop-speech'){stopSpeech();if($('#speech-status'))$('#speech-status').textContent='Pratinjau dihentikan.';}
|
||
else if(action==='approve'){const m=state.modules.find(x=>x.id===id);if(m){m.status='ready';persist();render();preview(id);toast('Modul ditandai siap digunakan.');}}
|
||
else if(action==='try-auto'){const samples=sampleSources();samples.forEach(s=>{if(!state.sources.some(x=>x.id===s.id))state.sources.push(s);});persist();generate(samples.map(s=>s.id),'sample');}
|
||
else if(action==='samples'){let n=0;sampleSources().forEach(s=>{if(!state.sources.some(x=>x.id===s.id)){state.sources.push(s);n++;}});persist();render();toast(n?n+' materi contoh ditambahkan.':'Tiga materi contoh sudah tersedia.');}
|
||
else if(action==='source-preview'){
|
||
const s=state.sources.find(x=>x.id===id);if(s){const type=C.contentType(s),url=sourceURLs.get(id);const player=url&&type==='video'?'<video class="source-player" controls src="'+E(url)+'"></video>':url&&type==='audio'?'<audio class="source-player" controls src="'+E(url)+'"></audio>':'';openModal(modalHead(E(s.title),(s.sample?'Materi contoh · ':'')+E(s.format)+' · '+types[type==='pdf'?'text':type||'text'].label)+'<div class="modal-body">'+player+(s.text?'<div class="source-text">'+E(s.text)+'</div>':'<div class="inline-note">'+I('info')+'Berkas asli terdaftar. Isi belum dianalisis dalam demo; hasil baru berupa konsep dari nama berkas. Pratinjau media asli tersedia selama sesi upload.</div>')+'</div><div class="modal-foot">'+button('Tutup','close','','close')+'</div>');}
|
||
}
|
||
else if(action==='source-delete'){
|
||
const s=state.sources.find(x=>x.id===id);if(s)openModal(modalHead('Hapus materi dari perpustakaan?')+'<div class="modal-body"><p style="font-size:12px">Materi <b>'+E(s.title)+'</b> akan dihapus dari demo ini. Modul yang sudah terbentuk tetap tersimpan beserta catatan sumbernya.</p></div><div class="modal-foot">'+button('Batal','close','','close')+button('Hapus materi','source-delete-confirm','danger','trash','data-id="'+E(id)+'"')+'</div>');
|
||
}
|
||
else if(action==='source-delete-confirm'){state.sources=state.sources.filter(s=>s.id!==id);if(sourceURLs.has(id)){URL.revokeObjectURL(sourceURLs.get(id));sourceURLs.delete(id);}persist();$('#modal').close();render();toast('Materi dihapus dari perpustakaan demo.');}
|
||
else if(action==='simulate'){
|
||
generate([],'scheduled');
|
||
}
|
||
});
|
||
document.addEventListener('change',e=>{
|
||
if(e.target.matches('.brain-source-check')){if(e.target.checked)selectedBrain.add(e.target.value);else selectedBrain.delete(e.target.value);syncBrainSelection();}
|
||
if(e.target.id==='brain-select-all'){selectedBrain=e.target.checked?new Set(brainSources().map(s=>s.id)):new Set();syncBrainSelection();}
|
||
if(e.target.id==='schedule-enabled')$('#schedule-fields').hidden=!e.target.checked;
|
||
});
|
||
document.addEventListener('submit',e=>{
|
||
const form=e.target;
|
||
if(form.id==='login-form'){e.preventDefault();const email=$('#login-email').value.trim();if(!email||!$('#login-password').value){$('#login-error').textContent='Lengkapi email dan password.';return;}const raw=email.split('@')[0].split(/[._-]/)[0];state.user={email,name:raw.charAt(0).toUpperCase()+raw.slice(1)};state.remember=!!$('#login-form input[type=checkbox]').checked;persist();render();}
|
||
else if(form.id==='settings-form'){e.preventDefault();state.settings={topic:$('#setting-topic').value.trim(),audience:$('#setting-audience').value.trim()||'Seluruh karyawan',duration:Number($('#setting-duration').value),tone:$('#setting-tone').value,language:'Bahasa Indonesia',exercise:$('#setting-exercise').checked,quiz:$('#setting-quiz').checked,saved:true};persist();render();toast('Pengaturan modul AI disimpan.');}
|
||
else if(form.id==='ai-request-form'){
|
||
e.preventDefault();try{const current=readAIPlan(),prompt=$('#ai-prompt').value.trim();if(!prompt)return;const proposal=C.parseAIRequest(prompt,current);aiDraft=proposal;state.chat.push({role:'user',text:prompt},{role:'assistant',text:'Rencana: '+proposal.count+' '+(proposal.mode==='research'?'brief riset':proposal.mode==='both'?'draft konten dan '+proposal.count+' brief riset':'draft konten')+' dari Brain. '+(proposal.enabled?'Jadwal yang diusulkan: '+({daily:'setiap hari',workdays:'hari kerja',weekly:'setiap minggu',monthly:'setiap bulan'}[proposal.frequency])+' pukul '+proposal.time+' WIB. ':'Dijalankan saat Anda meminta. ')+'Atur jumlah dan jadwal di bawah, lalu buat sekarang atau simpan jadwal. Ini simulasi AI dan riset.'});persist();render();}catch(error){$('#plan-error').textContent=error.message;}
|
||
}
|
||
else if(form.id==='schedule-form'){
|
||
e.preventDefault();try{const o=readAIPlan();state.automation={count:o.count,mode:o.mode,topic:o.topic,prompt:o.prompt||'',researchURLs:o.researchURLs,scope:o.scope,sourceIDs:o.sourceIDs};aiDraft=o;
|
||
if(e.submitter?.dataset.intent==='run'){persist();generate([],'manual','',o);}
|
||
else{state.schedule={...state.schedule,enabled:o.enabled,frequency:o.frequency,time:o.time,weekday:o.weekday,monthday:o.monthday,plan:{...state.automation,sourceIDs:[...state.automation.sourceIDs]}};state.schedule.nextRun=o.enabled?C.nextRun(state.schedule).toISOString():null;aiDraft=null;persist();render();toast(o.enabled?'Jadwal AI disimpan. Proses demo berjalan selama halaman terbuka.':'Rencana disimpan, jadwal otomatis nonaktif.');}
|
||
}catch(error){$('#plan-error').textContent=error.message;}
|
||
}
|
||
else if(form.id==='generate-form'){e.preventDefault();const ids=[...form.querySelectorAll('input[name=generate-sources]:checked')].map(x=>x.value);if(!ids.length){$('#generate-error').textContent='Pilih minimal satu materi.';return;}generate(ids,'manual',$('#generate-title').value.trim());}
|
||
else if(form.id==='edit-form'){e.preventDefault();const m=state.modules.find(x=>x.id===previewID);if(m){m.title=$('#edit-title').value.trim();m.editedText=$('#edit-text').value.trim();m.status='draft';persist();render();preview(m.id);toast('Revisi modul disimpan sebagai draft.');}}
|
||
});
|
||
$('#file-input').addEventListener('change',e=>uploadFiles([...e.target.files]));
|
||
$('#modal').addEventListener('click',e=>{if(e.target===$('#modal'))$('#modal').close();});
|
||
$('#modal').addEventListener('close',stopSpeech);
|
||
setInterval(()=>{if(!state.user||busy||!state.schedule.enabled||!state.schedule.nextRun||Date.now()<new Date(state.schedule.nextRun).getTime())return;state.schedule.nextRun=C.nextRun(state.schedule).toISOString();persist();generate([],'scheduled','',state.schedule.plan||state.automation);},15000);
|
||
render();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|