Преглед на файлове

Optimized desktop and mobile UX

Toby Chui преди 2 седмици
родител
ревизия
43c19bd32b

+ 30 - 0
src/desktop.go

@@ -51,6 +51,36 @@ func DesktopInit() {
 		os.Exit(1)
 	}
 
+	//Register Desktop settings sub-items
+	registerSetting(settingModule{
+		Name:     "Wallpaper",
+		Desc:     "Desktop Wallpaper Settings",
+		IconPath: "SystemAO/desktop/img/personalization.png",
+		Group:    "Desktop",
+		StartDir: "SystemAO/desktop/settings/wallpaper.html",
+	})
+	registerSetting(settingModule{
+		Name:     "Sounds",
+		Desc:     "System Sound Settings",
+		IconPath: "SystemAO/desktop/img/personalization.png",
+		Group:    "Desktop",
+		StartDir: "SystemAO/desktop/settings/sounds.html",
+	})
+	registerSetting(settingModule{
+		Name:     "Theme",
+		Desc:     "System Theme Color",
+		IconPath: "SystemAO/desktop/img/personalization.png",
+		Group:    "Desktop",
+		StartDir: "SystemAO/desktop/settings/theme.html",
+	})
+	registerSetting(settingModule{
+		Name:     "Mobile UX",
+		Desc:     "Mobile Desktop Shortcuts",
+		IconPath: "SystemAO/desktop/img/personalization.png",
+		Group:    "Desktop",
+		StartDir: "SystemAO/desktop/settings/mobile_ux.html",
+	})
+
 	//Register Desktop Module
 	moduleHandler.RegisterModule(module.ModuleInfo{
 		Name:        "Desktop",

+ 6 - 0
src/setting.go

@@ -69,6 +69,12 @@ func system_setting_getSettingGroups() []settingGroup {
 			IconPath: "SystemAO/system_setting/img/module.svg",
 			Desc:     "List of modules loaded in the system",
 		},
+		{
+			Name:     "Desktop & Themes",
+			Group:    "Desktop",
+			IconPath: "SystemAO/desktop/img/personalization.png",
+			Desc:     "Personalize your desktop experience",
+		},
 		{
 			Name:     "Disk & Storage",
 			Group:    "Disk",

+ 1 - 0
src/web/SystemAO/desktop/img/icons/add_white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#F3F3F3"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>

+ 1 - 0
src/web/SystemAO/desktop/img/icons/close_white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#F3F3F3"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>

+ 1 - 0
src/web/SystemAO/desktop/img/icons/folder_blue.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#7CA7D8"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Z"/></svg>

+ 185 - 0
src/web/SystemAO/desktop/settings/mobile_ux.html

@@ -0,0 +1,185 @@
+<!-- Mobile UX Settings — loaded into SystemAO/system_setting/index.html via jQuery .load() -->
+<style>
+#mux-root {
+    --mux-text:   #1a1a1a;
+    --mux-dim:    #5a5a5a;
+    --mux-muted:  #9a9a9a;
+    --mux-border: rgba(0,0,0,0.10);
+    --mux-green:  #107c10;
+    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+    font-size: 14px;
+    color: var(--mux-text);
+    padding: 4px 0 32px;
+}
+body.dark #mux-root {
+    --mux-text:   #f0f0f0;
+    --mux-dim:    #b0b0b0;
+    --mux-muted:  #808080;
+    --mux-border: rgba(255,255,255,0.10);
+}
+#mux-root h3.section-title {
+    font-size: 15px;
+    font-weight: 600;
+    color: var(--mux-text);
+    margin: 0 0 4px;
+}
+#mux-root .section-desc {
+    font-size: 12px;
+    color: var(--mux-muted);
+    margin: 0 0 12px;
+}
+#mux-root .mux-section {
+    padding: 16px;
+    border-bottom: 1px solid var(--mux-border);
+}
+#mux-root .mux-section:last-child { border-bottom: none; }
+#mux-root .shortcut-label {
+    font-size: 12px;
+    color: var(--mux-dim);
+    margin-bottom: 4px;
+}
+#mux-root .shortcut-row { margin-bottom: 10px; }
+#mux-root .btn {
+    padding: 5px 14px;
+    border-radius: 4px;
+    border: 1px solid var(--mux-border);
+    background: var(--mux-green);
+    color: #fff;
+    font-size: 12px;
+    cursor: pointer;
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+    margin-top: 8px;
+}
+#mux-root .btn:hover { opacity: 0.85; }
+#mux-root .confirm-msg {
+    display: none;
+    margin-top: 8px;
+    padding: 8px 12px;
+    background: rgba(16,124,16,0.1);
+    border-radius: 4px;
+    font-size: 12px;
+    color: var(--mux-green);
+}
+body.dark #mux-root .confirm-msg { background: rgba(16,124,16,0.15); }
+body.dark #mux-root .ui.selection.dropdown {
+    background: #3a3a3a;
+    border-color: rgba(255,255,255,0.15);
+    color: #f0f0f0;
+}
+body.dark #mux-root .ui.dropdown .menu {
+    background: #3a3a3a;
+    border-color: rgba(255,255,255,0.15);
+}
+body.dark #mux-root .ui.dropdown .menu .item {
+    color: #f0f0f0;
+    border-top-color: rgba(255,255,255,0.08);
+}
+body.dark #mux-root .ui.dropdown .menu .item:hover {
+    background: rgba(255,255,255,0.08);
+}
+</style>
+
+<div id="mux-root">
+    <div class="mux-section">
+        <h3 class="section-title" locale="mux/title">Mobile Desktop Shortcuts</h3>
+        <p class="section-desc" locale="mux/desc">Choose the four quick-launch shortcuts shown on the mobile desktop interface, ordered from left to right.</p>
+
+        <div class="shortcut-row">
+            <div class="shortcut-label" locale="mux/sc1">Shortcut 1</div>
+            <div class="ui fluid search selection mux-dropbox dropdown">
+                <input type="hidden" id="mux-sc1">
+                <i class="dropdown icon"></i>
+                <div class="default text" locale="mux/sc1">Shortcut 1</div>
+                <div class="mux-applist menu"></div>
+            </div>
+        </div>
+        <div class="shortcut-row">
+            <div class="shortcut-label" locale="mux/sc2">Shortcut 2</div>
+            <div class="ui fluid search selection mux-dropbox dropdown">
+                <input type="hidden" id="mux-sc2">
+                <i class="dropdown icon"></i>
+                <div class="default text" locale="mux/sc2">Shortcut 2</div>
+                <div class="mux-applist menu"></div>
+            </div>
+        </div>
+        <div class="shortcut-row">
+            <div class="shortcut-label" locale="mux/sc3">Shortcut 3</div>
+            <div class="ui fluid search selection mux-dropbox dropdown">
+                <input type="hidden" id="mux-sc3">
+                <i class="dropdown icon"></i>
+                <div class="default text" locale="mux/sc3">Shortcut 3</div>
+                <div class="mux-applist menu"></div>
+            </div>
+        </div>
+        <div class="shortcut-row">
+            <div class="shortcut-label" locale="mux/sc4">Shortcut 4</div>
+            <div class="ui fluid search selection mux-dropbox dropdown">
+                <input type="hidden" id="mux-sc4">
+                <i class="dropdown icon"></i>
+                <div class="default text" locale="mux/sc4">Shortcut 4</div>
+                <div class="mux-applist menu"></div>
+            </div>
+        </div>
+
+        <button class="btn" onclick="muxSaveShortcuts()">
+            <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M2 8.5L6 12.5L14 4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
+            <span locale="mux/save">Save Shortcuts</span>
+        </button>
+        <div id="mux-save-confirm" class="confirm-msg"><span locale="mux/saved">Shortcuts saved. Open the mobile interface to see the changes.</span></div>
+    </div>
+</div>
+
+<script>
+(function () {
+    var muxLocale = NewAppLocale();
+
+    muxLocale.init("../locale/system_settings/desktop.json", function () {
+        muxLocale.translate();
+        muxLoadModules();
+    });
+
+    function muxReadPref(key, cb) {
+        $.ajax({ url: "../../system/desktop/preference", method: "POST",
+            data: { preference: key }, success: function (d) { if (cb) cb(d); } });
+    }
+
+    function muxWritePref(key, value) {
+        $.ajax({ url: "../../system/desktop/preference", method: "POST",
+            data: { preference: key, value: value } });
+    }
+
+    function muxLoadModules() {
+        $.get("../../system/modules/list", function (data) {
+            $(".mux-applist").html("");
+            data.forEach(function (app) {
+                if (app.Group !== "Interface Module" && app.Group !== "IME" && app.StartDir !== "") {
+                    $(".mux-applist").append('<div class="item" data-value="' + app.Name + '">' + app.Name + '</div>');
+                }
+            });
+
+            $(".mux-dropbox").dropdown();
+
+            muxReadPref("ao/mobile/shorcut/1", function (v) { if (v) { $("#mux-sc1").val(v); $("#mux-sc1").parent().dropdown("set selected", v); } });
+            muxReadPref("ao/mobile/shorcut/2", function (v) { if (v) { $("#mux-sc2").val(v); $("#mux-sc2").parent().dropdown("set selected", v); } });
+            muxReadPref("ao/mobile/shorcut/3", function (v) { if (v) { $("#mux-sc3").val(v); $("#mux-sc3").parent().dropdown("set selected", v); } });
+            muxReadPref("ao/mobile/shorcut/4", function (v) { if (v) { $("#mux-sc4").val(v); $("#mux-sc4").parent().dropdown("set selected", v); } });
+        });
+    }
+
+    window.muxSaveShortcuts = function () {
+        var sc1 = $("#mux-sc1").val();
+        var sc2 = $("#mux-sc2").val();
+        var sc3 = $("#mux-sc3").val();
+        var sc4 = $("#mux-sc4").val();
+        if (sc1) muxWritePref("ao/mobile/shorcut/1", sc1);
+        if (sc2) muxWritePref("ao/mobile/shorcut/2", sc2);
+        if (sc3) muxWritePref("ao/mobile/shorcut/3", sc3);
+        if (sc4) muxWritePref("ao/mobile/shorcut/4", sc4);
+        $("#mux-save-confirm").stop(true).finish().slideDown("fast").delay(3000).slideUp("fast", function () {
+            if (ao_module_virtualDesktop && parent.initShortcuts) parent.initShortcuts();
+        });
+    };
+}());
+</script>

+ 128 - 0
src/web/SystemAO/desktop/settings/sounds.html

@@ -0,0 +1,128 @@
+<!-- Sounds Settings — loaded into SystemAO/system_setting/index.html via jQuery .load() -->
+<style>
+#snd-root {
+    --snd-text:   #1a1a1a;
+    --snd-dim:    #5a5a5a;
+    --snd-muted:  #9a9a9a;
+    --snd-border: rgba(0,0,0,0.10);
+    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+    font-size: 14px;
+    color: var(--snd-text);
+    padding: 4px 0 32px;
+}
+body.dark #snd-root {
+    --snd-text:   #f0f0f0;
+    --snd-dim:    #b0b0b0;
+    --snd-muted:  #808080;
+    --snd-border: rgba(255,255,255,0.10);
+}
+#snd-root h3.section-title {
+    font-size: 15px;
+    font-weight: 600;
+    color: var(--snd-text);
+    margin: 0 0 4px;
+}
+#snd-root .section-desc {
+    font-size: 12px;
+    color: var(--snd-muted);
+    margin: 0 0 12px;
+}
+#snd-root .snd-section {
+    padding: 16px;
+    border-bottom: 1px solid var(--snd-border);
+}
+#snd-root .snd-section:last-child { border-bottom: none; }
+#snd-root .snd-path {
+    font-size: 13px;
+    color: var(--snd-dim);
+    font-style: italic;
+    margin: 6px 0;
+    word-break: break-all;
+}
+#snd-root .btn-group {
+    display: flex;
+    gap: 6px;
+    flex-wrap: wrap;
+    margin-top: 8px;
+}
+#snd-root .btn {
+    padding: 5px 12px;
+    border-radius: 4px;
+    border: 1px solid var(--snd-border);
+    background: transparent;
+    color: var(--snd-text);
+    font-size: 12px;
+    cursor: pointer;
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+}
+#snd-root .btn:hover { opacity: 0.75; }
+</style>
+
+<div id="snd-root">
+    <div class="snd-section">
+        <h3 class="section-title" locale="snd/title">Startup Sound</h3>
+        <p class="section-desc" locale="snd/desc">Choose a custom sound to play when you log in to the web desktop.</p>
+        <div id="snd-current-path" class="snd-path" locale="snd/disabled">Disabled</div>
+        <div class="btn-group">
+            <button class="btn" onclick="sndSelectFile()">
+                <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M1 4a1 1 0 011-1h4l2 2h6a1 1 0 011 1v6a1 1 0 01-1 1H2a1 1 0 01-1-1V4z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/></svg>
+                <span locale="snd/select">Select File</span>
+            </button>
+            <button class="btn" onclick="sndClear()" style="color:#c42b1c;">
+                <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
+                <span locale="snd/clear">Clear Selection</span>
+            </button>
+        </div>
+    </div>
+</div>
+
+<script>
+(function () {
+    var sndLocale = NewAppLocale();
+
+    sndLocale.init("../locale/system_settings/desktop.json", function () {
+        sndLocale.translate();
+
+        $.ajax({
+            url: "../../system/desktop/preference",
+            method: "POST",
+            data: { preference: "startup-audio" },
+            success: function (data) {
+                if (data) {
+                    $("#snd-current-path").text(data).css("font-style", "normal");
+                }
+            }
+        });
+    });
+
+    window.sndFileSelected = function (filedata) {
+        var filepath = filedata[0].filepath;
+        $("#snd-current-path").text(filepath).css("font-style", "normal");
+        $.ajax({
+            url: "../../system/desktop/preference",
+            method: "POST",
+            data: { preference: "startup-audio", value: filepath }
+        });
+    };
+
+    window.sndSelectFile = function () {
+        ao_module_openFileSelector(window.sndFileSelected, undefined, "file", false, {
+            fnameOverride: "sndFileSelected",
+            filter: ["mp3", "aac", "ogg", "wav"]
+        });
+    };
+
+    window.sndClear = function () {
+        $.ajax({
+            url: "../../system/desktop/preference",
+            method: "POST",
+            data: { preference: "startup-audio", remove: "true" },
+            success: function () {
+                $("#snd-current-path").text(sndLocale.getString("snd/disabled", "Disabled")).css("font-style", "italic");
+            }
+        });
+    };
+}());
+</script>

+ 205 - 0
src/web/SystemAO/desktop/settings/theme.html

@@ -0,0 +1,205 @@
+<!-- Theme Settings — loaded into SystemAO/system_setting/index.html via jQuery .load() -->
+<style>
+#thm-root {
+    --thm-text:   #1a1a1a;
+    --thm-dim:    #5a5a5a;
+    --thm-muted:  #9a9a9a;
+    --thm-border: rgba(0,0,0,0.10);
+    --thm-green:  #107c10;
+    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+    font-size: 14px;
+    color: var(--thm-text);
+    padding: 4px 0 32px;
+}
+body.dark #thm-root {
+    --thm-text:   #f0f0f0;
+    --thm-dim:    #b0b0b0;
+    --thm-muted:  #808080;
+    --thm-border: rgba(255,255,255,0.10);
+}
+#thm-root h3.section-title {
+    font-size: 15px;
+    font-weight: 600;
+    color: var(--thm-text);
+    margin: 0 0 4px;
+}
+#thm-root .section-desc {
+    font-size: 12px;
+    color: var(--thm-muted);
+    margin: 0 0 12px;
+}
+#thm-root .thm-section {
+    padding: 16px;
+    border-bottom: 1px solid var(--thm-border);
+}
+#thm-root .thm-section:last-child { border-bottom: none; }
+#thm-root .color-row {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    margin-bottom: 6px;
+    flex-wrap: wrap;
+}
+#thm-root .color-row-label {
+    width: 80px;
+    font-size: 12px;
+    color: var(--thm-dim);
+    flex-shrink: 0;
+}
+#thm-root .color-swatch {
+    width: 24px;
+    height: 24px;
+    border-radius: 3px;
+    border: 2px solid transparent;
+    cursor: pointer;
+    position: relative;
+    flex-shrink: 0;
+    transition: border-color 0.1s;
+}
+#thm-root .color-swatch:hover {
+    border-color: rgba(255,255,255,0.7);
+    outline: 1px solid rgba(0,0,0,0.3);
+}
+#thm-root .color-swatch.selected::after {
+    content: '';
+    position: absolute;
+    top: 50%; left: 50%;
+    transform: translate(-50%,-50%);
+    width: 8px; height: 8px;
+    border-radius: 50%;
+    background: rgba(255,255,255,0.9);
+}
+#thm-root .btn {
+    padding: 5px 12px;
+    border-radius: 4px;
+    border: 1px solid var(--thm-border);
+    background: transparent;
+    color: var(--thm-text);
+    font-size: 12px;
+    cursor: pointer;
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+    margin-top: 8px;
+}
+#thm-root .btn:hover { opacity: 0.75; }
+</style>
+
+<div id="thm-root">
+    <div class="thm-section">
+        <h3 class="section-title" locale="thm/title">Theme Color</h3>
+        <p class="section-desc" locale="thm/desc">Choose an accent color for window chrome, taskbar, and navigation elements.</p>
+        <div id="thm-color-grid">Loading…</div>
+        <button class="btn" onclick="thmRestoreDefault()">
+            <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M3 8a5 5 0 105-5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M3 4v4h4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
+            <span locale="thm/restore">Restore Default</span>
+        </button>
+    </div>
+</div>
+
+<script>
+(function () {
+    var thmLocale = NewAppLocale();
+    var thmPrevColor = {};
+    var thmCancelRestore = false;
+
+    thmLocale.init("../locale/system_settings/desktop.json", function () {
+        thmLocale.translate();
+        thmBuildColorGrid();
+    });
+
+    function hexToRgbA(hex, alpha) {
+        var c;
+        if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
+            c = hex.substring(1).split('');
+            if (c.length === 3) c = [c[0],c[0],c[1],c[1],c[2],c[2]];
+            c = '0x' + c.join('');
+            return 'rgba(' + [(c>>16)&255,(c>>8)&255,c&255].join(',') + ',' + alpha + ')';
+        }
+        return hex;
+    }
+
+    function thmReadPreference(key, cb) {
+        $.ajax({ url: "../../system/desktop/preference", method: "POST",
+            data: { preference: key }, success: function (d) { if (cb) cb(d); } });
+    }
+
+    function thmWritePreference(key, value, cb) {
+        $.ajax({ url: "../../system/desktop/preference", method: "POST",
+            data: { preference: key, value: value }, success: function (d) { if (cb) cb(d); } });
+    }
+
+    function thmTickSelected() {
+        thmReadPreference("themecolor", function (data) {
+            if (data && !data.error) {
+                $("#thm-color-grid .color-swatch").each(function () {
+                    $(this).toggleClass("selected", $(this).attr("data-hex") === data);
+                });
+            }
+        });
+    }
+
+    function thmBuildColorGrid() {
+        $.getJSON("../desktop/colors.json", function (data) {
+            var html = '';
+            for (var key in data) {
+                var label = key.charAt(0).toUpperCase() + key.slice(1);
+                html += '<div class="color-row"><span class="color-row-label">' + label + '</span>';
+                var shades = data[key];
+                for (var idx in shades) {
+                    if (parseInt(idx) < 300) continue;
+                    var hex = shades[idx];
+                    html += '<div class="color-swatch" data-hex="' + hex + '" style="background:' + hex + '" title="' + key + ' ' + idx + '"></div>';
+                }
+                html += '</div>';
+            }
+            $("#thm-color-grid").html(html);
+
+            $("#thm-color-grid").on("mouseenter", ".color-swatch", function () {
+                var hex = $(this).attr("data-hex");
+                if (!ao_module_virtualDesktop) return;
+                var pd = parent.window.document;
+                thmPrevColor.fw  = $(pd).find(".floatWindow .controls").css("background-color");
+                thmPrevColor.sb  = $(pd).find("#statusbar").css("background-color");
+                thmPrevColor.nav = $(pd).find("#navimenu").css("background-color");
+                $(pd).find(".floatWindow .controls").css("background-color", hexToRgbA(hex, 0.25));
+                $(pd).find("#statusbar").css("background-color", hex);
+                $(pd).find("#navimenu").css("background-color", hexToRgbA(hex, 0.5));
+            });
+
+            $("#thm-color-grid").on("mouseleave", ".color-swatch", function () {
+                if (!ao_module_virtualDesktop) return;
+                if (thmCancelRestore) { thmCancelRestore = false; return; }
+                var pd = parent.window.document;
+                $(pd).find(".floatWindow .controls").css("background-color", thmPrevColor.fw || "");
+                $(pd).find("#statusbar").css("background-color", thmPrevColor.sb || "");
+                $(pd).find("#navimenu").css("background-color", thmPrevColor.nav || "");
+            });
+
+            $("#thm-color-grid").on("click", ".color-swatch", function () {
+                var hex = $(this).attr("data-hex");
+                thmWritePreference("themecolor", hex, function (resp) {
+                    if (resp && resp.error) { alert(resp.error); return; }
+                    thmCancelRestore = true;
+                    thmTickSelected();
+                    if (ao_module_virtualDesktop) parent.setThemeColor(hex);
+                });
+            });
+
+            thmTickSelected();
+        });
+    }
+
+    window.thmRestoreDefault = function () {
+        $.ajax({
+            url: "../../system/file_system/preference",
+            data: { key: "themecolor", remove: true },
+            success: function (data) {
+                if (data && data.error) return;
+                $("#thm-color-grid .color-swatch").removeClass("selected");
+                if (ao_module_virtualDesktop) parent.setThemeColor("#262626");
+            }
+        });
+    };
+}());
+</script>

+ 283 - 0
src/web/SystemAO/desktop/settings/wallpaper.html

@@ -0,0 +1,283 @@
+<!-- Wallpaper Settings — loaded into SystemAO/system_setting/index.html via jQuery .load() -->
+<!-- Paths resolve relative to SystemAO/system_setting/                                   -->
+<style>
+#wp-root {
+    --wp-text:    #1a1a1a;
+    --wp-dim:     #5a5a5a;
+    --wp-muted:   #9a9a9a;
+    --wp-border:  rgba(0,0,0,0.10);
+    --wp-green:   #107c10;
+    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+    font-size: 14px;
+    color: var(--wp-text);
+    padding: 4px 0 32px;
+}
+body.dark #wp-root {
+    --wp-text:    #f0f0f0;
+    --wp-dim:     #b0b0b0;
+    --wp-muted:   #808080;
+    --wp-border:  rgba(255,255,255,0.10);
+}
+#wp-root h3.section-title {
+    font-size: 15px;
+    font-weight: 600;
+    color: var(--wp-text);
+    margin: 0 0 4px;
+}
+#wp-root .section-desc {
+    font-size: 12px;
+    color: var(--wp-muted);
+    margin: 0 0 12px;
+}
+#wp-root .wp-section {
+    padding: 16px;
+    border-bottom: 1px solid var(--wp-border);
+}
+#wp-root .wp-section:last-child { border-bottom: none; }
+#wp-root .preview-main {
+    width: 100%;
+    max-height: 200px;
+    object-fit: cover;
+    border-radius: 6px;
+    border: 1px solid var(--wp-border);
+    background: #ccc;
+    display: block;
+}
+#wp-root .preview-grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: 6px;
+    margin-top: 10px;
+}
+#wp-root .preview-grid img {
+    width: 100%;
+    aspect-ratio: 16/9;
+    object-fit: cover;
+    border-radius: 4px;
+    border: 1px solid var(--wp-border);
+    background: #eee;
+}
+#wp-root .wp-btn-group {
+    display: flex;
+    gap: 6px;
+    flex-wrap: wrap;
+    margin-top: 8px;
+}
+#wp-root .btn {
+    padding: 5px 12px;
+    border-radius: 4px;
+    border: 1px solid var(--wp-border);
+    background: transparent;
+    color: var(--wp-text);
+    font-size: 12px;
+    cursor: pointer;
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+}
+#wp-root .btn:hover { opacity: 0.8; }
+#wp-root .btn.primary {
+    background: var(--wp-green);
+    color: #fff;
+    border-color: var(--wp-green);
+}
+#wp-root .confirm-msg {
+    display: none;
+    margin-top: 8px;
+    padding: 8px 12px;
+    background: rgba(16,124,16,0.1);
+    border-radius: 4px;
+    font-size: 12px;
+    color: var(--wp-green);
+}
+body.dark #wp-root .confirm-msg { background: rgba(16,124,16,0.15); }
+#wp-root .wp-path-text {
+    font-size: 13px;
+    color: var(--wp-dim);
+    font-style: italic;
+    margin: 6px 0;
+    word-break: break-all;
+}
+</style>
+
+<div id="wp-root">
+    <div class="wp-section">
+        <h3 class="section-title" locale="wp/pack-title">Wallpaper Pack</h3>
+        <p class="section-desc" locale="wp/pack-desc">Set your desktop background wallpaper theme.</p>
+        <img id="wp-main-preview" class="preview-main" src="">
+        <div id="wp-preview-grid" class="preview-grid"></div>
+        <div style="margin-top:12px;">
+            <select id="wp-pack-select" class="ui fluid dropdown allowSelectDefaultThemes" onchange="wpHandlePackChange(this.value)">
+                <option value="">Wallpaper Packs</option>
+            </select>
+        </div>
+        <div class="wp-btn-group">
+            <button class="btn primary allowSelectDefaultThemes" onclick="wpApply()">
+                <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M2 8.5L6 12.5L14 4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
+                <span locale="wp/apply">Apply Wallpaper</span>
+            </button>
+        </div>
+        <div id="wp-apply-confirm" class="confirm-msg"><span locale="wp/applied">Wallpaper updated. You should see the change in a moment.</span></div>
+    </div>
+
+    <div class="wp-section">
+        <h3 class="section-title" locale="wp/interval-title">Wallpaper Interval</h3>
+        <p class="section-desc" locale="wp/interval-desc">Set the interval between wallpaper image cycles.</p>
+        <select id="wp-interval" class="ui fluid dropdown" onchange="wpHandleIntervalChange(this.value)">
+            <option value="10">10 seconds</option>
+            <option value="30">30 seconds</option>
+            <option value="60">60 seconds</option>
+            <option value="180">3 minutes</option>
+            <option value="300">5 minutes</option>
+            <option value="600">10 minutes</option>
+            <option value="1800">30 minutes</option>
+            <option value="3600">1 hour</option>
+        </select>
+        <div id="wp-interval-confirm" class="confirm-msg"><span locale="wp/interval-updated">Wallpaper interval updated. This setting applies to this browser only.</span></div>
+    </div>
+
+    <div class="wp-section">
+        <h3 class="section-title" locale="wp/folder-title">Custom Wallpaper Folder</h3>
+        <p class="section-desc" locale="wp/folder-desc">Use images from a folder as desktop wallpapers instead of the built-in packs.</p>
+        <div id="wp-folder-path" class="wp-path-text" locale="wp/folder-disabled">Disabled</div>
+        <div class="wp-btn-group">
+            <button class="btn" onclick="wpSelectFolder()">
+                <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M1 4a1 1 0 011-1h4l2 2h6a1 1 0 011 1v6a1 1 0 01-1 1H2a1 1 0 01-1-1V4z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/></svg>
+                <span locale="wp/folder-select">Select Folder</span>
+            </button>
+            <button class="btn" onclick="wpClearFolder()" style="color:#c42b1c;">
+                <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
+                <span locale="wp/folder-clear">Clear Selection</span>
+            </button>
+        </div>
+    </div>
+</div>
+
+<script>
+(function () {
+    var wpLocale = NewAppLocale();
+    var wpThemeList = [];
+    var wpStartingUp = true;
+
+    wpLocale.init("../locale/system_settings/desktop.json", function () {
+        wpLocale.translate();
+
+        $("#wp-pack-select").dropdown();
+        $("#wp-interval").dropdown();
+
+        var savedInterval = localStorage.getItem("ao/desktop/backgroundInterval") || "30";
+        $("#wp-interval").dropdown("set selected", savedInterval);
+
+        wpInitFolderState(function (currentTheme) {
+            wpInitPreview(currentTheme);
+        });
+    });
+
+    function wpInitFolderState(cb) {
+        $.get("../../system/desktop/theme?get=true", function (data) {
+            if (data.includes(":/")) {
+                $("#wp-folder-path").text(data).css("font-style", "normal");
+                $(".allowSelectDefaultThemes").addClass("disabled");
+            } else {
+                $("#wp-folder-path").text(wpLocale.getString("wp/folder-disabled", "Disabled")).css("font-style", "italic");
+            }
+            if (cb) cb(data);
+        });
+    }
+
+    function wpInitPreview(currentTheme) {
+        $.get("../../system/desktop/theme", function (data) {
+            wpThemeList = data;
+            $("#wp-pack-select").html('<option value="">Wallpaper Packs</option>');
+            wpThemeList.forEach(function (tp) {
+                var encoded = encodeURIComponent(JSON.stringify(tp));
+                var label = tp.Theme.charAt(0).toUpperCase() + tp.Theme.slice(1);
+                $("#wp-pack-select").append('<option value="' + encoded + '">' + label + '</option>');
+            });
+
+            $("#wp-main-preview").attr("src", "../../img/desktop/bg/nobg.jpg");
+
+            if (currentTheme && currentTheme.includes(":/")) {
+                $.get("../../system/desktop/theme?load=" + currentTheme, function (imgList) {
+                    wpLoadCustomPreview(imgList);
+                    wpStartingUp = false;
+                });
+            } else {
+                var matched = null;
+                wpThemeList.forEach(function (t) { if (t.Theme === currentTheme) matched = t; });
+                if (matched) {
+                    wpLoadPackPreview(matched);
+                    var label = currentTheme.charAt(0).toUpperCase() + currentTheme.slice(1);
+                    $("#wp-pack-select").dropdown("set selected", label);
+                }
+                wpStartingUp = false;
+            }
+        });
+    }
+
+    function wpLoadPackPreview(themeObj) {
+        var imgs = themeObj.Bglist;
+        $("#wp-main-preview").attr("src", "../../img/desktop/bg/" + themeObj.Theme + "/" + imgs[0]);
+        $("#wp-preview-grid").html("");
+        for (var i = 1; i < imgs.length && i < 5; i++) {
+            $("#wp-preview-grid").append('<img src="../../img/desktop/bg/' + themeObj.Theme + '/' + imgs[i] + '">');
+        }
+    }
+
+    function wpLoadCustomPreview(imgList) {
+        $("#wp-main-preview").attr("src", "../../media/?file=" + imgList[0]);
+        $("#wp-preview-grid").html("");
+        for (var i = 1; i < imgList.length && i < 5; i++) {
+            $("#wp-preview-grid").append('<img src="../../media/?file=' + imgList[i] + '">');
+        }
+    }
+
+    window.wpHandlePackChange = function (value) {
+        if (!value) return;
+        wpLoadPackPreview(JSON.parse(decodeURIComponent(value)));
+    };
+
+    window.wpHandleIntervalChange = function (newInterval) {
+        if (wpStartingUp) return;
+        localStorage.setItem("ao/desktop/backgroundInterval", newInterval);
+        $("#wp-interval-confirm").stop(true).finish().slideDown("fast").delay(3000).slideUp("fast");
+        if (ao_module_virtualDesktop && parent.backgroundIntervalCounter) {
+            parent.clearInterval(parent.backgroundIntervalCounter);
+            parent.initBackgroundSwitchingAnimation();
+        }
+    };
+
+    window.wpApply = function () {
+        var val = $("#wp-pack-select").val();
+        if (!val) return;
+        var themeObj = JSON.parse(decodeURIComponent(val));
+        $.get("../../system/desktop/theme?set=" + themeObj.Theme, function () {
+            if (ao_module_virtualDesktop) parent.changeDesktopTheme(themeObj.Theme);
+            wpInitPreview(themeObj.Theme);
+            $("#wp-apply-confirm").stop(true).finish().slideDown("fast").delay(3000).slideUp("fast");
+        });
+    };
+
+    window.wpFolderSelected = function (filedata) {
+        var filepath = filedata[0].filepath;
+        $("#wp-folder-path").text(filepath).css("font-style", "normal");
+        $.get("../../system/desktop/theme?set=" + filepath, function () {
+            if (ao_module_virtualDesktop) parent.changeDesktopTheme(filepath);
+            $(".allowSelectDefaultThemes").addClass("disabled");
+            wpInitFolderState(function (t) { wpInitPreview(t); });
+        });
+    };
+
+    window.wpSelectFolder = function () {
+        ao_module_openFileSelector(window.wpFolderSelected, undefined, "folder", false, {fnameOverride: "wpFolderSelected"});
+    };
+
+    window.wpClearFolder = function () {
+        $.get("../../system/desktop/theme?set=default", function () {
+            if (ao_module_virtualDesktop) parent.changeDesktopTheme("default");
+            $(".allowSelectDefaultThemes").removeClass("disabled");
+            wpInitFolderState(function (t) { wpInitPreview(t); });
+        });
+    };
+}());
+</script>

+ 35 - 0
src/web/SystemAO/locale/system_settings.json

@@ -17,6 +17,11 @@
                 "menu/group/Security & Auth": "安全與登入",
                 "menu/group/Developer Options": "開發者選項",
                 "menu/group/About ArOZ": "關於 ArozOS",
+                "menu/group/Desktop & Themes": "桌面與佈景主題",
+                "tab/desktop/Wallpaper": "桌布",
+                "tab/desktop/Sounds": "音效",
+                "tab/desktop/Theme": "佈景主題",
+                "tab/desktop/Mobile UX": "行動介面",
                 "tab/info/Overview": "系統概覽",
                 "tab/info/Host Info": "伺服器資訊",
                 "tab/info/Performance": "效能",
@@ -133,6 +138,11 @@
                 "menu/group/Security & Auth": "安全與登入",
                 "menu/group/Developer Options": "開發者選項",
                 "menu/group/About ArOZ": "關於 ArozOS",
+                "menu/group/Desktop & Themes": "桌面與佈景主題",
+                "tab/desktop/Wallpaper": "桌布",
+                "tab/desktop/Sounds": "音效",
+                "tab/desktop/Theme": "佈景主題",
+                "tab/desktop/Mobile UX": "行動介面",
                 "tab/info/Overview": "系統概覽",
                 "tab/info/Host Info": "伺服器資訊",
                 "tab/info/Performance": "效能",
@@ -248,6 +258,11 @@
                 "menu/group/Security & Auth": "安全与登入",
                 "menu/group/Developer Options": "开发者选项",
                 "menu/group/About ArOZ": "关于 ArozOS",
+                "menu/group/Desktop & Themes": "桌面与主题",
+                "tab/desktop/Wallpaper": "壁纸",
+                "tab/desktop/Sounds": "音效",
+                "tab/desktop/Theme": "主题",
+                "tab/desktop/Mobile UX": "移动界面",
                 "tab/info/Overview": "系统概览",
                 "tab/info/Host Info": "服务器信息",
                 "tab/info/Performance": "性能",
@@ -364,6 +379,11 @@
                 "menu/group/Security & Auth": "Security & Auth",
                 "menu/group/Developer Options": "Developer Options",
                 "menu/group/About ArOZ": "About ArozOS",
+                "menu/group/Desktop & Themes": "Desktop & Themes",
+                "tab/desktop/Wallpaper": "Wallpaper",
+                "tab/desktop/Sounds": "Sounds",
+                "tab/desktop/Theme": "Theme",
+                "tab/desktop/Mobile UX": "Mobile UX",
                 "tab/info/Overview": "Overview",
                 "tab/info/Host Info": "Host Information",
                 "tab/info/Performance": "Performance",
@@ -479,6 +499,11 @@
                 "menu/group/Security & Auth": "Security & Auth",
                 "menu/group/Developer Options": "Developer Options",
                 "menu/group/About ArOZ": "About ArozOS",
+                "menu/group/Desktop & Themes": "Desktop & Themes",
+                "tab/desktop/Wallpaper": "Wallpaper",
+                "tab/desktop/Sounds": "Sounds",
+                "tab/desktop/Theme": "Theme",
+                "tab/desktop/Mobile UX": "Mobile UX",
                 "tab/info/Overview": "Overview",
                 "tab/info/Host Info": "Host Information",
                 "tab/info/Performance": "Performance",
@@ -594,6 +619,11 @@
                 "menu/group/Security & Auth": "セキュリティと認証",
                 "menu/group/Developer Options": "開発者オプション",
                 "menu/group/About ArOZ": "ArozOSについて",
+                "menu/group/Desktop & Themes": "デスクトップとテーマ",
+                "tab/desktop/Wallpaper": "壁紙",
+                "tab/desktop/Sounds": "サウンド",
+                "tab/desktop/Theme": "テーマ",
+                "tab/desktop/Mobile UX": "モバイルUX",
                 "tab/info/Overview": "概要",
                 "tab/info/Host Info": "ホスト情報",
                 "tab/info/Performance": "パフォーマンス",
@@ -658,6 +688,11 @@
                 "menu/group/Security & Auth": "보안 및 인증",
                 "menu/group/Developer Options": "개발자 옵션",
                 "menu/group/About ArOZ": "ArozOS 정보",
+                "menu/group/Desktop & Themes": "바탕화면 및 테마",
+                "tab/desktop/Wallpaper": "배경화면",
+                "tab/desktop/Sounds": "소리",
+                "tab/desktop/Theme": "테마",
+                "tab/desktop/Mobile UX": "모바일 UX",
                 "tab/info/Overview": "개요",
                 "tab/info/Host Info": "호스트 정보",
                 "tab/info/Performance": "성능",

+ 251 - 0
src/web/SystemAO/locale/system_settings/desktop.json

@@ -0,0 +1,251 @@
+{
+    "author": "tobychui",
+    "version": "1.0",
+    "keys": {
+        "zh-tw": {
+            "name": "繁體中文(台灣)",
+            "strings": {
+                "wp/pack-title": "桌布主題",
+                "wp/pack-desc": "設定桌面背景桌布主題",
+                "wp/apply": "套用桌布",
+                "wp/applied": "桌布已更新,稍候即可看到變化",
+                "wp/interval-title": "桌布切換間隔",
+                "wp/interval-desc": "設定桌布圖片循環的間隔時間",
+                "wp/interval-updated": "桌布間隔已更新,此設定僅適用於此瀏覽器",
+                "wp/folder-title": "自訂桌布資料夾",
+                "wp/folder-desc": "使用指定資料夾中的圖片作為桌面桌布,而非系統內建桌布",
+                "wp/folder-disabled": "未設定",
+                "wp/folder-select": "選擇資料夾",
+                "wp/folder-clear": "清除選擇",
+                "snd/title": "啟動音效",
+                "snd/desc": "設定登入桌面時播放的自訂音效",
+                "snd/disabled": "未設定",
+                "snd/select": "選擇檔案",
+                "snd/clear": "清除選擇",
+                "thm/title": "主題色彩",
+                "thm/desc": "選擇視窗框、工作列及導覽列的強調色",
+                "thm/restore": "還原預設",
+                "mux/title": "行動桌面捷徑",
+                "mux/desc": "選擇行動桌面介面上顯示的四個快速啟動捷徑,由左至右排列",
+                "mux/sc1": "捷徑 1",
+                "mux/sc2": "捷徑 2",
+                "mux/sc3": "捷徑 3",
+                "mux/sc4": "捷徑 4",
+                "mux/save": "儲存捷徑",
+                "mux/saved": "捷徑已儲存,開啟行動介面即可查看變更"
+            },
+            "titles": {},
+            "placeholder": {}
+        },
+        "zh-hk": {
+            "name": "繁體中文(香港)",
+            "strings": {
+                "wp/pack-title": "桌布主題",
+                "wp/pack-desc": "設定桌面背景桌布主題",
+                "wp/apply": "套用桌布",
+                "wp/applied": "桌布已更新,稍候即可看到變化",
+                "wp/interval-title": "桌布切換間隔",
+                "wp/interval-desc": "設定桌布圖片循環的間隔時間",
+                "wp/interval-updated": "桌布間隔已更新,此設定僅適用於此瀏覽器",
+                "wp/folder-title": "自訂桌布資料夾",
+                "wp/folder-desc": "使用指定資料夾中的圖片作為桌面桌布,而非系統內建桌布",
+                "wp/folder-disabled": "未設定",
+                "wp/folder-select": "選擇資料夾",
+                "wp/folder-clear": "清除選擇",
+                "snd/title": "啟動音效",
+                "snd/desc": "設定登入桌面時播放的自訂音效",
+                "snd/disabled": "未設定",
+                "snd/select": "選擇檔案",
+                "snd/clear": "清除選擇",
+                "thm/title": "主題色彩",
+                "thm/desc": "選擇視窗框、工作列及導覽列的強調色",
+                "thm/restore": "還原預設",
+                "mux/title": "行動桌面捷徑",
+                "mux/desc": "選擇行動桌面介面上顯示的四個快速啟動捷徑,由左至右排列",
+                "mux/sc1": "捷徑 1",
+                "mux/sc2": "捷徑 2",
+                "mux/sc3": "捷徑 3",
+                "mux/sc4": "捷徑 4",
+                "mux/save": "儲存捷徑",
+                "mux/saved": "捷徑已儲存,開啟行動介面即可查看變更"
+            },
+            "titles": {},
+            "placeholder": {}
+        },
+        "zh-cn": {
+            "name": "简体中文(简体)",
+            "strings": {
+                "wp/pack-title": "壁纸主题",
+                "wp/pack-desc": "设置桌面背景壁纸主题",
+                "wp/apply": "应用壁纸",
+                "wp/applied": "壁纸已更新,稍后即可看到变化",
+                "wp/interval-title": "壁纸切换间隔",
+                "wp/interval-desc": "设置壁纸图片循环的间隔时间",
+                "wp/interval-updated": "壁纸间隔已更新,此设置仅适用于此浏览器",
+                "wp/folder-title": "自定义壁纸文件夹",
+                "wp/folder-desc": "使用指定文件夹中的图片作为桌面壁纸,而非系统内置壁纸",
+                "wp/folder-disabled": "未设置",
+                "wp/folder-select": "选择文件夹",
+                "wp/folder-clear": "清除选择",
+                "snd/title": "启动音效",
+                "snd/desc": "设置登录桌面时播放的自定义音效",
+                "snd/disabled": "未设置",
+                "snd/select": "选择文件",
+                "snd/clear": "清除选择",
+                "thm/title": "主题颜色",
+                "thm/desc": "选择窗口框、任务栏和导航栏的强调色",
+                "thm/restore": "恢复默认",
+                "mux/title": "移动桌面快捷方式",
+                "mux/desc": "选择移动桌面界面上显示的四个快速启动快捷方式,从左到右排列",
+                "mux/sc1": "快捷方式 1",
+                "mux/sc2": "快捷方式 2",
+                "mux/sc3": "快捷方式 3",
+                "mux/sc4": "快捷方式 4",
+                "mux/save": "保存快捷方式",
+                "mux/saved": "快捷方式已保存,打开移动界面即可查看变更"
+            },
+            "titles": {},
+            "placeholder": {}
+        },
+        "en-US": {
+            "name": "English (US)",
+            "strings": {
+                "wp/pack-title": "Wallpaper Pack",
+                "wp/pack-desc": "Set your desktop background wallpaper theme.",
+                "wp/apply": "Apply Wallpaper",
+                "wp/applied": "Wallpaper updated. You should see the change in a moment.",
+                "wp/interval-title": "Wallpaper Interval",
+                "wp/interval-desc": "Set the interval between wallpaper image cycles.",
+                "wp/interval-updated": "Wallpaper interval updated. This setting applies to this browser only.",
+                "wp/folder-title": "Custom Wallpaper Folder",
+                "wp/folder-desc": "Use images from a folder as desktop wallpapers instead of the built-in packs.",
+                "wp/folder-disabled": "Disabled",
+                "wp/folder-select": "Select Folder",
+                "wp/folder-clear": "Clear Selection",
+                "snd/title": "Startup Sound",
+                "snd/desc": "Choose a custom sound to play when you log in to the web desktop.",
+                "snd/disabled": "Disabled",
+                "snd/select": "Select File",
+                "snd/clear": "Clear Selection",
+                "thm/title": "Theme Color",
+                "thm/desc": "Choose an accent color for window chrome, taskbar, and navigation elements.",
+                "thm/restore": "Restore Default",
+                "mux/title": "Mobile Desktop Shortcuts",
+                "mux/desc": "Choose the four quick-launch shortcuts shown on the mobile desktop interface, ordered from left to right.",
+                "mux/sc1": "Shortcut 1",
+                "mux/sc2": "Shortcut 2",
+                "mux/sc3": "Shortcut 3",
+                "mux/sc4": "Shortcut 4",
+                "mux/save": "Save Shortcuts",
+                "mux/saved": "Shortcuts saved. Open the mobile interface to see the changes."
+            },
+            "titles": {},
+            "placeholder": {}
+        },
+        "en-us": {
+            "name": "English (US)",
+            "strings": {
+                "wp/pack-title": "Wallpaper Pack",
+                "wp/pack-desc": "Set your desktop background wallpaper theme.",
+                "wp/apply": "Apply Wallpaper",
+                "wp/applied": "Wallpaper updated. You should see the change in a moment.",
+                "wp/interval-title": "Wallpaper Interval",
+                "wp/interval-desc": "Set the interval between wallpaper image cycles.",
+                "wp/interval-updated": "Wallpaper interval updated. This setting applies to this browser only.",
+                "wp/folder-title": "Custom Wallpaper Folder",
+                "wp/folder-desc": "Use images from a folder as desktop wallpapers instead of the built-in packs.",
+                "wp/folder-disabled": "Disabled",
+                "wp/folder-select": "Select Folder",
+                "wp/folder-clear": "Clear Selection",
+                "snd/title": "Startup Sound",
+                "snd/desc": "Choose a custom sound to play when you log in to the web desktop.",
+                "snd/disabled": "Disabled",
+                "snd/select": "Select File",
+                "snd/clear": "Clear Selection",
+                "thm/title": "Theme Color",
+                "thm/desc": "Choose an accent color for window chrome, taskbar, and navigation elements.",
+                "thm/restore": "Restore Default",
+                "mux/title": "Mobile Desktop Shortcuts",
+                "mux/desc": "Choose the four quick-launch shortcuts shown on the mobile desktop interface, ordered from left to right.",
+                "mux/sc1": "Shortcut 1",
+                "mux/sc2": "Shortcut 2",
+                "mux/sc3": "Shortcut 3",
+                "mux/sc4": "Shortcut 4",
+                "mux/save": "Save Shortcuts",
+                "mux/saved": "Shortcuts saved. Open the mobile interface to see the changes."
+            },
+            "titles": {},
+            "placeholder": {}
+        },
+        "ja-jp": {
+            "name": "日本語",
+            "strings": {
+                "wp/pack-title": "壁紙テーマ",
+                "wp/pack-desc": "デスクトップの背景壁紙テーマを設定します",
+                "wp/apply": "壁紙を適用",
+                "wp/applied": "壁紙を更新しました。まもなく変更が反映されます",
+                "wp/interval-title": "壁紙切り替え間隔",
+                "wp/interval-desc": "壁紙画像のサイクル間隔を設定します",
+                "wp/interval-updated": "壁紙間隔を更新しました。この設定はこのブラウザのみに適用されます",
+                "wp/folder-title": "カスタム壁紙フォルダー",
+                "wp/folder-desc": "システム内蔵の壁紙の代わりに、指定したフォルダーの画像をデスクトップ壁紙として使用します",
+                "wp/folder-disabled": "無効",
+                "wp/folder-select": "フォルダーを選択",
+                "wp/folder-clear": "選択を解除",
+                "snd/title": "起動サウンド",
+                "snd/desc": "Webデスクトップにログイン時に再生するカスタムサウンドを選択します",
+                "snd/disabled": "無効",
+                "snd/select": "ファイルを選択",
+                "snd/clear": "選択を解除",
+                "thm/title": "テーマカラー",
+                "thm/desc": "ウィンドウフレーム、タスクバー、ナビゲーション要素のアクセントカラーを選択します",
+                "thm/restore": "デフォルトに戻す",
+                "mux/title": "モバイルデスクトップショートカット",
+                "mux/desc": "モバイルデスクトップインターフェースに表示される4つのクイック起動ショートカットを左から右の順に選択します",
+                "mux/sc1": "ショートカット 1",
+                "mux/sc2": "ショートカット 2",
+                "mux/sc3": "ショートカット 3",
+                "mux/sc4": "ショートカット 4",
+                "mux/save": "ショートカットを保存",
+                "mux/saved": "ショートカットを保存しました。モバイルインターフェースを開いて変更を確認してください"
+            },
+            "titles": {},
+            "placeholder": {}
+        },
+        "ko-kr": {
+            "name": "한국어",
+            "strings": {
+                "wp/pack-title": "배경화면 테마",
+                "wp/pack-desc": "바탕화면 배경 테마를 설정합니다",
+                "wp/apply": "배경화면 적용",
+                "wp/applied": "배경화면이 업데이트되었습니다. 잠시 후 변경사항이 표시됩니다",
+                "wp/interval-title": "배경화면 전환 간격",
+                "wp/interval-desc": "배경화면 이미지 순환 간격을 설정합니다",
+                "wp/interval-updated": "배경화면 간격이 업데이트되었습니다. 이 설정은 이 브라우저에만 적용됩니다",
+                "wp/folder-title": "사용자 정의 배경화면 폴더",
+                "wp/folder-desc": "시스템 내장 배경화면 대신 지정된 폴더의 이미지를 데스크탑 배경화면으로 사용합니다",
+                "wp/folder-disabled": "비활성화",
+                "wp/folder-select": "폴더 선택",
+                "wp/folder-clear": "선택 취소",
+                "snd/title": "시작 소리",
+                "snd/desc": "웹 데스크톱에 로그인할 때 재생할 사용자 정의 소리를 선택합니다",
+                "snd/disabled": "비활성화",
+                "snd/select": "파일 선택",
+                "snd/clear": "선택 취소",
+                "thm/title": "테마 색상",
+                "thm/desc": "창 테두리, 작업표시줄 및 탐색 요소의 강조 색상을 선택합니다",
+                "thm/restore": "기본값 복원",
+                "mux/title": "모바일 데스크탑 바로가기",
+                "mux/desc": "모바일 데스크탑 인터페이스에 표시되는 4개의 빠른 실행 바로가기를 왼쪽에서 오른쪽 순서로 선택합니다",
+                "mux/sc1": "바로가기 1",
+                "mux/sc2": "바로가기 2",
+                "mux/sc3": "바로가기 3",
+                "mux/sc4": "바로가기 4",
+                "mux/save": "바로가기 저장",
+                "mux/saved": "바로가기가 저장되었습니다. 모바일 인터페이스를 열어 변경사항을 확인하세요"
+            },
+            "titles": {},
+            "placeholder": {}
+        }
+    }
+}

+ 7 - 0
src/web/SystemAO/system_setting/main.js

@@ -23,6 +23,13 @@ ao_module_getSystemThemeColor(function (color) {
     preferredTheme = color === 'whiteTheme' ? 'light' : 'dark';
 });
 
+/* Called by the desktop broadcastDesktopThemeChange() when the user toggles dark/light mode */
+window.desktopThemeChanged = function (theme) {
+    var isDark = (theme === 'dark');
+    document.body.classList.toggle('dark', isDark);
+    preferredTheme = isDark ? 'dark' : 'light';
+};
+
 /* ── Bootstrap ── */
 applocale.init('../locale/system_settings.json', function (cache) {
     localeCache = cache;

+ 17 - 5
src/web/desktop.html

@@ -6115,13 +6115,12 @@
         }
 
         function personalization() {
-            //Show the personalization window
             newFloatWindow({
-                url: "SystemAO/desktop/personalization.html",
+                url: "SystemAO/system_setting/index.html#" + encodeURIComponent(JSON.stringify({group: "Desktop", name: "Wallpaper"})),
                 appicon: "SystemAO/desktop/img/personalization.png",
-                width:640,
-                height:480,
-                title: "Personalization"
+                width: 1200,
+                height: 580,
+                title: "System Settings"
             });
             hideAllContextMenus();
         }
@@ -7232,12 +7231,25 @@
             $("body").removeClass("whiteTheme").addClass("darkTheme");
             $("#powerIcon").attr("src","img/system/power-white.svg");
             updateThemeToggleBtn();
+            broadcastDesktopThemeChange("dark");
         }
 
         function setWhiteTheme(){
             $("body").removeClass("darkTheme").addClass("whiteTheme");
             $("#powerIcon").attr("src","img/system/power.svg");
             updateThemeToggleBtn();
+            broadcastDesktopThemeChange("light");
+        }
+
+        function broadcastDesktopThemeChange(theme){
+            $(".floatWindow iframe").each(function(){
+                try {
+                    var fw = this.contentWindow;
+                    if (fw && typeof fw.desktopThemeChanged === "function"){
+                        fw.desktopThemeChanged(theme);
+                    }
+                } catch(e) {}
+            });
         }
 
         function toggleDesktopTheme(){

+ 1053 - 1752
src/web/mobile.html

@@ -1,1761 +1,1062 @@
 <!DOCTYPE html>
 <html>
-    <head>
-        <title>ArozOS Mobile</title>
-        <meta charset="UTF-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-        <link rel="manifest" href="manifest.webmanifest">
-        <link rel="stylesheet" href="script/semantic/semantic.css">
-        <link rel="stylesheet" href="script/ao.css">
-        <script type="text/javascript" src="script/jquery.min.js"></script>
-        <script type="text/javascript" src="script/semantic/semantic.js"></script>
-        <script type="text/javascript" src="script/ao_module.js"></script>
-        <script type="text/javascript" src="./script/applocale.js"></script>
-        <style>
-
-            body{
-                height: 100%;
-            }
-            .themeColor{
-                background-color:#1c1c1c;
-            }
-
-            .taskBar{
-                position:fixed;
-                top:0px;
-                left:0px;
-                width:30px;
-                height:100%;
-                overflow: visible !important;
-                z-index:999;
-            }
-            #mainFrame{
-                width: calc(100% - 30px);
-                position:fixed;
-                top:0px;
-                right:0px;
-                overflow: hidden;
-
-            }
-            .toggleTaskBar{
-                position:absolute;
-                bottom:0px;
-                right:0px;
-                margin-right:-20px;
-                width:30px;
-                height:90px;
-                border-radius: 0px 10px 10px 0px;
-            }
-
-            #listMenu{
-                z-index:1000;
-                position:fixed;
-                top:0px;
-                left:0px;
-                width:100%;
-                height:100%;
-                padding:12px;
-                background-color: #fcffff;
-                box-shadow: 3px 3px 5px 0px rgba(207, 207, 207, 0.37);
-            }
-            #listMenu .searchBar {
-                width: 100%;
-                border-bottom: 2px solid #34b7eb;
-            }
-            
-            #listMenu .listItemWrapper {
-                overflow: hidden;
-            }
-
-            #listMenu .listItemWrapper .groups {
-                background-color: #f5f5f5;
-                width: 120px;
-                height: calc(100% - 40px);
-                display: inline-block;
-                overflow-y: auto;
-                padding-top: 8px;
-            }
-            
-            #listMenu .listItemWrapper .groups .item {
-                padding-left: 7px;
-                padding-bottom: 6px;
-                padding-top: 6px;
-                cursor: pointer;
-            }
-            
-            #listMenu .listItemWrapper .groups .item.selected {
-                color: #34b7eb;
-            }
-            
-            #listMenu .listItemWrapper .groups .item:hover {
-                background-color: #dedede;
-            }
-
-            #listMenuItem{
-                width: calc(100% - 130px) !important;
-                float: right;
-                overflow-y: auto;
-                height: calc(100% - 40px) !important;
-            }
-
-            #listMenuItem img{
-                width: 50px;
-            }
-
-            .module.item{
-                padding:8px;
-            }
-
-            .hideListMenuButton{
-                position: absolute;
-                bottom:22px;
-                left:32px;
-                cursor:pointer;
-            }
-
-            .hideListMenuButton img{
-                left:12px;
-                width:50px;
-            }
-
-            .listMenuLauncher{
-                position:absolute;
-                bottom:12px;
-                left:12px;
-            }
-
-            .listMenuLauncher .functionbtn{
-                cursor:pointer;
-            }
-            
-            .blurred{
-                filter: blur(3px);
-            }
-
-            #backdrop {
-                background-repeat: no-repeat;
-                background-size: cover;
-                background-position: center;
-                background-image: url('img/desktop/bg/init.jpg');
-                width: 100%;
-                height: 100%;
-                overflow-x: hidden;
-                pointer-events: none;
-            }
-
-            #windowButtonWrapper{
-                padding-top:6px;
-                max-height: calc(100% - 4em);
-                overflow-y: auto;
-            }
-
-            .floatWindowButton{
-                padding: 5px;
-            }
-
-            .floatWindowButton .minimizedIcon{
-                width: 20px;
-                height:20px;
-            }
-
-            .floatWindowButton .normalElements{
-                padding:8px;
-                border-bottom: 1px solid #4a4a4a;
-                cursor:pointer;
-                vertical-align:middle;
-                position: relative;
-            }
-
-            .floatWindowButton .normalizedIcon{
-                width:40;
-                height:40px;
-                margin-right:12px;
-            }
-
-            .normalElements .windowTitle{
-                display:inline-block;
-                width:200px !important;
-                vertical-align:middle;
-                white-space: nowrap;
-                overflow: hidden;
-                text-overflow: ellipsis;  
-            }  
-
-            .fwtab{
-                position: absolute;
-                top:0px;
-                right: 0px;
-                width: 100%;
-                height: 100%;
-            }
-
-            #windowWrapper{
-                height: 100%;
-                width: 100%;
-                position: absolute;
-                top:0px;
-                right:0px;
-            }
-
-            .floatWindowWrapper{
-                width: 100%;
-                height: 100%;
-            }
-
-            .floatWindow{
-                width: 100%;
-                height: 100%;
-                position: absolute;
-            }
-
-
-
-            .fwtab iframe{
-                border: 0px solid transparent;
-                height: 100%;
-            }
-
-            .floatWindowButton .closebutton{
-                position: absolute;
-                font-size: 120%;
-                color:white;
-                right: 0em;
-                top: 0px;
-                margin-top: -8px;
-                margin-left: -4px;
-                padding: 3px;
-            }
-
-            .floatWindowButton .externalbutton{
-                position: absolute;
-                font-size: 120%;
-                color:white;
-                right: 1.6em;
-                top: 0px;
-                margin-top: -8px;
-                margin-left: -4px;
-                padding: 3px;
-            }
-
-            #conndrop{
-                padding: 0.5em;
-                position: fixed;
-                top: 0px;
-                right: 0px;
-                z-index: 115;
-                width: 100px;
-                display: none; 
-                justify-content: flex-end;
-                pointer-events: none;
-            }
-
-            .extendOnly{
-                display:none;
-            }
-            
-
-            /* Magic css to make the connection logo blink */
-            @-moz-keyframes blink {
-                0% {
-                    opacity:1;
-                }
-                50% {
-                    opacity:0;
-                }
-                100% {
-                    opacity:1;
-                }
-            } 
-
-            @-webkit-keyframes blink {
-                0% {
-                    opacity:1;
-                }
-                50% {
-                    opacity:0;
-                }
-                100% {
-                    opacity:1;
-                }
-            }
-            /* IE */
-            @-ms-keyframes blink {
-                0% {
-                    opacity:1;
-                }
-                50% {
-                    opacity:0;
-                }
-                100% {
-                    opacity:1;
-                }
-            } 
-            /* Opera and prob css3 final iteration */
-            @keyframes blink {
-                0% {
-                    opacity:1;
-                }
-                50% {
-                    opacity:0;
-                }
-                100% {
-                    opacity:1;
-                }
-            } 
-            .blink-image {
-                -moz-animation: blink normal 2s infinite ease-in-out; /* Firefox */
-                -webkit-animation: blink normal 2s infinite ease-in-out; /* Webkit */
-                -ms-animation: blink normal 2s infinite ease-in-out; /* IE */
-                animation: blink normal 2s infinite ease-in-out; /* Opera and prob css3 final iteration */
-            }
-
-            #sidebarToggleOverlay{
-                width: 100%;
-                height: 100%;
-                z-index: 995;
-                position: fixed;
-                top:0px;
-                left:0px;
-                display:none;
-            }
-
-            #userprofile{
-                z-index: 90;
-                position: fixed;
-                top: 0px;
-                left: 3em;
-                right: 3em;
-                background-color: rgba(255,255,255,0.95);
-                backdrop-filter: blur(4px);
-                border-bottom-left-radius: 20px;
-                border-bottom-right-radius: 20px;
-                padding-left: 1.2em;
-                padding-right: 1.2em;
-                padding-top: 1.2em;
-                display:none;
-
-                
-                box-shadow: 6px 8px 5px 2px rgba(0,0,0,0.2);
-                -webkit-box-shadow: 6px 8px 5px 2px rgba(0,0,0,0.2);
-                -moz-box-shadow: 6px 8px 5px 2px rgba(0,0,0,0.2);
-                
-            }
-
-            .clock{
-                position: fixed; 
-                left: 0px; 
-                width: 100%; 
-                top: 0px; 
-                padding-top: 0.3em;
-                padding-bottom: 0.7em;
-                color: white; 
-                text-align: center; 
-                pointer-events: none;
-            }
-
-            #shortcuts{
-                position: absolute;
-                bottom: 0px;
-                right: 0px;
-                width: calc(100% - 20px);
-                padding: 1em;
-            }
-
-            body.horizontal #shortcuts{
-                position: absolute;
-                top: 0px;
-                right: 0px;
-                width: 5em;
-                height: 100%;
-                padding: 1em;
-                border-left: 1px solid rgba(168, 168, 168, 0.2);
-                backdrop-filter: blur(3px);
-            }
-
-            body.horizontal #shortcuts .ui.four.column.grid{
-                display: -webkit-flex;
-                display: flex;
-
-                -webkit-flex-direction: column;
-                flex-direction: column;
-
-                -webkit-align-content: space-between;
-                align-content: space-between;
-
-                -webkit-flex-flow: row wrap;
-                flex-flow: row wrap;
-            }
-
-            #shortcuts .clickable.image{
-                cursor: pointer;
-                border-radius: 10px;
-                padding: 0.1em;
-            }
-
-            body.horizontal #shortcuts .shortcutObject{
-                width: 5em;
-                margin-top: 1em;
-            }
-
-            .disabled{
-                pointer-events: none;
-            }
-
-            #alternativeAccountList{
-                padding-bottom: 0.6em;
-            }
-
-            .alternativeAccount{
-                padding-top: 0.6em !important;
-                padding-bottom: 0.6em !important;
-                margin: 0;
-        
-            }
-
-            .alternativeAccount .usericon{
-                width: 35px !important;
-            }
-
-            .alternativeAccount .username{
-                font-weight: 400;
-            }
-        </style>
-    </head>
-    <body>
-        <div class="taskBar themeColor" >
-            <div class="toggleTaskBar themeColor" shown="false" onclick="toggleTaskBar(this);">
-                <img class="ui image sidebararrow" style="margin-top:8px; margin-left: -10px;" src="img/mobile/keyboard_arrow_right-white-48dp.svg"></img>
-            </div>
-            <div class="listMenuLauncher">
-                <img onclick="showListMenu();" class="functionbtn" src="img/mobile/apps-white-48dp.svg" style="width: 30px;"/>
-                <img onclick="showProfileInfo();" class="functionbtn extendOnly" src="img/mobile/account_circle_white_48dp.svg" style="width: 30px;"/>
-                <img onclick="openDesktopCustomization();" class="functionbtn extendOnly" src="img/mobile/palette_white_48dp.svg" style="width: 30px;"/>
-                <img onclick="openDesktopAsFolder();" class="functionbtn extendOnly" src="img/mobile/folder_open_white_24dp.svg" style="width: 30px;"/>
-                <img onclick="fullscreen();" class="functionbtn extendOnly" src="img/mobile/fullscreen_white_48dp.svg" style="width: 30px;"/>
-                <img onclick="openSystemSettings();" class="functionbtn extendOnly" src="img/mobile/tune_white_24dp.svg" style="width: 30px;"/>
-
-                <img onclick="showDesktop();" class="functionbtn extendOnly" src="img/mobile/layers_clear_white_48dp.svg" style="width: 30px;"/>
-            </div>
-            <div id="windowButtonWrapper">
-               
-            </div>
+<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;}
+.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{font-size:24px;}
+.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{font-size:17px;width:22px;text-align:center;}
+
+/* ── 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;}
+</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 id="userprofile">
-            <div class="ui minimal comments">
-                <div class="ui comment">
-                    <div class="avatar" style="height: 2.5em">
-                        <img class="usericon" src="img/desktop/system_icon/user.svg">
-                    </div>
-                    <div class="content">
-                        <dic class="author" id="username" style="font-weight: 600 !important;">User</dic>
-                        <div class="text" id="usergroups" style="color: #616162;">@user</div>
-                    </div>
-                </div>
-            </div>
-            <div class="ui divider"></div>
-            <div id="alternativeAccountList">
-
-            </div>
-            <div class="ui fluid basic button" style="" onclick="openSwitchAccountPanel(); toggleProfileInfo();">
-                <i class="ui blue user plus icon" style="margin-right: 0.6em;"></i> <span locale="account/switch/addAccount">Add another account</span>
-            </div>
-            <div id="signoutAllButton" style="margin-top: 0.6em; margin-bottom: 1.2em;" class="ui fluid basic black button" onclick="logoutAllAccounts();">
-                <i class="log out icon icon" style="margin-right: 0.6em;"></i> <span locale="account/switch/signoutAll">Sign-out all accounts</span>
-            </div>
-            <button locale="quickAccess/logout" onclick="logout();" class="ui basic blue mini button" style="position: absolute; top: 1em; right: 1em;">Logout</button>
-            <button onclick="toggleProfileInfo();" class="circular ui themecolor icon button" style="position: absolute; bottom: 0px; right: 0px; margin-bottom: -10px; margin-right: -10px; color: white;">
-                <i class="remove icon"></i>
-              </button>
+    </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" id="cp-dark-ic">🌙</span>
+            <span id="cp-dark-lbl">Dark Mode</span>
+        </button>
+        <button class="cp-tile" onclick="cpFullscreen()">
+            <span class="cp-tile-icon">⛶</span>
+            <span>Fullscreen</span>
+        </button>
+        <button class="cp-tile" onclick="cpWallpaper()">
+            <span class="cp-tile-icon">🖼</span>
+            <span>Wallpaper</span>
+        </button>
+        <button class="cp-tile" onclick="cpSettings()">
+            <span class="cp-tile-icon">⚙️</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">👤</span>Add / Switch Account</button>
+        <button class="cp-btn red" onclick="cpLogout()"><span class="cp-btn-ic">⏏</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="listMenu" style="left:-100%;">
-            <div class="searchBar" onkeydown="searchModule(event);">
-                <div class="ui icon fluid input" style="border-radius: 0px !important;">
-                    <input id="searchBar" type="text" placeholder="Search">
-                    <i class="search icon"></i>
-                </div>
-            </div>
-            <dib class="listItemWrapper">
-                <div class="groups">
-                    <div locale="listmenu/catergory/searchResults" id="searchResults" class="item" style="display:none;">Search Results</div>
-                    <div locale="listmenu/catergory/all" class="item groupType selected" group="All">All</div>
-                    <div locale="listmenu/catergory/media" class="item groupType" group="Media">Media</div>
-                    <div locale="listmenu/catergory/office" class="item groupType" group="Office">Office</div>
-                    <div locale="listmenu/catergory/download" class="item groupType" group="Download">Download</div>
-                    <div locale="listmenu/catergory/files" class="item groupType" group="Files">Files</div>
-                    <div locale="listmenu/catergory/internet" class="item groupType" group="Internet">Internet</div>
-                    <div locale="listmenu/catergory/settings" class="item groupType" group="System Settings">System Settings</div>
-                    <div locale="listmenu/catergory/tools" class="item groupType" group="System Tools">System Tools</div>
-                    <div locale="listmenu/catergory/utils" class="item groupType" group="Utilities">Utilities</div>
-                    <div locale="listmenu/catergory/other" class="item groupType" group="Other">Other</div>
-                    <div class="hideListMenuButton" onclick="closeListMenu();">
-                        <img class="ui image" src="img/mobile/cancel-black-48dp.svg"></img>
-                    </div>
-                </div>
-                <div id="listMenuItem" class="items" align="left">
-    
-                </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="mainFrame">
-            <div id="backdrop">
-                
-            </div>
-            <div class="clock">
-                ArozOS Desktop
-            </div>
-           
-            <div id="windowWrapper">
-                
-            </div>
-            <div id="shortcuts">
-                <!-- System Shortcuts -->
-                <div class="ui divider verticalDisplayOnly"></div>
-                <div class="ui four column grid">
-                    <div class="column shortcutObject">
-                        <img id="shortcut1" module="" onclick="launchThisModule(this);" class="ui medium clickable image" src="img/mobile/pending.svg">
-                    </div>
-                    <div class="column shortcutObject">
-                        <img id="shortcut2" module="" onclick="launchThisModule(this);" class="ui medium clickable image" src="img/mobile/pending.svg">
-                    </div>
-                    <div class="column shortcutObject">
-                        <img id="shortcut3" module="" onclick="launchThisModule(this);" class="ui medium clickable image" src="img/mobile/pending.svg">
-                    </div>
-                    <div class="column shortcutObject">
-                        <img id="shortcut4" module="" onclick="launchThisModule(this);" class="ui medium clickable image" src="img/mobile/pending.svg">
-                    </div>
-                </div>
-            </div>
-           
-            <div id="conndrop">
-                <img class="ui mini image blink-image" src="SystemAO/desktop/icons/connlost.svg">
+        <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 id="sidebarToggleOverlay"></div>
-       
-
-        <script>
-            /*
-                ArOZ Online Mobile Interfacae
-
-                This script must maintain compatibility to the desktop.system interface
-                regarding all cross iframe access functions.
-
-            */
-            var isDesktopMode = true;   //Try to emulate Desktop mode
-            var moduleInstalled = [];   //List of installed modules on the system
-            var desktopThemeList = [];  //List of themes installed, same as desktop theme
-            var listMenuShown = false;
-            var hostInfo;
-
-            //IME mockup
-            window.ime = null;
-
-            //Clock related
-            var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
-            var daysNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
-
-            //initiation Functions
-            initHostInfo();
-            initModuleList();
-            bindGroupTypeEvents();
-            initBackdropImage();
-            initUserDefinedThemeColor();
-            bindBackgroundClickActions();
-            initStartupSounds();
-            initDesktopUserInfo();
-            initShortcuts();
-
-            //Bind background click
-            function bindBackgroundClickActions(){
-                $("#mainFrame").on("touchstart click", function(evt){
-                    if ($(this).hasClass("blurred") && $(".taskBar").find(".toggleTaskBar").attr("shown") == "true"){
-                        //hide the taskbar
-                        hideTaskBar();
-                    }
-                });
-
-                $("#sidebarToggleOverlay").on("touchstart click", function(evt){
-                    if ($(".taskBar").find(".toggleTaskBar").attr("shown") == "true"){
-                        //hide the taskbar
-                        evt.preventDefault();
-                        hideTaskBar();
-                    }
-                });
-            }
-           
-            //Check and prepare localization
-            if (applocale){
-                //Applocale found. Do localization
-                applocale.init("./SystemAO/locale/desktop.json", function(){
-                    applocale.translate();
-                });
-            }else{
-                //Applocale not found. Is this a trim down version of ArozOS?
-                var applocale = {
-                    getString: function(key, original){
-                        return original;
-                    }
-                }
-            }
-
-            //Connection check
-            setInterval(function() {
-                $.ajax({
-                    url: 'system/auth/checkLogin',
-                    success: function(data) {
-                        if (data == true) {
-                            //Continue session
-                        } else {
-                            //Session timeout. Redirect to system index
-                            window.location.href = "index.html";
-                        }
-                        $("#conndrop").hide();
-                        $("#conndrop").css("display", "none");
-                    },
-                    error: function(evt) {
-                        //Server closed or freezed?
-                        $("#conndrop").show();
-                        $("#conndrop").css("display", "flex");
-                    },
-                    timeout: 15000
-                });
-            }, 15000);
-
-            //Initiate the host information
-            function initHostInfo(){
-                $.get("system/desktop/host",function(data){
-                    hostInfo = data;
-                    document.title = hostInfo.Hostname;
-                });
-            }
-
-            //Initialize background image
-            function initBackdropImage(){
-                $.get("system/desktop/theme", function(data) {
-                    //Return a list of theme stored on the system
-                    desktopThemeList = data;
-                    $.get("system/desktop/theme?get=true", function(data) {
-                        //Get the user theme settings
-                        changeDesktopTheme(data);
-                    });
-                });
-            }
-
-            function initUserDefinedThemeColor(){
-                $.ajax({
-                    url: "system/desktop/preference",
-                    data: {preference: "themecolor"},
-                    method: "POST",
-                    success: function(data){
-                        console.log(data);
-                        if (data.error == undefined && data != ""){
-                            setThemeColor(data);
-                        }
-                    }
-                });
-            }
-
-            function changeDesktopTheme(themename){
-                //Match the given theme to the themename
-                if (themename.includes(":/") == false){
-                    //This is a path
-                    for (var i =0; i < desktopThemeList.length ; i++){
-                        if (desktopThemeList[i].Theme == themename){
-                            var targetImage = desktopThemeList[i].Bglist[0];
-                            $("#backdrop").css("background-image", `url(img/desktop/bg/${themename}/${targetImage})`)
-                        }
-                    }
-                }else{
-                    //This is a path (user defined background folder)
-                    $.get("system/desktop/theme?load=" + themename, function(data){
-                        if (data.error !== undefined){
-                            //The folder is gone. Use default instead
-                            console.log(data.error);
-                            changeDesktopTheme("default");
-                        }else{
-                            $("#backdrop").css("background-image", `url(media/?file=${data[0]})`)
-                        }
-                    });
-                }
-            }
-           
-            function toggleFullScreen() {
-                var doc = window.document;
-                var docEl = doc.documentElement;
-
-                var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
-                var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
-
-                if(!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {
-                    requestFullScreen.call(docEl);
-                }
-                else {
-                    cancelFullScreen.call(doc);
-                }
-            }
+    </div>
 
-
-
-            //Sidebar
-            function toggleTaskBar(obj){
-                if ($(obj).attr("shown") == "true"){
-                    hideTaskBar();
-                }else{
-                    showTaskBar();
-                }
-            }
-
-            function hideTaskBar(){
-                //Hide the taskBar
-                $(".taskBar").animate({
-                    width: "30px"
-                },300, "swing");
-                $(".taskBar").find('.sidebararrow').attr("src","img/mobile/keyboard_arrow_right-white-48dp.svg");
-                $(".taskBar").find(".toggleTaskBar").attr("shown","false");
-                $("#mainFrame").removeClass("blurred");
-
-                //Minimize all fwbuttons
-                $(".floatWindowButton").each(function(){
-                    $(this).find(".minimizedElements").show();
-                    $(this).find(".normalElements").hide();
-                });
-
-                $("#sidebarToggleOverlay").hide();
-                $(".extendOnly").slideUp("fast");
-
-                //Check the number of floatWindow
-                if ($(".floatWindowWrapper").length == 0){
-                    //Reaching desktop layer
-                    showShortcuts();
-                }else{
-                    //Check if all float windows are hidden
-                    var allHidden = $(".floatWindowWrapper").is(":hidden");
-                    if (!allHidden){
-                        hideShortcuts();
-                    }
-                    
-                }
-            }
-
-            function showTaskBar(){
-                //Show the taskbar
-                $(".taskBar").animate({
-                    width: "300px"
-                }, 300, "swing", function(){
-                    //Switch all fwbuttons to normal size
-                    $(".floatWindowButton").each(function(){
-                        $(this).find(".minimizedElements").hide();
-                        $(this).find(".normalElements").show();
-                     });
-                     $(".extendOnly").slideDown("fast");
-                });
-                $(".taskBar").find('.sidebararrow').attr("src","img/mobile/keyboard_arrow_left-white-48dp.svg");
-                $(".taskBar").find(".toggleTaskBar").attr("shown","true");
-                $("#mainFrame").addClass("blurred");
-                $("#sidebarToggleOverlay").show();
-                
-            }
-
-            //List all modules
-            function initModuleList(){
-                $.ajax({
-                    url: "system/modules/list",
-                    success: function(data) {
-                        moduleInstalled = data;
-                        listModulesType("All");
-                    }
-                });
-            }
-
-            //ListMenu group classification events
-            function bindGroupTypeEvents(){
-                $(".groupType").on("click",function(evt){
-                    //Clear all serach results
-                    $("#searchResults").slideUp('fast');
-                    listModulesType($(this).attr("group"));
-                    $(".groupType.selected").removeClass("selected");
-                    $(this).addClass("selected");
-                    /*
-                    var classificationObject = this;
-                    if ($(this).attr("group") == "All"){
-                        $('.item.module').show();
-                    } else if ($(this).attr("group") == "Other"){
-                        var excludeList = ["Media", "Office", "Download", "Files", "Internet", "System Settings", "System Tools", "Utilties"];
-                        $('.item.module').each(function(){
-                            if (excludeList.includes($(this).attr("group")) == false){
-                                $(this).show();
-                            }else{
-                                $(this).hide();
-                            }
-                        });
-                    }else{
-                        $('.item.module').each(function(){
-                            if ($(this).attr("group") == $(classificationObject).attr("group")){
-                                $(this).show();
-                            }else{
-                                $(this).hide();
-                            }
-                        });
-                    }
-                    */
-                   
-                });
-            }
-
-            function searchModule(event) {
-                if (event.which == 13) {
-                    var keyword = $("#searchBar").val().toLowerCase();
-                    var results = [];
-                    var lessAccurateResults = [];
-                    $(".groupType.selected").removeClass("selected");
-                    $("#searchResults").addClass("selected").slideDown('fast');
-                    //Load all search results
-                    for (var i = 0; i < moduleInstalled.length; i++) {
-                        var thisModule = moduleInstalled[i];
-                        var pathInfo = thisModule["StartDir"].split("/");
-                        for (var j = 0; j < pathInfo.length; j++) {
-                            pathInfo[j] = pathInfo[j].toLowerCase();
-                        }
-                        if (thisModule["Name"].toLowerCase().includes(keyword)) {
-                            results.push(thisModule);
-                        } else if (pathInfo.includes(keyword.toLowerCase()) || pathInfo.includes(keyword.split(" ").join("_").toLowerCase())) {
-                            lessAccurateResults.push(thisModule);
-                        }
-                    }
-                    results = results.concat(lessAccurateResults);
-                    //Append the results to list
-                    $("#listMenuItem").html("");
-                    for (var i = 0; i < results.length; i++) {
-                        generateListMenuItem(results[i]);
-                    }
-
-                    if (results.length == 0) {
-                        //Append a custom no results div to the content
-                        $("#listMenuItem").append(`<div class="item module"><span><img class="ui middle aligned tiny image" src="img/system/not found.png" style="padding-right: 12px;">No Results</span></div>`);
-                    }
-                }
-            }
-           
-
-            function listModulesType(groupType) {
-                var listingItems = [];
-                if (groupType == "All") {
-                    //List all of the items
-                    for (var i = 0; i < moduleInstalled.length; i++) {
-                        if (moduleInstalled[i]["StartDir"] != "") {
-                            listingItems.push(moduleInstalled[i]);
-                        }
-                    }
-                } else if (groupType == "Other") {
-                    var excludeList = ["Media", "Office", "Download", "Files", "Internet", "System Settings", "System Tools", "Utilties"];
-                    //List the Other groups
-                    for (var i = 0; i < moduleInstalled.length; i++) {
-                        if (excludeList.includes(moduleInstalled[i]["Group"]) == false && moduleInstalled[i]["StartDir"] != "") {
-                            listingItems.push(moduleInstalled[i]);
-                        }
-                    }
-                } else {
-                    //List the given group
-                    for (var i = 0; i < moduleInstalled.length; i++) {
-                        if (moduleInstalled[i]["Group"] == groupType && moduleInstalled[i]["StartDir"] != "") {
-                            listingItems.push(moduleInstalled[i]);
-                        }
-                    }
-                }
-
-                //List the item to the listmenu
-                $("#listMenuItem").html("");
-                for (var i = 0; i < listingItems.length; i++) {
-                    var thisModule = listingItems[i];
-                    generateListMenuItem(thisModule);
-                }
-                if (listingItems.length == 0) {
-                    $("#listMenuItem").html(`<div class="item module"><span><img class="ui middle aligned tiny image" src="img/system/not found.png" style="padding-right: 12px;">No WebApp found</span></div>`)
-                }
-            }
-
-            function generateListMenuItem(thisModule) {
-                var icon = thisModule["IconPath"];
-                if (icon == "") {
-                    //Use default system icon
-                    icon = "img/system/service.png";
-                }
-                var name = thisModule["Name"];
-                var startdir = thisModule["StartDir"];
-                var group = thisModule["Group"];
-                var fwsupport = "false";
-                if (thisModule["SupportFW"]) {
-                    fwsupport = "true";
-                }
-                $("#listMenuItem").append(`<div class="item module" module="${name}" startdir="${startdir}" fw="${fwsupport}" group="${group}" onclick="openModuleFromMenu(this);">
-                    <span>
-                        <img class="ui middle aligned tiny image" src="${icon}" style="padding-right: 12px;"></img>
-                        ${name}
-                    </span>
-                </div>`);
-            }
-
-            function closeListMenu(callback = undefined){
-                if (listMenuShown == false){
-                    //Already closed
-                    if (typeof callback != "undefined"){
-                        callback();
-                    }
-                    return;
-                }
-                $("#listMenu").animate({
-                    left: window.innerWidth * -1
-                },300,"swing", function(){
-                    $(".taskBar").animate({
-                        width: "300px",
-                    },300,"swing", function(){
-                        
-                        if (typeof callback != "undefined"){
-                            callback();
-                        }else{
-                            $(".extendOnly").slideDown("fast");
-                        }
-                    });
-                    
-                });
-
-                listMenuShown = false;
-            }
-
-            //Bind swip events
-            document.addEventListener('touchstart', handleTouchStart, false);        
-            document.addEventListener('touchmove', handleTouchMove, false);
-
-            var xDown = null;                                                        
-            var yDown = null;
-            var swipeTarget = null;
-            var targetType = "fw";
-            function getTouches(evt) {
-            return evt.touches ||             // browser API
-                    evt.originalEvent.touches; // jQuery
-            }                                                     
-
-            function handleTouchStart(evt) {
-                const firstTouch = getTouches(evt)[0];
-                if ($(evt.target).hasClass("taskBar")){
-                        swipeTarget = evt.target;
-                        targetType = "taskbar"
-                }else if ($(evt.target).hasClass("toggleTaskBar")){
-
-                
-                }else{
-                    if (evt.path != undefined){
-                        swipeTarget = getFloatWindowObjectFromPath(evt.path);
-                    }
-                    
-                }
-                
-                xDown = firstTouch.clientX; 
-                yDown = firstTouch.clientY;
-            };
-
-            function getFloatWindowObjectFromPath(path){
-                var targetObject = undefined;
-                targetType = undefined
-                path.forEach(object => {
-                    if ($(object).hasClass("floatWindowButton")){
-                        targetObject = object
-                        targetType = "fw"
-                    }
-                })
-
-                return targetObject;
-            }
-
-            function handleTouchMove(evt) {
-                if ( ! xDown || ! yDown ) {
-                    return;
-                }
-
-                var xUp = evt.touches[0].clientX;                                    
-                var yUp = evt.touches[0].clientY;
-
-                var xDiff = xDown - xUp;
-                var yDiff = yDown - yUp;
-
-                if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
-                    if ( xDiff > 0 ) {
-                        /* left swipe */ 
-                        if (swipeTarget !== undefined && targetType == "fw"){
-                            closeFloatWindow($(swipeTarget).attr("windowId"));
-                        }else if (swipeTarget !== undefined && targetType == "taskbar"){
-                            hideTaskBar();
-                        }
-                    } else {
-                        /* right swipe */
-                        if (swipeTarget !== undefined && targetType == "fw"){
-                            //Allow left or right swipe to remove fw
-                            closeFloatWindow($(swipeTarget).attr("windowId"));
-                        }else  if (swipeTarget !== undefined && targetType == "taskbar"){
-                            //Right swipe on taskbar
-                            if ($(swipeTarget).find(".toggleTaskBar").attr("shown") == "true"){
-                                //Already shown. Toggle list menu
-                                showListMenu();
-                            }else{
-                                showTaskBar();
-                            }
-                            
-                        }
-                    }                       
-                } else {
-                    if ( yDiff > 0 ) {
-                        /* up swipe */ 
-                    } else { 
-                        /* down swipe */
-                    }                                                                 
-                }
-                /* reset values */
-                xDown = null;
-                yDown = null;
-                swipeTarget = null;
-            };
-
-
-            function showListMenu(callback = undefined){
-                if ($(".taskBar").find(".toggleTaskBar").attr("shown") == "false"){
-                    $(".taskBar").find(".toggleTaskBar").attr("shown", "true");
-                    $(".taskBar").find('.sidebararrow').attr("src","img/mobile/keyboard_arrow_left-white-48dp.svg");
-                    $("#mainFrame").addClass("blurred");
-                }
-                $(".taskBar").animate({
-                    width: window.innerWidth,
-                },300, "swing", function(){
-                    //Switch all fwbuttons to normal size
-                    $(".floatWindowButton").each(function(){
-                        $(this).find(".minimizedElements").hide();
-                        $(this).find(".normalElements").show();
-                     });
-
-                     //Show list menu
-                    $("#listMenu").animate({
-                        left: 0
-                    },300,"swing", function(){
-                        if (typeof callback != "undefined"){
-                            callback();
-                        }
-                    });
-                });
-                listMenuShown = true;
-                $("#sidebarToggleOverlay").show();
-            }
-
-            function openModule(moduleName) {
-                $.get("system/modules/getLaunchPara?module=" + moduleName, function(data) {
-                    console.log(data);
-                    if (data.error !== undefined) {
-                        //Something went wrong.
-                        console.log("Unable to open module " + moduleName);
-                        if (data.error == "Not logged in."){
-                            //Session expired
-                            window.location.href = "login.html";
-                        }
-                    }else {
-                        //Launch the given module
-                        var url = data["StartDir"];
-                        var size = [undefined, undefined];
-                        var icon = "img/system/favicon.png";
-                        if (data["IconPath"] != "") {
-                            icon = data["IconPath"];
-                        }
-                        var title = data["Name"];
-                        //Check if the module support fw mode. If yes, launch with fwmode url. IF not, launch to index
-                        if (data["SupportFW"] == true) {
-                            if (data["LaunchFWDir"] != null) {
-                                url = data["LaunchFWDir"];
-                            }
-                            if (data["InitFWSize"] != null) {
-                                size = data["InitFWSize"];
-                            }
-                        }
-
-                        //Launch the given module
-                        newFloatWindow({
-                            url: url,
-                            width: size[0],
-                            height: size[1],
-                            appicon: icon,
-                            title: title
-                        });
-
-                        closeListMenu(function(){
-                            hideTaskBar();
-                        });
-                        
-                    }
-                });
-            }
-
-            function openModuleFromMenu(object) {
-                var moduleName = $(object).attr("module");
-                openModule(moduleName);
-            }
-
-            //In mobile interface, there will be some option ignored by default
-            function newFloatWindow(options){
-                //Hide all other floatWindows
-                $(".floatWindowWrapper").fadeOut("fast");
-
-                //Hide shortcuts
-                hideShortcuts();
-
-                //Construct the new window
-                var windowID = Date.now();
-                var parent = options.parent || "";
-                var callback = options.callback || "";
-                //Create a new iframe
-                $("#windowWrapper").append(` <div class="floatWindowWrapper" windowId="${windowID}">
-                <div class="floatWindow" windowId="${windowID}" parent="${parent}" callback="${callback}">
-                    <div class="fwtab">
-                        <iframe src="${options.url}" style="width: 100%; height: 100%;"></iframe>
-                    </div>
-                </div>
-                </div>`);
-
-                //Create the button
-                var title = options.title || "New FloatWindow";
-
-                $("#windowButtonWrapper").append(`
-                    <div class="floatWindowButton" windowID="${windowID}" onclick="focusTab(this)">
-                        <div class="minimizedElements">
-                            <img class="minimizedIcon" src="${options.appicon}">
-                        </div>
-                        <div class="normalElements" style="display:none;">
-                            <img class="ui normalizedIcon middle aligned image" src="${options.appicon}">
-                            <span class="windowTitle" style="color:white;">${title}</span>
-                            <div class="closebutton" onclick="closefw(this);"><i class="remove icon"></i></div>
-                            <div class="externalbutton" onclick="openfwInNewTab(this);"><i class="external icon"></i></div>
-                        </div>
-                        
-                    </div>
-                `);
-            }  
-
-            ///Functions realted to window resize and auto scaling
-            function updateWindowDimensions(){
-                $("#backdrop").css({
-                    width: window.innerWidth,
-                    height: window.innerHeight
-                });
-                
-                //Check if the device is used horizontally or vertically
-                if (window.innerWidth > window.innerHeight){
-                    //Holding horizontally
-                    $("body").addClass("horizontal");
-                    $("#shortcuts").find(".shortcutObject").removeClass("column");
-                    $(".verticalDisplayOnly").hide();
-                }else{
-                    //Holding vertically
-                    $("body").removeClass("horizontal");
-                    $("#shortcuts").find(".shortcutObject").addClass("column");
-                    if (!listMenuShown){
-                        $("#listMenu").css("left", window.innerWidth * -1);
-                    }
-                    $(".horizontalDisplayOnly").hide();
-                }
-            }
-
-
-            updateWindowDimensions();
-            $(window).on("resize", function(data){
-                updateWindowDimensions();
-
-                //Handle resize and reposition of the list menu
-                if (listMenuShown){
-                    $("#listMenu").css({
-                        left: 0
-                    });
-                }else{
-                    $("#listMenu").css({
-                        left: window.innerWidth * -1
-                    });
-                }
+    <!-- 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 on your desktop</p></div>
+        <div id="files-grid-wrap">
+            <div id="files-grid"></div>
+            <div id="files-empty">No folders 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>
+
+<!-- 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){
+        $("#cp-dark-ic").text("☀"); $("#cp-dark-lbl").text("Light Mode");
+        $("#cp-dark-btn").addClass("on");
+    }else{
+        $("#cp-dark-ic").text("🌙"); $("#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;}
+        var folders=data.filter(function(f){return f.IsDir&&!f.IsShortcut;});
+        if(!folders.length){$("#files-empty").show();return;}
+        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");
+}
+
+/* ─────────────────────────────────────────────
+   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>'
+                );
             });
-
-            //Float Window APIs
-            function setFloatWindowTitle(id, title){
-                $(".floatWindowButton").each(function(){
-                    if ($(this).attr("windowId") == id){
-                        $(this).find(".windowTitle").text(title);
-                    }
-                })
-            }
-
-            function getFloatWindowByID(id){
-                var targetObejct = undefined;
-                $(".floatWindowWrapper").each(function(){
-                    if ($(this).attr("windowId") == id){
-                        targetObejct = $(this);
-                    }
-                });
-                return targetObejct;
-            }
-
-            function MoveFloatWindowToTop(targetfw){
-                //Check if this windows is already topped
-                if ($(targetfw).is(":visible")){
-                    //Already topped
-                    return;
-                }
-
-                $(".floatWindowWrapper").each(function(){
-                    $(this).fadeOut("fast");
-                });
-
-                console.log(targetfw);
-                $(targetfw).parent().fadeIn("fast");
-            }
-
-            function focusTab(object){
-                var windowID = $(object).attr("windowId");
-                //Hide all other flowWindows
-                $(".floatWindowWrapper").fadeOut("fast");
-
-                //Show the target fw
-                var fw = getFloatWindowByID(windowID);
-                hideShortcuts();
-                fw.fadeIn("fast", function(){
-                    hideTaskBar();
-                });
-                
-            }
-
-            function getFloatWindowByID(id){
-                var targetObejct = undefined;
-                $(".floatWindowWrapper").each(function(){
-                    if ($(this).attr("windowId") == id){
-                        targetObejct = $(this);
-                    }
-                });
-
-                return targetObejct;
-            }
-
-            function setFloatWindowResizePolicy(id, allowResize){
-                //Disabled in mobile mode
-            }
-
-            function setFloatWindowSize(id, width, height){
-                //Disabled in mobile mode
-            }
-
-            function closeFloatWindow(windowID){
-                //Get the content iframe with that windowID
-                var contentWindow = getFloatWindowByID(windowID);
-                if (contentWindow == undefined){
-                    return;
-                }
-                contentWindow = contentWindow.find("iframe")[0].contentWindow;
-                try {
-                    if (contentWindow.ao_module_close === undefined) {
-                        //This module do not use ao_module wrapper. Close it directly.
-                        closeFwProcess(windowID);
-                    } else {
-                        //Pass the closing events to the window itself
-                        contentWindow.ao_module_close();
-                    }
-                } catch (ex) {
-                    //Problems of closing floatWindow. Force closing.
-                    closeFwProcess(windowID);
-                }
-            } 
-
-            function closefw(object){
-                var windowID = $(object).parent().parent().attr("windowID");
-                closeFloatWindow(windowID);
-            }
-
-            function openfwInNewTab(object){
-                event.preventDefault();
-                event.stopImmediatePropagation();
-                var windowID = $(object).parent().parent().attr("windowID");
-                var targetFloatWindow = null;
-                $(".floatWindowWrapper").each(function(){
-                    if ($(this).attr("windowid") == windowID){
-                        targetFloatWindow = $(this);
-                    }
-                });
-                if (targetFloatWindow == null){
-                    return;
-                }
-                var iframe = $(targetFloatWindow).find("iframe");
-                var url = $(iframe)[0].contentWindow.location.href;
-                window.open(url);
-            }
-
-
-            //Get the window below this window id, can return null if there is no window
-            function getPreviousWindowObject(windowID){
-                var previousWindowID = null;
-                var result = null;
-                $(".floatWindowButton").each(function(){
-                    if ($(this).attr("windowid") == windowID){
-                        result = previousWindowID;
-                    }else{
-                        let thisWindowID = $(this).attr("windowid");
-                        previousWindowID = thisWindowID;
-                    }
-                });
-
-                return result;
-            }
-
-            function closeFwProcess(windowID){
-                //Get the previous window object
-                var previousWindowID = getPreviousWindowObject(windowID);
-                
-                //Remove the window object
-                $(".floatWindowWrapper").each(function(){
-                    if ($(this).attr("windowId") == windowID){
-                        $(this).fadeOut("fast",function(){
-                            $(this).remove(); 
-                        });
-                    }
-                });
-
-                //Remove the button object
-                $(".floatWindowButton").each(function(){
-                    if ($(this).attr("windowId") == windowID){
-                        $(this).fadeOut("fast",function(){
-                            $(this).remove(); 
-                        });
-                    }
-                });
-
-                //Focus the window behind if exists
-                setTimeout(function(){
-                    if (previousWindowID != null){
-                        //Hide all other flowWindows
-                        $(".floatWindowWrapper").fadeOut("fast");
-
-                        //Show the target fw
-                        var fw = getFloatWindowByID(previousWindowID);
-                        console.log(fw);
-                        fw.fadeIn("fast", function(){
-                            
-                        });
-                    }
-                }, 100);
-                
-                
-            }
-
-            function bindObjectToIMEEvents(object){
-                console.log("IME not supported in mobile desktop mode")
-            };
-
-            function openFileWithModule(moduleLaunchInfo, openFileList) {
-                var url = moduleLaunchInfo["StartDir"];
-                var size = [undefined, undefined];
-                var title = moduleLaunchInfo["Name"];
-                var icon = "img/system/favicon.png";
-                if (moduleLaunchInfo["IconPath"] != "") {
-                    icon = moduleLaunchInfo["IconPath"];
-                }
-                //Use floatWindow if exists
-                if (moduleLaunchInfo["SupportFW"] == true && moduleLaunchInfo["LaunchFWDir"] != "") {
-                    url = moduleLaunchInfo["LaunchFWDir"];
-                    if (moduleLaunchInfo["InitFWSize"] !== null) {
-                        size = moduleLaunchInfo["InitFWSize"]
-                    }
-                }
-                //Use embedded mode if exists
-                if (moduleLaunchInfo["SupportEmb"] == true && moduleLaunchInfo["LaunchEmb"] != "") {
-                    url = moduleLaunchInfo["LaunchEmb"];
-                    if (moduleLaunchInfo["InitEmbSize"] !== null) {
-                        size = moduleLaunchInfo["InitEmbSize"]
-                    }
-                }
-
-                var openParamter = encodeURIComponent(JSON.stringify(openFileList));
-
-                //Add launch files info and launch floatWindow
-                newFloatWindow({
-                    url: url + "#" + openParamter,
-                    width: size[0],
-                    height: size[1],
-                    appicon: icon,
-                    title: title
-                });
-            }
-
-            //Mobile interface do not support pin function yet
-            function PinFloatWindowToTopMostMode(object){
-            }
-
-            function UnpinFloatWindowFromTopMostMode(object){
-
-            }
-
-            /*
-                List Menu Specific function groups
-            */
-
-            function openDesktopAsFolder(){
-                newFloatWindow({
-                    url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent("user:/Desktop/"),
-                    appicon: "SystemAO/file_system/img/small_icon.png",
-                    width:1080,
-                    height:580,
-                    title: "File Manager"
-                });
-
-                hideTaskBar();
-            }
-
-            function openDesktopCustomization(){
-                newFloatWindow({
-                    url: "SystemAO/desktop/personalization.html",
-                    appicon: "SystemAO/desktop/img/personalization.png",
-                    width:640,
-                    height:480,
-                    title: "Personalization"
-                });
-
-                hideTaskBar();
-            }
-
-            function showDesktop(){
-                $(".floatWindowWrapper").fadeOut("fast");
-                hideTaskBar();
-                showShortcuts();
-            }
-
-            //Theme color placeholder functions
-            function setThemeColor(color){
-                $(".themeColor").css("background-color", color);
-
-            }
-
-            //Startup sound
-            function initStartupSounds(){
-                $.ajax({
-                    url: "../../system/desktop/preference",
-                    method: "POST",
-                    data: {preference: "startup-audio"},
-                    success: function(data){
-                        if (data == undefined || data == ""){
-                            return;
-                        }
-                        var currentGlobalVol = localStorage.getItem("global_volume");
-                        if (currentGlobalVol != null && currentGlobalVol != undefined && currentGlobalVol != ""){
-                            
-                        }else{
-                            currentGlobalVol = 0;
-                        }
-
-                        var audio = new Audio("media?file=" + data);
-                        audio.volume = currentGlobalVol;
-                        audio.play();
-                    }
-                });
-            }
-
-            function fullscreen() {
-                //Opening full screen will lead to hidden of all iframe for unknown reasons
-                var isInFullScreen = (document.fullscreenElement && document.fullscreenElement !== null) ||
-                    (document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
-                    (document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
-                    (document.msFullscreenElement && document.msFullscreenElement !== null);
-                var elem = document.documentElement;
-                if (!isInFullScreen) {
-                    if (elem.requestFullscreen) {
-                        elem.requestFullscreen();
-                    } else if (elem.mozRequestFullScreen) { /* Firefox */
-                        elem.mozRequestFullScreen();
-                    } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
-                        elem.webkitRequestFullscreen();
-                    } else if (elem.msRequestFullscreen) { /* IE/Edge */
-                        elem.msRequestFullscreen();
-                    }
-                } else {
-                    if (document.exitFullscreen) {
-                        document.exitFullscreen();
-                    } else if (document.webkitExitFullscreen) {
-                        document.webkitExitFullscreen();
-                    } else if (document.mozCancelFullScreen) {
-                        document.mozCancelFullScreen();
-                    } else if (document.msExitFullscreen) {
-                        document.msExitFullscreen();
-                    }
-                }
-
-                hideTaskBar();
-            }
-
-            function initDesktopUserInfo(){
-                $.get("system/desktop/user", function(data){
-                    if (data.error !== undefined){
-                        alert(data.error);
-                    }else{
-                        userInfo = data;
-                        //Update the user tag
-                        $("#username").text(userInfo.Username);
-                        $("#usergroups").text("@" + userInfo.UserGroups.join("/"));
-                        $("#usergroups").attr("title",userInfo.UserGroups.join(" / "));
-                        if (data.UserIcon !== ""){
-                            $(".usericon").attr("src",data.UserIcon);
-                        }
-
-                        listAllStoredAccounts();
-                    }
-                });
-            }
-
-            function showProfileInfo(){
-                hideTaskBar();
-                setTimeout(function(){
-                    toggleProfileInfo();
-                }, 500);
-            }
-
-            function initShortcuts(){
-                initShortcut(1, $("#shortcut1"));
-                initShortcut(2, $("#shortcut2"));
-                initShortcut(3, $("#shortcut3"));
-                initShortcut(4, $("#shortcut4"));
-            }
-
-            function initShortcut(id, displayTarget){
-                getStorage("ao/mobile/shorcut/" + id, function(data){
-                    if (data != ""){
-                        $.get("system/modules/getLaunchPara?module=" + data, function(para){
-                            displayTarget.attr("src", para.IconPath);
-                            if (para.StartDir == ""){
-                                displayTarget.addClass("disabled");
-                            }else{
-                                displayTarget.attr("module", encodeURIComponent(JSON.stringify(para.Name)));
-                            }
-                        });
-                    }else{
-                        //No data for this shortcut
-                        displayTarget.attr("src", "img/desktop/system_icon/bad_shortcut.png");
-                        displayTarget.addClass("disabled");
-                    }
-                });
-            }
-
-            function showShortcuts(){
-                if (window.innerWidth > window.innerHeight){
-                    //Horizontal
-                    $("#shortcuts").fadeIn("fast");
-                }else{
-                    //Vertical
-                    $("#shortcuts").slideDown("fast");
-                }
-                
-            }
-
-            function hideShortcuts(){
-                if (window.innerWidth > window.innerHeight){
-                    //Horizontal
-                    $("#shortcuts").fadeOut("fast");
-                }else{
-                    //Vertical
-                    $("#shortcuts").slideUp("fast");
-                }
-                
-            }
-
-            function launchThisModule(object){
-                var moduleName = JSON.parse(decodeURIComponent($(object).attr("module")));
-                if (moduleName != ""){
-                    openModule(moduleName);
-                }
-            }
-
-            function toggleProfileInfo(){
-                $("#userprofile").transition('drop');
-            }
-
-            function openSystemSettings(){
-                openModule("System Setting");
-            }
-
-            function logout() {
-                loggingOut = true;
-                if (confirm("Exiting Session. Confirm?")){
-                    $.get("system/auth/logout", function() {
-                        window.location.href = "/";
-                    });
-                    toggleProfileInfo();
-                }
-                
-            }
-
-            function setStorage(key, value, callback = undefined) {
-                $.ajax({
-                    url: "system/desktop/preference",
-                    data: {preference: key, value: value},
-                    method: "POST",
-                    success: function(data) {
-                        if (data.error !== undefined) {
-                            console.log(data.error);
-                        } else {
-                            if (callback !== undefined) {
-                                callback();
-                            }
-
-                        }
-                    }
-                });
-            }
-
-            function getStorage(key, callback) {
-                $.ajax({
-                    url: "system/desktop/preference",
-                    data: {preference: key},
-                    method: "POST",
-                    success: callback
-                });
-            }
-
-            
-            //Keep the clock updated
-            setInterval(function(){
-                updateClockTime();
-            },5000);
-            updateClockTime();
-            function updateClockTime(){
-                var d = new Date();
-                var display = monthNames[d.getMonth()] + " " + d.getDate() + " " + zeropad(d.getHours(),2) + ":" + zeropad(d.getMinutes(),2);
-                
-                if ($(".notification.object").length > 0){
-                    display += `<span style="color: #f54242; margin-left: 8px; float: center;"><i class="notice circle icon"></i></span>`;
-                }
-                $(".clock").html(display);
-                var largedate = monthNames[d.getMonth()] + " " + d.getDate() + " " + d.getFullYear() 
-                $("#largedate").text(largedate);
-                var dow = daysNames[d.getDay()];
-                $("#dayofweek").text(dow);
-            }
-
-            function zeropad(n, width, z) {
-                z = z || '0';
-                n = n + '';
-                return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
-            }
-
-            /*
-                Account switching functions
-            */
-
-            function listAllStoredAccounts(){
-                $("#alternativeAccountList").empty();
-            
-                //Request server side for the account pool
-                $.get("system/auth/u/list", function(data){
-                    if (data.error != undefined){
-                        $("#signoutAllButton").addClass('disabled');
-                        $("#alternativeAccountList").append(`<div class="ui message">
-                            <i class="ui green check circle icon"></i> ${applocale.getString("account/switch/noAlternative", "No other account stored on this browser")}
-                        </div>`);
-                        return;
-                    }else{
-                        if (data.length > 1){
-                            data.forEach(function(account){
-                                if (account.Username == userInfo.Username){
-                                    //Skip
-                                    return;
-                                }
-                                
-                                $.get("system/desktop/user?target=" + account.Username, function(data){
-                                    let userIcon = data.UserIcon;
-                                    if (userIcon == ""){
-                                        userIcon = "img/desktop/system_icon/user.svg"
-                                    }
-                                    $("#alternativeAccountList").append(`
-                                        <div class="alternativeAccount ${account.IsExpired?"expired":""}" acname="${account.Username}" onclick="switchAccount(this);">
-                                            <div class="ui header">
-                                                <img class="usericon" src="${userIcon}">
-                                                <div class="content" style="font-size: 95% !important;">
-                                                    <span class="username">${account.Username}</span> ${(data.IsAdmin)?'<i style="margin-left: 0.4em; color: rgb(38, 50, 56);" class="small shield alternate icon themed text isAdminIcon"></i>':""}
-                                                    <div class="sub header usergroup">${!account.IsExpired?"<i class='ui green check circle icon' style='margin-right: 0px;'></i> " + applocale.getString("account/switch/sessionValid", "Session Valid"):"<i class='ui red times circle icon' style='margin-right: 0px;'></i> " + applocale.getString("account/switch/sessionExpired", "Session Expired")}</div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    `);
-                                });
-                            });
-                            $("#signoutAllButton").removeClass('disabled');
-                        }else{
-                            $("#signoutAllButton").addClass('disabled');
-                            $("#alternativeAccountList").append(`<div class="ui message">
-                                <i class="ui green check circle icon"></i> ${applocale.getString("account/switch/noAlternative", "No other account stored on this browser")}
-                            </div>`);
-                            return;
-                        }
-                    }
-                })
-            }
-
-            function switchAccount(object){
-                let targetUsername = $(object).attr("acname");
-                if (targetUsername == undefined || targetUsername == ""){
-                    console.log("Unable to load username from element")
-                    return;
-                }
-
-                //Check if it is expired
-                if ($(object).hasClass("expired")){
-                    openSwitchAccountPanel();
-                    return;
-                }
-
-                $.ajax({
-                    url: "system/auth/u/switch",
-                    data: {
-                        "username": targetUsername,
-                    },
-                    success: function(data){
-                        if (data.error != undefined){
-                            alert(data.error);
-                        }else{
-                            window.location.reload();
-                        }
-                    }
-                })
-            }
-
-            function openSwitchAccountPanel(){
-                var uuid = newFloatWindow({
-                    url: 'SystemAO/advance/switchAccount.html',
-                    width: 470,
-                    height: 680,
-                    appicon: "SystemAO/desktop/img/account-switch.png",
-                    title: "Switch Account"
-                });
-            }
-
-            function logoutAllAccounts(){
-                if (confirm(applocale.getString("account/switch/logout/confirm", "This will logout all other accounts from this browser. Confirm?"))){
-                    $.ajax({
-                        url: "system/auth/u/logoutAll",
-                        success: function(data){
-                            if (data.error != undefined){
-                                alert(data.error);
-                            }else{
-                                //Reset the browser pool id
-                                localStorage.removeItem("ao_acc");
-                                listAllStoredAccounts();
-                                toggleProfileInfo();
-                            }
-                        }
-                    })
-                }
-            }
-
-            //mobile mode not support re-initialization. Refresh page.
-            function initDesktop(){
-                window.location.reload();
-            }
-
-        </script>
-    </body>
-</html>
+        });
+    });
+}
+
+/* ─────────────────────────────────────────────
+   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,"&quot;").replace(/'/g,"&#39;");}
+</script>
+</body>
+</html>