feat: replace delete ✕ with pi pi-trash + inline confirmation (matches ComfyUI assets pattern)

This commit is contained in:
Immac
2026-03-01 23:24:46 -06:00
parent d32cdf4682
commit d097fb869f
5 changed files with 61 additions and 19 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,4 +1,4 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["immac_style_mixer/StyleMixerPanel-DxBlfZqC.js","immac_style_mixer/vendor-DPfS0z4g.js"])))=>i.map(i=>d[i]);
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["immac_style_mixer/StyleMixerPanel-317gZSvY.js","immac_style_mixer/vendor-DPfS0z4g.js"])))=>i.map(i=>d[i]);
import{r as v,a as R,R as h}from"./vendor-DPfS0z4g.js";const g="modulepreload",b=function(t){return"/"+t},f={},I=function(e,n,o){let l=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const r=document.querySelector("meta[property=csp-nonce]"),i=(r==null?void 0:r.nonce)||(r==null?void 0:r.getAttribute("nonce"));l=Promise.allSettled(n.map(a=>{if(a=b(a),a in f)return;f[a]=!0;const d=a.endsWith(".css"),_=d?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${a}"]${_}`))return;const s=document.createElement("link");if(s.rel=d?"stylesheet":g,d||(s.as="script"),s.crossOrigin="",s.href=a,i&&s.setAttribute("nonce",i),document.head.appendChild(s),d)return new Promise((E,S)=>{s.addEventListener("load",E),s.addEventListener("error",()=>S(new Error(`Unable to preload CSS for ${a}`)))})}))}function c(r){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=r,window.dispatchEvent(i),!i.defaultPrevented)throw r}return l.then(r=>{for(const i of r||[])i.status==="rejected"&&c(i.reason);return e().catch(c)})};var x={exports:{}},m={};/**
* @license React
* react-jsx-runtime.production.min.js
@@ -7,4 +7,4 @@ import{r as v,a as R,R as h}from"./vendor-DPfS0z4g.js";const g="modulepreload",b
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var P=v,j=Symbol.for("react.element"),k=Symbol.for("react.fragment"),O=Object.prototype.hasOwnProperty,L=P.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,C={key:!0,ref:!0,__self:!0,__source:!0};function w(t,e,n){var o,l={},c=null,r=null;n!==void 0&&(c=""+n),e.key!==void 0&&(c=""+e.key),e.ref!==void 0&&(r=e.ref);for(o in e)O.call(e,o)&&!C.hasOwnProperty(o)&&(l[o]=e[o]);if(t&&t.defaultProps)for(o in e=t.defaultProps,e)l[o]===void 0&&(l[o]=e[o]);return{$$typeof:j,type:t,key:c,ref:r,props:l,_owner:L.current}}m.Fragment=k;m.jsx=w;m.jsxs=w;x.exports=m;var u=x.exports,p={},y=R;p.createRoot=y.createRoot,p.hydrateRoot=y.hydrateRoot;const T=h.lazy(()=>I(()=>import("./StyleMixerPanel-DxBlfZqC.js"),__vite__mapDeps([0,1])));function U(){return new Promise(t=>{function e(){if(window.app){t();return}const n=setInterval(()=>{window.app&&(clearInterval(n),t())},50);setTimeout(()=>{clearInterval(n),console.error("[ImmacStyleMixer] Timeout waiting for ComfyUI app"),t()},5e3)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()})}async function M(){if(await U(),!window.app){console.error("[ImmacStyleMixer] ComfyUI app not available");return}window.app.extensionManager.registerSidebarTab({id:"immac-style-mixer",icon:"pi pi-palette",title:"Style Mixer",tooltip:"Immac Style Mixer",type:"custom",render:t=>{const e=document.createElement("div");e.id="immac-style-mixer-root",e.style.position="absolute",e.style.inset="0",e.style.overflow="hidden",t.style.position="relative",t.style.height="100%",t.appendChild(e),p.createRoot(e).render(u.jsx(h.StrictMode,{children:u.jsx(v.Suspense,{fallback:u.jsx("div",{style:{padding:"1rem"},children:"Loading…"}),children:u.jsx(T,{})})}))}}),window.app.registerExtension({name:"ImmacStyleMixer"})}M().catch(console.error);export{u as j};
*/var P=v,j=Symbol.for("react.element"),k=Symbol.for("react.fragment"),O=Object.prototype.hasOwnProperty,L=P.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,C={key:!0,ref:!0,__self:!0,__source:!0};function w(t,e,n){var o,l={},c=null,r=null;n!==void 0&&(c=""+n),e.key!==void 0&&(c=""+e.key),e.ref!==void 0&&(r=e.ref);for(o in e)O.call(e,o)&&!C.hasOwnProperty(o)&&(l[o]=e[o]);if(t&&t.defaultProps)for(o in e=t.defaultProps,e)l[o]===void 0&&(l[o]=e[o]);return{$$typeof:j,type:t,key:c,ref:r,props:l,_owner:L.current}}m.Fragment=k;m.jsx=w;m.jsxs=w;x.exports=m;var u=x.exports,p={},y=R;p.createRoot=y.createRoot,p.hydrateRoot=y.hydrateRoot;const T=h.lazy(()=>I(()=>import("./StyleMixerPanel-317gZSvY.js"),__vite__mapDeps([0,1])));function U(){return new Promise(t=>{function e(){if(window.app){t();return}const n=setInterval(()=>{window.app&&(clearInterval(n),t())},50);setTimeout(()=>{clearInterval(n),console.error("[ImmacStyleMixer] Timeout waiting for ComfyUI app"),t()},5e3)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()})}async function M(){if(await U(),!window.app){console.error("[ImmacStyleMixer] ComfyUI app not available");return}window.app.extensionManager.registerSidebarTab({id:"immac-style-mixer",icon:"pi pi-palette",title:"Style Mixer",tooltip:"Immac Style Mixer",type:"custom",render:t=>{const e=document.createElement("div");e.id="immac-style-mixer-root",e.style.position="absolute",e.style.inset="0",e.style.overflow="hidden",t.style.position="relative",t.style.height="100%",t.appendChild(e),p.createRoot(e).render(u.jsx(h.StrictMode,{children:u.jsx(v.Suspense,{fallback:u.jsx("div",{style:{padding:"1rem"},children:"Loading…"}),children:u.jsx(T,{})})}))}}),window.app.registerExtension({name:"ImmacStyleMixer"})}M().catch(console.error);export{u as j};
+24 -3
View File
@@ -17,6 +17,7 @@ export default function MixCard({ mix, styles, isActive, onActivate, onUpdate, o
const [nameInput, setNameInput] = useState(mix.name)
const [uploading, setUploading] = useState(false)
const [lightboxOpen, setLightboxOpen] = useState(false)
const [pendingDelete, setPendingDelete] = useState(false)
const fileRef = useRef<HTMLInputElement>(null)
async function handleImageFile(file: File) {
@@ -104,9 +105,29 @@ export default function MixCard({ mix, styles, isActive, onActivate, onUpdate, o
>
<i className={mix.favorite ? 'pi pi-bookmark-fill' : 'pi pi-bookmark'} />
</button>
<button title="Delete mix" onClick={onDelete} style={{ ...iconBtn, color: '#e55' }}>
✕
</button>
{pendingDelete ? (
<>
<span style={{ fontSize: 11, color: 'var(--p-text-muted-color, #888)', whiteSpace: 'nowrap' }}>Delete?</span>
<button
title="Cancel"
onClick={() => setPendingDelete(false)}
style={{ ...iconBtn, fontSize: 11, color: 'var(--p-text-muted-color, #888)' }}
>
Cancel
</button>
<button
title="Confirm delete"
onClick={onDelete}
style={{ ...iconBtn, color: '#e55' }}
>
<i className="pi pi-trash" />
</button>
</>
) : (
<button title="Delete mix" onClick={() => setPendingDelete(true)} style={{ ...iconBtn, color: 'var(--p-text-muted-color, #888)' }}>
<i className="pi pi-trash" />
</button>
)}
</div>
{/* Cover image */}
+34 -13
View File
@@ -16,6 +16,7 @@ export default function StyleCard({ style, onUpdate, onDelete }: Props) {
const [valueInput, setValueInput] = useState(style.value)
const [uploading, setUploading] = useState(false)
const [lightboxOpen, setLightboxOpen] = useState(false)
const [pendingDelete, setPendingDelete] = useState(false)
const fileRef = useRef<HTMLInputElement>(null)
function commitName() {
@@ -56,19 +57,39 @@ export default function StyleCard({ style, onUpdate, onDelete }: Props) {
position: 'relative',
}}
>
{/* Favorite + Delete row */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<button
title={style.favorite ? 'Remove bookmark' : 'Bookmark'}
onClick={() => onUpdate({ ...style, favorite: !style.favorite })}
style={iconBtn}
>
<i className={style.favorite ? 'pi pi-bookmark-fill' : 'pi pi-bookmark'} />
</button>
<button title="Delete style" onClick={onDelete} style={{ ...iconBtn, color: '#e55' }}>
✕
</button>
</div>
{/* Bookmark + Delete row */}
{pendingDelete ? (
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<span style={{ flex: 1, fontSize: 11, color: 'var(--p-text-muted-color, #888)' }}>Delete style?</span>
<button
title="Cancel"
onClick={() => setPendingDelete(false)}
style={{ ...iconBtn, fontSize: 12, color: 'var(--p-text-muted-color, #888)' }}
>
Cancel
</button>
<button
title="Confirm delete"
onClick={onDelete}
style={{ ...iconBtn, fontSize: 12, color: '#e55', fontWeight: 600 }}
>
<i className="pi pi-trash" />
</button>
</div>
) : (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<button
title={style.favorite ? 'Remove bookmark' : 'Bookmark'}
onClick={() => onUpdate({ ...style, favorite: !style.favorite })}
style={iconBtn}
>
<i className={style.favorite ? 'pi pi-bookmark-fill' : 'pi pi-bookmark'} />
</button>
<button title="Delete style" onClick={() => setPendingDelete(true)} style={{ ...iconBtn, color: 'var(--p-text-muted-color, #888)' }}>
<i className="pi pi-trash" />
</button>
</div>
)}
{/* Image area */}
<div style={{ position: 'relative', width: '100%', paddingBottom: '100%' }}>