@vasakgroup/vue-libvasak 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- (function(r,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(r=typeof globalThis<"u"?globalThis:r||self,n(r.VueLibVasak={},r.Vue))})(this,(function(r,n){"use strict";const y=(t,e)=>{const i=t.__vccOpts||t;for(const[l,s]of e)i[l]=s;return i},Q={},Y={class:"sidebar"};function K(t,e){return n.openBlock(),n.createElementBlock("div",Y,[n.renderSlot(t.$slots,"default")])}const z=y(Q,[["render",K]]),X={href:"#",class:"sidebar-button"},ee=["src","alt"],N=n.defineComponent({__name:"SideButton",props:{title:{type:String,default:"Link"},image:{type:String,default:""}},setup(t){return(e,i)=>(n.openBlock(),n.createElementBlock("a",X,[n.createElementVNode("img",{src:t.image,alt:t.title,class:"img-fluid"},null,8,ee)]))}});function te(t,e,i,l){if(typeof e=="function"?t!==e||!l:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return i==="m"?l:i==="a"?l.call(t):l?l.value:e.get(t)}function ne(t,e,i,l,s){if(typeof e=="function"?t!==e||!0:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,i),i}typeof SuppressedError=="function"&&SuppressedError;var k;const h="__TAURI_TO_IPC_KEY__";function ie(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}async function a(t,e={},i){return window.__TAURI_INTERNALS__.invoke(t,e,i)}class le{get rid(){return te(this,k,"f")}constructor(e){k.set(this,void 0),ne(this,k,e)}async close(){return a("plugin:resources|close",{rid:this.rid})}}k=new WeakMap;class D{constructor(...e){this.type="Logical",e.length===1?"Logical"in e[0]?(this.width=e[0].Logical.width,this.height=e[0].Logical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toPhysical(e){return new p(this.width*e,this.height*e)}[h](){return{width:this.width,height:this.height}}toJSON(){return this[h]()}}class p{constructor(...e){this.type="Physical",e.length===1?"Physical"in e[0]?(this.width=e[0].Physical.width,this.height=e[0].Physical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toLogical(e){return new D(this.width/e,this.height/e)}[h](){return{width:this.width,height:this.height}}toJSON(){return this[h]()}}class w{constructor(e){this.size=e}toLogical(e){return this.size instanceof D?this.size:this.size.toLogical(e)}toPhysical(e){return this.size instanceof p?this.size:this.size.toPhysical(e)}[h](){return{[`${this.size.type}`]:{width:this.size.width,height:this.size.height}}}toJSON(){return this[h]()}}class I{constructor(...e){this.type="Logical",e.length===1?"Logical"in e[0]?(this.x=e[0].Logical.x,this.y=e[0].Logical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toPhysical(e){return new b(this.x*e,this.y*e)}[h](){return{x:this.x,y:this.y}}toJSON(){return this[h]()}}class b{constructor(...e){this.type="Physical",e.length===1?"Physical"in e[0]?(this.x=e[0].Physical.x,this.y=e[0].Physical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toLogical(e){return new I(this.x/e,this.y/e)}[h](){return{x:this.x,y:this.y}}toJSON(){return this[h]()}}class _{constructor(e){this.position=e}toLogical(e){return this.position instanceof I?this.position:this.position.toLogical(e)}toPhysical(e){return this.position instanceof b?this.position:this.position.toPhysical(e)}[h](){return{[`${this.position.type}`]:{x:this.position.x,y:this.position.y}}}toJSON(){return this[h]()}}var u;(function(t){t.WINDOW_RESIZED="tauri://resize",t.WINDOW_MOVED="tauri://move",t.WINDOW_CLOSE_REQUESTED="tauri://close-requested",t.WINDOW_DESTROYED="tauri://destroyed",t.WINDOW_FOCUS="tauri://focus",t.WINDOW_BLUR="tauri://blur",t.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",t.WINDOW_THEME_CHANGED="tauri://theme-changed",t.WINDOW_CREATED="tauri://window-created",t.WEBVIEW_CREATED="tauri://webview-created",t.DRAG_ENTER="tauri://drag-enter",t.DRAG_OVER="tauri://drag-over",t.DRAG_DROP="tauri://drag-drop",t.DRAG_LEAVE="tauri://drag-leave"})(u||(u={}));async function A(t,e){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(t,e),await a("plugin:event|unlisten",{event:t,eventId:e})}async function $(t,e,i){var l;const s=typeof i?.target=="string"?{kind:"AnyLabel",label:i.target}:(l=i?.target)!==null&&l!==void 0?l:{kind:"Any"};return a("plugin:event|listen",{event:t,target:s,handler:ie(e)}).then(c=>async()=>A(t,c))}async function ae(t,e,i){return $(t,l=>{A(t,l.id),e(l)},i)}async function se(t,e){await a("plugin:event|emit",{event:t,payload:e})}async function oe(t,e,i){await a("plugin:event|emit_to",{target:typeof t=="string"?{kind:"AnyLabel",label:t}:t,event:e,payload:i})}class f extends le{constructor(e){super(e)}static async new(e,i,l){return a("plugin:image|new",{rgba:C(e),width:i,height:l}).then(s=>new f(s))}static async fromBytes(e){return a("plugin:image|from_bytes",{bytes:C(e)}).then(i=>new f(i))}static async fromPath(e){return a("plugin:image|from_path",{path:e}).then(i=>new f(i))}async rgba(){return a("plugin:image|rgba",{rid:this.rid}).then(e=>new Uint8Array(e))}async size(){return a("plugin:image|size",{rid:this.rid})}}function C(t){return t==null?null:typeof t=="string"?t:t instanceof f?t.rid:t}var x;(function(t){t[t.Critical=1]="Critical",t[t.Informational=2]="Informational"})(x||(x={}));class re{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}var W;(function(t){t.None="none",t.Normal="normal",t.Indeterminate="indeterminate",t.Paused="paused",t.Error="error"})(W||(W={}));function V(){return new L(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}async function B(){return a("plugin:window|get_all_windows").then(t=>t.map(e=>new L(e,{skip:!0})))}const E=["tauri://created","tauri://error"];class L{constructor(e,i={}){var l;this.label=e,this.listeners=Object.create(null),i?.skip||a("plugin:window|create",{options:{...i,parent:typeof i.parent=="string"?i.parent:(l=i.parent)===null||l===void 0?void 0:l.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async s=>this.emit("tauri://error",s))}static async getByLabel(e){var i;return(i=(await B()).find(l=>l.label===e))!==null&&i!==void 0?i:null}static getCurrent(){return V()}static async getAll(){return B()}static async getFocusedWindow(){for(const e of await B())if(await e.isFocused())return e;return null}async listen(e,i){return this._handleTauriEvent(e,i)?()=>{const l=this.listeners[e];l.splice(l.indexOf(i),1)}:$(e,i,{target:{kind:"Window",label:this.label}})}async once(e,i){return this._handleTauriEvent(e,i)?()=>{const l=this.listeners[e];l.splice(l.indexOf(i),1)}:ae(e,i,{target:{kind:"Window",label:this.label}})}async emit(e,i){if(E.includes(e)){for(const l of this.listeners[e]||[])l({event:e,id:-1,payload:i});return}return se(e,i)}async emitTo(e,i,l){if(E.includes(i)){for(const s of this.listeners[i]||[])s({event:i,id:-1,payload:l});return}return oe(e,i,l)}_handleTauriEvent(e,i){return E.includes(e)?(e in this.listeners?this.listeners[e].push(i):this.listeners[e]=[i],!0):!1}async scaleFactor(){return a("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return a("plugin:window|inner_position",{label:this.label}).then(e=>new b(e))}async outerPosition(){return a("plugin:window|outer_position",{label:this.label}).then(e=>new b(e))}async innerSize(){return a("plugin:window|inner_size",{label:this.label}).then(e=>new p(e))}async outerSize(){return a("plugin:window|outer_size",{label:this.label}).then(e=>new p(e))}async isFullscreen(){return a("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return a("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return a("plugin:window|is_maximized",{label:this.label})}async isFocused(){return a("plugin:window|is_focused",{label:this.label})}async isDecorated(){return a("plugin:window|is_decorated",{label:this.label})}async isResizable(){return a("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return a("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return a("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return a("plugin:window|is_closable",{label:this.label})}async isVisible(){return a("plugin:window|is_visible",{label:this.label})}async title(){return a("plugin:window|title",{label:this.label})}async theme(){return a("plugin:window|theme",{label:this.label})}async isAlwaysOnTop(){return a("plugin:window|is_always_on_top",{label:this.label})}async center(){return a("plugin:window|center",{label:this.label})}async requestUserAttention(e){let i=null;return e&&(e===x.Critical?i={type:"Critical"}:i={type:"Informational"}),a("plugin:window|request_user_attention",{label:this.label,value:i})}async setResizable(e){return a("plugin:window|set_resizable",{label:this.label,value:e})}async setEnabled(e){return a("plugin:window|set_enabled",{label:this.label,value:e})}async isEnabled(){return a("plugin:window|is_enabled",{label:this.label})}async setMaximizable(e){return a("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return a("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return a("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return a("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return a("plugin:window|maximize",{label:this.label})}async unmaximize(){return a("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return a("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return a("plugin:window|minimize",{label:this.label})}async unminimize(){return a("plugin:window|unminimize",{label:this.label})}async show(){return a("plugin:window|show",{label:this.label})}async hide(){return a("plugin:window|hide",{label:this.label})}async close(){return a("plugin:window|close",{label:this.label})}async destroy(){return a("plugin:window|destroy",{label:this.label})}async setDecorations(e){return a("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return a("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return a("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return a("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return a("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return a("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return a("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){return a("plugin:window|set_size",{label:this.label,value:e instanceof w?e:new w(e)})}async setMinSize(e){return a("plugin:window|set_min_size",{label:this.label,value:e instanceof w?e:e?new w(e):null})}async setMaxSize(e){return a("plugin:window|set_max_size",{label:this.label,value:e instanceof w?e:e?new w(e):null})}async setSizeConstraints(e){function i(l){return l?{Logical:l}:null}return a("plugin:window|set_size_constraints",{label:this.label,value:{minWidth:i(e?.minWidth),minHeight:i(e?.minHeight),maxWidth:i(e?.maxWidth),maxHeight:i(e?.maxHeight)}})}async setPosition(e){return a("plugin:window|set_position",{label:this.label,value:e instanceof _?e:new _(e)})}async setFullscreen(e){return a("plugin:window|set_fullscreen",{label:this.label,value:e})}async setSimpleFullscreen(e){return a("plugin:window|set_simple_fullscreen",{label:this.label,value:e})}async setFocus(){return a("plugin:window|set_focus",{label:this.label})}async setFocusable(e){return a("plugin:window|set_focusable",{label:this.label,value:e})}async setIcon(e){return a("plugin:window|set_icon",{label:this.label,value:C(e)})}async setSkipTaskbar(e){return a("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return a("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return a("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return a("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setBackgroundColor(e){return a("plugin:window|set_background_color",{color:e})}async setCursorPosition(e){return a("plugin:window|set_cursor_position",{label:this.label,value:e instanceof _?e:new _(e)})}async setIgnoreCursorEvents(e){return a("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return a("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return a("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setBadgeCount(e){return a("plugin:window|set_badge_count",{label:this.label,value:e})}async setBadgeLabel(e){return a("plugin:window|set_badge_label",{label:this.label,value:e})}async setOverlayIcon(e){return a("plugin:window|set_overlay_icon",{label:this.label,value:e?C(e):void 0})}async setProgressBar(e){return a("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return a("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async setTitleBarStyle(e){return a("plugin:window|set_title_bar_style",{label:this.label,value:e})}async setTheme(e){return a("plugin:window|set_theme",{label:this.label,value:e})}async onResized(e){return this.listen(u.WINDOW_RESIZED,i=>{i.payload=new p(i.payload),e(i)})}async onMoved(e){return this.listen(u.WINDOW_MOVED,i=>{i.payload=new b(i.payload),e(i)})}async onCloseRequested(e){return this.listen(u.WINDOW_CLOSE_REQUESTED,async i=>{const l=new re(i);await e(l),l.isPreventDefault()||await this.destroy()})}async onDragDropEvent(e){const i=await this.listen(u.DRAG_ENTER,o=>{e({...o,payload:{type:"enter",paths:o.payload.paths,position:new b(o.payload.position)}})}),l=await this.listen(u.DRAG_OVER,o=>{e({...o,payload:{type:"over",position:new b(o.payload.position)}})}),s=await this.listen(u.DRAG_DROP,o=>{e({...o,payload:{type:"drop",paths:o.payload.paths,position:new b(o.payload.position)}})}),c=await this.listen(u.DRAG_LEAVE,o=>{e({...o,payload:{type:"leave"}})});return()=>{i(),s(),l(),c()}}async onFocusChanged(e){const i=await this.listen(u.WINDOW_FOCUS,s=>{e({...s,payload:!0})}),l=await this.listen(u.WINDOW_BLUR,s=>{e({...s,payload:!1})});return()=>{i(),l()}}async onScaleChanged(e){return this.listen(u.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(u.WINDOW_THEME_CHANGED,e)}}var O;(function(t){t.Disabled="disabled",t.Throttle="throttle",t.Suspend="suspend"})(O||(O={}));var T;(function(t){t.Default="default",t.FluentOverlay="fluentOverlay"})(T||(T={}));var R;(function(t){t.AppearanceBased="appearanceBased",t.Light="light",t.Dark="dark",t.MediumLight="mediumLight",t.UltraDark="ultraDark",t.Titlebar="titlebar",t.Selection="selection",t.Menu="menu",t.Popover="popover",t.Sidebar="sidebar",t.HeaderView="headerView",t.Sheet="sheet",t.WindowBackground="windowBackground",t.HudWindow="hudWindow",t.FullScreenUI="fullScreenUI",t.Tooltip="tooltip",t.ContentBackground="contentBackground",t.UnderWindowBackground="underWindowBackground",t.UnderPageBackground="underPageBackground",t.Mica="mica",t.Blur="blur",t.Acrylic="acrylic",t.Tabbed="tabbed",t.TabbedDark="tabbedDark",t.TabbedLight="tabbedLight"})(R||(R={}));var P;(function(t){t.FollowsWindowActiveState="followsWindowActiveState",t.Active="active",t.Inactive="inactive"})(P||(P={})),typeof SuppressedError=="function"&&SuppressedError;async function ce(t,e={},i){return window.__TAURI_INTERNALS__.invoke(t,e,i)}async function de(t){try{return await ce("plugin:vicons|get_icon",{name:t})}catch(e){console.error("[Icon Error] Error obteniendo icono:",e)}return""}function ue(t){try{const e=atob(t.substring(0,32)),i=new Uint8Array(e.length);for(let l=0;l<e.length;l++)i[l]=e.charCodeAt(l);return i[0]===137&&i[1]===80&&i[2]===78&&i[3]===71?"image/png":"image/svg+xml"}catch(e){return console.error("Error identificando tipo de imagen:",e),"image/png"}}async function S(t){try{const e=await de(t);return`data:${ue(e)};base64,${e}`}catch(e){return console.error("[Icon Error] Error obteniendo icono:",e),""}}const he={"data-tauri-drag-region":""},me=["src","alt"],be={"data-tauri-drag-region":""},ge={"data-tauri-drag-region":""},we=["src"],ye=["src"],pe=["src"],fe=n.defineComponent({__name:"TopBar",props:{title:String,image:String},setup(t){const e=n.ref(null),i=V(),l=n.ref(""),s=n.ref(""),c=n.ref(""),o=t;return n.onMounted(async()=>{l.value=await S("window-close"),s.value=await S("window-minimize"),c.value=await S("window-maximize")}),(m,d)=>(n.openBlock(),n.createElementBlock("div",{"data-tauri-drag-region":"",class:"flex h-8 px-4 py-1 bg-primary dark:bg-primary-dark rounded-t-corner-window justify-between align-center",ref_key:"bar",ref:e},[n.createElementVNode("div",he,[n.createElementVNode("img",{src:o.image,"data-tauri-drag-region":"",class:"h-6 w-auto",alt:o.title},null,8,me)]),n.createElementVNode("div",be,n.toDisplayString(o.title),1),n.createElementVNode("div",ge,[n.createElementVNode("span",{class:"win-button",onClick:d[0]||(d[0]=g=>n.unref(i).minimize())},[n.createElementVNode("img",{src:s.value,class:"h-6 w-6 inline-block",alt:"Minimize"},null,8,we)]),n.createElementVNode("span",{class:"win-button",onClick:d[1]||(d[1]=g=>n.unref(i).toggleMaximize())},[n.createElementVNode("img",{src:c.value,class:"h-6 w-6 inline-block",alt:"Maximize"},null,8,ye)]),n.createElementVNode("span",{class:"win-button",onClick:d[2]||(d[2]=g=>n.unref(i).close())},[n.createElementVNode("img",{src:l.value,class:"h-6 w-6 inline-block",alt:"Close"},null,8,pe)])])],512))}}),v=n.defineComponent({__name:"WindowFrame",props:{title:{type:String,default:"Vasak"},image:{type:String,default:""}},setup(t){const e=t;return(i,l)=>(n.openBlock(),n.createElementBlock(n.Fragment,null,[n.createVNode(fe,{title:e.title,image:e.image},null,8,["title","image"]),n.renderSlot(i.$slots,"default")],64))}}),ke=["type","disabled"],_e={key:0,class:"w-4 h-4 animate-spin rounded-full border-2 border-current border-t-transparent"},Ce=["src","alt"],xe={key:2},Be=["src","alt"],M=y(n.defineComponent({__name:"ActionButton",props:{label:{},disabled:{type:Boolean,default:!1},variant:{default:"primary"},loading:{type:Boolean,default:!1},customClass:{default:()=>({})},size:{default:"md"},fullWidth:{type:Boolean,default:!1},iconSrc:{default:""},iconAlt:{default:""},iconRight:{type:Boolean,default:!1},type:{default:"button"},stopPropagation:{type:Boolean,default:!1},preventDefault:{type:Boolean,default:!1}},emits:["click"],setup(t,{emit:e}){const i=t,l=e,s={primary:"bg-primary dark:bg-primary-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-primary/90 dark:hover:bg-primary-dark/90",secondary:"bg-secondary dark:bg-secondary-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-secondary/80 dark:hover:bg-secondary-dark/80",danger:"bg-status-error dark:bg-status-error-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-status-error/90 dark:hover:bg-status-error-dark/90"},c={sm:"px-2 py-1 text-xs",md:"px-3 py-1 text-sm",lg:"px-4 py-2 text-base"},o=m=>{i.stopPropagation&&m.stopPropagation(),i.preventDefault&&m.preventDefault(),!i.disabled&&!i.loading&&l("click")};return(m,d)=>(n.openBlock(),n.createElementBlock("button",{type:i.type,onClick:o,class:n.normalizeClass(["rounded-corner transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2",[s[i.variant],c[i.size],i.fullWidth?"w-full":"",i.iconSrc&&!i.label?"px-2 py-2":"",t.customClass]]),disabled:i.disabled||i.loading},[t.loading?(n.openBlock(),n.createElementBlock("span",_e)):n.createCommentVNode("",!0),i.iconSrc&&!i.iconRight?(n.openBlock(),n.createElementBlock("img",{key:1,src:i.iconSrc,alt:i.iconAlt||i.label,class:"w-4 h-4"},null,8,Ce)):n.createCommentVNode("",!0),i.label?(n.openBlock(),n.createElementBlock("span",xe,n.toDisplayString(i.label),1)):n.createCommentVNode("",!0),i.iconSrc&&i.iconRight?(n.openBlock(),n.createElementBlock("img",{key:3,src:i.iconSrc,alt:i.iconAlt||i.label,class:"w-4 h-4"},null,8,Be)):n.createCommentVNode("",!0)],10,ke))}}),[["__scopeId","data-v-2eaf6e27"]]),Ee={class:"text-base font-semibold m-0 text-primary dark:text-primary-dark"},F=y(n.defineComponent({__name:"ConfigSection",props:{title:{},icon:{default:""},customClass:{default:()=>({})}},setup(t){return(e,i)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["flex flex-col gap-4 p-4 background rounded-corner",t.customClass])},[n.createElementVNode("h3",Ee,n.toDisplayString(t.icon?`${t.icon} ${t.title}`:t.title),1),n.renderSlot(e.$slots,"default",{},void 0,!0)],2))}}),[["__scopeId","data-v-45bd4eab"]]),Se={class:"flex items-center gap-3 flex-1 min-w-0"},ze=["src","alt"],Ne={class:"min-w-0"},De={class:"font-semibold truncate"},Ie={key:0,class:"text-xs text-gray-400 truncate"},Ae={key:1,class:"text-xs text-gray-400 truncate"},$e={key:2,class:"text-xs text-gray-400 flex gap-2 mt-1"},We={key:1,class:"w-2 h-2 rounded-full bg-green-500"},U=n.defineComponent({__name:"DeviceCard",props:{icon:{},title:{},subtitle:{default:""},metadata:{default:""},extraInfo:{default:()=>[]},isConnected:{type:Boolean,default:!1},showActionButton:{type:Boolean,default:!0},actionLabel:{default:"Conectar"},showStatusIndicator:{type:Boolean,default:!1},customClass:{default:""},clickable:{type:Boolean,default:!1}},emits:["action","click"],setup(t,{emit:e}){const i=e,l=()=>{i("action")},s=()=>{i("click")};return(c,o)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["flex items-center justify-between background rounded-vsk px-6 py-3 mb-4",[{"border-l-4 border-green-500":t.isConnected},t.customClass]]),onClick:s},[n.createElementVNode("div",Se,[n.createElementVNode("img",{src:t.icon,alt:t.title,class:"h-7 w-7 shrink-0"},null,8,ze),n.createElementVNode("div",Ne,[n.createElementVNode("div",De,n.toDisplayString(t.title),1),t.subtitle?(n.openBlock(),n.createElementBlock("div",Ie,n.toDisplayString(t.subtitle),1)):n.createCommentVNode("",!0),t.metadata?(n.openBlock(),n.createElementBlock("div",Ae,n.toDisplayString(t.metadata),1)):n.createCommentVNode("",!0),t.extraInfo&&t.extraInfo.length>0?(n.openBlock(),n.createElementBlock("div",$e,[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(t.extraInfo,(m,d)=>(n.openBlock(),n.createElementBlock("span",{key:d},n.toDisplayString(m),1))),128))])):n.createCommentVNode("",!0)])]),t.showActionButton?(n.openBlock(),n.createElementBlock("button",{key:0,class:"bg-vsk-primary rounded-vsk px-4 py-2 text-sm font-semibold cursor-pointer hover:opacity-70",onClick:n.withModifiers(l,["stop"])},n.toDisplayString(t.actionLabel),1)):n.createCommentVNode("",!0),t.showStatusIndicator&&t.isConnected?(n.openBlock(),n.createElementBlock("div",We)):n.createCommentVNode("",!0)],2))}}),Ve=["for"],G=y(n.defineComponent({__name:"FormGroup",props:{label:{},htmlFor:{default:""},customClass:{default:()=>({})},labelClass:{default:()=>({})}},setup(t){return(e,i)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["flex flex-col gap-2",t.customClass])},[t.label?(n.openBlock(),n.createElementBlock("label",{key:0,for:t.htmlFor,class:n.normalizeClass(["text-sm font-medium text-primary dark:text-primary-dark",t.labelClass])},n.toDisplayString(t.label),11,Ve)):n.createCommentVNode("",!0),n.renderSlot(e.$slots,"default",{},void 0,!0)],2))}}),[["__scopeId","data-v-c0d9d9cb"]]),j=y(n.defineComponent({__name:"ListCard",props:{clickable:{type:Boolean,default:!1},customClass:{default:()=>({})}},emits:["click"],setup(t,{emit:e}){const i=t,l=e,s=()=>{i.clickable&&l("click")};return(c,o)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["flex items-center justify-between background p-3 rounded-vsk border border-vsk-primary/70 transition-colors duration-200",{"hover:bg-vsk-primary/5 cursor-pointer":i.clickable},t.customClass]),onClick:s},[n.renderSlot(c.$slots,"default",{},void 0,!0)],2))}}),[["__scopeId","data-v-f86d47da"]]),Le={class:"background rounded-corner flex flex-row items-center gap-2 justify-between w-full h-auto p-4 transition-all duration-200 hover:bg-ui-surface/80 dark:hover:bg-ui-surface-dark/80"},Oe=["src","alt","title"],Te={key:1,class:"w-8 h-8 flex items-center justify-center"},Re=["src","alt"],Pe=["min","max","value"],H=n.defineComponent({__name:"SliderControl",props:{icon:{},alt:{default:""},tooltip:{default:""},modelValue:{},min:{default:0},max:{default:100},showButton:{type:Boolean,default:!1},iconClass:{default:()=>({})},getPercentageClass:{type:Function,default:()=>""}},emits:["update:modelValue","buttonClick"],setup(t,{emit:e}){const i=t,l=e,s=n.computed(()=>{if(i.max<=i.min)return 0;const d=i.max-i.min,g=i.modelValue-i.min;return Math.round(g/d*100)}),c=n.computed(()=>i.getPercentageClass?i.getPercentageClass(s.value):""),o=d=>{const g=d.target;l("update:modelValue",Number(g.value))},m=()=>{l("buttonClick")};return(d,g)=>(n.openBlock(),n.createElementBlock("div",Le,[t.showButton?(n.openBlock(),n.createElementBlock("button",{key:0,onClick:m,class:"w-8 h-8 flex items-center justify-center rounded-corner transition-all duration-200 hover:bg-ui-surface/80 dark:hover:bg-ui-surface-dark/80 hover:scale-110 active:scale-95"},[n.createElementVNode("img",{src:t.icon,alt:t.alt,title:t.tooltip,class:n.normalizeClass(["w-6 h-6 transition-all duration-200",t.iconClass])},null,10,Oe)])):(n.openBlock(),n.createElementBlock("div",Te,[n.createElementVNode("img",{src:t.icon,alt:t.alt,class:"w-6 h-6 transition-all duration-200"},null,8,Re)])),n.createElementVNode("input",{type:"range",min:t.min,max:t.max,value:t.modelValue,onInput:o,class:"flex-1 transition-all duration-200 hover:scale-105"},null,40,Pe),n.createElementVNode("span",{class:n.normalizeClass(["w-12 text-right transition-all duration-200 font-medium",c.value])},n.toDisplayString(s.value)+"% ",3)]))}}),ve=["disabled"],q=n.defineComponent({__name:"SwitchToggle",props:{isOn:{type:Boolean},disabled:{type:Boolean,default:!1},size:{default:"small"},activeClass:{default:"bg-primary dark:bg-primary-dark"},inactiveClass:{default:"background"},customClass:{default:""}},emits:["toggle"],setup(t,{emit:e}){const i=t,l=e,s=()=>{l("toggle",!i.isOn)};return(c,o)=>(n.openBlock(),n.createElementBlock("button",{type:"button",onClick:s,disabled:t.disabled,class:n.normalizeClass(["relative inline-flex items-center rounded-full transition-colors",t.size==="small"?"h-6 w-11":"h-7 w-12",t.isOn?t.activeClass:t.inactiveClass,t.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer",t.customClass])},[n.createElementVNode("span",{class:n.normalizeClass(["inline-block transform rounded-full bg-white shadow transition-transform",t.size==="small"?"h-4 w-4":"h-6 w-6",t.isOn?t.size==="small"?"translate-x-6":"translate-x-5":"translate-x-1"])},null,2)],10,ve))}}),Me=["disabled"],Fe=["src","alt","title"],J=n.defineComponent({__name:"ToggleControl",props:{icon:{},alt:{default:""},tooltip:{default:""},isActive:{type:Boolean,default:!1},isLoading:{type:Boolean,default:!1},iconClass:{default:()=>({})},customClass:{default:()=>({})}},emits:["click"],setup(t,{emit:e}){const i=e,l=()=>{i("click")};return(s,c)=>(n.openBlock(),n.createElementBlock("button",{onClick:l,class:n.normalizeClass(["p-2 rounded-corner background hover:opacity-50 transition-all duration-300 h-17.5 w-17.5 group relative overflow-hidden hover:scale-105 hover:shadow-lg active:scale-95",{"animate-pulse":t.isLoading,"ring-2 ring-primary dark:ring-primary-dark":t.isActive,"opacity-60":!t.isActive,...t.customClass}]),disabled:t.isLoading},[n.createElementVNode("img",{src:t.icon,alt:t.alt,title:t.tooltip,class:n.normalizeClass(["m-auto w-12.5 h-12.5 transition-all duration-300 group-hover:scale-110 relative z-10",{"animate-spin":t.isLoading,"filter brightness-75":!t.isActive,"drop-shadow-lg":t.isActive,...t.iconClass}])},null,10,Fe)],10,Me))}}),Ue=["title"],Ge=["src","alt"],je={key:0,class:"absolute bottom-1 right-1 bg-primary dark:bg-primary-dark text-white text-xs rounded-full w-4 h-4 flex items-center justify-center font-bold animate-bounce"},Z=n.defineComponent({__name:"TrayIconButton",props:{icon:{},alt:{default:""},tooltip:{default:""},badge:{default:null},iconClass:{default:()=>({})},customClass:{default:""},tooltipClass:{default:""},showCustomTooltip:{type:Boolean,default:!1},customTooltipText:{default:""}},emits:["click"],setup(t,{emit:e}){const i=e,l=n.ref(!1),s=()=>{i("click")};return(c,o)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["p-1 rounded-corner relative hover:bg-primary dark:hover:bg-primary-dark group transition-all duration-300",t.customClass]),title:t.tooltip,onClick:s,onMouseenter:o[0]||(o[0]=m=>l.value=!0),onMouseleave:o[1]||(o[1]=m=>l.value=!1)},[n.createElementVNode("img",{src:t.icon,alt:t.alt,class:n.normalizeClass(["m-auto h-5.5 w-auto transition-all duration-300",t.iconClass])},null,10,Ge),t.badge!==null&&t.badge>0?(n.openBlock(),n.createElementBlock("div",je,n.toDisplayString(t.badge),1)):n.createCommentVNode("",!0),t.showCustomTooltip&&t.customTooltipText?(n.openBlock(),n.createElementBlock("div",{key:1,class:n.normalizeClass(["absolute top-1 left-1/2 transform -translate-x-1/2 text-xs font-semibold p-1 rounded-corner transition-all duration-300 pointer-events-none background",[t.tooltipClass,{"opacity-0 -translate-y-2":!l.value,"opacity-100 translate-y-0":l.value}]])},n.toDisplayString(t.customTooltipText),3)):n.createCommentVNode("",!0),n.renderSlot(c.$slots,"default")],42,Ue))}}),He=[z,N,v,M,F,U,G,j,H,q,J,Z],qe={install(t){He.forEach(e=>{t.component(e.name,e)})}};r.ActionButton=M,r.ConfigSection=F,r.DeviceCard=U,r.FormGroup=G,r.ListCard=j,r.SideBar=z,r.SideButton=N,r.SliderControl=H,r.SwitchToggle=q,r.ToggleControl=J,r.TrayIconButton=Z,r.WindowFrame=v,r.default=qe,Object.defineProperties(r,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(s,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("@vasakgroup/plugin-vicons"),require("@tauri-apps/api/event"),require("@tauri-apps/api/window")):typeof define=="function"&&define.amd?define(["exports","vue","@vasakgroup/plugin-vicons","@tauri-apps/api/event","@tauri-apps/api/window"],e):(s=typeof globalThis<"u"?globalThis:s||self,e(s.VueLibVasak={},s.Vue,s.VasakVicons,s.TauriEvent,s.TauriWindow))})(this,(function(s,e,b,I,L){"use strict";const j={class:"flex flex-col gap-2"},A=["aria-expanded"],M={key:1,class:"flex flex-col gap-1"},g=e.defineComponent({__name:"SideGroup",props:{title:{},collapsed:{type:Boolean,default:!1},defaultOpen:{type:Boolean,default:!0}},setup(t){const n=t,a=e.ref(n.defaultOpen);return e.watch(()=>n.collapsed,l=>{l&&(a.value=!0)}),(l,i)=>(e.openBlock(),e.createElementBlock("section",j,[t.collapsed?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"group flex w-full items-center justify-between rounded-corner px-2 py-1 text-tx-muted text-xs uppercase tracking-[0.08em] hover:bg-ui-surface/60","aria-expanded":a.value,onClick:i[0]||(i[0]=r=>a.value=!a.value)},[e.createElementVNode("span",null,e.toDisplayString(t.title),1),e.createElementVNode("span",{class:e.normalizeClass(["text-[10px] transition-transform duration-200",a.value?"rotate-180":""])}," v ",2)],8,A)),a.value||t.collapsed?(e.openBlock(),e.createElementBlock("div",M,[e.renderSlot(l.$slots,"default")])):e.createCommentVNode("",!0)]))}}),F=["title","aria-label","disabled","aria-current"],O=["src"],P={key:1},W={key:0,class:"min-w-0 flex-1 truncate font-medium"},G={key:1,class:"rounded-corner bg-ui-surface px-2 py-0.5 font-semibold text-tx-muted text-xs"},h=e.defineComponent({__name:"SideButton",props:{label:{},icon:{default:""},active:{type:Boolean,default:!1},collapsed:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},badge:{default:""}},emits:["click"],setup(t){const n=t,a=e.ref(""),l=e.toRef(n,"icon");let i=null,r=!1,c=0;async function d(){const o=++c,m=l.value;if(!m){a.value="";return}const k=await b.getIconSource(m);o===c&&(a.value=k)}return e.onMounted(async()=>{const o=await I.listen("vicons:theme-changed",d);if(r){o();return}i=o,await d()}),e.onUnmounted(()=>{r=!0,i?.()}),e.watch(l,d),(o,m)=>(e.openBlock(),e.createElementBlock("button",{type:"button",title:t.collapsed?t.label:void 0,"aria-label":t.collapsed?t.label:void 0,disabled:t.disabled,"aria-current":t.active?"page":void 0,class:e.normalizeClass(["group relative flex w-full items-center gap-3 rounded-corner border px-3 py-2 text-left text-sm transition-all duration-200",[t.active?"border-secondary bg-primary/15 text-tx-main shadow-sm":"border-transparent bg-ui-bg/30 hover:border-ui-border hover:bg-ui-surface/70",t.disabled?"cursor-not-allowed opacity-60":"cursor-pointer",t.collapsed?"justify-center px-2":""]]),onClick:m[0]||(m[0]=k=>o.$emit("click"))},[e.createElementVNode("span",{class:e.normalizeClass(["flex h-8 w-8 shrink-0 items-center justify-center rounded-corner font-semibold text-xs uppercase tracking-wide",t.active?"border-secondary bg-primary/20":""]),"aria-hidden":"true"},[a.value?(e.openBlock(),e.createElementBlock("img",{key:0,src:a.value,alt:"",class:"h-8 w-8 object-contain"},null,8,O)):(e.openBlock(),e.createElementBlock("span",P,e.toDisplayString(t.label.charAt(0).toUpperCase()),1))],2),t.collapsed?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",W,e.toDisplayString(t.label),1)),!t.collapsed&&t.badge!==""?(e.openBlock(),e.createElementBlock("span",G,e.toDisplayString(t.badge),1)):e.createCommentVNode("",!0)],10,F))}}),q={key:0,class:"flex flex-col gap-2 border-ui-border border-b p-2"},R={class:"flex items-center gap-2"},U=["aria-label","aria-expanded"],H={key:0,class:"min-w-0 flex-1"},J={key:0,class:"truncate font-semibold text-sm"},K={key:1,class:"truncate text-tx-muted text-xs"},Q={key:0},X={key:1,class:"flex justify-center border-ui-border border-b p-2"},Y=["aria-label","aria-expanded"],Z={class:"flex-1 space-y-3 overflow-y-auto p-2"},x=e.defineComponent({__name:"SideBar",props:{title:{default:""},subtitle:{default:""},categories:{default:()=>[]},modelValue:{default:""},collapsed:{type:Boolean,default:void 0},collapseLabel:{default:"Collapse"},expandLabel:{default:"Expand"}},emits:["update:modelValue","update:collapsed","change"],setup(t,{expose:n,emit:a}){const l=t,i=a,r=e.ref(l.collapsed??!1),c=e.ref(!1);let d=null;const o=e.computed(()=>c.value||r.value),m=e.computed(()=>!!(l.title||l.subtitle));e.computed(()=>l.categories.length>0);function k(){c.value=d?.matches??!1}function T(){r.value=!r.value,i("update:collapsed",r.value)}function Ae(u){i("update:modelValue",u),i("change",u)}return e.watch(()=>l.collapsed,u=>{u!==void 0&&(r.value=u)}),e.onMounted(()=>{d=window.matchMedia("(max-width: 767px)"),k(),d.addEventListener("change",k)}),e.onBeforeUnmount(()=>d?.removeEventListener("change",k)),n({collapsed:o}),(u,Pe)=>(e.openBlock(),e.createElementBlock("aside",{class:e.normalizeClass(["relative z-30 flex h-full shrink-0 flex-col rounded-corner border border-ui-border bg-ui-bg/80 transition-all duration-300",["w-[84px]",o.value?"md:w-[84px]":"md:w-72"]])},[m.value||u.$slots.header?(e.openBlock(),e.createElementBlock("header",q,[e.createElementVNode("div",R,[e.createElementVNode("button",{type:"button",class:"hidden h-10 w-10 items-center justify-center rounded-corner border border-ui-border bg-ui-surface/70 font-semibold text-sm md:inline-flex","aria-label":o.value?t.expandLabel:t.collapseLabel,"aria-expanded":!o.value,onClick:T},e.toDisplayString(o.value?">":"<"),9,U),m.value&&!o.value?(e.openBlock(),e.createElementBlock("div",H,[t.title?(e.openBlock(),e.createElementBlock("p",J,e.toDisplayString(t.title),1)):e.createCommentVNode("",!0),t.subtitle?(e.openBlock(),e.createElementBlock("p",K,e.toDisplayString(t.subtitle),1)):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0)]),u.$slots.header&&!o.value?(e.openBlock(),e.createElementBlock("div",Q,[e.renderSlot(u.$slots,"header",{collapsed:o.value})])):e.createCommentVNode("",!0)])):(e.openBlock(),e.createElementBlock("div",X,[e.createElementVNode("button",{type:"button",class:"hidden h-10 w-10 items-center justify-center rounded-corner border border-ui-border bg-ui-surface/70 font-semibold text-sm md:inline-flex","aria-label":o.value?t.expandLabel:t.collapseLabel,"aria-expanded":!o.value,onClick:T},e.toDisplayString(o.value?">":"<"),9,Y)])),e.createElementVNode("div",Z,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.categories,y=>(e.openBlock(),e.createBlock(g,{key:y.id,title:y.title,collapsed:o.value},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(y.items,f=>(e.openBlock(),e.createBlock(h,{key:f.id,label:f.label,icon:f.icon,badge:f.badge,disabled:f.disabled,collapsed:o.value,active:t.modelValue===f.id,onClick:We=>Ae(f.id)},null,8,["label","icon","badge","disabled","collapsed","active","onClick"]))),128))]),_:2},1032,["title","collapsed"]))),128)),e.renderSlot(u.$slots,"default",{collapsed:o.value})])],2))}}),_={"data-tauri-drag-region":""},v=["src","alt"],ee={"data-tauri-drag-region":""},te={"data-tauri-drag-region":""},ae=["src"],le=["src"],oe=["src"],ne=e.defineComponent({__name:"TopBar",props:{title:String,image:String},setup(t){const n=e.ref(null),a=L.getCurrentWindow(),l=e.ref(""),i=e.ref(""),r=e.ref(""),c=t;return e.onMounted(async()=>{l.value=await b.getIconSource("window-close"),i.value=await b.getIconSource("window-minimize"),r.value=await b.getIconSource("window-maximize")}),(d,o)=>(e.openBlock(),e.createElementBlock("div",{"data-tauri-drag-region":"",class:"flex h-8 px-4 py-1 bg-primary dark:bg-primary-dark rounded-t-corner-window justify-between align-center",ref_key:"bar",ref:n},[e.createElementVNode("div",_,[e.createElementVNode("img",{src:c.image,"data-tauri-drag-region":"",class:"h-6 w-auto",alt:c.title},null,8,v)]),e.createElementVNode("div",ee,e.toDisplayString(c.title),1),e.createElementVNode("div",te,[e.createElementVNode("span",{class:"win-button",onClick:o[0]||(o[0]=m=>e.unref(a).minimize())},[e.createElementVNode("img",{src:i.value,class:"h-6 w-6 inline-block",alt:"Minimize"},null,8,ae)]),e.createElementVNode("span",{class:"win-button",onClick:o[1]||(o[1]=m=>e.unref(a).toggleMaximize())},[e.createElementVNode("img",{src:r.value,class:"h-6 w-6 inline-block",alt:"Maximize"},null,8,le)]),e.createElementVNode("span",{class:"win-button",onClick:o[2]||(o[2]=m=>e.unref(a).close())},[e.createElementVNode("img",{src:l.value,class:"h-6 w-6 inline-block",alt:"Close"},null,8,oe)])])],512))}}),C=e.defineComponent({__name:"WindowFrame",props:{title:{default:"Vasak"},image:{default:""}},setup(t){const n=t;return(a,l)=>(e.openBlock(),e.createElementBlock(e.Fragment,null,[e.createVNode(ne,{title:n.title,image:n.image},null,8,["title","image"]),e.renderSlot(a.$slots,"default")],64))}}),ie=["type","disabled"],se={key:0,class:"w-4 h-4 animate-spin rounded-full border-2 border-current border-t-transparent"},re=["src","alt"],ce={key:2},de=["src","alt"],me=e.defineComponent({__name:"ActionButton",props:{label:{},disabled:{type:Boolean,default:!1},variant:{default:"primary"},loading:{type:Boolean,default:!1},customClass:{default:()=>({})},size:{default:"md"},fullWidth:{type:Boolean,default:!1},iconSrc:{default:""},iconAlt:{default:""},iconRight:{type:Boolean,default:!1},type:{default:"button"},stopPropagation:{type:Boolean,default:!1},preventDefault:{type:Boolean,default:!1}},emits:["click"],setup(t,{emit:n}){const a=t,l=n,i={primary:"bg-primary dark:bg-primary-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-primary/90 dark:hover:bg-primary-dark/90",secondary:"bg-secondary dark:bg-secondary-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-secondary/80 dark:hover:bg-secondary-dark/80",danger:"bg-status-error dark:bg-status-error-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-status-error/90 dark:hover:bg-status-error-dark/90"},r={sm:"px-2 py-1 text-xs",md:"px-3 py-1 text-sm",lg:"px-4 py-2 text-base"},c=d=>{a.stopPropagation&&d.stopPropagation(),a.preventDefault&&d.preventDefault(),!a.disabled&&!a.loading&&l("click")};return(d,o)=>(e.openBlock(),e.createElementBlock("button",{type:a.type,onClick:c,class:e.normalizeClass(["rounded-corner transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2",[i[a.variant],r[a.size],a.fullWidth?"w-full":"",a.iconSrc&&!a.label?"px-2 py-2":"",t.customClass]]),disabled:a.disabled||a.loading},[t.loading?(e.openBlock(),e.createElementBlock("span",se)):e.createCommentVNode("",!0),a.iconSrc&&!a.iconRight?(e.openBlock(),e.createElementBlock("img",{key:1,src:a.iconSrc,alt:a.iconAlt||a.label,class:"w-4 h-4"},null,8,re)):e.createCommentVNode("",!0),a.label?(e.openBlock(),e.createElementBlock("span",ce,e.toDisplayString(a.label),1)):e.createCommentVNode("",!0),a.iconSrc&&a.iconRight?(e.openBlock(),e.createElementBlock("img",{key:3,src:a.iconSrc,alt:a.iconAlt||a.label,class:"w-4 h-4"},null,8,de)):e.createCommentVNode("",!0)],10,ie))}}),p=(t,n)=>{const a=t.__vccOpts||t;for(const[l,i]of n)a[l]=i;return a},B=p(me,[["__scopeId","data-v-2eaf6e27"]]),ue={class:"text-base font-semibold m-0 text-primary dark:text-primary-dark"},w=p(e.defineComponent({__name:"ConfigSection",props:{title:{},icon:{default:""},customClass:{default:()=>({})}},setup(t){return(n,a)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["flex flex-col gap-4 p-4 background rounded-corner",t.customClass])},[e.createElementVNode("h3",ue,e.toDisplayString(t.icon?`${t.icon} ${t.title}`:t.title),1),e.renderSlot(n.$slots,"default",{},void 0,!0)],2))}}),[["__scopeId","data-v-45bd4eab"]]),fe={class:"flex items-center gap-3 flex-1 min-w-0"},ke=["src","alt"],be={class:"min-w-0"},pe={class:"font-semibold truncate"},ge={key:0,class:"text-xs text-gray-400 truncate"},he={key:1,class:"text-xs text-gray-400 truncate"},ye={key:2,class:"text-xs text-gray-400 flex gap-2 mt-1"},xe={key:1,class:"w-2 h-2 rounded-full bg-green-500"},E=e.defineComponent({__name:"DeviceCard",props:{icon:{},title:{},subtitle:{default:""},metadata:{default:""},extraInfo:{default:()=>[]},isConnected:{type:Boolean,default:!1},showActionButton:{type:Boolean,default:!0},actionLabel:{default:"Conectar"},showStatusIndicator:{type:Boolean,default:!1},customClass:{default:""},clickable:{type:Boolean,default:!1}},emits:["action","click"],setup(t,{emit:n}){const a=n,l=()=>{a("action")},i=()=>{a("click")};return(r,c)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["flex items-center justify-between background rounded-vsk px-6 py-3 mb-4",[{"border-l-4 border-green-500":t.isConnected},t.customClass]]),onClick:i},[e.createElementVNode("div",fe,[e.createElementVNode("img",{src:t.icon,alt:t.title,class:"h-7 w-7 shrink-0"},null,8,ke),e.createElementVNode("div",be,[e.createElementVNode("div",pe,e.toDisplayString(t.title),1),t.subtitle?(e.openBlock(),e.createElementBlock("div",ge,e.toDisplayString(t.subtitle),1)):e.createCommentVNode("",!0),t.metadata?(e.openBlock(),e.createElementBlock("div",he,e.toDisplayString(t.metadata),1)):e.createCommentVNode("",!0),t.extraInfo&&t.extraInfo.length>0?(e.openBlock(),e.createElementBlock("div",ye,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.extraInfo,(d,o)=>(e.openBlock(),e.createElementBlock("span",{key:o},e.toDisplayString(d),1))),128))])):e.createCommentVNode("",!0)])]),t.showActionButton?(e.openBlock(),e.createElementBlock("button",{key:0,class:"bg-vsk-primary rounded-vsk px-4 py-2 text-sm font-semibold cursor-pointer hover:opacity-70",onClick:e.withModifiers(l,["stop"])},e.toDisplayString(t.actionLabel),1)):e.createCommentVNode("",!0),t.showStatusIndicator&&t.isConnected?(e.openBlock(),e.createElementBlock("div",xe)):e.createCommentVNode("",!0)],2))}}),Ce=["for"],$=p(e.defineComponent({__name:"FormGroup",props:{label:{},htmlFor:{default:""},customClass:{default:()=>({})},labelClass:{default:()=>({})}},setup(t){return(n,a)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["flex flex-col gap-2",t.customClass])},[t.label?(e.openBlock(),e.createElementBlock("label",{key:0,for:t.htmlFor,class:e.normalizeClass(["text-sm font-medium text-primary dark:text-primary-dark",t.labelClass])},e.toDisplayString(t.label),11,Ce)):e.createCommentVNode("",!0),e.renderSlot(n.$slots,"default",{},void 0,!0)],2))}}),[["__scopeId","data-v-c0d9d9cb"]]),V=p(e.defineComponent({__name:"ListCard",props:{clickable:{type:Boolean,default:!1},customClass:{default:()=>({})}},emits:["click"],setup(t,{emit:n}){const a=t,l=n,i=()=>{a.clickable&&l("click")};return(r,c)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["flex items-center justify-between background p-3 rounded-vsk border border-vsk-primary/70 transition-colors duration-200",{"hover:bg-vsk-primary/5 cursor-pointer":a.clickable},t.customClass]),onClick:i},[e.renderSlot(r.$slots,"default",{},void 0,!0)],2))}}),[["__scopeId","data-v-f86d47da"]]),Be={class:"background rounded-corner flex flex-row items-center gap-2 justify-between w-full h-auto p-4 transition-all duration-200 hover:bg-ui-surface/80 dark:hover:bg-ui-surface-dark/80"},we=["src","alt","title"],Ee={key:1,class:"w-8 h-8 flex items-center justify-center"},$e=["src","alt"],Ve=["min","max","value"],S=e.defineComponent({__name:"SliderControl",props:{icon:{},alt:{default:""},tooltip:{default:""},modelValue:{},min:{default:0},max:{default:100},showButton:{type:Boolean,default:!1},iconClass:{default:()=>({})},getPercentageClass:{type:Function,default:()=>""}},emits:["update:modelValue","buttonClick"],setup(t,{emit:n}){const a=t,l=n,i=e.computed(()=>{if(a.max<=a.min)return 0;const o=a.max-a.min,m=a.modelValue-a.min;return Math.round(m/o*100)}),r=e.computed(()=>a.getPercentageClass?a.getPercentageClass(i.value):""),c=o=>{const m=o.target;l("update:modelValue",Number(m.value))},d=()=>{l("buttonClick")};return(o,m)=>(e.openBlock(),e.createElementBlock("div",Be,[t.showButton?(e.openBlock(),e.createElementBlock("button",{key:0,onClick:d,class:"w-8 h-8 flex items-center justify-center rounded-corner transition-all duration-200 hover:bg-ui-surface/80 dark:hover:bg-ui-surface-dark/80 hover:scale-110 active:scale-95"},[e.createElementVNode("img",{src:t.icon,alt:t.alt,title:t.tooltip,class:e.normalizeClass(["w-6 h-6 transition-all duration-200",t.iconClass])},null,10,we)])):(e.openBlock(),e.createElementBlock("div",Ee,[e.createElementVNode("img",{src:t.icon,alt:t.alt,class:"w-6 h-6 transition-all duration-200"},null,8,$e)])),e.createElementVNode("input",{type:"range",min:t.min,max:t.max,value:t.modelValue,onInput:c,class:"flex-1 transition-all duration-200 hover:scale-105"},null,40,Ve),e.createElementVNode("span",{class:e.normalizeClass(["w-12 text-right transition-all duration-200 font-medium",r.value])},e.toDisplayString(i.value)+"% ",3)]))}}),Se=["disabled"],N=e.defineComponent({__name:"SwitchToggle",props:{isOn:{type:Boolean},disabled:{type:Boolean,default:!1},size:{default:"small"},activeClass:{default:"bg-primary dark:bg-primary-dark"},inactiveClass:{default:"background"},customClass:{default:""}},emits:["toggle"],setup(t,{emit:n}){const a=t,l=n,i=()=>{l("toggle",!a.isOn)};return(r,c)=>(e.openBlock(),e.createElementBlock("button",{type:"button",onClick:i,disabled:t.disabled,class:e.normalizeClass(["relative inline-flex items-center rounded-full transition-colors",t.size==="small"?"h-6 w-11":"h-7 w-12",t.isOn?t.activeClass:t.inactiveClass,t.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer",t.customClass])},[e.createElementVNode("span",{class:e.normalizeClass(["inline-block transform rounded-full bg-white shadow transition-transform",t.size==="small"?"h-4 w-4":"h-6 w-6",t.isOn?t.size==="small"?"translate-x-6":"translate-x-5":"translate-x-1"])},null,2)],10,Se))}}),Ne=["disabled"],ze=["src","alt","title"],z=e.defineComponent({__name:"ToggleControl",props:{icon:{},alt:{default:""},tooltip:{default:""},isActive:{type:Boolean,default:!1},isLoading:{type:Boolean,default:!1},iconClass:{default:()=>({})},customClass:{default:()=>({})}},emits:["click"],setup(t,{emit:n}){const a=n,l=()=>{a("click")};return(i,r)=>(e.openBlock(),e.createElementBlock("button",{onClick:l,class:e.normalizeClass(["p-2 rounded-corner background hover:opacity-50 transition-all duration-300 h-17.5 w-17.5 group relative overflow-hidden hover:scale-105 hover:shadow-lg active:scale-95",{"animate-pulse":t.isLoading,"ring-2 ring-primary dark:ring-primary-dark":t.isActive,"opacity-60":!t.isActive,...t.customClass}]),disabled:t.isLoading},[e.createElementVNode("img",{src:t.icon,alt:t.alt,title:t.tooltip,class:e.normalizeClass(["m-auto w-12.5 h-12.5 transition-all duration-300 group-hover:scale-110 relative z-10",{"animate-spin":t.isLoading,"filter brightness-75":!t.isActive,"drop-shadow-lg":t.isActive,...t.iconClass}])},null,10,ze)],10,Ne))}}),De=["title"],Te=["src","alt"],Ie={key:0,class:"absolute bottom-1 right-1 bg-primary dark:bg-primary-dark text-white text-xs rounded-full w-4 h-4 flex items-center justify-center font-bold animate-bounce"},D=e.defineComponent({__name:"TrayIconButton",props:{icon:{},alt:{default:""},tooltip:{default:""},badge:{default:null},iconClass:{default:()=>({})},customClass:{default:""},tooltipClass:{default:""},showCustomTooltip:{type:Boolean,default:!1},customTooltipText:{default:""}},emits:["click"],setup(t,{emit:n}){const a=n,l=e.ref(!1),i=()=>{a("click")};return(r,c)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["p-1 rounded-corner relative hover:bg-primary dark:hover:bg-primary-dark group transition-all duration-300",t.customClass]),title:t.tooltip,onClick:i,onMouseenter:c[0]||(c[0]=d=>l.value=!0),onMouseleave:c[1]||(c[1]=d=>l.value=!1)},[e.createElementVNode("img",{src:t.icon,alt:t.alt,class:e.normalizeClass(["m-auto h-5.5 w-auto transition-all duration-300",t.iconClass])},null,10,Te),t.badge!==null&&t.badge>0?(e.openBlock(),e.createElementBlock("div",Ie,e.toDisplayString(t.badge),1)):e.createCommentVNode("",!0),t.showCustomTooltip&&t.customTooltipText?(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(["absolute top-1 left-1/2 transform -translate-x-1/2 text-xs font-semibold p-1 rounded-corner transition-all duration-300 pointer-events-none background",[t.tooltipClass,{"opacity-0 -translate-y-2":!l.value,"opacity-100 translate-y-0":l.value}]])},e.toDisplayString(t.customTooltipText),3)):e.createCommentVNode("",!0),e.renderSlot(r.$slots,"default")],42,De))}}),Le=[x,h,g,C,B,w,E,$,V,S,N,z,D],je={install(t){Le.forEach(n=>{t.component(n.name,n)})}};s.ActionButton=B,s.ConfigSection=w,s.DeviceCard=E,s.FormGroup=$,s.ListCard=V,s.SideBar=x,s.SideButton=h,s.SideGroup=g,s.SliderControl=S,s.SwitchToggle=N,s.ToggleControl=z,s.TrayIconButton=D,s.WindowFrame=C,s.default=je,Object.defineProperties(s,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vasakgroup/vue-libvasak",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Vue componenets for VSK Applications",
5
5
  "main": "./dist/vue-libvasak.umd.js",
6
6
  "module": "./dist/vue-libvasak.es.js",
@@ -11,9 +11,13 @@
11
11
  ],
12
12
  "exports": {
13
13
  ".": {
14
+ "types": "./src/types/vue-libvasak.d.ts",
14
15
  "import": "./dist/vue-libvasak.es.js",
15
- "require": "./dist/vue-libvasak.umd.js"
16
- }
16
+ "require": "./dist/vue-libvasak.umd.js",
17
+ "default": "./dist/vue-libvasak.es.js"
18
+ },
19
+ "./style.css": "./dist/vue-libvasak.css",
20
+ "./package.json": "./package.json"
17
21
  },
18
22
  "keywords": [
19
23
  "vue",
@@ -41,22 +45,30 @@
41
45
  "url": "https://joaquindecima.gitlab.io/"
42
46
  },
43
47
  "license": "GPLv3",
44
- "dependencies": {
45
- "@tauri-apps/api": "^2.10.1",
46
- "@vasakgroup/plugin-vicons": "^2.0.0",
47
- "vue": "^3.5.28",
48
- "vue-router": "^5.0.3"
49
- },
50
48
  "peerDependencies": {
51
- "vue": "^3.0.0"
49
+ "vue": "^3.0.0",
50
+ "@tauri-apps/api": "^2.0.0",
51
+ "@vasakgroup/plugin-vicons": "^2.0.0"
52
52
  },
53
53
  "devDependencies": {
54
+ "@happy-dom/global-registrator": "^20.14.5",
55
+ "@tauri-apps/api": "^2.10.1",
56
+ "@types/bun": "^1.4.2",
57
+ "@vasakgroup/plugin-vicons": "^2.0.0",
58
+ "@vitejs/plugin-vue": "^6.0.4",
59
+ "@vue/test-utils": "^2.5.1",
60
+ "happy-dom": "^20.14.5",
54
61
  "typescript": "^5.9.3",
55
62
  "vite": "^7.3.1",
56
- "@vitejs/plugin-vue": "^6.0.4"
63
+ "vue": "^3.5.28",
64
+ "vue-router": "^5.0.3",
65
+ "vue-tsc": "^3.3.11"
57
66
  },
58
67
  "scripts": {
59
- "build": "vite build",
60
- "dev": "vite"
68
+ "build": "bunx vue-tsc --noEmit && bunx --bun vite build",
69
+ "dev": "bunx --bun vite",
70
+ "prepare": "bun run build",
71
+ "typecheck": "bunx vue-tsc --noEmit",
72
+ "test": "bun test"
61
73
  }
62
74
  }
package/readme.md ADDED
@@ -0,0 +1,58 @@
1
+ # vue-libvasak
2
+
3
+ A simple vue components for VSK Applications used in VasakOS. This library is used in [application-template](https://github.com/Vasak-OS/application-template). **Don't work in browser.**
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ yarn add @vasakgroup/vue-libvasak
9
+ ```
10
+
11
+ ## Use
12
+
13
+ We can use the components in our application as follows:
14
+
15
+ ### WindowFrame
16
+
17
+ ```vue
18
+ <script lang="ts">
19
+ import { defineComponent } from "vue";
20
+ import { WindowFrame } from "@vasakgroup/vue-libvasak";
21
+ </script>
22
+
23
+ <template>
24
+ <WindowFrame title="Hello World" img="file:///home/pato/image.svg">
25
+ <p>Hello World</p>
26
+ </WindowFrame>
27
+ </template>
28
+ ```
29
+
30
+ ### SideBar
31
+
32
+ ```vue
33
+ <script lang="ts">
34
+ import { defineComponent } from "vue";
35
+ import {
36
+ SideBar,
37
+ WindowFrame,
38
+ SideButton,
39
+ } from "@vasakgroup/vue-libvasak";
40
+ </script>
41
+
42
+ <template>
43
+ <WindowFrame title="Hello World">
44
+ <SideBar>
45
+ <SideButton url="/" title="Home" image="./home.webp" />
46
+ <SideButton url="/about" title="About" image="./about.webp" />
47
+ </SideBar>
48
+ </WindowFrame>
49
+ </template>
50
+ ```
51
+
52
+ ## Contributors
53
+
54
+ <center>
55
+ <a href="https://github.com/Vasak-OS/vue-libvasak/graphs/contributors">
56
+ <img src="https://contrib.rocks/image?repo=Vasak-OS/vue-libvasak" />
57
+ </a>
58
+ </center>
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import SideBar from "./sidebar/SideBar.vue";
2
2
  import SideButton from "./sidebar/SideButton.vue";
3
+ import SideGroup from "./sidebar/SideGroup.vue";
3
4
  import WindowFrame from "./window/WindowFrame.vue";
4
5
  import ActionButton from "./controls/ActionButton.vue";
5
6
  import ConfigSection from "./layout/ConfigSection.vue";
@@ -15,6 +16,7 @@ import type { App } from "vue";
15
16
  const components = [
16
17
  SideBar,
17
18
  SideButton,
19
+ SideGroup,
18
20
  WindowFrame,
19
21
  ActionButton,
20
22
  ConfigSection,
@@ -38,6 +40,7 @@ export default {
38
40
  export {
39
41
  SideBar,
40
42
  SideButton,
43
+ SideGroup,
41
44
  WindowFrame,
42
45
  ActionButton,
43
46
  ConfigSection,
@@ -49,3 +52,5 @@ export {
49
52
  ToggleControl,
50
53
  TrayIconButton,
51
54
  };
55
+
56
+ export type { SidebarCategory, SidebarItem } from "./sidebar/tipos";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Que TypeScript entienda los `.vue`.
3
+ *
4
+ * Acá había un `declare module '*'`, que no es un shim: es apagar el chequeo de
5
+ * **todos** los imports del proyecto y dejarlos en `any`. Con eso, los tipos que
6
+ * esta librería publica no los comprobaba nadie —ni siquiera contra sus propios
7
+ * componentes—, que para un paquete cuya razón de ser es que otras seis
8
+ * aplicaciones lo usen es justo lo que no puede pasar.
9
+ */
10
+ declare module '*.vue' {
11
+ import type { DefineComponent } from 'vue';
12
+
13
+ const componente: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>;
14
+ export default componente;
15
+ }
@@ -1,7 +1,183 @@
1
- <script lang="ts" setup></script>
1
+ <script lang="ts" setup>
2
+ /**
3
+ * La barra lateral de las aplicaciones de VasakOS.
4
+ *
5
+ * Es la de Configuración, que es la que todas las demás venían copiando: mismo
6
+ * `aside` con borde y esquina redondeada, mismo plegado a 84 píxeles, mismos
7
+ * grupos con título. Que estén acá y no copiadas en cada repositorio es lo que
8
+ * hace que las ventanas se lean como partes del mismo escritorio en vez de
9
+ * parecerse por casualidad.
10
+ *
11
+ * # Los dos caminos
12
+ *
13
+ * Con `categories` se arma sola: cada categoría es un grupo plegable y cada
14
+ * elemento un botón, con el activo marcado contra `modelValue`. Es el camino de
15
+ * Configuración y del monitor, donde la barra **es** la navegación.
16
+ *
17
+ * Con la ranura por omisión se pone cualquier cosa adentro —discos, pasos de un
18
+ * asistente, una línea de tiempo— y la barra aporta nada más que el marco y el
19
+ * plegado. La ranura recibe `collapsed`, porque lo que se dibuja adentro casi
20
+ * siempre tiene que saberlo.
21
+ *
22
+ * Los dos caminos conviven: lo declarativo va primero y la ranura después.
23
+ *
24
+ * # Por qué no hay traducciones acá
25
+ *
26
+ * Una librería de componentes que traduce obliga a todas las aplicaciones a
27
+ * compartir sus claves. Los textos entran por propiedades; el `aria-label` del
28
+ * botón de plegar también, que es el único que no se ve pero se oye.
29
+ */
30
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
31
+ import SideGroup from './SideGroup.vue';
32
+ import SideButton from './SideButton.vue';
33
+ import type { SidebarCategory } from './tipos';
34
+
35
+ const props = withDefaults(
36
+ defineProps<{
37
+ /** El nombre de la ventana. Sin él y sin `subtitle` no hay área de título. */
38
+ title?: string;
39
+ subtitle?: string;
40
+ categories?: SidebarCategory[];
41
+ /** El identificador del elemento activo. */
42
+ modelValue?: string;
43
+ /** Plegada desde afuera. Sin esto, la barra se maneja sola. */
44
+ collapsed?: boolean;
45
+ /** Lo que oye un lector de pantalla en el botón de plegar. */
46
+ collapseLabel?: string;
47
+ expandLabel?: string;
48
+ }>(),
49
+ {
50
+ title: '',
51
+ subtitle: '',
52
+ categories: () => [],
53
+ modelValue: '',
54
+ collapsed: undefined,
55
+ collapseLabel: 'Collapse',
56
+ expandLabel: 'Expand',
57
+ }
58
+ );
59
+
60
+ const emit = defineEmits<{
61
+ 'update:modelValue': [value: string];
62
+ 'update:collapsed': [value: boolean];
63
+ change: [value: string];
64
+ }>();
65
+
66
+ const plegadaAMano = ref(props.collapsed ?? false);
67
+ const esAngosta = ref(false);
68
+ let consulta: MediaQueryList | null = null;
69
+
70
+ /**
71
+ * Plegada por decisión o por ancho.
72
+ *
73
+ * Por debajo de 768 píxeles no hay lugar para el texto de los botones, así que
74
+ * la barra se pliega sola y el botón de plegar no se muestra: ofrecer
75
+ * desplegarla ahí sería ofrecer algo que no entra.
76
+ */
77
+ const plegada = computed(() => esAngosta.value || plegadaAMano.value);
78
+ const hayTitulo = computed(() => Boolean(props.title || props.subtitle));
79
+ const hayCategorias = computed(() => props.categories.length > 0);
80
+
81
+ function revisar() {
82
+ esAngosta.value = consulta?.matches ?? false;
83
+ }
84
+
85
+ function alternar() {
86
+ plegadaAMano.value = !plegadaAMano.value;
87
+ emit('update:collapsed', plegadaAMano.value);
88
+ }
89
+
90
+ function elegir(id: string) {
91
+ emit('update:modelValue', id);
92
+ emit('change', id);
93
+ }
94
+
95
+ // Controlada desde afuera cuando la aplicación pasa `collapsed`: así dos
96
+ // ventanas de la misma aplicación pueden recordar cómo la dejó la persona.
97
+ watch(
98
+ () => props.collapsed,
99
+ (valor) => {
100
+ if (valor !== undefined) {
101
+ plegadaAMano.value = valor;
102
+ }
103
+ }
104
+ );
105
+
106
+ onMounted(() => {
107
+ consulta = window.matchMedia('(max-width: 767px)');
108
+ revisar();
109
+ consulta.addEventListener('change', revisar);
110
+ });
111
+
112
+ onBeforeUnmount(() => consulta?.removeEventListener('change', revisar));
113
+
114
+ defineExpose({ collapsed: plegada });
115
+ </script>
2
116
 
3
117
  <template>
4
- <div class="sidebar">
5
- <slot />
6
- </div>
118
+ <aside
119
+ class="relative z-30 flex h-full shrink-0 flex-col rounded-corner border border-ui-border bg-ui-bg/80 transition-all duration-300"
120
+ :class="['w-[84px]', plegada ? 'md:w-[84px]' : 'md:w-72']">
121
+ <header
122
+ v-if="hayTitulo || $slots.header"
123
+ class="flex flex-col gap-2 border-ui-border border-b p-2">
124
+ <div class="flex items-center gap-2">
125
+ <button
126
+ type="button"
127
+ class="hidden h-10 w-10 items-center justify-center rounded-corner border border-ui-border bg-ui-surface/70 font-semibold text-sm md:inline-flex"
128
+ :aria-label="plegada ? expandLabel : collapseLabel"
129
+ :aria-expanded="!plegada"
130
+ @click="alternar">
131
+ {{ plegada ? '&gt;' : '&lt;' }}
132
+ </button>
133
+ <!-- El área de título es opcional: hay ventanas donde el nombre ya está
134
+ en la barra superior y repetirlo acá gasta la mitad del alto. -->
135
+ <div v-if="hayTitulo && !plegada" class="min-w-0 flex-1">
136
+ <p v-if="title" class="truncate font-semibold text-sm">{{ title }}</p>
137
+ <p v-if="subtitle" class="truncate text-tx-muted text-xs">{{ subtitle }}</p>
138
+ </div>
139
+ </div>
140
+
141
+ <!-- Lo que va antes que cualquier categoría: la búsqueda de la tienda,
142
+ por ejemplo. Plegada no entra un campo de texto —84 píxeles es el
143
+ ancho del icono— así que se esconde en vez de quedar ilegible. -->
144
+ <div v-if="$slots.header && !plegada">
145
+ <slot name="header" :collapsed="plegada" />
146
+ </div>
147
+ </header>
148
+
149
+ <!-- Sin área de título el botón de plegar necesita su propio lugar, o la
150
+ barra deja de poder plegarse. -->
151
+ <div v-else class="flex justify-center border-ui-border border-b p-2">
152
+ <button
153
+ type="button"
154
+ class="hidden h-10 w-10 items-center justify-center rounded-corner border border-ui-border bg-ui-surface/70 font-semibold text-sm md:inline-flex"
155
+ :aria-label="plegada ? expandLabel : collapseLabel"
156
+ :aria-expanded="!plegada"
157
+ @click="alternar">
158
+ {{ plegada ? '&gt;' : '&lt;' }}
159
+ </button>
160
+ </div>
161
+
162
+ <div class="flex-1 space-y-3 overflow-y-auto p-2">
163
+ <SideGroup
164
+ v-for="category in categories"
165
+ :key="category.id"
166
+ :title="category.title"
167
+ :collapsed="plegada">
168
+ <SideButton
169
+ v-for="item in category.items"
170
+ :key="item.id"
171
+ :label="item.label"
172
+ :icon="item.icon"
173
+ :badge="item.badge"
174
+ :disabled="item.disabled"
175
+ :collapsed="plegada"
176
+ :active="modelValue === item.id"
177
+ @click="elegir(item.id)" />
178
+ </SideGroup>
179
+
180
+ <slot :collapsed="plegada" />
181
+ </div>
182
+ </aside>
7
183
  </template>
@@ -1,18 +1,115 @@
1
1
  <script lang="ts" setup>
2
- const props = defineProps({
3
- title: {
4
- type: String,
5
- default: "Link",
6
- },
7
- image: {
8
- type: String,
9
- default: "",
10
- },
2
+ /**
3
+ * Un elemento de la barra lateral.
4
+ *
5
+ * El icono sale del tema del escritorio y se vuelve a resolver cuando la
6
+ * persona cambia de tema: por eso no se recibe una ruta sino un nombre. Plegado
7
+ * queda sólo el icono, y el nombre pasa al `title` para que el globo lo diga —
8
+ * sin eso, una barra plegada es una columna de dibujos sin explicación.
9
+ */
10
+ import { getIconSource } from '@vasakgroup/plugin-vicons';
11
+ import { listen, type UnlistenFn } from '@tauri-apps/api/event';
12
+ import { onMounted, onUnmounted, ref, toRef, watch } from 'vue';
13
+
14
+ const props = withDefaults(
15
+ defineProps<{
16
+ label: string;
17
+ icon?: string;
18
+ active?: boolean;
19
+ collapsed?: boolean;
20
+ disabled?: boolean;
21
+ badge?: string | number;
22
+ }>(),
23
+ { icon: '', active: false, collapsed: false, disabled: false, badge: '' }
24
+ );
25
+
26
+ defineEmits<{ click: [] }>();
27
+
28
+ const fuente = ref('');
29
+ const icono = toRef(props, 'icon');
30
+ let soltar: UnlistenFn | null = null;
31
+ let desmontado = false;
32
+
33
+ /** Cuántas resoluciones se pidieron. De las que estén en vuelo, sólo vale la última. */
34
+ let ultimoPedido = 0;
35
+
36
+ async function resolver() {
37
+ const mio = ++ultimoPedido;
38
+ const nombre = icono.value;
39
+ if (!nombre) {
40
+ fuente.value = '';
41
+ return;
42
+ }
43
+ const resuelto = await getIconSource(nombre);
44
+ // Cambiar de icono y cambiar de tema resuelven en paralelo, y el tema tarda
45
+ // lo que tarde el backend. Sin el testigo, la respuesta vieja llega última y
46
+ // deja puesto el icono anterior — que es el mismo síntoma que se venía a
47
+ // evitar, pero intermitente y según cuál tarde más.
48
+ if (mio === ultimoPedido) {
49
+ fuente.value = resuelto;
50
+ }
51
+ }
52
+
53
+ onMounted(async () => {
54
+ // El oyente **antes** de la primera resolución. El tema de iconos cambia en
55
+ // caliente, y resolver el primero tarda: un cambio de tema durante esa
56
+ // espera no lo escuchaba nadie, y el botón se quedaba con el icono del tema
57
+ // anterior hasta el cambio siguiente.
58
+ const dejarDeEscuchar = await listen('vicons:theme-changed', resolver);
59
+ // Registrarse tarda, y en una lista que se desplaza un botón puede irse
60
+ // antes de que termine. Ahí `onUnmounted` ya pasó y no vio nada que soltar:
61
+ // el oyente quedaba registrado para siempre sobre un componente muerto.
62
+ if (desmontado) {
63
+ dejarDeEscuchar();
64
+ return;
65
+ }
66
+ soltar = dejarDeEscuchar;
67
+
68
+ // Las dos resoluciones pueden cruzarse —ésta y la que dispare un cambio de
69
+ // tema—, y de eso se encarga el testigo de `resolver`.
70
+ await resolver();
71
+ });
72
+
73
+ onUnmounted(() => {
74
+ desmontado = true;
75
+ soltar?.();
11
76
  });
77
+
78
+ watch(icono, resolver);
12
79
  </script>
13
80
 
14
81
  <template>
15
- <a href="#" class="sidebar-button">
16
- <img :src="image" :alt="title" class="img-fluid" />
17
- </a>
82
+ <button
83
+ type="button"
84
+ :title="collapsed ? label : undefined"
85
+ :aria-label="collapsed ? label : undefined"
86
+ :disabled="disabled"
87
+ :aria-current="active ? 'page' : undefined"
88
+ class="group relative flex w-full items-center gap-3 rounded-corner border px-3 py-2 text-left text-sm transition-all duration-200"
89
+ :class="[
90
+ active
91
+ ? 'border-secondary bg-primary/15 text-tx-main shadow-sm'
92
+ : 'border-transparent bg-ui-bg/30 hover:border-ui-border hover:bg-ui-surface/70',
93
+ disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer',
94
+ collapsed ? 'justify-center px-2' : '',
95
+ ]"
96
+ @click="$emit('click')">
97
+ <span
98
+ class="flex h-8 w-8 shrink-0 items-center justify-center rounded-corner font-semibold text-xs uppercase tracking-wide"
99
+ :class="active ? 'border-secondary bg-primary/20' : ''"
100
+ aria-hidden="true">
101
+ <img v-if="fuente" :src="fuente" alt="" class="h-8 w-8 object-contain">
102
+ <!-- Sin icono, la inicial: un hueco vacío del mismo tamaño deja la fila
103
+ desalineada contra las que sí lo tienen. -->
104
+ <span v-else>{{ label.charAt(0).toUpperCase() }}</span>
105
+ </span>
106
+
107
+ <span v-if="!collapsed" class="min-w-0 flex-1 truncate font-medium">{{ label }}</span>
108
+
109
+ <span
110
+ v-if="!collapsed && badge !== ''"
111
+ class="rounded-corner bg-ui-surface px-2 py-0.5 font-semibold text-tx-muted text-xs">
112
+ {{ badge }}
113
+ </span>
114
+ </button>
18
115
  </template>