Ver código fonte

mobile ui optimize

Toby Chui 2 semanas atrás
pai
commit
291752290a
1 arquivos alterados com 161 adições e 0 exclusões
  1. 161 0
      src/web/mobile.html

+ 161 - 0
src/web/mobile.html

@@ -284,6 +284,34 @@ body.dark-mode .cp-btn.red{color:#ff6b6b;}
 /* ── Connection lost ── */
 #conndrop{position:fixed;top:max(env(safe-area-inset-top),8px);left:50%;transform:translateX(-50%);z-index:200;background:rgba(196,43,28,.9);backdrop-filter:blur(8px);border-radius:20px;padding:5px 13px;display:none;align-items:center;gap:7px;font-size:12px;color:#fff;}
 #conndrop img{width:14px;height:14px;}
+
+/* ── FW Tab Context Menu ── */
+#fw-ctx-overlay{position:fixed;inset:0;z-index:210;display:none;}
+#fw-ctx-overlay.on{display:block;}
+#fw-ctx-menu{
+    position:fixed;z-index:211;display:none;
+    background:rgba(235,235,235,.94);backdrop-filter:blur(40px) saturate(200%);-webkit-backdrop-filter:blur(40px) saturate(200%);
+    border-radius:16px;box-shadow:0 8px 32px rgba(0,0,0,.32);
+    min-width:220px;overflow:hidden;
+    transform-origin:top left;
+    animation:fw-ctx-in .14s ease;
+}
+body.dark-mode #fw-ctx-menu{background:rgba(30,30,30,.96);}
+@keyframes fw-ctx-in{from{opacity:0;transform:scale(.88);}to{opacity:1;transform:scale(1);}}
+.fw-ctx-item{
+    display:flex;align-items:center;gap:12px;
+    padding:13px 16px;font-size:14px;color:#111;cursor:pointer;
+    border:none;background:none;width:100%;text-align:left;
+    -webkit-tap-highlight-color:transparent;transition:background .1s;
+}
+body.dark-mode .fw-ctx-item{color:#f0f0f0;}
+.fw-ctx-item:active{background:rgba(0,0,0,.07);}
+body.dark-mode .fw-ctx-item:active{background:rgba(255,255,255,.09);}
+.fw-ctx-item+.fw-ctx-item{border-top:1px solid rgba(0,0,0,.06);}
+body.dark-mode .fw-ctx-item+.fw-ctx-item{border-top-color:rgba(255,255,255,.08);}
+.fw-ctx-item.red{color:#c42b1c;}
+body.dark-mode .fw-ctx-item.red{color:#ff6b6b;}
+.fw-ctx-item svg{flex-shrink:0;opacity:.75;}
 </style>
 </head>
 <body>
@@ -433,6 +461,33 @@ body.dark-mode .cp-btn.red{color:#ff6b6b;}
     <div id="fw-body"></div>
 </div>
 
+<!-- FW tab context menu -->
+<div id="fw-ctx-overlay" onclick="fwCtxClose()"></div>
+<div id="fw-ctx-menu">
+    <button class="fw-ctx-item" onclick="fwCtxNewTab()">
+        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
+            <path d="M6 3H3a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
+            <path d="M9 2h5v5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
+            <path d="M14 2L8 8" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
+        </svg>
+        Open in New Tab
+    </button>
+    <button class="fw-ctx-item" onclick="fwCtxCopy()">
+        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
+            <rect x="5" y="5" width="9" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/>
+            <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-7A1.5 1.5 0 0 0 1 3.5v7A1.5 1.5 0 0 0 2.5 12H4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
+        </svg>
+        Copy URL
+    </button>
+    <button class="fw-ctx-item red" onclick="fwCtxCloseWindow()">
+        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
+            <circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.4"/>
+            <path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
+        </svg>
+        Close Window
+    </button>
+</div>
+
 <!-- Connection indicator -->
 <div id="conndrop">
     <img src="SystemAO/desktop/icons/connlost.svg">
@@ -1112,6 +1167,112 @@ function connCheck(){
 ───────────────────────────────────────────── */
 function escH(s){return $("<span>").text(s||"").html();}
 function escA(s){return(s||"").replace(/"/g,"&quot;").replace(/'/g,"&#39;");}
+
+/* ─────────────────────────────────────────────
+   FW TAB LONG-PRESS CONTEXT MENU
+───────────────────────────────────────────── */
+var _fwCtxWid = null;
+var _fwLongPressTimer = null;
+var _fwLongPressFired = false;
+
+(function(){
+    var fwTabs = document.getElementById("fw-tabs");
+    var startX, startY;
+
+    fwTabs.addEventListener("touchstart", function(e){
+        var tab = e.target.closest(".fw-tab");
+        if(!tab || e.target.closest(".fw-tab-x")) return;
+        var wid = tab.getAttribute("data-wid");
+        var touch = e.touches[0];
+        startX = touch.clientX; startY = touch.clientY;
+        _fwLongPressFired = false;
+        _fwLongPressTimer = setTimeout(function(){
+            _fwLongPressFired = true;
+            fwCtxShow(wid, startX, startY);
+            if(navigator.vibrate) navigator.vibrate(35);
+        }, 500);
+    }, {passive: true});
+
+    fwTabs.addEventListener("touchmove", function(e){
+        if(!_fwLongPressTimer) return;
+        var t = e.touches[0];
+        if(Math.abs(t.clientX - startX) > 8 || Math.abs(t.clientY - startY) > 8){
+            clearTimeout(_fwLongPressTimer);
+            _fwLongPressTimer = null;
+        }
+    }, {passive: true});
+
+    fwTabs.addEventListener("touchend", function(){
+        clearTimeout(_fwLongPressTimer);
+        _fwLongPressTimer = null;
+    }, {passive: true});
+
+    // Suppress the click that follows a long-press
+    fwTabs.addEventListener("click", function(e){
+        if(_fwLongPressFired){
+            _fwLongPressFired = false;
+            e.stopPropagation();
+            e.preventDefault();
+        }
+    }, true);
+})();
+
+function fwCtxShow(wid, x, y){
+    _fwCtxWid = wid;
+    var menu = document.getElementById("fw-ctx-menu");
+    var menuW = 224, menuH = 150;
+    var left = Math.min(x, window.innerWidth  - menuW - 10);
+    var top  = Math.min(y, window.innerHeight - menuH - 10);
+    left = Math.max(10, left);
+    top  = Math.max(10, top);
+    menu.style.left = left + "px";
+    menu.style.top  = top  + "px";
+    document.getElementById("fw-ctx-overlay").classList.add("on");
+    menu.style.display = "block";
+    // re-trigger animation
+    menu.style.animation = "none";
+    menu.offsetHeight; // reflow
+    menu.style.animation = "";
+}
+
+function fwCtxClose(){
+    _fwCtxWid = null;
+    document.getElementById("fw-ctx-overlay").classList.remove("on");
+    document.getElementById("fw-ctx-menu").style.display = "none";
+}
+
+function fwCtxGetUrl(){
+    if(!_fwCtxWid) return null;
+    var fw = getFloatWindowByID(_fwCtxWid);
+    if(!fw) return null;
+    try{ return fw.find("iframe")[0].src || null; }catch(e){ return null; }
+}
+
+function fwCtxNewTab(){
+    var url = fwCtxGetUrl();
+    fwCtxClose();
+    if(url) window.open(url, "_blank");
+}
+
+function fwCtxCopy(){
+    var url = fwCtxGetUrl();
+    fwCtxClose();
+    if(!url) return;
+    if(navigator.clipboard){
+        navigator.clipboard.writeText(url).catch(function(){});
+    } else {
+        var ta = document.createElement("textarea");
+        ta.value = url; ta.style.cssText = "position:fixed;opacity:0";
+        document.body.appendChild(ta); ta.select();
+        document.execCommand("copy"); document.body.removeChild(ta);
+    }
+}
+
+function fwCtxCloseWindow(){
+    var wid = _fwCtxWid;
+    fwCtxClose();
+    if(wid) closeFloatWindow(wid);
+}
 </script>
 </body>
 </html>