| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278 |
- <!DOCTYPE html>
- <html>
- <head>
- <title>ArozOS</title>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
- <link rel="manifest" href="manifest.webmanifest">
- <script src="script/jquery.min.js"></script>
- <script src="script/ao_module.js"></script>
- <script src="script/applocale.js"></script>
- <style>
- /* ── Reset ── */
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
- html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
- body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
- -webkit-font-smoothing: antialiased;
- user-select: none;
- -webkit-user-select: none;
- }
- /* ── Wallpaper ── */
- #backdrop {
- position: fixed; inset: 0; z-index: 0;
- background: url('img/desktop/bg/init.jpg') center/cover no-repeat;
- transition: background-image 0.5s;
- }
- #backdrop-dim {
- position: fixed; inset: 0; z-index: 1;
- background: rgba(0,0,0,0.28);
- pointer-events: none;
- transition: background 0.3s;
- }
- body.dark-mode #backdrop-dim { background: rgba(0,0,0,0.52); }
- /* ── Launchpad container ── */
- #launchpad { position: fixed; inset: 0; z-index: 2; overflow: hidden; }
- .lp-section {
- position: absolute; width: 100%; height: 100%; top: 0; left: 0;
- transition: transform 0.42s cubic-bezier(0.4, 0, 0.2, 1);
- will-change: transform;
- }
- /* ── Section 1: Home ── */
- #sec-home {
- display: flex; flex-direction: column; align-items: center;
- padding-top: max(env(safe-area-inset-top), 20px);
- }
- #clock-widget {
- margin-top: 14vh; text-align: center; color: #fff;
- text-shadow: 0 2px 14px rgba(0,0,0,0.45); pointer-events: none;
- }
- #clock-time { font-size: clamp(60px, 18vw, 92px); font-weight: 100; letter-spacing: -2px; line-height: 1; }
- #clock-date { font-size: clamp(14px, 4vw, 18px); font-weight: 300; margin-top: 8px; opacity: 0.88; }
- #home-hint {
- position: absolute; bottom: calc(max(env(safe-area-inset-bottom), 12px) + 120px);
- left: 0; right: 0; text-align: center;
- color: rgba(255,255,255,0.5); font-size: 11px; pointer-events: none;
- animation: hint 2.2s ease-in-out infinite;
- }
- @keyframes hint { 0%,100%{opacity:.5;transform:translateY(0)} 50%{opacity:.15;transform:translateY(-5px)} }
- #home-hint svg { display: block; margin: 0 auto 2px; }
- /* Dock */
- #dock {
- position: absolute;
- bottom: calc(max(env(safe-area-inset-bottom), 10px) + 8px);
- left: 14px; right: 14px;
- }
- #dock-inner {
- background: rgba(255,255,255,0.22);
- backdrop-filter: blur(22px) saturate(180%);
- -webkit-backdrop-filter: blur(22px) saturate(180%);
- border-radius: 26px; padding: 10px 6px;
- display: grid; grid-template-columns: repeat(4,1fr);
- }
- body.dark-mode #dock-inner { background: rgba(0,0,0,0.38); }
- .dock-item {
- display: flex; flex-direction: column; align-items: center; gap: 4px;
- cursor: pointer; padding: 7px 4px; border-radius: 16px;
- transition: opacity .15s; -webkit-tap-highlight-color: transparent;
- }
- .dock-item:active { opacity: .55; }
- .dock-item img { width: clamp(46px,13vw,60px); height: clamp(46px,13vw,60px); border-radius: 14px; object-fit: cover; box-shadow: 0 2px 10px rgba(0,0,0,.3); }
- .dock-label { font-size: 10px; color: rgba(255,255,255,.9); text-shadow: 0 1px 3px rgba(0,0,0,.5); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 68px; text-align: center; }
- /* ── Section 2: Apps ── */
- #sec-apps { display: flex; flex-direction: column; }
- #apps-header {
- padding: calc(max(env(safe-area-inset-top),16px) + 10px) 16px 8px;
- flex-shrink: 0; display: flex; align-items: center; justify-content: space-between;
- }
- #apps-header h2 { font-size: 26px; font-weight: 700; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.4); }
- #apps-edit-btn {
- flex-shrink: 0;
- background: rgba(255,255,255,.2); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
- border: 1px solid rgba(255,255,255,.28); border-radius: 14px;
- padding: 5px 16px; color: #fff; font-size: 14px; font-weight: 500;
- cursor: pointer; -webkit-tap-highlight-color: transparent; transition: background .15s, color .15s;
- margin-right: 3em; /* Avoid the control panel button */
- }
- #apps-edit-btn.on { background: rgba(255,255,255,.9); color: #111; border-color: transparent; }
- #apps-edit-btn:active { opacity: .6; }
- /* drag states */
- .app-item.dragging { opacity: .25; }
- .app-item.drag-target .app-icon-wrap { outline: 2px solid rgba(255,255,255,.85); outline-offset: 3px; border-radius: 16px; }
- /* removed-app ghost tiles */
- .app-item.app-removed { opacity: .35; }
- .app-restore {
- position:absolute; top:-5px; left:-5px; width:26px; height:26px;
- border-radius:50%; background:#107c10;
- display:flex; align-items:center; justify-content:center;
- z-index:5; cursor:pointer; border:2px solid #fff;
- }
- .app-restore img { width:13px; height:13px; pointer-events:none; display:block; }
- #app-search-wrap {
- margin: 6px 14px 4px; flex-shrink: 0;
- display: flex; align-items: center; gap: 8px;
- background: rgba(255,255,255,.18); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
- border-radius: 12px; padding: 8px 12px;
- border: 1px solid rgba(255,255,255,.22);
- }
- body.dark-mode #app-search-wrap { background: rgba(0,0,0,.3); border-color: rgba(255,255,255,.12); }
- #app-search-input { flex:1; background:transparent; border:none; outline:none; color:#fff; font-size:14px; }
- #app-search-input::placeholder { color: rgba(255,255,255,.55); }
- #edit-done-btn {
- position: absolute; top: calc(max(env(safe-area-inset-top),14px) + 12px); right: 14px;
- background: rgba(255,255,255,.92); color: #111; border: none; border-radius: 14px;
- padding: 6px 16px; font-size: 14px; font-weight: 600; cursor: pointer;
- display: none; z-index: 10; box-shadow: 0 2px 10px rgba(0,0,0,.18);
- }
- #app-grid-wrap {
- flex: 1; overflow-y: auto; overflow-x: hidden;
- -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
- padding: 4px 8px calc(max(env(safe-area-inset-bottom),12px) + 8px);
- }
- #app-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 2px 0; }
- @media(min-width:460px){ #app-grid{grid-template-columns:repeat(5,1fr);} }
- @media(min-width:700px){ #app-grid{grid-template-columns:repeat(6,1fr);} }
- .app-item {
- display: flex; flex-direction: column; align-items: center; gap: 5px;
- padding: 9px 4px; cursor: pointer; border-radius: 18px;
- transition: background .15s; position: relative;
- -webkit-tap-highlight-color: transparent;
- }
- .app-item:active:not(.jiggling) { background: rgba(255,255,255,.12); }
- .app-icon-wrap { position: relative; width: clamp(52px,14vw,68px); height: clamp(52px,14vw,68px); }
- .app-icon-wrap img {
- width: 100%; height: 100%; border-radius: 16px; object-fit: cover;
- box-shadow: 0 2px 8px rgba(0,0,0,.3); pointer-events: none;
- display: block;
- }
- .app-name { font-size: clamp(9px,2.4vw,11px); color: rgba(255,255,255,.94); text-shadow: 0 1px 3px rgba(0,0,0,.5); text-align: center; max-width: 76px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; pointer-events: none; }
- /* Jiggle */
- @keyframes jiggle { 0%{transform:rotate(-1.8deg) scale(1.02)} 50%{transform:rotate(1.8deg) scale(1.02)} 100%{transform:rotate(-1.8deg) scale(1.02)} }
- .jiggling .app-icon-wrap { animation: jiggle .32s infinite ease-in-out; }
- /* Delete badge */
- .app-del { position:absolute; top:-5px; left:-5px; width:26px; height:26px; border-radius:50%; background:#c42b1c; display:none; z-index:5; cursor:pointer; border:2px solid #fff; align-items:center; justify-content:center; }
- .editing .app-del { display: flex; }
- .app-del img { width:13px; height:13px; pointer-events:none; display:block; }
- /* Drag ghost */
- #drag-ghost { position:fixed; pointer-events:none; z-index:999; width:clamp(52px,14vw,68px); height:clamp(52px,14vw,68px); border-radius:16px; overflow:hidden; opacity:.82; box-shadow:0 10px 28px rgba(0,0,0,.45); transform:scale(1.12); display:none; }
- #drag-ghost img { width:100%; height:100%; object-fit:cover; }
- /* ── Section 3: Files ── */
- #sec-files { display: flex; flex-direction: column; }
- #files-header {
- padding: calc(max(env(safe-area-inset-top),16px) + 10px) 16px 8px;
- flex-shrink: 0;
- }
- #files-header h2 { font-size: 26px; font-weight: 700; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.4); }
- #files-header p { font-size: 12px; color: rgba(255,255,255,.6); margin-top: 3px; }
- #files-grid-wrap {
- flex:1; overflow-y:auto; overflow-x:hidden;
- -webkit-overflow-scrolling:touch; overscroll-behavior:contain;
- padding: 10px 12px calc(max(env(safe-area-inset-bottom),12px)+8px);
- }
- #files-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
- @media(min-width:460px){#files-grid{grid-template-columns:repeat(4,1fr);}}
- .folder-item {
- display:flex; flex-direction:column; align-items:center; gap:6px;
- padding:14px 6px; border-radius:16px; cursor:pointer;
- background:rgba(255,255,255,.13); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
- border:1px solid rgba(255,255,255,.18);
- transition:background .15s; -webkit-tap-highlight-color:transparent;
- }
- body.dark-mode .folder-item{background:rgba(0,0,0,.28);border-color:rgba(255,255,255,.1);}
- .folder-item:active{background:rgba(255,255,255,.24);}
- .folder-emoji{width:40px;height:40px;display:block;object-fit:contain;}
- .shortcut-icon{width:40px;height:40px;border-radius:10px;object-fit:cover;display:block;background:rgba(255,255,255,.25);}
- .folder-name{font-size:11px;color:rgba(255,255,255,.92);text-shadow:0 1px 3px rgba(0,0,0,.4);text-align:center;max-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
- #files-empty{text-align:center;color:rgba(255,255,255,.5);margin-top:64px;font-size:14px;display:none;}
- /* ── Page dots ── */
- #page-dots {
- position:fixed; bottom:36px;
- left:50%; transform:translateX(-50%); display:flex; gap:7px; z-index:50; pointer-events:none;
- }
- .pd{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.35);transition:background .2s,transform .2s;}
- .pd.on{background:rgba(255,255,255,.9);transform:scale(1.35);}
- /* ── Control Panel ── */
- #cp-btn {
- position:fixed; top:max(env(safe-area-inset-top),10px); right:10px; z-index:82;
- width:36px; height:36px; border-radius:50%;
- background:rgba(0,0,0,.32); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
- border:1px solid rgba(255,255,255,.22);
- display:flex; align-items:center; justify-content:center;
- cursor:pointer; -webkit-tap-highlight-color:transparent;
- }
- #cp-btn:active{opacity:.6;}
- #cp-backdrop{position:fixed;inset:0;z-index:80;display:none;}
- #cp-backdrop.on{display:block;}
- #cp-panel {
- position:fixed; top:0; right:0; z-index:81;
- width:min(310px,90vw); max-height:86vh; overflow-y:auto;
- background:rgba(235,235,235,.9); backdrop-filter:blur(40px) saturate(200%); -webkit-backdrop-filter:blur(40px) saturate(200%);
- border-radius:0 0 0 22px; box-shadow:0 8px 36px rgba(0,0,0,.28);
- padding-top:max(env(safe-area-inset-top),20px);
- transform:translateY(-110%); transition:transform .34s cubic-bezier(.4,0,.2,1);
- }
- body.dark-mode #cp-panel{background:rgba(28,28,28,.93);}
- #cp-panel.on{transform:translateY(0);}
- .cp-user{display:flex;align-items:center;gap:12px;padding:12px 16px 10px;}
- .cp-av{width:44px;height:44px;border-radius:50%;object-fit:cover;flex-shrink:0;}
- .cp-name{font-size:14px;font-weight:600;color:#111;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
- body.dark-mode .cp-name{color:#f0f0f0;}
- .cp-grp{font-size:11px;color:#666;margin-top:1px;}
- body.dark-mode .cp-grp{color:#aaa;}
- .cp-sep{height:1px;background:rgba(0,0,0,.08);margin:0 16px;}
- body.dark-mode .cp-sep{background:rgba(255,255,255,.1);}
- .cp-grid{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:10px 14px;}
- .cp-tile{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:12px 8px;background:rgba(0,0,0,.06);border-radius:14px;border:none;cursor:pointer;font-size:11px;font-weight:500;color:#111;-webkit-tap-highlight-color:transparent;transition:opacity .15s;}
- body.dark-mode .cp-tile{background:rgba(255,255,255,.1);color:#f0f0f0;}
- .cp-tile:active{opacity:.55;}
- .cp-tile.on{background:#111;color:#fff;}
- body.dark-mode .cp-tile.on{background:#e8e8e8;color:#111;}
- .cp-tile-icon{display:flex;align-items:center;justify-content:center;}
- .cp-tile-icon img{width:26px;height:26px;display:block;}
- /* Light/dark icon pair switching */
- img.cp-ic-w{display:none !important;}
- body.dark-mode img.cp-ic-b{display:none !important;}
- body.dark-mode img.cp-ic-w{display:block !important;}
- .cp-list-lbl{font-size:10px;font-weight:700;color:#888;text-transform:uppercase;letter-spacing:.4px;padding:6px 16px 2px;}
- .cp-acc-item{display:flex;align-items:center;gap:10px;padding:8px 16px;cursor:pointer;-webkit-tap-highlight-color:transparent;}
- .cp-acc-item:active{background:rgba(0,0,0,.05);}
- body.dark-mode .cp-acc-item:active{background:rgba(255,255,255,.05);}
- .cp-acc-item img{width:32px;height:32px;border-radius:50%;}
- .cp-acc-name{font-size:13px;color:#111;}
- body.dark-mode .cp-acc-name{color:#f0f0f0;}
- .cp-btns{padding:4px 14px 16px;}
- .cp-btn{width:100%;padding:10px 14px;margin-top:7px;background:rgba(0,0,0,.06);border:none;border-radius:12px;font-size:13px;font-weight:500;color:#111;cursor:pointer;display:flex;align-items:center;gap:10px;-webkit-tap-highlight-color:transparent;}
- body.dark-mode .cp-btn{background:rgba(255,255,255,.1);color:#f0f0f0;}
- .cp-btn:active{opacity:.55;}
- .cp-btn.red{color:#c42b1c;}
- body.dark-mode .cp-btn.red{color:#ff6b6b;}
- .cp-btn-ic{width:22px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
- .cp-btn-ic img{width:18px;height:18px;display:block;}
- /* ── Float Window Layer ── */
- #fw-layer{position:fixed;inset:0;z-index:100;display:none;flex-direction:column;background:#000;}
- #fw-layer.on{display:flex;}
- #fw-bar {
- background:rgba(18,18,18,.96); backdrop-filter:blur(10px);
- padding:calc(max(env(safe-area-inset-top),10px)+4px) 10px 8px;
- display:flex; align-items:center; gap:7px; flex-shrink:0;
- overflow-x:auto; overflow-y:hidden; scrollbar-width:none;
- }
- #fw-bar::-webkit-scrollbar{display:none;}
- #fw-back{flex-shrink:0;background:rgba(255,255,255,.16);border:none;border-radius:8px;padding:5px 11px;color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;gap:4px;-webkit-tap-highlight-color:transparent;}
- #fw-back:active{opacity:.55;}
- #fw-tabs{display:flex;gap:6px;flex-shrink:0;}
- .fw-tab{background:rgba(255,255,255,.12);border:none;border-radius:8px;padding:4px 10px;color:rgba(255,255,255,.65);font-size:12px;cursor:pointer;display:flex;align-items:center;gap:5px;min-width:60px;max-width:160px;-webkit-tap-highlight-color:transparent;transition:background .15s;}
- .fw-tab.on{background:rgba(255,255,255,.26);color:#fff;}
- .fw-tab img{width:16px;height:16px;border-radius:3px;flex-shrink:0;}
- .fw-tab-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
- .fw-tab-x{width:14px;height:14px;border-radius:50%;background:rgba(255,255,255,.2);display:inline-flex;align-items:center;justify-content:center;font-size:9px;flex-shrink:0;cursor:pointer;}
- .fw-tab-x:active{background:#c42b1c;}
- #fw-body{flex:1;position:relative;overflow:hidden;}
- .floatWindowWrapper{position:absolute;inset:0;display:none;}
- .floatWindowWrapper.on{display:block;}
- .floatWindow{width:100%;height:100%;}
- .fwtab{width:100%;height:100%;}
- .fwtab iframe{width:100%;height:100%;border:none;}
- /* ── Connection lost ── */
- #conndrop{position:fixed;top:max(env(safe-area-inset-top),8px);left:50%;transform:translateX(-50%);z-index:200;background:rgba(196,43,28,.9);backdrop-filter:blur(8px);border-radius:20px;padding:5px 13px;display:none;align-items:center;gap:7px;font-size:12px;color:#fff;}
- #conndrop img{width:14px;height:14px;}
- /* ── FW Tab Context Menu ── */
- #fw-ctx-overlay{position:fixed;inset:0;z-index:210;display:none;}
- #fw-ctx-overlay.on{display:block;}
- #fw-ctx-menu{
- position:fixed;z-index:211;display:none;
- background:rgba(235,235,235,.94);backdrop-filter:blur(40px) saturate(200%);-webkit-backdrop-filter:blur(40px) saturate(200%);
- border-radius:16px;box-shadow:0 8px 32px rgba(0,0,0,.32);
- min-width:220px;overflow:hidden;
- transform-origin:top left;
- animation:fw-ctx-in .14s ease;
- }
- body.dark-mode #fw-ctx-menu{background:rgba(30,30,30,.96);}
- @keyframes fw-ctx-in{from{opacity:0;transform:scale(.88);}to{opacity:1;transform:scale(1);}}
- .fw-ctx-item{
- display:flex;align-items:center;gap:12px;
- padding:13px 16px;font-size:14px;color:#111;cursor:pointer;
- border:none;background:none;width:100%;text-align:left;
- -webkit-tap-highlight-color:transparent;transition:background .1s;
- }
- body.dark-mode .fw-ctx-item{color:#f0f0f0;}
- .fw-ctx-item:active{background:rgba(0,0,0,.07);}
- body.dark-mode .fw-ctx-item:active{background:rgba(255,255,255,.09);}
- .fw-ctx-item+.fw-ctx-item{border-top:1px solid rgba(0,0,0,.06);}
- body.dark-mode .fw-ctx-item+.fw-ctx-item{border-top-color:rgba(255,255,255,.08);}
- .fw-ctx-item.red{color:#c42b1c;}
- body.dark-mode .fw-ctx-item.red{color:#ff6b6b;}
- .fw-ctx-item svg{flex-shrink:0;opacity:.75;}
- </style>
- </head>
- <body>
- <div id="backdrop"></div>
- <div id="backdrop-dim"></div>
- <!-- Page indicator -->
- <div id="page-dots">
- <div class="pd on" data-s="0"></div>
- <div class="pd" data-s="1"></div>
- <div class="pd" data-s="2"></div>
- </div>
- <!-- Control panel trigger -->
- <div id="cp-btn" onclick="cpToggle()">
- <svg width="17" height="17" viewBox="0 0 17 17" fill="none">
- <circle cx="4.5" cy="4.5" r="1.8" fill="white"/>
- <circle cx="12.5" cy="4.5" r="1.8" fill="white"/>
- <circle cx="4.5" cy="12.5" r="1.8" fill="white"/>
- <circle cx="12.5" cy="12.5" r="1.8" fill="white"/>
- </svg>
- </div>
- <div id="cp-backdrop" onclick="cpClose()"></div>
- <div id="cp-panel">
- <div class="cp-user">
- <img class="cp-av" id="cp-av" src="img/desktop/system_icon/user.svg">
- <div style="flex:1;min-width:0;">
- <div class="cp-name" id="cp-name">User</div>
- <div class="cp-grp" id="cp-grp">@user</div>
- </div>
- </div>
- <div class="cp-sep"></div>
- <div class="cp-grid">
- <button class="cp-tile" id="cp-dark-btn" onclick="cpToggleDark()">
- <span class="cp-tile-icon">
- <img id="cp-dark-ic" src="SystemAO/desktop/img/icons/lightmode_black.svg">
- </span>
- <span id="cp-dark-lbl">Dark Mode</span>
- </button>
- <button class="cp-tile" onclick="cpFullscreen()">
- <span class="cp-tile-icon">
- <img class="cp-ic-b" src="SystemAO/desktop/img/icons/fullscreen_black.svg">
- <img class="cp-ic-w" src="SystemAO/desktop/img/icons/fullscreen_white.svg">
- </span>
- <span>Fullscreen</span>
- </button>
- <button class="cp-tile" onclick="cpWallpaper()">
- <span class="cp-tile-icon">
- <img class="cp-ic-b" src="SystemAO/desktop/img/icons/wallpaper_black.svg">
- <img class="cp-ic-w" src="SystemAO/desktop/img/icons/wallpaper_white.svg">
- </span>
- <span>Wallpaper</span>
- </button>
- <button class="cp-tile" onclick="cpSettings()">
- <span class="cp-tile-icon">
- <img class="cp-ic-b" src="SystemAO/desktop/img/icons/settings_black.svg">
- <img class="cp-ic-w" src="SystemAO/desktop/img/icons/settings_white.svg">
- </span>
- <span>Settings</span>
- </button>
- </div>
- <div class="cp-sep"></div>
- <div class="cp-list-lbl">Other Accounts</div>
- <div id="cp-acc-list"></div>
- <div class="cp-btns">
- <button class="cp-btn" onclick="cpAddAccount()">
- <span class="cp-btn-ic">
- <img class="cp-ic-b" src="SystemAO/desktop/img/icons/switch_user_black.svg">
- <img class="cp-ic-w" src="SystemAO/desktop/img/icons/switch_user_white.svg">
- </span>
- Add / Switch Account
- </button>
- <button class="cp-btn red" onclick="cpLogout()">
- <span class="cp-btn-ic">
- <img class="cp-ic-b" src="SystemAO/desktop/img/icons/signout_black.svg">
- <img class="cp-ic-w" src="SystemAO/desktop/img/icons/signout_white.svg">
- </span>
- Sign Out
- </button>
- </div>
- </div>
- <!-- Launchpad -->
- <div id="launchpad">
- <!-- Section 1: Home -->
- <div class="lp-section" id="sec-home">
- <div id="clock-widget">
- <div id="clock-time">00:00</div>
- <div id="clock-date">Saturday, January 1</div>
- </div>
- <div id="home-hint">
- <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
- <path d="M9 13V5M5 9L9 5L13 9" stroke="rgba(255,255,255,0.55)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
- </svg>
- Swipe up for apps
- </div>
- <div id="dock">
- <div id="dock-inner">
- <div class="dock-item" onclick="launchShortcut(1)"><img id="d1" src="img/mobile/pending.svg"><span class="dock-label" id="dl1"></span></div>
- <div class="dock-item" onclick="launchShortcut(2)"><img id="d2" src="img/mobile/pending.svg"><span class="dock-label" id="dl2"></span></div>
- <div class="dock-item" onclick="launchShortcut(3)"><img id="d3" src="img/mobile/pending.svg"><span class="dock-label" id="dl3"></span></div>
- <div class="dock-item" onclick="launchShortcut(4)"><img id="d4" src="img/mobile/pending.svg"><span class="dock-label" id="dl4"></span></div>
- </div>
- </div>
- </div>
- <!-- Section 2: Apps -->
- <div class="lp-section" id="sec-apps">
- <div id="apps-header">
- <h2>Apps</h2>
- <button id="apps-edit-btn" onclick="toggleEdit()">Edit</button>
- </div>
- <div id="app-search-wrap">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
- <circle cx="6.5" cy="6.5" r="4.5" stroke="rgba(255,255,255,0.65)" stroke-width="1.4"/>
- <path d="M10 10L14 14" stroke="rgba(255,255,255,0.65)" stroke-width="1.4" stroke-linecap="round"/>
- </svg>
- <input id="app-search-input" type="text" placeholder="Search apps…" oninput="filterApps(this.value)" autocomplete="off">
- </div>
- <div id="app-grid-wrap"><div id="app-grid"></div></div>
- </div>
- <!-- Section 3: Files -->
- <div class="lp-section" id="sec-files">
- <div id="files-header"><h2>Desktop</h2><p>Folders & Shortcuts</p></div>
- <div id="files-grid-wrap">
- <div id="files-grid"></div>
- <div id="files-empty">No folders or shortcuts on desktop</div>
- </div>
- </div>
- </div>
- <!-- Drag ghost -->
- <div id="drag-ghost"><img id="drag-ghost-img" src=""></div>
- <!-- Float window layer -->
- <div id="fw-layer">
- <div id="fw-bar">
- <button id="fw-back" onclick="fwBack()">
- <svg width="13" height="13" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8L10 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
- Back
- </button>
- <div id="fw-tabs"></div>
- </div>
- <div id="fw-body"></div>
- </div>
- <!-- FW tab context menu -->
- <div id="fw-ctx-overlay" onclick="fwCtxClose()"></div>
- <div id="fw-ctx-menu">
- <button class="fw-ctx-item" onclick="fwCtxNewTab()">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
- <path d="M6 3H3a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
- <path d="M9 2h5v5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
- <path d="M14 2L8 8" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
- </svg>
- Open in New Tab
- </button>
- <button class="fw-ctx-item" onclick="fwCtxCopy()">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
- <rect x="5" y="5" width="9" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/>
- <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-7A1.5 1.5 0 0 0 1 3.5v7A1.5 1.5 0 0 0 2.5 12H4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
- </svg>
- Copy URL
- </button>
- <button class="fw-ctx-item red" onclick="fwCtxCloseWindow()">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
- <circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.4"/>
- <path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
- </svg>
- Close Window
- </button>
- </div>
- <!-- Connection indicator -->
- <div id="conndrop">
- <img src="SystemAO/desktop/icons/connlost.svg">
- No connection
- </div>
- <script>
- /* ============================================================
- ArozOS Mobile Interface — Launchpad Design
- Maintains full float-window API compatibility with desktop.html
- ============================================================ */
- /* ── Virtual desktop flag (read by ao_module.js in loaded iframes) ── */
- window.isDesktopMode = true;
- window.ime = null;
- /* ── State ── */
- var moduleInstalled = [];
- var desktopThemeList = [];
- var userInfo = {};
- var shortcuts = {1:null, 2:null, 3:null, 4:null};
- var currentSec = 0;
- var isDark = false;
- var isEditMode = false;
- var appOrder = []; // ordered array of active module names
- var removedApps = []; // module names the user has hidden
- var dragSrcName = null;
- var dragTargetName = null;
- var dragGhostOff = {x:0, y:0};
- var isDragging = false;
- var _autoScrollRaf = null;
- var _autoScrollDir = 0;
- /* ── Locale ── */
- var mLoc = (typeof NewAppLocale === "function") ? NewAppLocale() : {getString:function(k,d){return d;},init:function(f,cb){if(cb)cb();},translate:function(){}};
- mLoc.init("SystemAO/locale/desktop.json", function(){ mLoc.translate(); });
- /* ── Clock data (must be defined before clockLoop() is called below) ── */
- var _mon=["January","February","March","April","May","June","July","August","September","October","November","December"];
- var _day=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
- /* ── Boot ── */
- initTheme();
- initBackdrop();
- initAccentColor();
- initModules();
- initShortcuts();
- initUserInfo();
- initFiles();
- initStartupAudio();
- clockLoop();
- connCheck();
- goSec(0); // initialise section positions so they don't all stack at translateY(0)
- $.get("system/desktop/host", function(d){ if(d.Hostname) document.title = d.Hostname; });
- /* ─────────────────────────────────────────────
- THEME
- ───────────────────────────────────────────── */
- function initTheme(){
- $.get("system/file_system/preference?key=file_explorer/theme", function(d){
- setDarkMode(d === "darkTheme");
- });
- }
- function setDarkMode(dark){
- isDark = dark;
- $("body").toggleClass("dark-mode", dark);
- if(dark){
- // Panel is dark → use white icon; button shows "Light Mode" (click to leave dark)
- $("#cp-dark-ic").attr("src","SystemAO/desktop/img/icons/darkmode_white.svg");
- $("#cp-dark-lbl").text("Light Mode");
- $("#cp-dark-btn").addClass("on");
- }else{
- // Panel is light → use black icon; button shows "Dark Mode" (click to enter dark)
- $("#cp-dark-ic").attr("src","SystemAO/desktop/img/icons/lightmode_black.svg");
- $("#cp-dark-lbl").text("Dark Mode");
- $("#cp-dark-btn").removeClass("on");
- }
- }
- /* Called by parent.desktopThemeChanged — not applicable here (standalone page),
- but defined for forward-compat if ever embedded */
- window.desktopThemeChanged = function(theme){ setDarkMode(theme === "dark"); };
- /* ─────────────────────────────────────────────
- WALLPAPER / ACCENT
- ───────────────────────────────────────────── */
- function initBackdrop(){
- $.get("system/desktop/theme", function(list){
- desktopThemeList = list;
- $.get("system/desktop/theme?get=true", function(t){ changeDesktopTheme(t); });
- });
- }
- function changeDesktopTheme(t){
- if(!t.includes(":/")){
- for(var i=0;i<desktopThemeList.length;i++){
- if(desktopThemeList[i].Theme===t){
- var bg = desktopThemeList[i].Bglist[0];
- $("#backdrop").css("background-image","url(img/desktop/bg/"+t+"/"+bg+")");
- return;
- }
- }
- }else{
- $.get("system/desktop/theme?load="+t,function(d){
- if(d.error){changeDesktopTheme("default");return;}
- $("#backdrop").css("background-image","url(media/?file="+d[0]+")");
- });
- }
- }
- function initAccentColor(){
- pref("themecolor",function(c){ if(c&&!c.error) setThemeColor(c); });
- }
- function setThemeColor(c){
- /* Backward-compat: modules call parent.setThemeColor() */
- document.documentElement.style.setProperty("--accent",c);
- }
- /* ─────────────────────────────────────────────
- MODULE LIST + APP GRID
- ───────────────────────────────────────────── */
- function initModules(){
- $.ajax({url:"system/modules/list",success:function(data){
- moduleInstalled = data;
- var saved = localStorage.getItem("mobile/appOrder");
- if(saved){try{appOrder=JSON.parse(saved);}catch(e){appOrder=[];}}
- var savedRm = localStorage.getItem("mobile/removedApps");
- if(savedRm){try{removedApps=JSON.parse(savedRm);}catch(e){removedApps=[];}}
- renderGrid("");
- }});
- }
- function getOrderedModules(){
- var result = [];
- appOrder.forEach(function(n){
- var m=moduleInstalled.find(function(x){return x.Name===n;});
- if(m) result.push(m);
- });
- // Auto-include newly installed modules (not in appOrder and not explicitly removed)
- moduleInstalled.forEach(function(m){
- if(m.StartDir
- && !result.find(function(x){return x.Name===m.Name;})
- && removedApps.indexOf(m.Name)===-1){
- result.push(m);
- }
- });
- return result;
- }
- function renderGrid(filter){
- var grid = $("#app-grid"); grid.empty();
- // Rebuild appOrder from active (non-removed) modules when not filtering
- if(!filter){
- appOrder = getOrderedModules()
- .filter(function(m){return !!m.StartDir;})
- .map(function(m){return m.Name;});
- }
- var idx=0;
- getOrderedModules().forEach(function(m){
- if(!m.StartDir) return;
- if(filter && !m.Name.toLowerCase().includes(filter.toLowerCase())) return;
- var ic = m.IconPath||"img/system/service.png";
- var name = escH(m.Name);
- var attr = escA(m.Name);
- grid.append('<div class="app-item" data-mod="'+attr+'" data-i="'+idx+'">'
- +'<div class="app-icon-wrap">'
- +'<img src="'+escA(ic)+'" onerror="this.src=\'img/system/service.png\'">'
- +'<div class="app-del" onclick="appDelTap(event,\''+attr+'\')">'
- +'<img src="SystemAO/desktop/img/icons/close_white.svg">'
- +'</div>'
- +'</div>'
- +'<span class="app-name">'+name+'</span>'
- +'</div>');
- idx++;
- });
- // In edit mode (no filter active) show removed apps as ghost tiles at the bottom
- if(isEditMode && !filter){
- removedApps.forEach(function(name){
- var m=moduleInstalled.find(function(x){return x.Name===name;});
- if(!m||!m.StartDir) return;
- var ic = m.IconPath||"img/system/service.png";
- var attr = escA(m.Name);
- grid.append('<div class="app-item app-removed" data-mod="'+attr+'">'
- +'<div class="app-icon-wrap">'
- +'<img src="'+escA(ic)+'" onerror="this.src=\'img/system/service.png\'">'
- +'<div class="app-restore" onclick="appRestoreTap(event,\''+attr+'\')">'
- +'<img src="SystemAO/desktop/img/icons/add_white.svg">'
- +'</div>'
- +'</div>'
- +'<span class="app-name">'+escH(m.Name)+'</span>'
- +'</div>');
- });
- }
- bindAppTouch();
- }
- function filterApps(q){
- if(isEditMode) exitEdit();
- renderGrid(q||"");
- }
- function bindAppTouch(){
- $("#app-grid .app-item").off("touchstart pointerdown").on("touchstart pointerdown",function(e){
- if(!isEditMode) return;
- // Don't start drag when the touch lands on a badge button
- if($(e.target).closest(".app-del,.app-restore").length) return;
- startDrag(e,this);
- });
- $("#app-grid .app-item").off("click").on("click",function(){
- if(isEditMode)return;
- openModule($(this).data("mod"));
- });
- }
- /* ── Edit mode ── */
- function toggleEdit(){ isEditMode ? exitEdit() : enterEdit(); }
- function enterEdit(){
- isEditMode=true;
- $("#apps-edit-btn").text("Done").addClass("on");
- renderGrid(""); // rebuild DOM so ghost tiles appear
- // Apply edit-mode classes to the freshly created active items
- $("#app-grid .app-item:not(.app-removed)").addClass("jiggling editing");
- }
- function exitEdit(){
- isEditMode=false;
- $("#apps-edit-btn").text("Edit").removeClass("on");
- localStorage.setItem("mobile/appOrder",JSON.stringify(appOrder));
- renderGrid(""); // rebuild DOM to remove ghost tiles
- }
- function appDelTap(e,name){
- e.stopPropagation();
- var i=appOrder.indexOf(name);
- if(i>-1) appOrder.splice(i,1);
- if(removedApps.indexOf(name)===-1) removedApps.push(name);
- localStorage.setItem("mobile/appOrder",JSON.stringify(appOrder));
- localStorage.setItem("mobile/removedApps",JSON.stringify(removedApps));
- enterEdit(); // enterEdit renders + applies edit classes
- }
- function appRestoreTap(e,name){
- e.stopPropagation();
- var i=removedApps.indexOf(name);
- if(i>-1) removedApps.splice(i,1);
- if(appOrder.indexOf(name)===-1) appOrder.push(name);
- localStorage.setItem("mobile/appOrder",JSON.stringify(appOrder));
- localStorage.setItem("mobile/removedApps",JSON.stringify(removedApps));
- enterEdit(); // enterEdit renders + applies edit classes
- }
- /* ── Drag-to-reorder ── */
- function startDrag(e,el){
- e.preventDefault();
- isDragging=true;
- dragSrcName=$(el).data("mod");
- dragTargetName=null;
- var t=e.touches?e.touches[0]:e;
- var r=el.getBoundingClientRect();
- dragGhostOff={x:t.clientX-r.left,y:t.clientY-r.top};
- $("#drag-ghost-img").attr("src",$(el).find("img").first().attr("src"));
- $("#drag-ghost").css({display:"block",left:t.clientX-dragGhostOff.x,top:t.clientY-dragGhostOff.y});
- $(el).addClass("dragging");
- $(document).on("touchmove.dg pointermove.dg",onDragMove);
- $(document).on("touchend.dg pointerup.dg",onDragEnd);
- }
- function onDragMove(e){
- if(!isDragging)return;
- var t=e.touches?e.touches[0]:e;
- var gx=t.clientX-dragGhostOff.x, gy=t.clientY-dragGhostOff.y;
- $("#drag-ghost").css({left:gx,top:gy});
- /* ── auto-scroll when ghost nears the top/bottom edge of the list ── */
- var wrap=document.getElementById("app-grid-wrap");
- var wr=wrap.getBoundingClientRect();
- var threshold=60;
- var prev=_autoScrollDir;
- if(gy<wr.top+threshold) _autoScrollDir=-1;
- else if(gy+60>wr.bottom-threshold) _autoScrollDir=1;
- else _autoScrollDir=0;
- if(_autoScrollDir!==0&&prev===0) _runAutoScroll();
- /* ── highlight drop target (no re-render during drag) ── */
- var found=null;
- $("#app-grid .app-item").each(function(){
- var r=this.getBoundingClientRect();
- if(t.clientX>=r.left&&t.clientX<=r.right&&t.clientY>=r.top&&t.clientY<=r.bottom){
- var n=$(this).data("mod");
- if(n!==dragSrcName) found=n;
- }
- });
- if(found!==dragTargetName){
- $(".app-item").removeClass("drag-target");
- dragTargetName=found;
- if(found) $(".app-item[data-mod='"+found+"']").addClass("drag-target");
- }
- }
- function _runAutoScroll(){
- if(!isDragging||_autoScrollDir===0){_autoScrollRaf=null;return;}
- document.getElementById("app-grid-wrap").scrollTop+=_autoScrollDir*5;
- _autoScrollRaf=requestAnimationFrame(_runAutoScroll);
- }
- function onDragEnd(){
- isDragging=false;
- _autoScrollDir=0;
- if(_autoScrollRaf){cancelAnimationFrame(_autoScrollRaf);_autoScrollRaf=null;}
- $("#drag-ghost").hide();
- $(document).off("touchmove.dg pointermove.dg touchend.dg pointerup.dg");
- /* commit reorder on drop */
- if(dragTargetName){
- var a=appOrder.indexOf(dragSrcName), b=appOrder.indexOf(dragTargetName);
- if(a>-1&&b>-1){appOrder.splice(a,1);appOrder.splice(b,0,dragSrcName);}
- }
- localStorage.setItem("mobile/appOrder",JSON.stringify(appOrder));
- renderGrid(""); enterEdit();
- }
- /* ─────────────────────────────────────────────
- DESKTOP FILES (Section 3)
- ───────────────────────────────────────────── */
- function initFiles(){
- $.get("system/desktop/listDesktop",function(data){
- if(!data||data.error){$("#files-empty").show();return;}
- // URL shortcuts shown first
- var urlShortcuts=data.filter(function(f){return f.IsShortcut&&f.ShortcutType==="url";});
- // Folders shown after
- var folders=data.filter(function(f){return f.IsDir&&!f.IsShortcut;});
- if(!urlShortcuts.length&&!folders.length){$("#files-empty").show();return;}
- urlShortcuts.forEach(function(f){
- var icon=f.ShortcutImage||"";
- var name=escH(f.ShortcutName||f.Filename);
- var url=escA(f.ShortcutPath);
- $("#files-grid").append(
- '<div class="folder-item" onclick="openUrlShortcut(\''+url+'\')">'
- +'<img class="shortcut-icon" src="'+escA(icon)+'" onerror="this.src=\'img/system/favicon.png\'">'
- +'<span class="folder-name">'+name+'</span>'
- +'</div>'
- );
- });
- folders.forEach(function(f){
- $("#files-grid").append(
- '<div class="folder-item" onclick="openFolder(\''+escA(f.Filepath)+'\')">'
- +'<img class="folder-emoji" src="SystemAO/desktop/img/icons/folder_blue.svg">'
- +'<span class="folder-name">'+escH(f.Filename)+'</span>'
- +'</div>'
- );
- });
- });
- }
- function openFolder(fp){
- window.open("SystemAO/file_system/file_explorer.html#"+encodeURIComponent(fp),"_blank");
- }
- function openUrlShortcut(url){
- window.open(url,"_blank");
- }
- /* ─────────────────────────────────────────────
- DOCK SHORTCUTS
- ───────────────────────────────────────────── */
- function initShortcuts(){[1,2,3,4].forEach(function(i){initShortcut(i);});}
- function initShortcut(id){
- pref("ao/mobile/shorcut/"+id,function(name){
- if(!name){$("#d"+id).attr("src","img/mobile/pending.svg");return;}
- $.get("system/modules/getLaunchPara?module="+encodeURIComponent(name),function(p){
- if(p.error)return;
- shortcuts[id]=p;
- $("#d"+id).attr("src",p.IconPath||"img/system/service.png");
- $("#dl"+id).text(p.Name);
- });
- });
- }
- function launchShortcut(id){if(shortcuts[id])openModule(shortcuts[id].Name);}
- /* ─────────────────────────────────────────────
- SECTION NAVIGATION
- ───────────────────────────────────────────── */
- function goSec(n){
- if(n<0||n>2)return;
- if(n!==1&&isEditMode)exitEdit();
- currentSec=n;
- ["#sec-home","#sec-apps","#sec-files"].forEach(function(s,i){
- $(s).css("transform","translateY("+(i-n)*100+"%)");
- });
- $(".pd").removeClass("on");
- $(".pd[data-s='"+n+"']").addClass("on");
- }
- /* Swipe handling per-section */
- var _sy=0;
- function _trackStart(e){_sy=e.touches?e.touches[0].clientY:e.clientY;}
- function _swipeDelta(e){var y=e.changedTouches?e.changedTouches[0].clientY:e.clientY;return _sy-y;}
- // Section 1: anywhere swipe up → section 2
- $("#sec-home").on("touchstart",function(e){_sy=e.touches[0].clientY;})
- .on("touchend",function(e){if(_swipeDelta(e)>55)goSec(1);});
- // Section 2 header: swipe down → sec 1, swipe up → sec 3
- $("#apps-header,#app-search-wrap").on("touchstart",function(e){_sy=e.touches[0].clientY;})
- .on("touchend",function(e){var d=_swipeDelta(e);if(d<-55)goSec(0);else if(d>55)goSec(2);});
- // App grid edge-overscroll
- var _agw=document.getElementById("app-grid-wrap");
- _agw.addEventListener("touchstart",function(e){_sy=e.touches[0].clientY;},{passive:true});
- _agw.addEventListener("touchend",function(e){
- var d=_sy-e.changedTouches[0].clientY;
- if(_agw.scrollTop<=0&&d<-70)goSec(0);
- if(_agw.scrollTop+_agw.clientHeight>=_agw.scrollHeight-2&&d>70)goSec(2);
- },{passive:true});
- // Section 3 header: swipe down → sec 2
- $("#files-header").on("touchstart",function(e){_sy=e.touches[0].clientY;})
- .on("touchend",function(e){if(_swipeDelta(e)<-55)goSec(1);});
- var _fgw=document.getElementById("files-grid-wrap");
- _fgw.addEventListener("touchstart",function(e){_sy=e.touches[0].clientY;},{passive:true});
- _fgw.addEventListener("touchend",function(e){
- if(_fgw.scrollTop<=0&&(_sy-e.changedTouches[0].clientY)<-70)goSec(1);
- },{passive:true});
- /* ─────────────────────────────────────────────
- CLOCK
- ───────────────────────────────────────────── */
- var _mon=["January","February","March","April","May","June","July","August","September","October","November","December"];
- var _day=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
- function clockLoop(){updateClock();setInterval(updateClock,15000);}
- function updateClock(){
- var d=new Date();
- $("#clock-time").text(pad2(d.getHours())+":"+pad2(d.getMinutes()));
- $("#clock-date").text(_day[d.getDay()]+", "+_mon[d.getMonth()]+" "+d.getDate());
- }
- function pad2(n){return n<10?"0"+n:""+n;}
- /* ─────────────────────────────────────────────
- USER INFO
- ───────────────────────────────────────────── */
- function initUserInfo(){
- $.get("system/desktop/user",function(d){
- if(d.error)return;
- userInfo=d;
- $("#cp-name").text(d.Username);
- $("#cp-grp").text("@"+(d.UserGroups||[]).join("/"));
- if(d.UserIcon)$("#cp-av").attr("src",d.UserIcon);
- loadCpAccounts();
- });
- }
- function loadCpAccounts(){
- $("#cp-acc-list").empty();
- $.get("system/auth/u/list",function(data){
- if(!data||data.error)return;
- data.forEach(function(ac){
- if(ac.Username===userInfo.Username)return;
- $.get("system/desktop/user?target="+ac.Username,function(ud){
- var ic=ud.UserIcon||"img/desktop/system_icon/user.svg";
- $("#cp-acc-list").append(
- '<div class="cp-acc-item" onclick="cpSwitch(\''+escA(ac.Username)+'\')">'
- +'<img src="'+escA(ic)+'">'
- +'<span class="cp-acc-name">'+escH(ac.Username)+(ac.IsExpired?" (expired)":"")+"</span>"
- +'</div>'
- );
- });
- });
- });
- }
- /* ─────────────────────────────────────────────
- CONTROL PANEL
- ───────────────────────────────────────────── */
- function cpToggle(){cpOpen?cpClose():cpShow();}
- var cpOpen=false;
- function cpShow(){cpOpen=true;$("#cp-panel,#cp-backdrop").addClass("on");}
- function cpClose(){cpOpen=false;$("#cp-panel,#cp-backdrop").removeClass("on");}
- function cpToggleDark(){
- setDarkMode(!isDark);
- $.get("system/file_system/preference?key=file_explorer/theme&value="+(isDark?"darkTheme":"whiteTheme"));
- }
- function cpFullscreen(){
- cpClose();
- var el=document.documentElement;
- if(!document.fullscreenElement){(el.requestFullscreen||el.webkitRequestFullscreen||el.mozRequestFullScreen).call(el);}
- else{(document.exitFullscreen||document.webkitExitFullscreen||document.mozCancelFullScreen).call(document);}
- }
- function cpWallpaper(){
- cpClose();
- newFloatWindow({
- url:"SystemAO/system_setting/index.html#"+encodeURIComponent(JSON.stringify({group:"Desktop",name:"Wallpaper"})),
- appicon:"SystemAO/desktop/img/personalization.png",
- title:"System Settings"
- });
- }
- function cpSettings(){cpClose();openModule("System Setting");}
- function cpLogout(){
- cpClose();
- if(confirm(mLoc.getString("quickAccess/logout/confirm","Sign out of ArozOS?"))){
- $.get("system/auth/logout",function(){window.location.href="/";});
- }
- }
- function cpAddAccount(){
- cpClose();
- newFloatWindow({url:"SystemAO/advance/switchAccount.html",appicon:"SystemAO/desktop/img/account-switch.png",title:"Switch Account"});
- }
- function cpSwitch(username){
- cpClose();
- $.ajax({url:"system/auth/u/switch",data:{username:username},success:function(d){
- if(d.error)alert(d.error);else window.location.reload();
- }});
- }
- /* ─────────────────────────────────────────────
- MODULE OPENING
- ───────────────────────────────────────────── */
- function openModule(name){
- $.get("system/modules/getLaunchPara?module="+encodeURIComponent(name),function(d){
- if(d.error){if(d.error==="Not logged in.")window.location.href="login.html";return;}
- var url=d.StartDir, ic=d.IconPath||"img/system/favicon.png";
- if(d.SupportFW&&d.LaunchFWDir)url=d.LaunchFWDir;
- newFloatWindow({url:url,appicon:ic,title:d.Name});
- });
- }
- function openFileWithModule(info,files){
- var url=info.StartDir, ic=info.IconPath||"img/system/favicon.png";
- if(info.SupportFW&&info.LaunchFWDir)url=info.LaunchFWDir;
- if(info.SupportEmb&&info.LaunchEmb)url=info.LaunchEmb;
- newFloatWindow({url:url+"#"+encodeURIComponent(JSON.stringify(files)),appicon:ic,title:info.Name});
- }
- /* ─────────────────────────────────────────────
- FLOAT WINDOW LAYER (full API compatibility)
- ───────────────────────────────────────────── */
- function newFloatWindow(opts){
- var wid = Date.now()+"";
- var par = opts.parent||"", cb = opts.callback||"";
- var url = opts.url||"", title = opts.title||"Window", ic = opts.appicon||"img/system/favicon.png";
- // Structure: .floatWindowWrapper > .floatWindow[windowId,parent,callback] > .fwtab > iframe
- var wrap=$('<div class="floatWindowWrapper" windowId="'+wid+'">'
- +'<div class="floatWindow" windowId="'+wid+'" parent="'+escA(par)+'" callback="'+escA(cb)+'">'
- +'<div class="fwtab">'
- +'<iframe src="'+escA(url)+'" allow="fullscreen"></iframe>'
- +'</div></div></div>');
- $("#fw-body").append(wrap);
- // Hide others, show this
- $(".floatWindowWrapper").removeClass("on").hide();
- wrap.addClass("on").show();
- // Tab button
- var tab=$('<div class="fw-tab on" data-wid="'+wid+'">'
- +'<img src="'+escA(ic)+'" onerror="this.src=\'img/system/favicon.png\'">'
- +'<span class="fw-tab-title">'+escH(title)+'</span>'
- +'<span class="fw-tab-x" onclick="fwTabClose(event,\''+wid+'\')">×</span>'
- +'</div>');
- tab.on("click",function(e){if(!$(e.target).hasClass("fw-tab-x"))fwFocus(wid);});
- $("#fw-tabs .fw-tab").removeClass("on");
- $("#fw-tabs").append(tab);
- $("#fw-layer").addClass("on");
- return wid;
- }
- function fwBack(){
- // Close all windows and return to launchpad
- $(".floatWindowWrapper").each(function(){closeFwProcess($(this).attr("windowId"));});
- $("#fw-layer").removeClass("on");
- }
- function fwFocus(wid){
- $(".floatWindowWrapper").removeClass("on").hide();
- $(".fw-tab").removeClass("on");
- var fw=getFloatWindowByID(wid);
- if(fw)fw.addClass("on").show();
- $(".fw-tab[data-wid='"+wid+"']").addClass("on");
- }
- function fwTabClose(e,wid){e.stopPropagation();closeFloatWindow(wid);}
- function closeFwProcess(wid){
- var prev=getPreviousWindowObject(wid);
- $(".floatWindowWrapper[windowId='"+wid+"']").remove();
- $(".fw-tab[data-wid='"+wid+"']").remove();
- if(!$(".floatWindowWrapper").length){
- $("#fw-layer").removeClass("on");
- }else if(prev){
- fwFocus(prev);
- }else{
- fwFocus($(".floatWindowWrapper").last().attr("windowId"));
- }
- }
- function closeFloatWindow(wid){
- var fw=getFloatWindowByID(wid);
- if(!fw)return;
- try{
- var cw=fw.find("iframe")[0].contentWindow;
- if(cw&&cw.ao_module_close){cw.ao_module_close();return;}
- }catch(e){}
- closeFwProcess(wid);
- }
- function getFloatWindowByID(id){
- var f=null;
- $(".floatWindowWrapper").each(function(){if($(this).attr("windowId")===""+id)f=$(this);});
- return f;
- }
- function MoveFloatWindowToTop(fw){
- var id=$(fw).attr("windowId")||$(fw).find(".floatWindow").attr("windowId");
- if(id)fwFocus(id);
- }
- function setFloatWindowTitle(id,t){$(".fw-tab[data-wid='"+id+"'] .fw-tab-title").text(t);}
- function setFloatWindowResizePolicy(){}
- function setFloatWindowSize(){}
- function setFloatWindowMinimizePolicy(){}
- function PinFloatWindowToTopMostMode(){}
- function UnpinFloatWindowFromTopMostMode(){}
- function bindObjectToIMEEvents(){}
- function getPreviousWindowObject(wid){
- var prev=null,res=null;
- $(".fw-tab").each(function(){
- var w=$(this).data("wid");
- if(w===wid)res=prev;else prev=w;
- });
- return res;
- }
- /* Legacy taskbar stubs (no sidebar in new design) */
- function showTaskBar(){}
- function hideTaskBar(){}
- function showShortcuts(){}
- function hideShortcuts(){}
- function showDesktop(){$("#fw-layer").removeClass("on");}
- function openDesktopCustomization(){cpWallpaper();}
- function openDesktopAsFolder(){
- window.open("SystemAO/file_system/file_explorer.html#"+encodeURIComponent("user:/Desktop/"),"_blank");
- }
- function openSystemSettings(){cpSettings();}
- function initDesktop(){window.location.reload();}
- function fullscreen(){cpFullscreen();}
- function logout(){cpLogout();}
- function toggleProfileInfo(){}
- /* ─────────────────────────────────────────────
- STARTUP AUDIO
- ───────────────────────────────────────────── */
- function initStartupAudio(){
- pref("startup-audio",function(d){
- if(!d)return;
- var v=parseFloat(localStorage.getItem("global_volume"))||0.7;
- var a=new Audio("media?file="+d);
- a.volume=v; a.play().catch(function(){});
- });
- }
- /* ─────────────────────────────────────────────
- STORAGE HELPERS
- ───────────────────────────────────────────── */
- function pref(key,cb){
- $.ajax({url:"system/desktop/preference",method:"POST",data:{preference:key},success:cb});
- }
- function setPref(key,val,cb){
- $.ajax({url:"system/desktop/preference",method:"POST",data:{preference:key,value:val},
- success:function(d){if(!d.error&&cb)cb();}});
- }
- function getStorage(key,cb){pref(key,cb);}
- function setStorage(key,val,cb){setPref(key,val,cb);}
- /* ─────────────────────────────────────────────
- CONNECTION CHECK
- ───────────────────────────────────────────── */
- function connCheck(){
- setInterval(function(){
- $.ajax({url:"system/auth/checkLogin",timeout:15000,
- success:function(d){if(!d)window.location.href="index.html";$("#conndrop").hide();},
- error:function(){$("#conndrop").css("display","flex");}
- });
- },15000);
- }
- /* ─────────────────────────────────────────────
- UTILITY
- ───────────────────────────────────────────── */
- function escH(s){return $("<span>").text(s||"").html();}
- function escA(s){return(s||"").replace(/"/g,""").replace(/'/g,"'");}
- /* ─────────────────────────────────────────────
- FW TAB LONG-PRESS CONTEXT MENU
- ───────────────────────────────────────────── */
- var _fwCtxWid = null;
- var _fwLongPressTimer = null;
- var _fwLongPressFired = false;
- (function(){
- var fwTabs = document.getElementById("fw-tabs");
- var startX, startY;
- fwTabs.addEventListener("touchstart", function(e){
- var tab = e.target.closest(".fw-tab");
- if(!tab || e.target.closest(".fw-tab-x")) return;
- var wid = tab.getAttribute("data-wid");
- var touch = e.touches[0];
- startX = touch.clientX; startY = touch.clientY;
- _fwLongPressFired = false;
- _fwLongPressTimer = setTimeout(function(){
- _fwLongPressFired = true;
- fwCtxShow(wid, startX, startY);
- if(navigator.vibrate) navigator.vibrate(35);
- }, 500);
- }, {passive: true});
- fwTabs.addEventListener("touchmove", function(e){
- if(!_fwLongPressTimer) return;
- var t = e.touches[0];
- if(Math.abs(t.clientX - startX) > 8 || Math.abs(t.clientY - startY) > 8){
- clearTimeout(_fwLongPressTimer);
- _fwLongPressTimer = null;
- }
- }, {passive: true});
- fwTabs.addEventListener("touchend", function(){
- clearTimeout(_fwLongPressTimer);
- _fwLongPressTimer = null;
- }, {passive: true});
- // Suppress the click that follows a long-press
- fwTabs.addEventListener("click", function(e){
- if(_fwLongPressFired){
- _fwLongPressFired = false;
- e.stopPropagation();
- e.preventDefault();
- }
- }, true);
- })();
- function fwCtxShow(wid, x, y){
- _fwCtxWid = wid;
- var menu = document.getElementById("fw-ctx-menu");
- var menuW = 224, menuH = 150;
- var left = Math.min(x, window.innerWidth - menuW - 10);
- var top = Math.min(y, window.innerHeight - menuH - 10);
- left = Math.max(10, left);
- top = Math.max(10, top);
- menu.style.left = left + "px";
- menu.style.top = top + "px";
- document.getElementById("fw-ctx-overlay").classList.add("on");
- menu.style.display = "block";
- // re-trigger animation
- menu.style.animation = "none";
- menu.offsetHeight; // reflow
- menu.style.animation = "";
- }
- function fwCtxClose(){
- _fwCtxWid = null;
- document.getElementById("fw-ctx-overlay").classList.remove("on");
- document.getElementById("fw-ctx-menu").style.display = "none";
- }
- function fwCtxGetUrl(){
- if(!_fwCtxWid) return null;
- var fw = getFloatWindowByID(_fwCtxWid);
- if(!fw) return null;
- try{ return fw.find("iframe")[0].src || null; }catch(e){ return null; }
- }
- function fwCtxNewTab(){
- var url = fwCtxGetUrl();
- fwCtxClose();
- if(url) window.open(url, "_blank");
- }
- function fwCtxCopy(){
- var url = fwCtxGetUrl();
- fwCtxClose();
- if(!url) return;
- if(navigator.clipboard){
- navigator.clipboard.writeText(url).catch(function(){});
- } else {
- var ta = document.createElement("textarea");
- ta.value = url; ta.style.cssText = "position:fixed;opacity:0";
- document.body.appendChild(ta); ta.select();
- document.execCommand("copy"); document.body.removeChild(ta);
- }
- }
- function fwCtxCloseWindow(){
- var wid = _fwCtxWid;
- fwCtxClose();
- if(wid) closeFloatWindow(wid);
- }
- </script>
- </body>
- </html>
|