From d9f99bc58bb82270dc9c7d3a6c21c2afe3525da9 Mon Sep 17 00:00:00 2001 From: Bonchellon Date: Tue, 7 Jul 2026 15:28:17 +0300 Subject: [PATCH] Custom workspace dropdown + Launch-Unreal button Replace the ugly native ) */ +.xsel{position:relative} +.xsel-trig{display:flex;align-items:center;gap:9px;width:100%;background:var(--input-bg);border:1px solid var(--border); + border-radius:10px;padding:10px 12px;cursor:pointer;color:var(--txt);font-family:var(--font);font-size:13px;text-align:left} +.xsel-trig:hover,.xsel.open .xsel-trig{border-color:var(--accent)} +.xsel.open .xsel-trig{box-shadow:0 0 0 3px rgba(124,110,246,.15)} +.xsel-trig > svg{width:15px;height:15px;color:var(--faint);flex:0 0 auto} +.xsel-val{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.xsel-ph{color:var(--faint)} +.xsel-chev{flex:0 0 auto;color:var(--faint);display:grid;place-items:center;transition:transform .18s} +.xsel-chev svg{width:15px;height:15px} +.xsel.open .xsel-chev{transform:rotate(180deg)} +.xsel-list{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:200;max-height:264px;overflow-y:auto;padding:5px; + background:var(--elevated);border:1px solid var(--border);border-radius:12px;box-shadow:0 20px 48px -14px rgba(0,0,0,.5);animation:updin .16s ease} +.xsel-empty{padding:12px;text-align:center;color:var(--faint);font-size:12.5px} +.xsel-item{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:9px;cursor:pointer} +.xsel-item:hover{background:var(--hover)} +.xsel-item.on{background:rgba(124,110,246,.12)} +.xsel-ic{width:15px;flex:0 0 auto;display:grid;place-items:center;color:var(--accent-2)} +.xsel-ic svg{width:13px;height:13px} +.xsel-body{display:flex;flex-direction:column;gap:1px;min-width:0} +.xsel-name{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.xsel-sub{font-size:11px;color:var(--faint);font-family:var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} + +/* Launch-Unreal button (white icon on black / black on white per theme) */ +.ue-btn{display:flex;align-items:center;gap:9px;flex:0 0 auto;align-self:center;height:40px;margin:0 8px;padding:0 15px; + border:none;border-radius:11px;cursor:pointer;background:var(--txt);color:var(--win-a);transition:filter .15s,transform .06s} +.ue-btn:hover{filter:brightness(1.12)} +.ue-btn:active{transform:scale(.97)} +.ue-ic{width:24px;height:24px;flex:0 0 auto;display:grid;place-items:center} +.ue-ic svg{width:24px;height:24px} +.ue-lbl{display:flex;flex-direction:column;line-height:1.12;text-align:left} +.ue-a{font-size:9.5px;text-transform:uppercase;letter-spacing:.6px;opacity:.72} +.ue-b{font-size:13px;font-weight:700} + /* bottom status bar (always visible) + hover log peek */ .statusbar{flex:0 0 26px;display:flex;align-items:center;gap:10px;padding:0 12px;border-top:1px solid var(--border); background:var(--bar);font-size:11.5px;color:var(--muted);position:relative;user-select:none;z-index:60} diff --git a/src/App.tsx b/src/App.tsx index e7e7da1..fd68d78 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,9 @@ import { useEffect, useMemo, useRef, useState } from "react"; -import type { MouseEvent as ReactMouseEvent, CSSProperties } from "react"; +import type { MouseEvent as ReactMouseEvent, CSSProperties, ReactNode } from "react"; import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWebview } from "@tauri-apps/api/webview"; import { listen } from "@tauri-apps/api/event"; +import { openPath } from "@tauri-apps/plugin-opener"; import ModelViewer from "./ModelViewer"; import UpdateBanner from "./Updater"; import "./App.css"; @@ -38,6 +39,7 @@ const I = { revert: , clock: , log: , + ue: , }; /* ---------------- theme hook ---------------- */ @@ -104,6 +106,41 @@ export default function App() { return <>{content}; } +/* ---------------- custom styled dropdown (replaces the ugly native setPassword(e.target.value)} onKeyDown={(e) => e.key === "Enter" && connect()} />
-
- - {clients.length ? ( - - ) : ( + {clients.length ? ( + setClient(e.target.value)} /> - )} -
+ + )} {err &&
{err}
} + )} {pending.length > 0 ? (
diff --git a/src/i18n.ts b/src/i18n.ts index dbcd592..40a87e1 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -67,6 +67,9 @@ const D: Record = { "Get Latest": { ru: "Get Latest", de: "Get Latest", fr: "Get Latest", es: "Get Latest" }, "Working…": { ru: "Работаю…", de: "Arbeite…", fr: "En cours…", es: "Trabajando…" }, "pull from server": { ru: "забрать с сервера (pull)", de: "vom Server holen (pull)", fr: "récupérer du serveur (pull)", es: "traer del servidor (pull)" }, + "Launch": { ru: "Запустить", de: "Starten", fr: "Lancer", es: "Iniciar" }, + "Open this project in Unreal Engine": { ru: "Открыть проект в Unreal Engine", de: "Projekt in Unreal Engine öffnen", fr: "Ouvrir le projet dans Unreal Engine", es: "Abrir el proyecto en Unreal Engine" }, + "Launching Unreal…": { ru: "Запуск Unreal…", de: "Unreal wird gestartet…", fr: "Lancement d’Unreal…", es: "Iniciando Unreal…" }, // tabs / list "Changes": { ru: "Changes", de: "Änderungen", fr: "Modifications", es: "Cambios" }, diff --git a/src/p4.ts b/src/p4.ts index 6d239fb..e2c1c56 100644 --- a/src/p4.ts +++ b/src/p4.ts @@ -72,6 +72,7 @@ export const p4 = { describe: (change: string) => invoke("p4_describe", { change }), undoChange: (change: string) => invoke("p4_undo_change", { change }), openInExplorer: (target: string) => invoke("open_in_explorer", { target }), + findUproject: (scope: string) => invoke("find_uproject", { scope }), readDepot: async (depot: string): Promise => { const r: unknown = await invoke("read_depot", { depot }); if (r instanceof ArrayBuffer) return r;