@koishi-ce/plugin-config 1.1.0 → 1.1.2

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.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{Schema as e,ScopeStatus as t,Service as n,clone as r,icons as i,message as a,root as o,router as s,send as c,store as l,useContext as u,useI18n as d,useI18nText as f,useMenu as p}from"@koishi-ce/client";import{Fragment as m,computed as h,createBlock as g,createCommentVNode as _,createElementBlock as v,createElementVNode as y,createTextVNode as b,createVNode as x,defineComponent as S,h as C,inject as w,nextTick as T,normalizeClass as E,onActivated as D,openBlock as O,provide as k,ref as A,renderList as j,renderSlot as M,resolveComponent as N,toDisplayString as P,unref as F,watch as I,withCtx as L,withKeys as R,withModifiers as z}from"vue";import{useRoute as ee,useRouter as te}from"vue-router";var B=A(),V=A(),H=[`@koishi-ce/plugin-console`,`@koishi-ce/plugin-config`,`@koishi-ce/plugin-server`];function U(e){if(e.name&&H.includes(J(e.name)??``))return!0;if(e.children)return e.children.some(U)}function W(e,t){let n=e?.[t];if(n)return n;if(t.startsWith(`@koishijs/plugin-`))return e?.[`@koishi-ce/plugin-${t.slice(17)}`]}function G(e){function t(e,t){r.has(e)||e!==`console`&&(n.using[e]={required:t})}let n={impl:[],using:{},peer:{}},r=new Set,i=l.packages,a=i?.[e];if(!i||!a)return n;for(let e in a.package.peerDependencies??{}){if(!e.includes(`@koishi-ce/plugin-`)&&!e.includes(`@koishijs/plugin-`)&&!e.includes(`koishi-plugin-`))continue;let t=W(i,e);if(H.includes(t?.name??e))continue;let o=!a.package.peerDependenciesMeta?.[e]?.optional,s=!!t?.runtime?.id;n.peer[e]={required:o,active:s};for(let e of t?.manifest?.service.implements??[])r.add(e)}for(let e of a.manifest.service.implements)e!==`adapter`&&n.impl.push(e);for(let e of a.runtime?.required??[])t(e,!0);for(let e of a.runtime?.optional??[])t(e,!1);return a.runtime?.id&&!a.runtime?.forkable&&(n.warning=!0),a.runtime?.schema||(n.warning=!0),n}var K=h(()=>Object.fromEntries(Object.keys(l.packages??{}).filter(e=>e).map(e=>[e,G(e)]))),q=A();function J(e){if(!e)return e;if(e.startsWith(`./`))return Object.values(l.packages??{}).find(t=>t.paths?.includes(e))?.package?.name;if(e.includes(`/`)){let[t,n]=e.split(`/`);return[`${t}/koishi-plugin-${n}`].find(e=>e in(l.packages||{}))}return[`@koishi-ce/plugin-${e}`,`@koishijs/plugin-${e}`,`koishi-plugin-${e}`].find(e=>e in(l.packages||{}))}var Y=h(()=>{if(q.value)return J(q.value.name)}),ne=h(()=>{let e=K.value[Y.value??``];if(e){if(e.warning&&q.value?.disabled)return`warning`;for(let t in e.using)if(t in(l.services||{})){if(e.impl.includes(t))return`warning`}else if(e.using[t]?.required)return`warning`}});function re(e,t){let n=[];if(!t)return n;for(let r in t){if(r.startsWith(`$`))continue;let i=t[r],a={config:i,parent:e};r.startsWith(`~`)&&(a.disabled=!0,r=r.slice(1)),a.name=r.split(`:`,1)[0]??``,a.id=r,a.path=r.slice(a.name.length+1);let o=i?.$label;o!==void 0&&(a.label=o),r.startsWith(`group:`)&&(a.children=re(a,i)),n.push(a)}return n}var X=h(()=>{let e={name:``,id:``,path:``,label:o.$i18n.t(`config.view.globalSettings`),config:l.config,children:[]},t=[e],n=[],r={},i={"":e};for(let n of re(e,l.config?.plugins))t.push(n),a(n);function a(e){!e.config?.$collapsed&&e.children&&n.push(e.path),(r[e.name]||=[]).push(e.path),i[e.path]=e,e.children?.forEach(a)}return{data:t,forks:r,paths:i,expanded:n}});function Z(e){switch(l.packages?.[J(e.name)??``]?.runtime?.forks?.[e.path]?.status){case t.PENDING:return`pending`;case t.LOADING:return`loading`;case t.ACTIVE:return`active`;case t.FAILED:return`failed`;case t.DISPOSED:return`disposed`;default:return`disabled`}}async function Q(e){let t=e.parent?.path??``;c(`manager/remove`,t,e.id),await s.replace(`/plugins/${t}`)}var ie=[`id`],ae={class:`text-left`},oe={class:`path`},se={class:`text-right`},ce={class:`actions`},le=[`onClick`],ue=[`onClick`],de={class:`left`},fe={class:`right`},pe=S({__name:`forks`,setup(e){let{t}=d(),n=h(()=>B.value?.replace(/(koishi-|^@koishijs\/)plugin-/,``)),r=h(()=>l.packages?.[B.value]);function i(e){return`${e.label?`${e.label} `:``}[${e.path}]`}function a(e){let t=[i(e)];for(;e.parent;)e=e.parent,t.unshift(i(e));return t.shift(),t.join(` > `)}async function o(e){let t=n.value;e||(e=Math.random().toString(36).slice(2,8),c(`manager/unload`,``,`${t}:${e}`,{})),await s.push(`/plugins/${e}`),B.value=null}return(e,i)=>{let s=N(`k-icon`),c=N(`el-button`),l=N(`el-dialog`);return O(),g(l,{"model-value":!!F(B),"onUpdate:modelValue":i[1]||=e=>B.value=null,class:`dialog-config-fork`,"destroy-on-close":``},{header:L(({titleId:e,titleClass:n})=>[y(`span`,{id:e,class:E(n)},P(F(B)+(r.value?.workspace?F(t)(`config.forks.workspace`):``)),11,ie)]),footer:L(()=>[y(`div`,de,[F(X).forks[n.value]?.length?(O(),v(m,{key:0},[b(P(F(t)(`config.forks.count`,[F(X).forks[n.value]?.length])),1)],64)):(O(),v(m,{key:1},[b(P(F(t)(`config.forks.none`)),1)],64))]),y(`div`,fe,[x(c,{onClick:i[0]||=z(e=>o(),[`stop`])},{default:L(()=>[b(P(F(t)(`config.forks.add`)),1)]),_:1})])]),default:L(()=>[y(`table`,null,[(O(!0),v(m,null,j(F(X).forks[n.value],e=>(O(),v(`tr`,{key:e},[y(`td`,ae,[y(`span`,{class:E([`status-light`,F(Z)(F(X).paths[e])])},null,2),y(`span`,oe,P(a(F(X).paths[e])),1)]),y(`td`,se,[y(`span`,ce,[y(`span`,{class:`action`,onClick:z(t=>o(e),[`stop`])},[x(s,{name:`arrow-right`})],8,le),y(`span`,{class:`action`,onClick:z(t=>F(Q)(F(X).paths[e]),[`stop`])},[x(s,{name:`delete`})],8,ue)])])]))),128))])]),_:1},8,[`model-value`])}}}),me=S({__name:`global`,props:{current:{},modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let n=e,r=t,i=h({get:()=>n.modelValue,set:e=>r(`update:modelValue`,e)});return(t,n)=>{let r=N(`k-form`);return O(),g(r,{schema:F(l).packages[``].runtime.schema,initial:e.current.config,modelValue:i.value,"onUpdate:modelValue":n[0]||=e=>i.value=e},null,8,[`schema`,`initial`,`modelValue`])}}}),he={class:`modifier`},ge={class:`k-schema-header`},_e=S({__name:`modifier`,props:{modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let{t:n}=d(),r=e,i=t,a=h({get:()=>r.modelValue?.$filter,set:e=>i(`update:modelValue`,{...r.modelValue,$filter:e})});return(e,t)=>{let r=N(`k-filter`);return O(),v(`div`,he,[y(`h2`,ge,P(F(n)(`config.modifier.title`)),1),x(r,{modelValue:a.value,"onUpdate:modelValue":t[0]||=e=>a.value=e},null,8,[`modelValue`])])}}}),ve=S({__name:`group`,props:{current:{},modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let n=e,r=t,i=h({get:()=>n.modelValue,set:e=>r(`update:modelValue`,e)});return(e,t)=>(O(),g(_e,{modelValue:i.value,"onUpdate:modelValue":t[0]||=e=>i.value=e},null,8,[`modelValue`]))}}),$=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},ye=$(ve,[[`__scopeId`,`data-v-21d6aea8`]]),be=S({__name:`plugin`,props:{current:{},modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let{t:n}=d(),r=e,i=t,a=u(),o=h({get:()=>r.modelValue,set:e=>i(`update:modelValue`,e)}),s=h(()=>K.value[Y.value]),f=h(()=>l.packages[Y.value]),p=h(()=>f.value.workspace?n(`config.plugin.hintSource`):n(`config.plugin.hintAuthor`));return I(f,e=>{!e||e.runtime||c(`config/request-runtime`,e.name)},{immediate:!0}),k(`plugin:name`,Y),k(`plugin:env`,s),k(`manager.settings.local`,f),k(`manager.settings.config`,o),k(`manager.settings.current`,h(()=>r.current)),(t,r)=>{let i=N(`k-comment`),c=N(`k-slot`),u=N(`k-slot-item`),d=N(`k-activity-link`),h=N(`k-markdown`),S=N(`k-form`);return F(Y)?(O(),v(m,{key:0},[f.value.runtime?f.value.runtime.failed?(O(),g(i,{key:1,type:`danger`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.failure`,[p.value])),1)]),_:1})):(O(),g(c,{key:2,name:`plugin-details`},{default:L(()=>[x(u,{order:800},{default:L(()=>[x(c,{name:`plugin-dependency`,single:``},{default:L(()=>[(O(!0),v(m,null,j(s.value.peer,({required:e,active:t},r)=>(O(),g(i,{key:r,type:t?`success`:e?`warning`:`primary`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.dependency`,[F(n)(e?`config.plugin.dependencyRequired`:`config.plugin.dependencyOptional`),r,F(n)(t?`config.plugin.loaded`:`config.plugin.notLoaded`)])),1)]),_:2},1032,[`type`]))),128)),(O(!0),v(m,null,j(s.value.using,({required:e},t)=>(O(),g(i,{key:t,type:t in F(l).services?`success`:e?`warning`:`primary`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.service`,[F(n)(e?`config.plugin.serviceRequired`:`config.plugin.serviceOptional`),t,F(n)(t in F(l).services?`config.plugin.loaded`:`config.plugin.notLoaded`)])),1)]),_:2},1032,[`type`]))),128))]),_:1})]),_:1}),x(u,{order:600},{default:L(()=>[(O(!0),v(m,null,j(s.value.impl,t=>(O(),v(m,{key:t},[t in F(l).services&&e.current.disabled?(O(),g(i,{key:0,type:`warning`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.serviceConflict`,[t])),1)]),_:2},1024)):(O(),g(i,{key:1,type:e.current.disabled?`primary`:`success`},{default:L(()=>[y(`p`,null,P(F(n)(e.current.disabled?`config.plugin.serviceFuture`:`config.plugin.serviceProvided`,[t])),1)]),_:2},1032,[`type`]))],64))),128))]),_:1}),x(u,{order:400},{default:L(()=>[f.value.runtime.id&&!f.value.runtime.forkable&&e.current.disabled?(O(),g(i,{key:0,type:`warning`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.notReusable`)),1)]),_:1})):_(`v-if`,!0),F(X).forks[e.current.name]?.length>1?(O(),g(i,{key:1,type:`primary`},{default:L(()=>[y(`p`,null,[b(P(F(n)(`config.plugin.multiplePrefix`)),1),y(`span`,{class:`k-link`,onClick:r[0]||=z(e=>B.value=F(Y),[`stop`])},P(F(n)(`config.plugin.multipleAction`)),1),b(P(F(n)(`config.plugin.multipleSuffix`)),1)])]),_:1})):_(`v-if`,!0)]),_:1}),x(u,{order:300},{default:L(()=>[(O(!0),v(m,null,j(F(a).$router.pages,(t,r)=>(O(),v(m,{key:r},[t.ctx.extension?.paths.includes(e.current.path)&&!t.disabled()?(O(),g(i,{key:0,type:`success`},{default:L(()=>[y(`p`,null,[y(`span`,null,P(F(n)(`config.plugin.pages`)),1),x(d,{id:t.id},null,8,[`id`])])]),_:2},1024)):_(`v-if`,!0)],64))),128))]),_:1}),f.value.runtime?.usage?(O(),g(u,{key:0,order:-200},{default:L(()=>[x(h,{unsafe:``,class:`usage`,source:f.value.runtime?.usage},null,8,[`source`])]),_:1})):_(`v-if`,!0),x(u,{order:-600},{default:L(()=>[f.value.runtime.filter===!1?_(`v-if`,!0):(O(),g(_e,{key:0,modelValue:o.value,"onUpdate:modelValue":r[1]||=e=>o.value=e},null,8,[`modelValue`]))]),_:1}),x(u,{order:-1e3},{default:L(()=>[f.value.runtime.schema?(O(),g(S,{key:1,schema:f.value.runtime.schema,initial:e.current.config,modelValue:o.value,"onUpdate:modelValue":r[2]||=e=>o.value=e},{hint:L(()=>[b(P(p.value),1)]),_:1},8,[`schema`,`initial`,`modelValue`])):(O(),g(i,{key:0,type:`warning`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.noSchema`,[p.value])),1)]),_:1}))]),_:1})]),_:1})):(O(),g(i,{key:0},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.loading`)),1)]),_:1}))],64)):(O(),g(c,{key:1,name:`plugin-missing`,single:``},{default:L(()=>[x(i,{type:`danger`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.notInstalled`)),1)]),_:1})]),_:1}))}}}),xe={class:`search`},Se=[`title`],Ce={class:`right`},we=S({__name:`tree`,props:{modelValue:{}},emits:[`update:modelValue`],setup(e,{expose:t,emit:n}){let{t:r}=d(),i=e,a=ee(),o=p(`config.tree`),s=n,l=A(null),u=A(null),f=A(``);function m(e,t){return t.name.toLowerCase().includes(f.value.toLowerCase())}let h=A(!0);async function b(){await T();let e=l.value?.$el,t=e?.querySelector(`.el-tree-node.is-active`);!t||!t.offsetTop&&a.path.slice(9)||l.value.setScrollTop(t.offsetTop-(e.offsetHeight-t.offsetHeight)/2)}t({activate:b}),D(async()=>{b(),h.value=!1});function S(e){!e||h.value||b()}function C(e){return e.data.name===`group`?r(`config.tree.group`,[e.label||e.data.path]):e.label||e.data.name||r(`config.tree.pending`)}function w(e){return e.data.path!==``}function k(e,t,n){return n===`inner`?t.data.id.startsWith(`group:`):t.data.path!==``||n===`next`}function j(e,t,n,r){s(`update:modelValue`,e.path),window.dispatchEvent(new MouseEvent(r.type,r))}function M(e,t,n){c(`manager/meta`,e.path,{$collapsed:null})}function R(e,t,n){c(`manager/meta`,e.path,{$collapsed:!0})}function z(e,t,n,r){let i=n===`inner`?t:t.parent,a=i.childNodes.findIndex(t=>t.data.path===e.data.path);i.data.path||--a,c(`manager/teleport`,e.data.parent?.path??``,e.data.id,i.data.path,a)}function te(e){let t=[];return e.children&&t.push(`is-group`),!e.children&&!J(e.name)&&t.push(`is-disabled`),e.path===i.modelValue&&t.push(`is-active`),t.join(` `)}return I(f,e=>{u.value.filter(e)}),(e,t)=>{let n=N(`k-icon`),r=N(`el-input`),i=N(`el-tree`),a=N(`el-scrollbar`);return O(),g(a,{class:`plugin-tree`,ref_key:`root`,ref:l},{default:L(()=>[y(`div`,xe,[x(r,{modelValue:f.value,"onUpdate:modelValue":t[0]||=e=>f.value=e},{suffix:L(()=>[x(n,{name:`search`})]),_:1},8,[`modelValue`])]),x(i,{ref_key:`tree`,ref:u,"node-key":`path`,data:F(X).data,draggable:!0,"auto-expand-parent":!1,"default-expanded-keys":F(X).expanded,"expand-on-click-node":!1,"filter-node-method":m,props:{class:te},"allow-drag":w,"allow-drop":k,onNodeClick:j,onNodeContextmenu:F(o),onNodeDrop:z,onNodeExpand:M,onNodeCollapse:R},{default:L(({node:e})=>[y(`div`,{class:`item`,ref:S},[y(`div`,{class:`label`,title:C(e)},P(C(e)),9,Se),y(`div`,Ce,[F(J)(e.data.name)?(O(),v(`span`,{key:0,class:E([`status-light`,F(Z)(e.data)])},null,2)):_(`v-if`,!0)])],512)]),_:1},8,[`data`,`default-expanded-keys`,`props`,`onNodeContextmenu`])]),_:1},512)}}}),Te=S({__name:`index`,setup(t){let{t:n}=d(),i=ee(),o=te(),s=h({get(){let e=i.path.slice(9);return e in X.value.paths?e:``},set(e){e in X.value.paths||(e=``),o.replace(`/plugins/${e}`)}}),f=A(),p=A(``),y=A(),S=A();async function C(){await T(),y.value?.focus()}let w=A(),E=A(!1),D=A(),k=A(!1),j=A(null);I(w,e=>{e&&(E.value=!0)}),I(D,e=>{e&&(k.value=!0)}),I(()=>X.value.paths[s.value],e=>{q.value=e,f.value=r(e.config)},{immediate:!0});let M=u();M.define(`config.tree`,q),M.action(`config.tree.add-plugin`,{hidden:({config:e})=>e.tree.path&&!e.tree.children,action:({config:e})=>V.value=e.tree}),M.action(`config.tree.add-group`,{hidden:({config:e})=>e.tree.path&&!e.tree.children,action:({config:e})=>{j.value=e.tree.path}});function H(e){let t=Math.random().toString(36).slice(2,8);c(`manager/reload`,j.value,`group:${t}`,{$label:e}),o.replace(`/plugins/${t}`),j.value=null}M.action(`config.tree.clone`,{hidden:({config:e})=>!e.tree.path||!!e.tree.children,action:async({config:e})=>{let t=(e.tree.parent.path?e.tree.parent.children:X.value.data.slice(1)).findIndex(t=>t.path===e.tree.path),n=Math.random().toString(36).slice(2,8);c(`manager/unload`,e.tree.parent?.path??``,`${e.tree.name}:${n}`,e.tree.config,t+1),o.replace(`/plugins/${n}`)}}),M.action(`config.tree.manage`,{hidden:({config:e})=>!e.tree.path||!!e.tree.children,action:async({config:e})=>{B.value=e.tree.name}}),M.action(`config.tree.rename`,{disabled:({config:e})=>!e.tree.path,action:({config:e})=>{p.value=e.tree.label||(e.tree.name===`group`?e.tree.path:e.tree.name),D.value=e.tree}}),M.action(`config.tree.remove`,{disabled:({config:e})=>!e.tree.path||U(e.tree),action:({config:e})=>w.value=e.tree});function W(t){let r=l.packages[J(t)]?.runtime.schema;if(!r)return!0;try{return new e(r)(f.value),!0}catch{return a.error(n(`config.view.constraintError`)),!1}}M.action(`config.tree.save`,{shortcut:`ctrl+s`,disabled:e=>!e?.config?.tree||![`config`].includes(o.currentRoute.value?.meta?.activity.id),action:async({config:{tree:e}})=>{let{disabled:t,path:r}=e;if(!(!t&&!W(e.name))){if(!r)return c(`manager/app-reload`,f.value);try{await G(e,t?`unload`:`reload`),a.success(n(t?`config.view.saved`:`config.view.reloaded`))}catch{a.error(n(`config.view.operationError`))}}}}),M.action(`config.tree.toggle`,{disabled:({config:e})=>!e.tree.path||U(e.tree),action:async({config:{tree:e}})=>{let{disabled:t,name:r}=e;if(!(t&&!W(e.name)))try{await G(e,t?`reload`:`unload`),a.success(n(r===`group`?t?`config.view.groupEnabled`:`config.view.groupDisabled`:t?`config.view.pluginEnabled`:`config.view.pluginDisabled`))}catch{a.error(n(`config.view.operationError`))}}});async function G(e,t){return await c(`manager/${t}`,e.parent?.path??``,e.id,f.value)}function K(e,t){k.value=!1,e.label=t,c(`manager/meta`,e.path,{$label:t||null})}return(e,t)=>{let r=N(`k-content`),i=N(`el-button`),a=N(`el-dialog`),o=N(`el-input`),c=N(`k-layout`);return O(),g(c,{menu:`config.tree`,"menu-data":F(q)},{header:L(()=>[F(q).path?F(q).children?(O(),v(m,{key:1},[b(P(F(n)(`config.view.group`,[F(q).label||F(q).id])),1)],64)):(O(),v(m,{key:2},[b(P(F(q).label||F(q).name)+` [`+P(F(q).path)+`] `,1)],64)):(O(),v(m,{key:0},[b(P(F(n)(`config.view.globalSettings`)),1)],64))]),left:L(()=>[x(we,{ref_key:`tree`,ref:S,modelValue:s.value,"onUpdate:modelValue":t[0]||=e=>s.value=e},null,8,[`modelValue`])]),default:L(()=>[(O(),g(r,{class:`plugin-view`,key:s.value},{default:L(()=>[F(q).path?F(q).children?(O(),g(ye,{key:1,modelValue:f.value,"onUpdate:modelValue":t[2]||=e=>f.value=e,current:F(q)},null,8,[`modelValue`,`current`])):(O(),g(be,{key:2,current:F(q),modelValue:f.value,"onUpdate:modelValue":t[3]||=e=>f.value=e},null,8,[`current`,`modelValue`])):(O(),g(me,{key:0,current:F(q),modelValue:f.value,"onUpdate:modelValue":t[1]||=e=>f.value=e},null,8,[`current`,`modelValue`]))]),_:1})),x(a,{modelValue:E.value,"onUpdate:modelValue":t[6]||=e=>E.value=e,title:F(n)(`config.view.removeTitle`),"destroy-on-close":``,onClosed:t[7]||=e=>w.value=null},{footer:L(()=>[x(i,{onClick:t[4]||=e=>E.value=!1},{default:L(()=>[b(P(F(n)(`config.view.cancel`)),1)]),_:1}),x(i,{type:`danger`,onClick:t[5]||=e=>(E.value=!1,F(Q)(w.value),S.value?.activate())},{default:L(()=>[b(P(F(n)(`config.view.confirm`)),1)]),_:1})]),default:L(()=>[w.value?(O(),v(m,{key:0},[b(P(F(n)(w.value.children?`config.view.removeConfirmGroup`:`config.view.removeConfirmPlugin`,[w.value.label||(w.value.children?w.value.path:w.value.name)])),1)],64)):_(`v-if`,!0)]),_:1},8,[`modelValue`,`title`]),x(a,{modelValue:k.value,"onUpdate:modelValue":t[12]||=e=>k.value=e,title:F(n)(`config.view.renameTitle`),"destroy-on-close":``,onOpen:C,onClosed:t[13]||=e=>D.value=null},{footer:L(()=>[x(i,{onClick:t[10]||=e=>k.value=!1},{default:L(()=>[b(P(F(n)(`config.view.cancel`)),1)]),_:1}),x(i,{type:`primary`,onClick:t[11]||=e=>K(D.value,p.value)},{default:L(()=>[b(P(F(n)(`config.view.confirm`)),1)]),_:1})]),default:L(()=>[D.value?(O(),g(o,{key:0,ref_key:`inputEl`,ref:y,modelValue:p.value,"onUpdate:modelValue":t[8]||=e=>p.value=e,onKeydown:t[9]||=R(z(e=>K(D.value,p.value),[`stop`,`prevent`]),[`enter`])},null,8,[`modelValue`])):_(`v-if`,!0)]),_:1},8,[`modelValue`,`title`]),x(a,{"model-value":j.value!==null,"onUpdate:modelValue":t[18]||=e=>j.value=null,title:F(n)(`config.view.createGroupTitle`),"destroy-on-close":``,onOpen:C},{footer:L(()=>[x(i,{onClick:t[16]||=e=>j.value=null},{default:L(()=>[b(P(F(n)(`config.view.cancel`)),1)]),_:1}),x(i,{type:`primary`,onClick:t[17]||=e=>H(p.value)},{default:L(()=>[b(P(F(n)(`config.view.confirm`)),1)]),_:1})]),default:L(()=>[x(o,{ref_key:`inputEl`,ref:y,modelValue:p.value,"onUpdate:modelValue":t[14]||=e=>p.value=e,onKeydown:t[15]||=R(z(e=>H(p.value),[`stop`,`prevent`]),[`enter`])},null,8,[`modelValue`])]),_:1},8,[`model-value`,`title`])]),_:1},8,[`menu-data`])}}}),Ee={class:`title`},De={class:`content`},Oe=[`onClick`],ke=S({__name:`select`,setup(e){let{t}=d(),n=f(),r=A(``),i=A(),a=w(`plugin-select-filter`,e=>!0),o=h(()=>Object.values(l.packages).filter(({name:e,shortname:t})=>e&&t.includes(r.value.toLowerCase())&&a(l.packages[e])));function u(e){let t=V.value.path,n=Math.random().toString(36).slice(2,8);V.value=null,r.value=``,c(`manager/unload`,t,`${e}:${n}`,{}),s.push(`/plugins/${n}`)}return I(V,async e=>{e&&(await T(),await i.value.focus())},{flush:`post`}),(e,a)=>{let s=N(`k-icon`),c=N(`el-input`),d=N(`k-markdown`),f=N(`el-scrollbar`),p=N(`el-dialog`);return F(l).packages?(O(),g(p,{key:0,modelValue:!!F(V),"onUpdate:modelValue":a[1]||=e=>V.value=null,class:`plugin-select`},{header:L(()=>[M(e.$slots,`title`,{packages:o.value},()=>[y(`span`,Ee,P(F(t)(`config.select.title`)),1)]),x(c,{ref_key:`input`,ref:i,modelValue:r.value,"onUpdate:modelValue":a[0]||=e=>r.value=e},{suffix:L(()=>[x(s,{name:`search`})]),_:1},8,[`modelValue`])]),default:L(()=>[M(e.$slots,`tabs`,{packages:o.value}),y(`div`,De,[x(f,null,{default:L(()=>[(O(!0),v(m,null,j(o.value,({name:e,shortname:t,manifest:r})=>(O(),v(`div`,{class:`package`,key:e,onClick:z(e=>u(t),[`stop`])},[y(`h3`,null,P(t),1),x(d,{inline:``,class:`desc`,source:F(n)(r.description)},null,8,[`source`])],8,Oe))),128))]),_:1})])]),_:3},8,[`modelValue`])):_(`v-if`,!0)}}}),Ae={config:{title:`Plugin-Konfigurationen`,menu:{enableGroup:`Gruppe aktivieren`,disableGroup:`Gruppe deaktivieren`,enablePlugin:`Plugin aktivieren`,disablePlugin:`Plugin deaktivieren`,saveConfig:`Konfiguration speichern`,reloadConfig:`Konfiguration neu laden`,rename:`Umbenennen`,removeGroup:`Gruppe entfernen`,removePlugin:`Plugin entfernen`,clone:`Konfiguration klonen`,manage:`Mehrere Konfigurationen verwalten`,addPlugin:`Plugin hinzufügen`,addGroup:`Gruppe hinzufügen`,globalSettings:`Globale Einstellungen`},tree:{group:`Gruppe: {0}`,pending:`Ausstehend`},forks:{workspace:` (Arbeitsbereich)`,count:`Dieses Plugin hat derzeit {0} Konfigurationen.`,none:`Dieses Plugin wurde noch nicht konfiguriert.`,add:`Neue Konfiguration hinzufügen`},modifier:{title:`Filtereinstellungen`},select:{title:`Plugin auswählen`},plugin:{loading:`Plugin-Konfiguration wird geladen...`,failure:`Das Plugin konnte nicht geladen werden, möglicherweise aufgrund eines Problems im Plugin selbst. {0}`,hintSource:`Bitte prüfen Sie den Quellcode des Plugins.`,hintAuthor:`Bitte kontaktieren Sie den Plugin-Autor und melden Sie das Problem.`,dependency:`{0}: {1} ({2})`,dependencyRequired:`Erforderliche Abhängigkeit`,dependencyOptional:`Optionale Abhängigkeit`,service:`{0}: {1} ({2})`,serviceRequired:`Erforderlicher Service`,serviceOptional:`Optionaler Service`,loaded:`geladen`,notLoaded:`nicht geladen`,serviceConflict:`Dieses Plugin wird den Service {0} bereitstellen, aber dieser Service wird bereits von einem anderen Plugin implementiert.`,serviceProvided:`Dieses Plugin stellt den Service {0} bereit.`,serviceFuture:`Dieses Plugin wird nach der Aktivierung den Service {0} bereitstellen.`,notReusable:`Dieses Plugin läuft bereits und ist nicht wiederverwendbar. Eine erneute Aktivierung kann zu unerwarteten Problemen führen.`,multiplePrefix:`Dieses Plugin hat mehrere Konfigurationen.`,multipleAction:`Klicken Sie hier, um sie zu verwalten`,multipleSuffix:``,pages:`Von diesem Plugin bereitgestellte Seiten:`,noSchema:`Dieses Plugin deklariert keine Konfigurationsoptionen, was möglicherweise nicht beabsichtigt ist. {0}`,notInstalled:`Dieses Plugin ist noch nicht installiert.`},view:{globalSettings:`Globale Einstellungen`,group:`Gruppe: {0}`,removeTitle:`Entfernen bestätigen`,removeConfirmGroup:`Möchten Sie die Gruppe {0} wirklich entfernen? Diese Aktion kann nicht rückgängig gemacht werden!`,removeConfirmPlugin:`Möchten Sie das Plugin {0} wirklich entfernen? Diese Aktion kann nicht rückgängig gemacht werden!`,renameTitle:`Umbenennen`,createGroupTitle:`Gruppe erstellen`,cancel:`Abbrechen`,confirm:`OK`,constraintError:`Die aktuelle Konfiguration erfüllt die Einschränkungen nicht. Bitte überprüfen Sie sie!`,saved:`Konfiguration gespeichert.`,reloaded:`Konfiguration neu geladen.`,operationError:`Vorgang fehlgeschlagen. Bitte überprüfen Sie die Protokolle!`,groupEnabled:`Gruppe aktiviert.`,groupDisabled:`Gruppe deaktiviert.`,pluginEnabled:`Plugin aktiviert.`,pluginDisabled:`Plugin deaktiviert.`}}},je={config:{title:`Plugin Configurations`,menu:{enableGroup:`Enable Group`,disableGroup:`Disable Group`,enablePlugin:`Enable Plugin`,disablePlugin:`Disable Plugin`,saveConfig:`Save Configuration`,reloadConfig:`Reload Configuration`,rename:`Rename`,removeGroup:`Remove Group`,removePlugin:`Remove Plugin`,clone:`Clone Configuration`,manage:`Manage Multiple Configurations`,addPlugin:`Add Plugin`,addGroup:`Add Group`,globalSettings:`Global Settings`},tree:{group:`Group: {0}`,pending:`Pending`},forks:{workspace:` (workspace)`,count:`This plugin currently has {0} configurations.`,none:`This plugin has not been configured yet.`,add:`Add New Configuration`},modifier:{title:`Filter Settings`},select:{title:`Select Plugin`},plugin:{loading:`Loading plugin configuration...`,failure:`Failed to load the plugin, which may be caused by the plugin itself. {0}`,hintSource:`Please check the plugin source code.`,hintAuthor:`Please contact the plugin author and report this issue.`,dependency:`{0}: {1} ({2})`,dependencyRequired:`Required dependency`,dependencyOptional:`Optional dependency`,service:`{0}: {1} ({2})`,serviceRequired:`Required service`,serviceOptional:`Optional service`,loaded:`loaded`,notLoaded:`not loaded`,serviceConflict:`This plugin will provide the {0} service, but this service is already implemented by another plugin.`,serviceProvided:`This plugin provides the {0} service.`,serviceFuture:`This plugin will provide the {0} service once enabled.`,notReusable:`This plugin is already running and is not reusable. Enabling it again may cause unexpected problems.`,multiplePrefix:`This plugin has multiple configurations.`,multipleAction:`Click to manage`,multipleSuffix:``,pages:`Pages provided by this plugin:`,noSchema:`This plugin declares no configuration options, which may not be intended. {0}`,notInstalled:`This plugin is not installed yet.`},view:{globalSettings:`Global Settings`,group:`Group: {0}`,removeTitle:`Confirm Removal`,removeConfirmGroup:`Are you sure you want to remove the group {0}? This action cannot be undone!`,removeConfirmPlugin:`Are you sure you want to remove the plugin {0}? This action cannot be undone!`,renameTitle:`Rename`,createGroupTitle:`Create Group`,cancel:`Cancel`,confirm:`OK`,constraintError:`The current configuration does not satisfy the constraints. Please check it!`,saved:`Configuration saved.`,reloaded:`Configuration reloaded.`,operationError:`Operation failed. Please check the logs!`,groupEnabled:`Group enabled.`,groupDisabled:`Group disabled.`,pluginEnabled:`Plugin enabled.`,pluginDisabled:`Plugin disabled.`}}},Me={config:{title:`Configurations des plugins`,menu:{enableGroup:`Activer le groupe`,disableGroup:`Désactiver le groupe`,enablePlugin:`Activer le plugin`,disablePlugin:`Désactiver le plugin`,saveConfig:`Enregistrer la configuration`,reloadConfig:`Recharger la configuration`,rename:`Renommer`,removeGroup:`Supprimer le groupe`,removePlugin:`Supprimer le plugin`,clone:`Cloner la configuration`,manage:`Gérer plusieurs configurations`,addPlugin:`Ajouter un plugin`,addGroup:`Ajouter un groupe`,globalSettings:`Paramètres globaux`},tree:{group:`Groupe : {0}`,pending:`En attente`},forks:{workspace:` (espace de travail)`,count:`Ce plugin possède actuellement {0} configurations.`,none:`Ce plugin n'a pas encore été configuré.`,add:`Ajouter une configuration`},modifier:{title:`Paramètres du filtre`},select:{title:`Sélectionner un plugin`},plugin:{loading:`Chargement de la configuration du plugin...`,failure:`Échec du chargement du plugin, probablement causé par le plugin lui-même. {0}`,hintSource:`Veuillez vérifier le code source du plugin.`,hintAuthor:`Veuillez contacter l'auteur du plugin et signaler ce problème.`,dependency:`{0} : {1} ({2})`,dependencyRequired:`Dépendance requise`,dependencyOptional:`Dépendance facultative`,service:`{0} : {1} ({2})`,serviceRequired:`Service requis`,serviceOptional:`Service facultatif`,loaded:`chargé`,notLoaded:`non chargé`,serviceConflict:`Ce plugin fournira le service {0}, mais ce service est déjà implémenté par un autre plugin.`,serviceProvided:`Ce plugin fournit le service {0}.`,serviceFuture:`Ce plugin fournira le service {0} une fois activé.`,notReusable:`Ce plugin est déjà en cours d'exécution et n'est pas réutilisable. Le réactiver peut causer des problèmes inattendus.`,multiplePrefix:`Ce plugin possède plusieurs configurations.`,multipleAction:`Cliquez pour les gérer`,multipleSuffix:``,pages:`Pages fournies par ce plugin :`,noSchema:`Ce plugin ne déclare aucune option de configuration, ce qui n'est peut-être pas voulu. {0}`,notInstalled:`Ce plugin n'est pas encore installé.`},view:{globalSettings:`Paramètres globaux`,group:`Groupe : {0}`,removeTitle:`Confirmer la suppression`,removeConfirmGroup:`Voulez-vous vraiment supprimer le groupe {0} ? Cette action est irréversible !`,removeConfirmPlugin:`Voulez-vous vraiment supprimer le plugin {0} ? Cette action est irréversible !`,renameTitle:`Renommer`,createGroupTitle:`Créer un groupe`,cancel:`Annuler`,confirm:`OK`,constraintError:`La configuration actuelle ne satisfait pas les contraintes. Veuillez la vérifier !`,saved:`Configuration enregistrée.`,reloaded:`Configuration rechargée.`,operationError:`L'opération a échoué. Veuillez consulter les journaux !`,groupEnabled:`Groupe activé.`,groupDisabled:`Groupe désactivé.`,pluginEnabled:`Plugin activé.`,pluginDisabled:`Plugin désactivé.`}}},Ne={config:{title:`プラグイン設定`,menu:{enableGroup:`グループを有効化`,disableGroup:`グループを無効化`,enablePlugin:`プラグインを有効化`,disablePlugin:`プラグインを無効化`,saveConfig:`設定を保存`,reloadConfig:`設定を再読み込み`,rename:`名前の変更`,removeGroup:`グループを削除`,removePlugin:`プラグインを削除`,clone:`設定を複製`,manage:`複数の設定を管理`,addPlugin:`プラグインを追加`,addGroup:`グループを追加`,globalSettings:`グローバル設定`},tree:{group:`グループ:{0}`,pending:`追加予定`},forks:{workspace:`(ワークスペース)`,count:`このプラグインには現在 {0} 件の設定があります。`,none:`このプラグインはまだ設定されていません。`,add:`新しい設定を追加`},modifier:{title:`フィルター設定`},select:{title:`プラグインを選択`},plugin:{loading:`プラグイン設定を読み込み中……`,failure:`プラグインの読み込みに失敗しました。プラグイン自体の問題の可能性があります。{0}`,hintSource:`プラグインのソースコードを確認してください。`,hintAuthor:`プラグインの作者に連絡してこの問題を報告してください。`,dependency:`{0}:{1}({2})`,dependencyRequired:`必須の依存関係`,dependencyOptional:`任意の依存関係`,service:`{0}:{1}({2})`,serviceRequired:`必須のサービス`,serviceOptional:`任意のサービス`,loaded:`読み込み済み`,notLoaded:`未読み込み`,serviceConflict:`このプラグインは {0} サービスを提供しようとしていますが、このサービスはすでに他のプラグインが実装しています。`,serviceProvided:`このプラグインは {0} サービスを提供しています。`,serviceFuture:`このプラグインは有効化後に {0} サービスを提供します。`,notReusable:`このプラグインはすでに実行中で再利用できません。有効化すると予期しない問題が発生する可能性があります。`,multiplePrefix:`このプラグインには複数の設定があります。`,multipleAction:`クリックして管理`,multipleSuffix:``,pages:`このプラグインが提供するページ:`,noSchema:`このプラグインは設定項目を宣言していません。これは意図した動作ではない可能性があります。{0}`,notInstalled:`このプラグインはまだインストールされていません。`},view:{globalSettings:`グローバル設定`,group:`グループ:{0}`,removeTitle:`削除の確認`,removeConfirmGroup:`本当にグループ {0} を削除しますか?この操作は元に戻せません!`,removeConfirmPlugin:`本当にプラグイン {0} を削除しますか?この操作は元に戻せません!`,renameTitle:`名前の変更`,createGroupTitle:`グループを作成`,cancel:`キャンセル`,confirm:`確定`,constraintError:`現在の設定項目が制約を満たしていません。設定を確認してください!`,saved:`設定を保存しました。`,reloaded:`設定を再読み込みしました。`,operationError:`操作に失敗しました。ログを確認してください!`,groupEnabled:`グループを有効化しました。`,groupDisabled:`グループを無効化しました。`,pluginEnabled:`プラグインを有効化しました。`,pluginDisabled:`プラグインを無効化しました。`}}},Pe={config:{title:`Конфигурации плагинов`,menu:{enableGroup:`Включить группу`,disableGroup:`Отключить группу`,enablePlugin:`Включить плагин`,disablePlugin:`Отключить плагин`,saveConfig:`Сохранить конфигурацию`,reloadConfig:`Перезагрузить конфигурацию`,rename:`Переименовать`,removeGroup:`Удалить группу`,removePlugin:`Удалить плагин`,clone:`Клонировать конфигурацию`,manage:`Управлять несколькими конфигурациями`,addPlugin:`Добавить плагин`,addGroup:`Добавить группу`,globalSettings:`Глобальные настройки`},tree:{group:`Группа: {0}`,pending:`Ожидает добавления`},forks:{workspace:` (рабочая область)`,count:`У этого плагина сейчас {0} конфигураций.`,none:`Этот плагин ещё не настроен.`,add:`Добавить новую конфигурацию`},modifier:{title:`Настройки фильтра`},select:{title:`Выберите плагин`},plugin:{loading:`Загрузка конфигурации плагина...`,failure:`Не удалось загрузить плагин — возможно, проблема в самом плагине. {0}`,hintSource:`Проверьте исходный код плагина.`,hintAuthor:`Свяжитесь с автором плагина и сообщите о проблеме.`,dependency:`{0}: {1} ({2})`,dependencyRequired:`Обязательная зависимость`,dependencyOptional:`Необязательная зависимость`,service:`{0}: {1} ({2})`,serviceRequired:`Обязательный сервис`,serviceOptional:`Необязательный сервис`,loaded:`загружен`,notLoaded:`не загружен`,serviceConflict:`Этот плагин предоставит сервис {0}, но этот сервис уже реализован другим плагином.`,serviceProvided:`Этот плагин предоставляет сервис {0}.`,serviceFuture:`Этот плагин будет предоставлять сервис {0} после включения.`,notReusable:`Этот плагин уже запущен и не может использоваться повторно. Его повторное включение может привести к непредвиденным проблемам.`,multiplePrefix:`У этого плагина несколько конфигураций.`,multipleAction:`Нажмите, чтобы управлять ими`,multipleSuffix:``,pages:`Страницы, предоставляемые этим плагином:`,noSchema:`Этот плагин не объявляет параметров конфигурации, что может быть непреднамеренным. {0}`,notInstalled:`Этот плагин ещё не установлен.`},view:{globalSettings:`Глобальные настройки`,group:`Группа: {0}`,removeTitle:`Подтвердите удаление`,removeConfirmGroup:`Вы действительно хотите удалить группу {0}? Это действие нельзя отменить!`,removeConfirmPlugin:`Вы действительно хотите удалить плагин {0}? Это действие нельзя отменить!`,renameTitle:`Переименовать`,createGroupTitle:`Создать группу`,cancel:`Отмена`,confirm:`ОК`,constraintError:`Текущая конфигурация не удовлетворяет ограничениям. Проверьте её!`,saved:`Конфигурация сохранена.`,reloaded:`Конфигурация перезагружена.`,operationError:`Операция не удалась. Проверьте журналы!`,groupEnabled:`Группа включена.`,groupDisabled:`Группа отключена.`,pluginEnabled:`Плагин включён.`,pluginDisabled:`Плагин отключён.`}}},Fe={config:{title:`插件配置`,menu:{enableGroup:`启用分组`,disableGroup:`停用分组`,enablePlugin:`启用插件`,disablePlugin:`停用插件`,saveConfig:`保存配置`,reloadConfig:`重载配置`,rename:`重命名`,removeGroup:`移除分组`,removePlugin:`移除插件`,clone:`克隆配置`,manage:`管理多份配置`,addPlugin:`添加插件`,addGroup:`添加分组`,globalSettings:`全局设置`},tree:{group:`分组:{0}`,pending:`待添加`},forks:{workspace:` (工作区)`,count:`此插件目前存在 {0} 份配置。`,none:`此插件尚未被配置。`,add:`添加新配置`},modifier:{title:`过滤器设置`},select:{title:`选择插件`},plugin:{loading:`正在加载插件配置……`,failure:`插件加载失败,这可能是插件本身的问题所致。{0}`,hintSource:`请检查插件源代码。`,hintAuthor:`请联系插件作者并反馈此问题。`,dependency:`{0}:{1}({2})`,dependencyRequired:`必需依赖`,dependencyOptional:`可选依赖`,service:`{0}:{1}({2})`,serviceRequired:`必需服务`,serviceOptional:`可选服务`,loaded:`已加载`,notLoaded:`未加载`,serviceConflict:`此插件将会提供 {0} 服务,但此服务已被其他插件实现。`,serviceProvided:`此插件提供了 {0} 服务。`,serviceFuture:`此插件启用后将会提供 {0} 服务。`,notReusable:`此插件已在运行且不可重用,启用可能会导致非预期的问题。`,multiplePrefix:`此插件存在多份配置,`,multipleAction:`点击前往管理`,multipleSuffix:`。`,pages:`此插件提供了页面:`,noSchema:`此插件未声明配置项,这可能并非预期行为。{0}`,notInstalled:`此插件尚未安装。`},view:{globalSettings:`全局设置`,group:`分组:{0}`,removeTitle:`确认移除`,removeConfirmGroup:`确定要移除分组 {0} 吗?此操作不可撤销!`,removeConfirmPlugin:`确定要移除插件 {0} 吗?此操作不可撤销!`,renameTitle:`重命名`,createGroupTitle:`创建分组`,cancel:`取消`,confirm:`确定`,constraintError:`当前配置项不满足约束,请检查配置!`,saved:`配置已保存。`,reloaded:`配置已重载。`,operationError:`操作失败,请检查日志!`,groupEnabled:`分组已启用。`,groupDisabled:`分组已停用。`,pluginEnabled:`插件已启用。`,pluginDisabled:`插件已停用。`}}},Ie={config:{title:`外掛設定`,menu:{enableGroup:`啟用分組`,disableGroup:`停用分組`,enablePlugin:`啟用外掛`,disablePlugin:`停用外掛`,saveConfig:`儲存設定`,reloadConfig:`重新載入設定`,rename:`重新命名`,removeGroup:`移除分組`,removePlugin:`移除外掛`,clone:`複製設定`,manage:`管理多份設定`,addPlugin:`新增外掛`,addGroup:`新增分組`,globalSettings:`全域設定`},tree:{group:`分組:{0}`,pending:`待新增`},forks:{workspace:` (工作區)`,count:`此外掛目前存在 {0} 份設定。`,none:`此外掛尚未被設定。`,add:`新增設定`},modifier:{title:`過濾器設定`},select:{title:`選擇外掛`},plugin:{loading:`正在載入外掛設定……`,failure:`外掛載入失敗,這可能是外掛本身的問題所致。{0}`,hintSource:`請檢查外掛原始碼。`,hintAuthor:`請聯絡外掛作者並回報此問題。`,dependency:`{0}:{1}({2})`,dependencyRequired:`必需相依性`,dependencyOptional:`可選相依性`,service:`{0}:{1}({2})`,serviceRequired:`必需服務`,serviceOptional:`可選服務`,loaded:`已載入`,notLoaded:`未載入`,serviceConflict:`此外掛將會提供 {0} 服務,但此服務已被其他外掛實作。`,serviceProvided:`此外掛提供了 {0} 服務。`,serviceFuture:`此外掛啟用後將會提供 {0} 服務。`,notReusable:`此外掛已在執行且不可重用,啟用可能會導致非預期的問題。`,multiplePrefix:`此外掛存在多份設定,`,multipleAction:`點擊前往管理`,multipleSuffix:`。`,pages:`此外掛提供了頁面:`,noSchema:`此外掛未宣告設定項,這可能並非預期行為。{0}`,notInstalled:`此外掛尚未安裝。`},view:{globalSettings:`全域設定`,group:`分組:{0}`,removeTitle:`確認移除`,removeConfirmGroup:`確定要移除分組 {0} 嗎?此操作不可復原!`,removeConfirmPlugin:`確定要移除外掛 {0} 嗎?此操作不可復原!`,renameTitle:`重新命名`,createGroupTitle:`建立分組`,cancel:`取消`,confirm:`確定`,constraintError:`目前設定項不滿足約束,請檢查設定!`,saved:`設定已儲存。`,reloaded:`設定已重新載入。`,operationError:`操作失敗,請檢查日誌!`,groupEnabled:`分組已啟用。`,groupDisabled:`分組已停用。`,pluginEnabled:`外掛已啟用。`,pluginDisabled:`外掛已停用。`}}},Le={},Re={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function ze(e,t){return O(),v(`svg`,Re,[...t[0]||=[y(`path`,{fill:`currentColor`,d:`M452 60H488C499 60 508 68.95 508 80C508 91.05 499 100 488 100H452V136C452 147 443 156 432 156C420.1 156 412 147 412 136V100H376C364.1 100 356 91.05 356 80C356 68.95 364.1 60 376 60H412V24C412 12.95 420.1 4 432 4C443 4 452 12.95 452 24V60zM280.8 133.1L490.1 222.8C503.4 228.5 512 241.5 512 256C512 270.5 503.4 283.5 490.1 289.2L280.8 378.9C272.1 382.3 264.5 384 256 384C247.5 384 239 382.3 231.2 378.9L21.9 289.2C8.614 283.5 0 270.5 0 256C0 241.5 8.614 228.5 21.9 222.8L231.2 133.1C239 129.7 247.5 128 256 128C264.5 128 272.1 129.7 280.8 133.1V133.1zM66.26 256L250.1 334.8C251.1 335.6 253.1 336 256 336C258 336 260 335.6 261.9 334.8L445.7 256L261.9 177.2C260 176.4 258 176 256 176C253.1 176 251.1 176.4 250.1 177.2L66.26 256zM59.53 350.6C66.15 359.9 65.24 372.3 57.1 380.5L250.1 462.8C251.1 463.6 253.1 464 256 464C258 464 260 463.6 261.9 462.8L452 381.3C447.4 374.4 446.5 365.2 450.5 357.3C456.5 345.4 470.9 340.6 482.7 346.5L491.8 351.1C504.2 357.3 512 369.9 512 383.7C512 398.4 503.3 411.6 489.9 417.3L280.8 506.9C272.1 510.3 264.5 512 256 512C247.5 512 239 510.3 231.2 506.9L22.81 417.6C8.971 411.7 .001 398.1 .001 383C.001 370.9 5.87 359.5 15.76 352.4L26.05 345C36.84 337.3 51.83 339.8 59.53 350.6L59.53 350.6z`},null,-1)]])}var Be=$(Le,[[`render`,ze]]),Ve={},He={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function Ue(e,t){return O(),v(`svg`,He,[...t[0]||=[y(`path`,{fill:`currentColor`,d:`M144,0c13.3,0,24,10.8,24,24v88h-48V24C120,10.8,130.8,0,144,0z M336,0c13.3,0,24,10.8,24,24v88h-48V24
1
+ import{Schema as e,ScopeStatus as t,Service as n,clone as r,icons as i,message as a,root as o,router as s,send as c,store as l,useContext as u,useI18n as d,useI18nText as f,useMenu as p}from"@koishi-ce/client";import{Fragment as m,computed as h,createBlock as g,createCommentVNode as _,createElementBlock as v,createElementVNode as y,createTextVNode as b,createVNode as x,defineComponent as S,h as C,inject as w,nextTick as T,normalizeClass as E,onActivated as D,openBlock as O,provide as k,ref as A,renderList as j,renderSlot as M,resolveComponent as N,toDisplayString as P,unref as F,watch as I,withCtx as L,withKeys as R,withModifiers as z}from"vue";import{useRoute as ee,useRouter as te}from"vue-router";var B=A(),V=A(),H=[`@koishi-ce/plugin-console`,`@koishi-ce/plugin-config`,`@koishi-ce/plugin-server`];function U(e){if(e.name&&H.includes(J(e.name)??``))return!0;if(e.children)return e.children.some(U)}function W(e,t){let n=e?.[t];if(n)return n;if(t.startsWith(`@koishijs/plugin-`))return e?.[`@koishi-ce/plugin-${t.slice(17)}`]}function G(e){function t(e,t){r.has(e)||e!==`console`&&(n.using[e]={required:t})}let n={impl:[],using:{},peer:{}},r=new Set,i=l.packages,a=i?.[e];if(!i||!a)return n;for(let e in a.package.peerDependencies??{}){if(!e.includes(`@koishi-ce/plugin-`)&&!e.includes(`@koishijs/plugin-`)&&!e.includes(`koishi-plugin-`))continue;let t=W(i,e);if(H.includes(t?.name??e))continue;let o=!a.package.peerDependenciesMeta?.[e]?.optional,s=!!t?.runtime?.id;n.peer[e]={required:o,active:s};for(let e of t?.manifest?.service.implements??[])r.add(e)}for(let e of a.manifest.service.implements)e!==`adapter`&&n.impl.push(e);for(let e of a.runtime?.required??[])t(e,!0);for(let e of a.runtime?.optional??[])t(e,!1);return a.runtime?.id&&!a.runtime?.forkable&&(n.warning=!0),a.runtime?.schema||(n.warning=!0),n}var K=h(()=>Object.fromEntries(Object.keys(l.packages??{}).filter(e=>e).map(e=>[e,G(e)]))),q=A();function J(e){if(!e)return e;if(e.startsWith(`./`))return Object.values(l.packages??{}).find(t=>t.paths?.includes(e))?.package?.name;if(e.includes(`/`)){let[t,n]=e.split(`/`);return[`${t}/koishi-plugin-${n}`].find(e=>e in(l.packages||{}))}return[`@koishi-ce/plugin-${e}`,`@koishijs/plugin-${e}`,`koishi-plugin-${e}`].find(e=>e in(l.packages||{}))}var Y=h(()=>{if(q.value)return J(q.value.name)}),ne=h(()=>{let e=K.value[Y.value??``];if(e){if(e.warning&&q.value?.disabled)return`warning`;for(let t in e.using)if(t in(l.services||{})){if(e.impl.includes(t))return`warning`}else if(e.using[t]?.required)return`warning`}});function re(e,t){let n=[];if(!t)return n;for(let r in t){if(r.startsWith(`$`))continue;let i=t[r],a={config:i,parent:e};r.startsWith(`~`)&&(a.disabled=!0,r=r.slice(1)),a.name=r.split(`:`,1)[0]??``,a.id=r,a.path=r.slice(a.name.length+1);let o=i?.$label;o!==void 0&&(a.label=o),r.startsWith(`group:`)&&(a.children=re(a,i)),n.push(a)}return n}var X=h(()=>{let e={name:``,id:``,path:``,label:o.$i18n.t(`config.view.globalSettings`),config:l.config,children:[]},t=[e],n=[],r={},i={"":e};for(let n of re(e,l.config?.plugins))t.push(n),a(n);function a(e){!e.config?.$collapsed&&e.children&&n.push(e.path),(r[e.name]||=[]).push(e.path),i[e.path]=e,e.children?.forEach(a)}return{data:t,forks:r,paths:i,expanded:n}});function Z(e){switch(l.packages?.[J(e.name)??``]?.runtime?.forks?.[e.path]?.status){case t.PENDING:return`pending`;case t.LOADING:return`loading`;case t.ACTIVE:return`active`;case t.FAILED:return`failed`;case t.DISPOSED:return`disposed`;default:return`disabled`}}async function Q(e){let t=e.parent?.path??``;c(`manager/remove`,t,e.id),await s.replace(`/plugins/${t}`)}var ie=[`id`],ae={class:`text-left`},oe={class:`path`},se={class:`text-right`},ce={class:`actions`},le=[`onClick`],ue=[`onClick`],de={class:`left`},fe={class:`right`},pe=S({__name:`forks`,setup(e){let{t}=d(),n=h(()=>B.value?.replace(/(koishi-|^@koishijs\/)plugin-/,``)),r=h(()=>l.packages?.[B.value]);function i(e){return`${e.label?`${e.label} `:``}[${e.path}]`}function a(e){let t=[i(e)];for(;e.parent;)e=e.parent,t.unshift(i(e));return t.shift(),t.join(` > `)}async function o(e){let t=n.value;e||(e=Math.random().toString(36).slice(2,8),c(`manager/unload`,``,`${t}:${e}`,{})),await s.push(`/plugins/${e}`),B.value=null}return(e,i)=>{let s=N(`k-icon`),c=N(`el-button`),l=N(`el-dialog`);return O(),g(l,{"model-value":!!F(B),"onUpdate:modelValue":i[1]||=e=>B.value=null,class:`dialog-config-fork`,"destroy-on-close":``},{header:L(({titleId:e,titleClass:n})=>[y(`span`,{id:e,class:E(n)},P(F(B)+(r.value?.workspace?F(t)(`config.forks.workspace`):``)),11,ie)]),footer:L(()=>[y(`div`,de,[F(X).forks[n.value]?.length?(O(),v(m,{key:0},[b(P(F(t)(`config.forks.count`,[F(X).forks[n.value]?.length])),1)],64)):(O(),v(m,{key:1},[b(P(F(t)(`config.forks.none`)),1)],64))]),y(`div`,fe,[x(c,{onClick:i[0]||=z(e=>o(),[`stop`])},{default:L(()=>[b(P(F(t)(`config.forks.add`)),1)]),_:1})])]),default:L(()=>[y(`table`,null,[(O(!0),v(m,null,j(F(X).forks[n.value],e=>(O(),v(`tr`,{key:e},[y(`td`,ae,[y(`span`,{class:E([`status-light`,F(Z)(F(X).paths[e])])},null,2),y(`span`,oe,P(a(F(X).paths[e])),1)]),y(`td`,se,[y(`span`,ce,[y(`span`,{class:`action`,onClick:z(t=>o(e),[`stop`])},[x(s,{name:`arrow-right`})],8,le),y(`span`,{class:`action`,onClick:z(t=>F(Q)(F(X).paths[e]),[`stop`])},[x(s,{name:`delete`})],8,ue)])])]))),128))])]),_:1},8,[`model-value`])}}}),me=S({__name:`global`,props:{current:{},modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let n=e,r=t,i=h({get:()=>n.modelValue,set:e=>r(`update:modelValue`,e)});return(t,n)=>{let r=N(`k-form`);return O(),g(r,{schema:F(l).packages[``].runtime.schema,initial:e.current.config,modelValue:i.value,"onUpdate:modelValue":n[0]||=e=>i.value=e},null,8,[`schema`,`initial`,`modelValue`])}}}),he={class:`modifier`},ge={class:`k-schema-header`},_e=S({__name:`modifier`,props:{modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let{t:n}=d(),r=e,i=t,a=h({get:()=>r.modelValue?.$filter,set:e=>i(`update:modelValue`,{...r.modelValue,$filter:e})});return(e,t)=>{let r=N(`k-filter`);return O(),v(`div`,he,[y(`h2`,ge,P(F(n)(`config.modifier.title`)),1),x(r,{modelValue:a.value,"onUpdate:modelValue":t[0]||=e=>a.value=e},null,8,[`modelValue`])])}}}),ve=S({__name:`group`,props:{current:{},modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let n=e,r=t,i=h({get:()=>n.modelValue,set:e=>r(`update:modelValue`,e)});return(e,t)=>(O(),g(_e,{modelValue:i.value,"onUpdate:modelValue":t[0]||=e=>i.value=e},null,8,[`modelValue`]))}}),$=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},ye=$(ve,[[`__scopeId`,`data-v-21d6aea8`]]),be=S({__name:`plugin`,props:{current:{},modelValue:{}},emits:[`update:modelValue`],setup(e,{emit:t}){let{t:n}=d(),r=e,i=t,a=u(),o=h({get:()=>r.modelValue,set:e=>i(`update:modelValue`,e)}),s=h(()=>K.value[Y.value]),f=h(()=>l.packages[Y.value]),p=h(()=>f.value.workspace?n(`config.plugin.hintSource`):n(`config.plugin.hintAuthor`));return I(f,e=>{e&&!e.runtime&&c(`config/request-runtime`,e.name)},{immediate:!0}),k(`plugin:name`,Y),k(`plugin:env`,s),k(`manager.settings.local`,f),k(`manager.settings.config`,o),k(`manager.settings.current`,h(()=>r.current)),(t,r)=>{let i=N(`k-comment`),c=N(`k-slot`),u=N(`k-slot-item`),d=N(`k-activity-link`),h=N(`k-markdown`),S=N(`k-form`);return F(Y)?(O(),v(m,{key:0},[f.value.runtime?f.value.runtime.failed?(O(),g(i,{key:1,type:`danger`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.failure`,[p.value])),1)]),_:1})):(O(),g(c,{key:2,name:`plugin-details`},{default:L(()=>[x(u,{order:800},{default:L(()=>[x(c,{name:`plugin-dependency`,single:``},{default:L(()=>[(O(!0),v(m,null,j(s.value.peer,({required:e,active:t},r)=>(O(),g(i,{key:r,type:t?`success`:e?`warning`:`primary`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.dependency`,[F(n)(e?`config.plugin.dependencyRequired`:`config.plugin.dependencyOptional`),r,F(n)(t?`config.plugin.loaded`:`config.plugin.notLoaded`)])),1)]),_:2},1032,[`type`]))),128)),(O(!0),v(m,null,j(s.value.using,({required:e},t)=>(O(),g(i,{key:t,type:t in F(l).services?`success`:e?`warning`:`primary`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.service`,[F(n)(e?`config.plugin.serviceRequired`:`config.plugin.serviceOptional`),t,F(n)(t in F(l).services?`config.plugin.loaded`:`config.plugin.notLoaded`)])),1)]),_:2},1032,[`type`]))),128))]),_:1})]),_:1}),x(u,{order:600},{default:L(()=>[(O(!0),v(m,null,j(s.value.impl,t=>(O(),v(m,{key:t},[t in F(l).services&&e.current.disabled?(O(),g(i,{key:0,type:`warning`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.serviceConflict`,[t])),1)]),_:2},1024)):(O(),g(i,{key:1,type:e.current.disabled?`primary`:`success`},{default:L(()=>[y(`p`,null,P(F(n)(e.current.disabled?`config.plugin.serviceFuture`:`config.plugin.serviceProvided`,[t])),1)]),_:2},1032,[`type`]))],64))),128))]),_:1}),x(u,{order:400},{default:L(()=>[f.value.runtime.id&&!f.value.runtime.forkable&&e.current.disabled?(O(),g(i,{key:0,type:`warning`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.notReusable`)),1)]),_:1})):_(`v-if`,!0),F(X).forks[e.current.name]?.length>1?(O(),g(i,{key:1,type:`primary`},{default:L(()=>[y(`p`,null,[b(P(F(n)(`config.plugin.multiplePrefix`)),1),y(`span`,{class:`k-link`,onClick:r[0]||=z(e=>B.value=F(Y),[`stop`])},P(F(n)(`config.plugin.multipleAction`)),1),b(P(F(n)(`config.plugin.multipleSuffix`)),1)])]),_:1})):_(`v-if`,!0)]),_:1}),x(u,{order:300},{default:L(()=>[(O(!0),v(m,null,j(F(a).$router.pages,(t,r)=>(O(),v(m,{key:r},[t.ctx.extension?.paths.includes(e.current.path)&&!t.disabled()?(O(),g(i,{key:0,type:`success`},{default:L(()=>[y(`p`,null,[y(`span`,null,P(F(n)(`config.plugin.pages`)),1),x(d,{id:t.id},null,8,[`id`])])]),_:2},1024)):_(`v-if`,!0)],64))),128))]),_:1}),f.value.runtime?.usage?(O(),g(u,{key:0,order:-200},{default:L(()=>[x(h,{unsafe:``,class:`usage`,source:f.value.runtime?.usage},null,8,[`source`])]),_:1})):_(`v-if`,!0),x(u,{order:-600},{default:L(()=>[f.value.runtime.filter===!1?_(`v-if`,!0):(O(),g(_e,{key:0,modelValue:o.value,"onUpdate:modelValue":r[1]||=e=>o.value=e},null,8,[`modelValue`]))]),_:1}),x(u,{order:-1e3},{default:L(()=>[f.value.runtime.schema?(O(),g(S,{key:1,schema:f.value.runtime.schema,initial:e.current.config,modelValue:o.value,"onUpdate:modelValue":r[2]||=e=>o.value=e},{hint:L(()=>[b(P(p.value),1)]),_:1},8,[`schema`,`initial`,`modelValue`])):(O(),g(i,{key:0,type:`warning`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.noSchema`,[p.value])),1)]),_:1}))]),_:1})]),_:1})):(O(),g(i,{key:0},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.loading`)),1)]),_:1}))],64)):(O(),g(c,{key:1,name:`plugin-missing`,single:``},{default:L(()=>[x(i,{type:`danger`},{default:L(()=>[y(`p`,null,P(F(n)(`config.plugin.notInstalled`)),1)]),_:1})]),_:1}))}}}),xe={class:`search`},Se=[`title`],Ce={class:`right`},we=S({__name:`tree`,props:{modelValue:{}},emits:[`update:modelValue`],setup(e,{expose:t,emit:n}){let{t:r}=d(),i=e,a=ee(),o=p(`config.tree`),s=n,l=A(null),u=A(null),f=A(``);function m(e,t){return t.name.toLowerCase().includes(f.value.toLowerCase())}let h=A(!0);async function b(){await T();let e=l.value?.$el,t=e?.querySelector(`.el-tree-node.is-active`);!t||!t.offsetTop&&a.path.slice(9)||l.value.setScrollTop(t.offsetTop-(e.offsetHeight-t.offsetHeight)/2)}t({activate:b}),D(async()=>{b(),h.value=!1});function S(e){e&&!h.value&&b()}function C(e){return e.data.name===`group`?r(`config.tree.group`,[e.label||e.data.path]):e.label||e.data.name||r(`config.tree.pending`)}function w(e){return e.data.path!==``}function k(e,t,n){return n===`inner`?t.data.id.startsWith(`group:`):t.data.path!==``||n===`next`}function j(e,t,n,r){s(`update:modelValue`,e.path),window.dispatchEvent(new MouseEvent(r.type,r))}function M(e,t,n){c(`manager/meta`,e.path,{$collapsed:null})}function R(e,t,n){c(`manager/meta`,e.path,{$collapsed:!0})}function z(e,t,n,r){let i=n===`inner`?t:t.parent,a=i.childNodes.findIndex(t=>t.data.path===e.data.path);i.data.path||--a,c(`manager/teleport`,e.data.parent?.path??``,e.data.id,i.data.path,a)}function te(e){let t=[];return e.children&&t.push(`is-group`),!e.children&&!J(e.name)&&t.push(`is-disabled`),e.path===i.modelValue&&t.push(`is-active`),t.join(` `)}return I(f,e=>{u.value.filter(e)}),(e,t)=>{let n=N(`k-icon`),r=N(`el-input`),i=N(`el-tree`),a=N(`el-scrollbar`);return O(),g(a,{class:`plugin-tree`,ref_key:`root`,ref:l},{default:L(()=>[y(`div`,xe,[x(r,{modelValue:f.value,"onUpdate:modelValue":t[0]||=e=>f.value=e},{suffix:L(()=>[x(n,{name:`search`})]),_:1},8,[`modelValue`])]),x(i,{ref_key:`tree`,ref:u,"node-key":`path`,data:F(X).data,draggable:!0,"auto-expand-parent":!1,"default-expanded-keys":F(X).expanded,"expand-on-click-node":!1,"filter-node-method":m,props:{class:te},"allow-drag":w,"allow-drop":k,onNodeClick:j,onNodeContextmenu:F(o),onNodeDrop:z,onNodeExpand:M,onNodeCollapse:R},{default:L(({node:e})=>[y(`div`,{class:`item`,ref:S},[y(`div`,{class:`label`,title:C(e)},P(C(e)),9,Se),y(`div`,Ce,[F(J)(e.data.name)?(O(),v(`span`,{key:0,class:E([`status-light`,F(Z)(e.data)])},null,2)):_(`v-if`,!0)])],512)]),_:1},8,[`data`,`default-expanded-keys`,`props`,`onNodeContextmenu`])]),_:1},512)}}}),Te=S({__name:`index`,setup(t){let{t:n}=d(),i=ee(),o=te(),s=h({get(){let e=i.path.slice(9);return e in X.value.paths?e:``},set(e){e in X.value.paths||(e=``),o.replace(`/plugins/${e}`)}}),f=A(),p=A(``),y=A(),S=A();async function C(){await T(),y.value?.focus()}let w=A(),E=A(!1),D=A(),k=A(!1),j=A(null);I(w,e=>{e&&(E.value=!0)}),I(D,e=>{e&&(k.value=!0)}),I(()=>X.value.paths[s.value],e=>{q.value=e,f.value=r(e.config)},{immediate:!0});let M=u();M.define(`config.tree`,q),M.action(`config.tree.add-plugin`,{hidden:({config:e})=>e.tree.path&&!e.tree.children,action:({config:e})=>V.value=e.tree}),M.action(`config.tree.add-group`,{hidden:({config:e})=>e.tree.path&&!e.tree.children,action:({config:e})=>{j.value=e.tree.path}});function H(e){let t=Math.random().toString(36).slice(2,8);c(`manager/reload`,j.value,`group:${t}`,{$label:e}),o.replace(`/plugins/${t}`),j.value=null}M.action(`config.tree.clone`,{hidden:({config:e})=>!e.tree.path||!!e.tree.children,action:async({config:e})=>{let t=(e.tree.parent.path?e.tree.parent.children:X.value.data.slice(1)).findIndex(t=>t.path===e.tree.path),n=Math.random().toString(36).slice(2,8);c(`manager/unload`,e.tree.parent?.path??``,`${e.tree.name}:${n}`,e.tree.config,t+1),o.replace(`/plugins/${n}`)}}),M.action(`config.tree.manage`,{hidden:({config:e})=>!e.tree.path||!!e.tree.children,action:async({config:e})=>{B.value=e.tree.name}}),M.action(`config.tree.rename`,{disabled:({config:e})=>!e.tree.path,action:({config:e})=>{p.value=e.tree.label||(e.tree.name===`group`?e.tree.path:e.tree.name),D.value=e.tree}}),M.action(`config.tree.remove`,{disabled:({config:e})=>!e.tree.path||U(e.tree),action:({config:e})=>w.value=e.tree});function W(t){let r=l.packages[J(t)]?.runtime.schema;if(!r)return!0;try{return new e(r)(f.value),!0}catch{return a.error(n(`config.view.constraintError`)),!1}}M.action(`config.tree.save`,{shortcut:`ctrl+s`,disabled:e=>!e?.config?.tree||![`config`].includes(o.currentRoute.value?.meta?.activity.id),action:async({config:{tree:e}})=>{let{disabled:t,path:r}=e;if(t||W(e.name)){if(!r)return c(`manager/app-reload`,f.value);try{await G(e,t?`unload`:`reload`),a.success(n(t?`config.view.saved`:`config.view.reloaded`))}catch{a.error(n(`config.view.operationError`))}}}}),M.action(`config.tree.toggle`,{disabled:({config:e})=>!e.tree.path||U(e.tree),action:async({config:{tree:e}})=>{let{disabled:t,name:r}=e;if(!t||W(e.name))try{await G(e,t?`reload`:`unload`),a.success(n(r===`group`?t?`config.view.groupEnabled`:`config.view.groupDisabled`:t?`config.view.pluginEnabled`:`config.view.pluginDisabled`))}catch{a.error(n(`config.view.operationError`))}}});async function G(e,t){return await c(`manager/${t}`,e.parent?.path??``,e.id,f.value)}function K(e,t){k.value=!1,e.label=t,c(`manager/meta`,e.path,{$label:t||null})}return(e,t)=>{let r=N(`k-content`),i=N(`el-button`),a=N(`el-dialog`),o=N(`el-input`),c=N(`k-layout`);return O(),g(c,{menu:`config.tree`,"menu-data":F(q)},{header:L(()=>[F(q).path?F(q).children?(O(),v(m,{key:1},[b(P(F(n)(`config.view.group`,[F(q).label||F(q).id])),1)],64)):(O(),v(m,{key:2},[b(P(F(q).label||F(q).name)+` [`+P(F(q).path)+`] `,1)],64)):(O(),v(m,{key:0},[b(P(F(n)(`config.view.globalSettings`)),1)],64))]),left:L(()=>[x(we,{ref_key:`tree`,ref:S,modelValue:s.value,"onUpdate:modelValue":t[0]||=e=>s.value=e},null,8,[`modelValue`])]),default:L(()=>[(O(),g(r,{class:`plugin-view`,key:s.value},{default:L(()=>[F(q).path?F(q).children?(O(),g(ye,{key:1,modelValue:f.value,"onUpdate:modelValue":t[2]||=e=>f.value=e,current:F(q)},null,8,[`modelValue`,`current`])):(O(),g(be,{key:2,current:F(q),modelValue:f.value,"onUpdate:modelValue":t[3]||=e=>f.value=e},null,8,[`current`,`modelValue`])):(O(),g(me,{key:0,current:F(q),modelValue:f.value,"onUpdate:modelValue":t[1]||=e=>f.value=e},null,8,[`current`,`modelValue`]))]),_:1})),x(a,{modelValue:E.value,"onUpdate:modelValue":t[6]||=e=>E.value=e,title:F(n)(`config.view.removeTitle`),"destroy-on-close":``,onClosed:t[7]||=e=>w.value=null},{footer:L(()=>[x(i,{onClick:t[4]||=e=>E.value=!1},{default:L(()=>[b(P(F(n)(`config.view.cancel`)),1)]),_:1}),x(i,{type:`danger`,onClick:t[5]||=e=>(E.value=!1,F(Q)(w.value),S.value?.activate())},{default:L(()=>[b(P(F(n)(`config.view.confirm`)),1)]),_:1})]),default:L(()=>[w.value?(O(),v(m,{key:0},[b(P(F(n)(w.value.children?`config.view.removeConfirmGroup`:`config.view.removeConfirmPlugin`,[w.value.label||(w.value.children?w.value.path:w.value.name)])),1)],64)):_(`v-if`,!0)]),_:1},8,[`modelValue`,`title`]),x(a,{modelValue:k.value,"onUpdate:modelValue":t[12]||=e=>k.value=e,title:F(n)(`config.view.renameTitle`),"destroy-on-close":``,onOpen:C,onClosed:t[13]||=e=>D.value=null},{footer:L(()=>[x(i,{onClick:t[10]||=e=>k.value=!1},{default:L(()=>[b(P(F(n)(`config.view.cancel`)),1)]),_:1}),x(i,{type:`primary`,onClick:t[11]||=e=>K(D.value,p.value)},{default:L(()=>[b(P(F(n)(`config.view.confirm`)),1)]),_:1})]),default:L(()=>[D.value?(O(),g(o,{key:0,ref_key:`inputEl`,ref:y,modelValue:p.value,"onUpdate:modelValue":t[8]||=e=>p.value=e,onKeydown:t[9]||=R(z(e=>K(D.value,p.value),[`stop`,`prevent`]),[`enter`])},null,8,[`modelValue`])):_(`v-if`,!0)]),_:1},8,[`modelValue`,`title`]),x(a,{"model-value":j.value!==null,"onUpdate:modelValue":t[18]||=e=>j.value=null,title:F(n)(`config.view.createGroupTitle`),"destroy-on-close":``,onOpen:C},{footer:L(()=>[x(i,{onClick:t[16]||=e=>j.value=null},{default:L(()=>[b(P(F(n)(`config.view.cancel`)),1)]),_:1}),x(i,{type:`primary`,onClick:t[17]||=e=>H(p.value)},{default:L(()=>[b(P(F(n)(`config.view.confirm`)),1)]),_:1})]),default:L(()=>[x(o,{ref_key:`inputEl`,ref:y,modelValue:p.value,"onUpdate:modelValue":t[14]||=e=>p.value=e,onKeydown:t[15]||=R(z(e=>H(p.value),[`stop`,`prevent`]),[`enter`])},null,8,[`modelValue`])]),_:1},8,[`model-value`,`title`])]),_:1},8,[`menu-data`])}}}),Ee={class:`title`},De={class:`content`},Oe=[`onClick`],ke=S({__name:`select`,setup(e){let{t}=d(),n=f(),r=A(``),i=A(),a=w(`plugin-select-filter`,e=>!0),o=h(()=>Object.values(l.packages).filter(({name:e,shortname:t})=>e&&t.includes(r.value.toLowerCase())&&a(l.packages[e])));function u(e){let t=V.value.path,n=Math.random().toString(36).slice(2,8);V.value=null,r.value=``;let i=Object.values(l.packages??{}).find(t=>t.shortname===e)?.paths?.[0]??e;c(`manager/unload`,t,`${i}:${n}`,{}),s.push(`/plugins/${n}`)}return I(V,async e=>{e&&(await T(),await i.value.focus())},{flush:`post`}),(e,a)=>{let s=N(`k-icon`),c=N(`el-input`),d=N(`k-markdown`),f=N(`el-scrollbar`),p=N(`el-dialog`);return F(l).packages?(O(),g(p,{key:0,modelValue:!!F(V),"onUpdate:modelValue":a[1]||=e=>V.value=null,class:`plugin-select`},{header:L(()=>[M(e.$slots,`title`,{packages:o.value},()=>[y(`span`,Ee,P(F(t)(`config.select.title`)),1)]),x(c,{ref_key:`input`,ref:i,modelValue:r.value,"onUpdate:modelValue":a[0]||=e=>r.value=e},{suffix:L(()=>[x(s,{name:`search`})]),_:1},8,[`modelValue`])]),default:L(()=>[M(e.$slots,`tabs`,{packages:o.value}),y(`div`,De,[x(f,null,{default:L(()=>[(O(!0),v(m,null,j(o.value,({name:e,shortname:t,manifest:r})=>(O(),v(`div`,{class:`package`,key:e,onClick:z(e=>u(t),[`stop`])},[y(`h3`,null,P(t),1),x(d,{inline:``,class:`desc`,source:F(n)(r.description)},null,8,[`source`])],8,Oe))),128))]),_:1})])]),_:3},8,[`modelValue`])):_(`v-if`,!0)}}}),Ae={config:{title:`Plugin-Konfigurationen`,menu:{enableGroup:`Gruppe aktivieren`,disableGroup:`Gruppe deaktivieren`,enablePlugin:`Plugin aktivieren`,disablePlugin:`Plugin deaktivieren`,saveConfig:`Konfiguration speichern`,reloadConfig:`Konfiguration neu laden`,rename:`Umbenennen`,removeGroup:`Gruppe entfernen`,removePlugin:`Plugin entfernen`,clone:`Konfiguration klonen`,manage:`Mehrere Konfigurationen verwalten`,addPlugin:`Plugin hinzufügen`,addGroup:`Gruppe hinzufügen`,globalSettings:`Globale Einstellungen`},tree:{group:`Gruppe: {0}`,pending:`Ausstehend`},forks:{workspace:` (Arbeitsbereich)`,count:`Dieses Plugin hat derzeit {0} Konfigurationen.`,none:`Dieses Plugin wurde noch nicht konfiguriert.`,add:`Neue Konfiguration hinzufügen`},modifier:{title:`Filtereinstellungen`},select:{title:`Plugin auswählen`},plugin:{loading:`Plugin-Konfiguration wird geladen...`,failure:`Das Plugin konnte nicht geladen werden, möglicherweise aufgrund eines Problems im Plugin selbst. {0}`,hintSource:`Bitte prüfen Sie den Quellcode des Plugins.`,hintAuthor:`Bitte kontaktieren Sie den Plugin-Autor und melden Sie das Problem.`,dependency:`{0}: {1} ({2})`,dependencyRequired:`Erforderliche Abhängigkeit`,dependencyOptional:`Optionale Abhängigkeit`,service:`{0}: {1} ({2})`,serviceRequired:`Erforderlicher Service`,serviceOptional:`Optionaler Service`,loaded:`geladen`,notLoaded:`nicht geladen`,serviceConflict:`Dieses Plugin wird den Service {0} bereitstellen, aber dieser Service wird bereits von einem anderen Plugin implementiert.`,serviceProvided:`Dieses Plugin stellt den Service {0} bereit.`,serviceFuture:`Dieses Plugin wird nach der Aktivierung den Service {0} bereitstellen.`,notReusable:`Dieses Plugin läuft bereits und ist nicht wiederverwendbar. Eine erneute Aktivierung kann zu unerwarteten Problemen führen.`,multiplePrefix:`Dieses Plugin hat mehrere Konfigurationen.`,multipleAction:`Klicken Sie hier, um sie zu verwalten`,multipleSuffix:``,pages:`Von diesem Plugin bereitgestellte Seiten:`,noSchema:`Dieses Plugin deklariert keine Konfigurationsoptionen, was möglicherweise nicht beabsichtigt ist. {0}`,notInstalled:`Dieses Plugin ist noch nicht installiert.`},view:{globalSettings:`Globale Einstellungen`,group:`Gruppe: {0}`,removeTitle:`Entfernen bestätigen`,removeConfirmGroup:`Möchten Sie die Gruppe {0} wirklich entfernen? Diese Aktion kann nicht rückgängig gemacht werden!`,removeConfirmPlugin:`Möchten Sie das Plugin {0} wirklich entfernen? Diese Aktion kann nicht rückgängig gemacht werden!`,renameTitle:`Umbenennen`,createGroupTitle:`Gruppe erstellen`,cancel:`Abbrechen`,confirm:`OK`,constraintError:`Die aktuelle Konfiguration erfüllt die Einschränkungen nicht. Bitte überprüfen Sie sie!`,saved:`Konfiguration gespeichert.`,reloaded:`Konfiguration neu geladen.`,operationError:`Vorgang fehlgeschlagen. Bitte überprüfen Sie die Protokolle!`,groupEnabled:`Gruppe aktiviert.`,groupDisabled:`Gruppe deaktiviert.`,pluginEnabled:`Plugin aktiviert.`,pluginDisabled:`Plugin deaktiviert.`}}},je={config:{title:`Plugin Configurations`,menu:{enableGroup:`Enable Group`,disableGroup:`Disable Group`,enablePlugin:`Enable Plugin`,disablePlugin:`Disable Plugin`,saveConfig:`Save Configuration`,reloadConfig:`Reload Configuration`,rename:`Rename`,removeGroup:`Remove Group`,removePlugin:`Remove Plugin`,clone:`Clone Configuration`,manage:`Manage Multiple Configurations`,addPlugin:`Add Plugin`,addGroup:`Add Group`,globalSettings:`Global Settings`},tree:{group:`Group: {0}`,pending:`Pending`},forks:{workspace:` (workspace)`,count:`This plugin currently has {0} configurations.`,none:`This plugin has not been configured yet.`,add:`Add New Configuration`},modifier:{title:`Filter Settings`},select:{title:`Select Plugin`},plugin:{loading:`Loading plugin configuration...`,failure:`Failed to load the plugin, which may be caused by the plugin itself. {0}`,hintSource:`Please check the plugin source code.`,hintAuthor:`Please contact the plugin author and report this issue.`,dependency:`{0}: {1} ({2})`,dependencyRequired:`Required dependency`,dependencyOptional:`Optional dependency`,service:`{0}: {1} ({2})`,serviceRequired:`Required service`,serviceOptional:`Optional service`,loaded:`loaded`,notLoaded:`not loaded`,serviceConflict:`This plugin will provide the {0} service, but this service is already implemented by another plugin.`,serviceProvided:`This plugin provides the {0} service.`,serviceFuture:`This plugin will provide the {0} service once enabled.`,notReusable:`This plugin is already running and is not reusable. Enabling it again may cause unexpected problems.`,multiplePrefix:`This plugin has multiple configurations.`,multipleAction:`Click to manage`,multipleSuffix:``,pages:`Pages provided by this plugin:`,noSchema:`This plugin declares no configuration options, which may not be intended. {0}`,notInstalled:`This plugin is not installed yet.`},view:{globalSettings:`Global Settings`,group:`Group: {0}`,removeTitle:`Confirm Removal`,removeConfirmGroup:`Are you sure you want to remove the group {0}? This action cannot be undone!`,removeConfirmPlugin:`Are you sure you want to remove the plugin {0}? This action cannot be undone!`,renameTitle:`Rename`,createGroupTitle:`Create Group`,cancel:`Cancel`,confirm:`OK`,constraintError:`The current configuration does not satisfy the constraints. Please check it!`,saved:`Configuration saved.`,reloaded:`Configuration reloaded.`,operationError:`Operation failed. Please check the logs!`,groupEnabled:`Group enabled.`,groupDisabled:`Group disabled.`,pluginEnabled:`Plugin enabled.`,pluginDisabled:`Plugin disabled.`}}},Me={config:{title:`Configurations des plugins`,menu:{enableGroup:`Activer le groupe`,disableGroup:`Désactiver le groupe`,enablePlugin:`Activer le plugin`,disablePlugin:`Désactiver le plugin`,saveConfig:`Enregistrer la configuration`,reloadConfig:`Recharger la configuration`,rename:`Renommer`,removeGroup:`Supprimer le groupe`,removePlugin:`Supprimer le plugin`,clone:`Cloner la configuration`,manage:`Gérer plusieurs configurations`,addPlugin:`Ajouter un plugin`,addGroup:`Ajouter un groupe`,globalSettings:`Paramètres globaux`},tree:{group:`Groupe : {0}`,pending:`En attente`},forks:{workspace:` (espace de travail)`,count:`Ce plugin possède actuellement {0} configurations.`,none:`Ce plugin n'a pas encore été configuré.`,add:`Ajouter une configuration`},modifier:{title:`Paramètres du filtre`},select:{title:`Sélectionner un plugin`},plugin:{loading:`Chargement de la configuration du plugin...`,failure:`Échec du chargement du plugin, probablement causé par le plugin lui-même. {0}`,hintSource:`Veuillez vérifier le code source du plugin.`,hintAuthor:`Veuillez contacter l'auteur du plugin et signaler ce problème.`,dependency:`{0} : {1} ({2})`,dependencyRequired:`Dépendance requise`,dependencyOptional:`Dépendance facultative`,service:`{0} : {1} ({2})`,serviceRequired:`Service requis`,serviceOptional:`Service facultatif`,loaded:`chargé`,notLoaded:`non chargé`,serviceConflict:`Ce plugin fournira le service {0}, mais ce service est déjà implémenté par un autre plugin.`,serviceProvided:`Ce plugin fournit le service {0}.`,serviceFuture:`Ce plugin fournira le service {0} une fois activé.`,notReusable:`Ce plugin est déjà en cours d'exécution et n'est pas réutilisable. Le réactiver peut causer des problèmes inattendus.`,multiplePrefix:`Ce plugin possède plusieurs configurations.`,multipleAction:`Cliquez pour les gérer`,multipleSuffix:``,pages:`Pages fournies par ce plugin :`,noSchema:`Ce plugin ne déclare aucune option de configuration, ce qui n'est peut-être pas voulu. {0}`,notInstalled:`Ce plugin n'est pas encore installé.`},view:{globalSettings:`Paramètres globaux`,group:`Groupe : {0}`,removeTitle:`Confirmer la suppression`,removeConfirmGroup:`Voulez-vous vraiment supprimer le groupe {0} ? Cette action est irréversible !`,removeConfirmPlugin:`Voulez-vous vraiment supprimer le plugin {0} ? Cette action est irréversible !`,renameTitle:`Renommer`,createGroupTitle:`Créer un groupe`,cancel:`Annuler`,confirm:`OK`,constraintError:`La configuration actuelle ne satisfait pas les contraintes. Veuillez la vérifier !`,saved:`Configuration enregistrée.`,reloaded:`Configuration rechargée.`,operationError:`L'opération a échoué. Veuillez consulter les journaux !`,groupEnabled:`Groupe activé.`,groupDisabled:`Groupe désactivé.`,pluginEnabled:`Plugin activé.`,pluginDisabled:`Plugin désactivé.`}}},Ne={config:{title:`プラグイン設定`,menu:{enableGroup:`グループを有効化`,disableGroup:`グループを無効化`,enablePlugin:`プラグインを有効化`,disablePlugin:`プラグインを無効化`,saveConfig:`設定を保存`,reloadConfig:`設定を再読み込み`,rename:`名前の変更`,removeGroup:`グループを削除`,removePlugin:`プラグインを削除`,clone:`設定を複製`,manage:`複数の設定を管理`,addPlugin:`プラグインを追加`,addGroup:`グループを追加`,globalSettings:`グローバル設定`},tree:{group:`グループ:{0}`,pending:`追加予定`},forks:{workspace:`(ワークスペース)`,count:`このプラグインには現在 {0} 件の設定があります。`,none:`このプラグインはまだ設定されていません。`,add:`新しい設定を追加`},modifier:{title:`フィルター設定`},select:{title:`プラグインを選択`},plugin:{loading:`プラグイン設定を読み込み中……`,failure:`プラグインの読み込みに失敗しました。プラグイン自体の問題の可能性があります。{0}`,hintSource:`プラグインのソースコードを確認してください。`,hintAuthor:`プラグインの作者に連絡してこの問題を報告してください。`,dependency:`{0}:{1}({2})`,dependencyRequired:`必須の依存関係`,dependencyOptional:`任意の依存関係`,service:`{0}:{1}({2})`,serviceRequired:`必須のサービス`,serviceOptional:`任意のサービス`,loaded:`読み込み済み`,notLoaded:`未読み込み`,serviceConflict:`このプラグインは {0} サービスを提供しようとしていますが、このサービスはすでに他のプラグインが実装しています。`,serviceProvided:`このプラグインは {0} サービスを提供しています。`,serviceFuture:`このプラグインは有効化後に {0} サービスを提供します。`,notReusable:`このプラグインはすでに実行中で再利用できません。有効化すると予期しない問題が発生する可能性があります。`,multiplePrefix:`このプラグインには複数の設定があります。`,multipleAction:`クリックして管理`,multipleSuffix:``,pages:`このプラグインが提供するページ:`,noSchema:`このプラグインは設定項目を宣言していません。これは意図した動作ではない可能性があります。{0}`,notInstalled:`このプラグインはまだインストールされていません。`},view:{globalSettings:`グローバル設定`,group:`グループ:{0}`,removeTitle:`削除の確認`,removeConfirmGroup:`本当にグループ {0} を削除しますか?この操作は元に戻せません!`,removeConfirmPlugin:`本当にプラグイン {0} を削除しますか?この操作は元に戻せません!`,renameTitle:`名前の変更`,createGroupTitle:`グループを作成`,cancel:`キャンセル`,confirm:`確定`,constraintError:`現在の設定項目が制約を満たしていません。設定を確認してください!`,saved:`設定を保存しました。`,reloaded:`設定を再読み込みしました。`,operationError:`操作に失敗しました。ログを確認してください!`,groupEnabled:`グループを有効化しました。`,groupDisabled:`グループを無効化しました。`,pluginEnabled:`プラグインを有効化しました。`,pluginDisabled:`プラグインを無効化しました。`}}},Pe={config:{title:`Конфигурации плагинов`,menu:{enableGroup:`Включить группу`,disableGroup:`Отключить группу`,enablePlugin:`Включить плагин`,disablePlugin:`Отключить плагин`,saveConfig:`Сохранить конфигурацию`,reloadConfig:`Перезагрузить конфигурацию`,rename:`Переименовать`,removeGroup:`Удалить группу`,removePlugin:`Удалить плагин`,clone:`Клонировать конфигурацию`,manage:`Управлять несколькими конфигурациями`,addPlugin:`Добавить плагин`,addGroup:`Добавить группу`,globalSettings:`Глобальные настройки`},tree:{group:`Группа: {0}`,pending:`Ожидает добавления`},forks:{workspace:` (рабочая область)`,count:`У этого плагина сейчас {0} конфигураций.`,none:`Этот плагин ещё не настроен.`,add:`Добавить новую конфигурацию`},modifier:{title:`Настройки фильтра`},select:{title:`Выберите плагин`},plugin:{loading:`Загрузка конфигурации плагина...`,failure:`Не удалось загрузить плагин — возможно, проблема в самом плагине. {0}`,hintSource:`Проверьте исходный код плагина.`,hintAuthor:`Свяжитесь с автором плагина и сообщите о проблеме.`,dependency:`{0}: {1} ({2})`,dependencyRequired:`Обязательная зависимость`,dependencyOptional:`Необязательная зависимость`,service:`{0}: {1} ({2})`,serviceRequired:`Обязательный сервис`,serviceOptional:`Необязательный сервис`,loaded:`загружен`,notLoaded:`не загружен`,serviceConflict:`Этот плагин предоставит сервис {0}, но этот сервис уже реализован другим плагином.`,serviceProvided:`Этот плагин предоставляет сервис {0}.`,serviceFuture:`Этот плагин будет предоставлять сервис {0} после включения.`,notReusable:`Этот плагин уже запущен и не может использоваться повторно. Его повторное включение может привести к непредвиденным проблемам.`,multiplePrefix:`У этого плагина несколько конфигураций.`,multipleAction:`Нажмите, чтобы управлять ими`,multipleSuffix:``,pages:`Страницы, предоставляемые этим плагином:`,noSchema:`Этот плагин не объявляет параметров конфигурации, что может быть непреднамеренным. {0}`,notInstalled:`Этот плагин ещё не установлен.`},view:{globalSettings:`Глобальные настройки`,group:`Группа: {0}`,removeTitle:`Подтвердите удаление`,removeConfirmGroup:`Вы действительно хотите удалить группу {0}? Это действие нельзя отменить!`,removeConfirmPlugin:`Вы действительно хотите удалить плагин {0}? Это действие нельзя отменить!`,renameTitle:`Переименовать`,createGroupTitle:`Создать группу`,cancel:`Отмена`,confirm:`ОК`,constraintError:`Текущая конфигурация не удовлетворяет ограничениям. Проверьте её!`,saved:`Конфигурация сохранена.`,reloaded:`Конфигурация перезагружена.`,operationError:`Операция не удалась. Проверьте журналы!`,groupEnabled:`Группа включена.`,groupDisabled:`Группа отключена.`,pluginEnabled:`Плагин включён.`,pluginDisabled:`Плагин отключён.`}}},Fe={config:{title:`插件配置`,menu:{enableGroup:`启用分组`,disableGroup:`停用分组`,enablePlugin:`启用插件`,disablePlugin:`停用插件`,saveConfig:`保存配置`,reloadConfig:`重载配置`,rename:`重命名`,removeGroup:`移除分组`,removePlugin:`移除插件`,clone:`克隆配置`,manage:`管理多份配置`,addPlugin:`添加插件`,addGroup:`添加分组`,globalSettings:`全局设置`},tree:{group:`分组:{0}`,pending:`待添加`},forks:{workspace:` (工作区)`,count:`此插件目前存在 {0} 份配置。`,none:`此插件尚未被配置。`,add:`添加新配置`},modifier:{title:`过滤器设置`},select:{title:`选择插件`},plugin:{loading:`正在加载插件配置……`,failure:`插件加载失败,这可能是插件本身的问题所致。{0}`,hintSource:`请检查插件源代码。`,hintAuthor:`请联系插件作者并反馈此问题。`,dependency:`{0}:{1}({2})`,dependencyRequired:`必需依赖`,dependencyOptional:`可选依赖`,service:`{0}:{1}({2})`,serviceRequired:`必需服务`,serviceOptional:`可选服务`,loaded:`已加载`,notLoaded:`未加载`,serviceConflict:`此插件将会提供 {0} 服务,但此服务已被其他插件实现。`,serviceProvided:`此插件提供了 {0} 服务。`,serviceFuture:`此插件启用后将会提供 {0} 服务。`,notReusable:`此插件已在运行且不可重用,启用可能会导致非预期的问题。`,multiplePrefix:`此插件存在多份配置,`,multipleAction:`点击前往管理`,multipleSuffix:`。`,pages:`此插件提供了页面:`,noSchema:`此插件未声明配置项,这可能并非预期行为。{0}`,notInstalled:`此插件尚未安装。`},view:{globalSettings:`全局设置`,group:`分组:{0}`,removeTitle:`确认移除`,removeConfirmGroup:`确定要移除分组 {0} 吗?此操作不可撤销!`,removeConfirmPlugin:`确定要移除插件 {0} 吗?此操作不可撤销!`,renameTitle:`重命名`,createGroupTitle:`创建分组`,cancel:`取消`,confirm:`确定`,constraintError:`当前配置项不满足约束,请检查配置!`,saved:`配置已保存。`,reloaded:`配置已重载。`,operationError:`操作失败,请检查日志!`,groupEnabled:`分组已启用。`,groupDisabled:`分组已停用。`,pluginEnabled:`插件已启用。`,pluginDisabled:`插件已停用。`}}},Ie={config:{title:`外掛設定`,menu:{enableGroup:`啟用分組`,disableGroup:`停用分組`,enablePlugin:`啟用外掛`,disablePlugin:`停用外掛`,saveConfig:`儲存設定`,reloadConfig:`重新載入設定`,rename:`重新命名`,removeGroup:`移除分組`,removePlugin:`移除外掛`,clone:`複製設定`,manage:`管理多份設定`,addPlugin:`新增外掛`,addGroup:`新增分組`,globalSettings:`全域設定`},tree:{group:`分組:{0}`,pending:`待新增`},forks:{workspace:` (工作區)`,count:`此外掛目前存在 {0} 份設定。`,none:`此外掛尚未被設定。`,add:`新增設定`},modifier:{title:`過濾器設定`},select:{title:`選擇外掛`},plugin:{loading:`正在載入外掛設定……`,failure:`外掛載入失敗,這可能是外掛本身的問題所致。{0}`,hintSource:`請檢查外掛原始碼。`,hintAuthor:`請聯絡外掛作者並回報此問題。`,dependency:`{0}:{1}({2})`,dependencyRequired:`必需相依性`,dependencyOptional:`可選相依性`,service:`{0}:{1}({2})`,serviceRequired:`必需服務`,serviceOptional:`可選服務`,loaded:`已載入`,notLoaded:`未載入`,serviceConflict:`此外掛將會提供 {0} 服務,但此服務已被其他外掛實作。`,serviceProvided:`此外掛提供了 {0} 服務。`,serviceFuture:`此外掛啟用後將會提供 {0} 服務。`,notReusable:`此外掛已在執行且不可重用,啟用可能會導致非預期的問題。`,multiplePrefix:`此外掛存在多份設定,`,multipleAction:`點擊前往管理`,multipleSuffix:`。`,pages:`此外掛提供了頁面:`,noSchema:`此外掛未宣告設定項,這可能並非預期行為。{0}`,notInstalled:`此外掛尚未安裝。`},view:{globalSettings:`全域設定`,group:`分組:{0}`,removeTitle:`確認移除`,removeConfirmGroup:`確定要移除分組 {0} 嗎?此操作不可復原!`,removeConfirmPlugin:`確定要移除外掛 {0} 嗎?此操作不可復原!`,renameTitle:`重新命名`,createGroupTitle:`建立分組`,cancel:`取消`,confirm:`確定`,constraintError:`目前設定項不滿足約束,請檢查設定!`,saved:`設定已儲存。`,reloaded:`設定已重新載入。`,operationError:`操作失敗,請檢查日誌!`,groupEnabled:`分組已啟用。`,groupDisabled:`分組已停用。`,pluginEnabled:`外掛已啟用。`,pluginDisabled:`外掛已停用。`}}},Le={},Re={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function ze(e,t){return O(),v(`svg`,Re,[...t[0]||=[y(`path`,{fill:`currentColor`,d:`M452 60H488C499 60 508 68.95 508 80C508 91.05 499 100 488 100H452V136C452 147 443 156 432 156C420.1 156 412 147 412 136V100H376C364.1 100 356 91.05 356 80C356 68.95 364.1 60 376 60H412V24C412 12.95 420.1 4 432 4C443 4 452 12.95 452 24V60zM280.8 133.1L490.1 222.8C503.4 228.5 512 241.5 512 256C512 270.5 503.4 283.5 490.1 289.2L280.8 378.9C272.1 382.3 264.5 384 256 384C247.5 384 239 382.3 231.2 378.9L21.9 289.2C8.614 283.5 0 270.5 0 256C0 241.5 8.614 228.5 21.9 222.8L231.2 133.1C239 129.7 247.5 128 256 128C264.5 128 272.1 129.7 280.8 133.1V133.1zM66.26 256L250.1 334.8C251.1 335.6 253.1 336 256 336C258 336 260 335.6 261.9 334.8L445.7 256L261.9 177.2C260 176.4 258 176 256 176C253.1 176 251.1 176.4 250.1 177.2L66.26 256zM59.53 350.6C66.15 359.9 65.24 372.3 57.1 380.5L250.1 462.8C251.1 463.6 253.1 464 256 464C258 464 260 463.6 261.9 462.8L452 381.3C447.4 374.4 446.5 365.2 450.5 357.3C456.5 345.4 470.9 340.6 482.7 346.5L491.8 351.1C504.2 357.3 512 369.9 512 383.7C512 398.4 503.3 411.6 489.9 417.3L280.8 506.9C272.1 510.3 264.5 512 256 512C247.5 512 239 510.3 231.2 506.9L22.81 417.6C8.971 411.7 .001 398.1 .001 383C.001 370.9 5.87 359.5 15.76 352.4L26.05 345C36.84 337.3 51.83 339.8 59.53 350.6L59.53 350.6z`},null,-1)]])}var Be=$(Le,[[`render`,ze]]),Ve={},He={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function Ue(e,t){return O(),v(`svg`,He,[...t[0]||=[y(`path`,{fill:`currentColor`,d:`M144,0c13.3,0,24,10.8,24,24v88h-48V24C120,10.8,130.8,0,144,0z M336,0c13.3,0,24,10.8,24,24v88h-48V24
2
2
  C312,10.8,322.7,0,336,0z M408,144c13.3,0,24,10.7,24,24s-10.7,24-24,24h-8v64c0,80.2-59.9,146.6-136,158.2V512h-48v-97.8
3
3
  c-77-11.6-136-78-136-158.2v-64h-8c-13.2,0-24-10.7-24-24s10.8-24,24-24H408z M240,368c61.9,0,112-50.1,112-112v-64H128v64
4
4
  C128,317.9,178.1,368,240,368z`},null,-1),y(`path`,{fill:`currentColor`,d:`M444,416h36c11,0,20,9,20,20s-9,20-20,20h-36v36c0,11-9,20-20,20c-11.9,0-20-9-20-20v-36h-36c-11.9,0-20-9-20-20
package/lib/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import { resolve } from "node:path";
2
- import { Context, Logger, Schema, remove } from "@koishi-ce/koishi";
1
+ import { dirname, join, resolve } from "node:path";
2
+ import { Context, Logger, Schema, getServiceContext, remove } from "@koishi-ce/koishi";
3
3
  import { DataService } from "@koishi-ce/console";
4
4
  import { LocalScanner, getPluginShortname, isResidentInCache } from "@koishi-ce/registry";
5
5
  import { Loader } from "@koishi-ce/loader";
6
+ import { readFileSync } from "node:fs";
6
7
  //#region src/shared/packages.ts
7
8
  /**
8
9
  * 插件包元数据提供器(服务端共享实现)。
@@ -147,7 +148,7 @@ var PackageProvider = class extends DataService {
147
148
  * 前端据此判断某插件"必需/可选注入的服务"是否已可用。
148
149
  *
149
150
  * 遍历方式:从根上下文的 internal 对象出发沿原型链逐层收集类型为
150
- * "service" 的属性,再通过 Context.current 访问器反查服务实例所属的
151
+ * "service" 的属性,再经 getServiceContext 反查服务实例所属的
151
152
  * 上下文,取其作用域 uid。
152
153
  */
153
154
  var ServiceProvider = class extends DataService {
@@ -169,7 +170,7 @@ var ServiceProvider = class extends DataService {
169
170
  if (type !== "service") continue;
170
171
  const instance = this.ctx.get(key);
171
172
  if (!(instance instanceof Object)) continue;
172
- const ctx = Reflect.getOwnPropertyDescriptor(instance, Context.current)?.value;
173
+ const ctx = getServiceContext(instance);
173
174
  if (!ctx) continue;
174
175
  const name = key.replace(/^__/, "").replace(/__$/, "");
175
176
  if (ctx.scope.uid !== null) services[name] = ctx.scope.uid;
@@ -462,9 +463,14 @@ var ConfigWriter = class extends DataService {
462
463
  * 本仓库 koishi.yml 的插件键统一是相对路径(./plugins/...),而 Bun
463
464
  * 不会把未被依赖的 workspace 包链入 node_modules,仅靠 LocalScanner
464
465
  * 会漏掉大部分源码包——因此 collect 时额外按配置键逐个加载
465
- * workspace 包,并写入 paths(配置键 → 包名映射,供前端解析插件名)。
466
+ * workspace 包,并写入 paths(配置键 → 包名映射,供前端解析插件名);
467
+ * 未启用的包(含嵌套 monorepo 子包)则按 workspaces 声明展开收录。
466
468
  */
467
469
  const logger = new Logger("config");
470
+ /** 插件包名的收录口径:只认三种插件命名前缀(与 LocalScanner 一致) */
471
+ const PLUGIN_NAME_PATTERN = /^(koishi-plugin-|@koishi(?:-ce)?\/plugin-)/;
472
+ /** 宿主未声明 workspaces 时的兜底约定(模板默认布局) */
473
+ const FALLBACK_WORKSPACES = ["plugins/*", "external/*"];
468
474
  /**
469
475
  * 本机插件扫描器:在 LocalScanner 的基础上扩展 parsePackage,
470
476
  * 让"扫描到某个包"与"解析该插件的运行时信息"在同一次遍历中完成。
@@ -510,10 +516,8 @@ var PackageProvider$1 = class extends PackageProvider {
510
516
  return this.collectWorkspacePackages();
511
517
  }
512
518
  /**
513
- * 递归遍历 loader 配置的插件表(分组键的值内嵌套插件表),收集
514
- * 其中以相对路径键引用的 workspace 源码包:按目录读取 package.json
515
- * 生成数据条目,已存在于 node_modules 扫描结果中的包则就地合并
516
- * paths 字段,避免同名条目重复。
519
+ * 收集全部 workspace 源码包:koishi.yml 已有相对路径键引用的(已启用)
520
+ * 加上 workspaces 声明覆盖的(可能尚未启用,含嵌套 monorepo 子包)。
517
521
  */
518
522
  async collectWorkspacePackages() {
519
523
  const index = /* @__PURE__ */ new Map();
@@ -521,30 +525,102 @@ var PackageProvider$1 = class extends PackageProvider {
521
525
  ...object,
522
526
  paths: []
523
527
  });
528
+ /**
529
+ * 收录一个相对路径引用的 workspace 包:合并 paths、登记
530
+ * 「包名 → 配置键」反查;warm 控制是否预热运行时缓存。
531
+ */
532
+ const addPath = async (path, warm) => {
533
+ const object = await this.scanner.loadPath(resolve(this.scanner.baseDir, path));
534
+ if (!object) return;
535
+ const name = object.package.name;
536
+ const entry = index.get(name) ?? {
537
+ ...object,
538
+ paths: []
539
+ };
540
+ entry.paths ||= [];
541
+ if (!entry.paths.includes(path)) entry.paths.push(path);
542
+ index.set(name, entry);
543
+ this.pathKeys[name] ||= path;
544
+ if (warm) this.cache[path] ||= await this.parseExports(path);
545
+ };
524
546
  try {
525
547
  await walkPlugins(this.ctx.loader?.config?.plugins, async (key) => {
526
548
  const path = key.split(":", 1)[0];
527
549
  if (!path?.startsWith("./")) return;
528
- const object = await this.scanner.loadPath(resolve(this.scanner.baseDir, path));
529
- if (!object) return;
530
- const name = object.package.name;
531
- const entry = index.get(name) ?? {
532
- ...object,
533
- paths: []
534
- };
535
- entry.paths ||= [];
536
- if (!entry.paths.includes(path)) entry.paths.push(path);
537
- index.set(name, entry);
538
- this.pathKeys[name] ||= path;
539
- this.cache[path] ||= await this.parseExports(path);
550
+ await addPath(path, true);
540
551
  });
552
+ await this.collectWorkspacePlugins(addPath);
541
553
  } catch (error) {
542
554
  logger.warn(error);
543
555
  }
544
556
  return [...index.values()];
545
557
  }
558
+ /**
559
+ * 收集 workspaces 声明覆盖的未启用插件包(嵌套 monorepo 兼容)。
560
+ *
561
+ * 以宿主 package.json 的 workspaces 通配为唯一真相源:声明写多深,
562
+ * 收录就能看多深——external/ 下克隆的 monorepo 形态插件(packages/
563
+ * 子包在二级及更深)无需平铺即可见。Bun 只把被依赖引用的 workspace
564
+ * 包链入 node_modules,未启用的插件不在本机扫描视野内(yarn 全量
565
+ * 链接生态的自动发现语义失效),这里是唯一补齐入口。
566
+ *
567
+ * 通配是目录模式,拼上 /package.json 以清单文件为锚点扫描
568
+ * (Bun.Glob 的 onlyDirs 语义不可靠,win32 实测返回文件路径且分隔符
569
+ * 为反斜杠,归一为 / 后再派生收录键);负向模式(! 前缀)同样拼
570
+ * 锚点后对正向结果做差集——模板对 external 下 node_modules 的负向
571
+ * 通配即用于排除搬迁残留的依赖目录(注释里不写通配字面量,
572
+ * biome 会把其中的 JSDoc 起始序列误解析成类型)。收录口径与
573
+ * LocalScanner 的本机扫描一致:只认三种插件命名前缀,monorepo
574
+ * 根(@scope/monorepo 形态)与普通库不进列表;收录键
575
+ * ./<相对路径>(loader 的相对路径解析原生支持任意深度)。
576
+ */
577
+ async collectWorkspacePlugins(addPath) {
578
+ const baseDir = this.scanner.baseDir;
579
+ const patterns = readWorkspacePatterns(baseDir);
580
+ const anchor = (pattern) => `${pattern.replace(/\/+$/, "")}/package.json`;
581
+ const excluded = /* @__PURE__ */ new Set();
582
+ for (const pattern of patterns) {
583
+ if (!pattern.startsWith("!")) continue;
584
+ for (const rel of new Bun.Glob(anchor(pattern.slice(1))).scanSync({ cwd: baseDir })) excluded.add(normalizeSlashes(rel));
585
+ }
586
+ const seen = /* @__PURE__ */ new Set();
587
+ for (const pattern of patterns) {
588
+ if (pattern.startsWith("!")) continue;
589
+ for (const rel of new Bun.Glob(anchor(pattern)).scanSync({ cwd: baseDir })) {
590
+ const manifestPath = normalizeSlashes(rel);
591
+ if (excluded.has(manifestPath) || seen.has(manifestPath)) continue;
592
+ seen.add(manifestPath);
593
+ try {
594
+ const manifest = JSON.parse(readFileSync(join(baseDir, manifestPath), "utf8"));
595
+ if (typeof manifest.name !== "string" || !PLUGIN_NAME_PATTERN.test(manifest.name)) continue;
596
+ await addPath(`./${dirname(manifestPath)}`, false);
597
+ } catch (error) {
598
+ logger.warn(error);
599
+ }
600
+ }
601
+ }
602
+ }
546
603
  };
547
604
  /**
605
+ * 读取宿主 package.json 的 workspaces 通配声明;缺失、非数组(如 yarn
606
+ * no-hoist 的对象形态)或为空时回退模板默认约定——旧项目与 prod 形态
607
+ * (workspaces 已被脚手架删除)的一级目录可见性不回退。
608
+ */
609
+ function readWorkspacePatterns(baseDir) {
610
+ try {
611
+ const manifest = JSON.parse(readFileSync(join(baseDir, "package.json"), "utf8"));
612
+ if (Array.isArray(manifest.workspaces)) {
613
+ const patterns = manifest.workspaces.filter((pattern) => typeof pattern === "string");
614
+ if (patterns.length) return patterns;
615
+ }
616
+ } catch {}
617
+ return FALLBACK_WORKSPACES;
618
+ }
619
+ /** Bun.Glob 的 scan 在 win32 返回反斜杠分隔的相对路径,统一归一为 / */
620
+ function normalizeSlashes(path) {
621
+ return path.split("\\").join("/");
622
+ }
623
+ /**
548
624
  * 递归遍历插件配置表并对每个插件键执行回调:`$` 开头的键是内部控制
549
625
  * 字段,`group:` 分组键的值内嵌套下一层插件表,其余键原样传入回调。
550
626
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koishi-ce/plugin-config",
3
3
  "description": "Manage your bots and plugins with console",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.mjs",
7
7
  "types": "lib/index.d.ts",
@@ -67,15 +67,15 @@
67
67
  "@koishi-ce/koishi": "^1.0.0"
68
68
  },
69
69
  "devDependencies": {
70
- "@koishi-ce/client": "^1.1.0",
71
- "@koishi-ce/plugin-hmr": "^1.1.0",
70
+ "@koishi-ce/client": "^1.2.2",
71
+ "@koishi-ce/plugin-hmr": "^1.1.1",
72
72
  "element-plus": "^2.14.5",
73
73
  "vue": "^3.5.12",
74
74
  "vue-router": "^5.2.0"
75
75
  },
76
76
  "dependencies": {
77
77
  "@koishi-ce/console": "^1.0.0",
78
- "@koishi-ce/loader": "^1.0.6",
78
+ "@koishi-ce/loader": "^1.0.7",
79
79
  "@koishi-ce/registry": "^1.0.5"
80
80
  }
81
81
  }
@@ -265,6 +265,92 @@ writeFileSync(
265
265
  version: "1.0.0",
266
266
  }),
267
267
  );
268
+ // external/ 约定目录下的未启用插件(含一个非插件包与一个无清单目录,
269
+ // 均不应进列表);有清单但包名不合插件命名的 lib-tool 也不收录
270
+ mkdirSync(join(fixtureRoot, "external/war-game"), {
271
+ recursive: true,
272
+ });
273
+ writeFileSync(
274
+ join(fixtureRoot, "external/war-game/package.json"),
275
+ JSON.stringify({
276
+ name: "koishi-plugin-war-game",
277
+ version: "0.1.0",
278
+ }),
279
+ );
280
+ mkdirSync(join(fixtureRoot, "external/plain-lib"), {
281
+ recursive: true,
282
+ });
283
+ writeFileSync(
284
+ join(fixtureRoot, "external/plain-lib/package.json"),
285
+ JSON.stringify({
286
+ name: "plain-lib",
287
+ version: "1.0.0",
288
+ }),
289
+ );
290
+ mkdirSync(join(fixtureRoot, "external/no-manifest"), {
291
+ recursive: true,
292
+ });
293
+ // 嵌套 monorepo(新模板 workspaces 声明形态):子包在二级深度可收录,
294
+ // monorepo 根(@scope/monorepo 命名)不进列表,node_modules 残留探针
295
+ // 被负向通配排除;plugins/ 一级的未启用包(盲区修复)同样可收录
296
+ writeFileSync(
297
+ join(fixtureRoot, "package.json"),
298
+ JSON.stringify({
299
+ name: "fixture-host",
300
+ private: true,
301
+ workspaces: [
302
+ "plugins/*",
303
+ "external/**",
304
+ "!external/**/node_modules/**",
305
+ ],
306
+ }),
307
+ );
308
+ mkdirSync(
309
+ join(fixtureRoot, "external/infra/packages/war-lib"),
310
+ { recursive: true },
311
+ );
312
+ writeFileSync(
313
+ join(
314
+ fixtureRoot,
315
+ "external/infra/packages/war-lib/package.json",
316
+ ),
317
+ JSON.stringify({
318
+ name: "koishi-plugin-war-lib",
319
+ version: "0.2.0",
320
+ }),
321
+ );
322
+ writeFileSync(
323
+ join(fixtureRoot, "external/infra/package.json"),
324
+ JSON.stringify({
325
+ name: "@infra/monorepo",
326
+ version: "1.0.0",
327
+ private: true,
328
+ }),
329
+ );
330
+ mkdirSync(
331
+ join(fixtureRoot, "external/infra/node_modules/zzz"),
332
+ { recursive: true },
333
+ );
334
+ writeFileSync(
335
+ join(
336
+ fixtureRoot,
337
+ "external/infra/node_modules/zzz/package.json",
338
+ ),
339
+ JSON.stringify({
340
+ name: "koishi-plugin-zzz-pollution",
341
+ version: "1.0.0",
342
+ }),
343
+ );
344
+ mkdirSync(join(fixtureRoot, "plugins/local-tool"), {
345
+ recursive: true,
346
+ });
347
+ writeFileSync(
348
+ join(fixtureRoot, "plugins/local-tool/package.json"),
349
+ JSON.stringify({
350
+ name: "koishi-plugin-local-tool",
351
+ version: "0.3.0",
352
+ }),
353
+ );
268
354
  loader.baseDir = fixtureRoot;
269
355
 
270
356
  loader.config = {
@@ -408,6 +494,112 @@ describe("@koishi-ce/plugin-config", () => {
408
494
  ).toBeTruthy();
409
495
  });
410
496
 
497
+ it("扫描 external/ 约定目录收录未启用的 workspace 插件", async () => {
498
+ const provider = app.get(
499
+ "console.services.packages",
500
+ ) as unknown as {
501
+ get(): Promise<Dict<Record<string, unknown>>>;
502
+ pathKeys: Dict<string>;
503
+ cache: Dict<Record<string, unknown>>;
504
+ };
505
+ const data = await provider.get();
506
+ // 命名合规的插件包:收录并标注相对路径键
507
+ const war = data["koishi-plugin-war-game"];
508
+ expect(war?.["workspace"]).toBe(true);
509
+ expect(war?.["paths"]).toEqual([
510
+ "./external/war-game",
511
+ ]);
512
+ // 未启用包不预热运行时缓存(require 会提前求值模块)
513
+ expect(war?.["runtime"]).toBeUndefined();
514
+ // 「包名 → 配置键」反查已登记,request-runtime 按需解析可用
515
+ expect(
516
+ provider.pathKeys["koishi-plugin-war-game"],
517
+ ).toBe("./external/war-game");
518
+ expect(
519
+ provider.cache["./external/war-game"],
520
+ ).toBeUndefined();
521
+ // 非插件命名与无清单目录不进列表
522
+ expect(data["plain-lib"]).toBeUndefined();
523
+ expect(data["no-manifest"]).toBeUndefined();
524
+ });
525
+
526
+ it("workspaces 声明驱动收录嵌套 monorepo 子包与 plugins/ 未启用包", async () => {
527
+ const provider = app.get(
528
+ "console.services.packages",
529
+ ) as unknown as {
530
+ get(): Promise<Dict<Record<string, unknown>>>;
531
+ };
532
+ const data = await provider.get();
533
+ // 嵌套子包(external/** 任意深度):收录并以深路径为键
534
+ const lib = data["koishi-plugin-war-lib"];
535
+ expect(lib?.["workspace"]).toBe(true);
536
+ expect(lib?.["paths"]).toEqual([
537
+ "./external/infra/packages/war-lib",
538
+ ]);
539
+ expect(lib?.["runtime"]).toBeUndefined();
540
+ // plugins/ 一级的未启用包同样可收录(此前的可见性盲区)
541
+ expect(
542
+ data["koishi-plugin-local-tool"]?.["paths"],
543
+ ).toEqual(["./plugins/local-tool"]);
544
+ // monorepo 根(@scope/monorepo 命名)不进列表
545
+ expect(data["@infra/monorepo"]).toBeUndefined();
546
+ // node_modules 残留探针被负向通配排除(即使命名合规)
547
+ expect(
548
+ data["koishi-plugin-zzz-pollution"],
549
+ ).toBeUndefined();
550
+ });
551
+
552
+ it("readWorkspacePatterns:无清单 / 非数组 / 空数组时回退约定", async () => {
553
+ const { readWorkspacePatterns } = await import(
554
+ "../node/packages.ts"
555
+ );
556
+ const empty = mkdtempSync(
557
+ join(tmpdir(), "koishi-config-ws-"),
558
+ );
559
+ try {
560
+ // 无 package.json → 兜底约定
561
+ expect(readWorkspacePatterns(empty)).toEqual([
562
+ "plugins/*",
563
+ "external/*",
564
+ ]);
565
+ // 非数组(yarn no-hoist 对象形态)与空数组同理
566
+ writeFileSync(
567
+ join(empty, "package.json"),
568
+ JSON.stringify({
569
+ workspaces: { packages: ["external/*"] },
570
+ }),
571
+ );
572
+ expect(readWorkspacePatterns(empty)).toEqual([
573
+ "plugins/*",
574
+ "external/*",
575
+ ]);
576
+ writeFileSync(
577
+ join(empty, "package.json"),
578
+ JSON.stringify({ workspaces: [] }),
579
+ );
580
+ expect(readWorkspacePatterns(empty)).toEqual([
581
+ "plugins/*",
582
+ "external/*",
583
+ ]);
584
+ // 合法声明原样透传(负向模式保留 ! 前缀)
585
+ writeFileSync(
586
+ join(empty, "package.json"),
587
+ JSON.stringify({
588
+ workspaces: [
589
+ "external/**",
590
+ "!external/**/node_modules/**",
591
+ ],
592
+ }),
593
+ );
594
+ expect(readWorkspacePatterns(empty)).toEqual([
595
+ "external/**",
596
+ "!external/**/node_modules/**",
597
+ ]);
598
+ } finally {
599
+ rmSync(empty, { recursive: true, force: true });
600
+ }
601
+ });
602
+
411
603
  itQuiet(
412
604
  ["config"],
413
605
  "request-runtime 按路径键 / 短名解析并刷新,失败结果同样缓存",
@@ -510,6 +702,21 @@ describe("@koishi-ce/plugin-config", () => {
510
702
  );
511
703
  expect(messages.length).toBeGreaterThan(0);
512
704
  });
705
+
706
+ it("provide() 注册的服务同样上报(loader / watcher 形态)", async () => {
707
+ const provider = app.get(
708
+ "console.services.services",
709
+ ) as unknown as {
710
+ get(): Promise<Dict<number>>;
711
+ };
712
+ // cordis 3.18 的 ctx.provide() 不给值定义自有 "ctx" 属性
713
+ // (只有 tracker 符号,经 traceable 代理的属性访问可达),
714
+ // descriptor 查询落空时须以属性访问兜底,否则配置页
715
+ // 恒显示「必需服务未加载」
716
+ app.provide("probe.plain", { marker: 1 });
717
+ const data = await provider.get();
718
+ expect(data["probe.plain"]).toBeGreaterThanOrEqual(0);
719
+ });
513
720
  });
514
721
 
515
722
  describe("ConfigWriter 事件", () => {
@@ -10,9 +10,11 @@
10
10
  * 本仓库 koishi.yml 的插件键统一是相对路径(./plugins/...),而 Bun
11
11
  * 不会把未被依赖的 workspace 包链入 node_modules,仅靠 LocalScanner
12
12
  * 会漏掉大部分源码包——因此 collect 时额外按配置键逐个加载
13
- * workspace 包,并写入 paths(配置键 → 包名映射,供前端解析插件名)。
13
+ * workspace 包,并写入 paths(配置键 → 包名映射,供前端解析插件名);
14
+ * 未启用的包(含嵌套 monorepo 子包)则按 workspaces 声明展开收录。
14
15
  */
15
- import { resolve } from "node:path";
16
+ import { readFileSync } from "node:fs";
17
+ import { dirname, join, resolve } from "node:path";
16
18
  import { Logger } from "@koishi-ce/koishi";
17
19
  import {
18
20
  getPluginShortname,
@@ -23,6 +25,13 @@ import * as shared from "../shared/index.ts";
23
25
 
24
26
  const logger = new Logger("config");
25
27
 
28
+ /** 插件包名的收录口径:只认三种插件命名前缀(与 LocalScanner 一致) */
29
+ const PLUGIN_NAME_PATTERN =
30
+ /^(koishi-plugin-|@koishi(?:-ce)?\/plugin-)/;
31
+
32
+ /** 宿主未声明 workspaces 时的兜底约定(模板默认布局) */
33
+ const FALLBACK_WORKSPACES = ["plugins/*", "external/*"];
34
+
26
35
  /**
27
36
  * 本机插件扫描器:在 LocalScanner 的基础上扩展 parsePackage,
28
37
  * 让"扫描到某个包"与"解析该插件的运行时信息"在同一次遍历中完成。
@@ -81,10 +90,8 @@ export class PackageProvider extends shared.PackageProvider {
81
90
  }
82
91
 
83
92
  /**
84
- * 递归遍历 loader 配置的插件表(分组键的值内嵌套插件表),收集
85
- * 其中以相对路径键引用的 workspace 源码包:按目录读取 package.json
86
- * 生成数据条目,已存在于 node_modules 扫描结果中的包则就地合并
87
- * paths 字段,避免同名条目重复。
93
+ * 收集全部 workspace 源码包:koishi.yml 已有相对路径键引用的(已启用)
94
+ * 加上 workspaces 声明覆盖的(可能尚未启用,含嵌套 monorepo 子包)。
88
95
  */
89
96
  private async collectWorkspacePackages(): Promise<
90
97
  shared.PackageProvider.Data[]
@@ -100,6 +107,34 @@ export class PackageProvider extends shared.PackageProvider {
100
107
  paths: [],
101
108
  });
102
109
  }
110
+ /**
111
+ * 收录一个相对路径引用的 workspace 包:合并 paths、登记
112
+ * 「包名 → 配置键」反查;warm 控制是否预热运行时缓存。
113
+ */
114
+ const addPath = async (path: string, warm: boolean) => {
115
+ const object = await this.scanner.loadPath(
116
+ resolve(this.scanner.baseDir, path),
117
+ );
118
+ if (!object) return;
119
+ const name = object.package.name;
120
+ const entry = index.get(name) ?? {
121
+ ...object,
122
+ paths: [],
123
+ };
124
+ entry.paths ||= [];
125
+ if (!entry.paths.includes(path))
126
+ entry.paths.push(path);
127
+ index.set(name, entry);
128
+ // 登记「包名 → 配置键」反查,供 request-runtime 按路径解析
129
+ this.pathKeys[name] ||= path;
130
+ // 顺带解析运行时信息(loader.resolve 对路径键原生支持)。
131
+ // 已启用插件的模块本来就在 require.cache,预热无副作用;
132
+ // 未启用的 external 包不做预热——require 会提前求值模块,
133
+ // 其 peer 缺失时产生告警噪音,运行时信息留待前端选中时
134
+ // 经 request-runtime 按需解析
135
+ if (warm)
136
+ this.cache[path] ||= await this.parseExports(path);
137
+ };
103
138
  try {
104
139
  await walkPlugins(
105
140
  this.ctx.loader?.config?.plugins as
@@ -109,31 +144,117 @@ export class PackageProvider extends shared.PackageProvider {
109
144
  // 配置键形如 ./plugins/webui/config:uid,取 : 前的路径部分
110
145
  const path = key.split(":", 1)[0];
111
146
  if (!path?.startsWith("./")) return;
112
- const object = await this.scanner.loadPath(
113
- resolve(this.scanner.baseDir, path),
114
- );
115
- if (!object) return;
116
- const name = object.package.name;
117
- const entry = index.get(name) ?? {
118
- ...object,
119
- paths: [],
120
- };
121
- entry.paths ||= [];
122
- if (!entry.paths.includes(path))
123
- entry.paths.push(path);
124
- index.set(name, entry);
125
- // 登记「包名 → 配置键」反查,供 request-runtime 按路径解析
126
- this.pathKeys[name] ||= path;
127
- // 顺带解析运行时信息(loader.resolve 对路径键原生支持)
128
- this.cache[path] ||=
129
- await this.parseExports(path);
147
+ await addPath(path, true);
130
148
  },
131
149
  );
150
+ await this.collectWorkspacePlugins(addPath);
132
151
  } catch (error) {
133
152
  logger.warn(error);
134
153
  }
135
154
  return [...index.values()];
136
155
  }
156
+
157
+ /**
158
+ * 收集 workspaces 声明覆盖的未启用插件包(嵌套 monorepo 兼容)。
159
+ *
160
+ * 以宿主 package.json 的 workspaces 通配为唯一真相源:声明写多深,
161
+ * 收录就能看多深——external/ 下克隆的 monorepo 形态插件(packages/
162
+ * 子包在二级及更深)无需平铺即可见。Bun 只把被依赖引用的 workspace
163
+ * 包链入 node_modules,未启用的插件不在本机扫描视野内(yarn 全量
164
+ * 链接生态的自动发现语义失效),这里是唯一补齐入口。
165
+ *
166
+ * 通配是目录模式,拼上 /package.json 以清单文件为锚点扫描
167
+ * (Bun.Glob 的 onlyDirs 语义不可靠,win32 实测返回文件路径且分隔符
168
+ * 为反斜杠,归一为 / 后再派生收录键);负向模式(! 前缀)同样拼
169
+ * 锚点后对正向结果做差集——模板对 external 下 node_modules 的负向
170
+ * 通配即用于排除搬迁残留的依赖目录(注释里不写通配字面量,
171
+ * biome 会把其中的 JSDoc 起始序列误解析成类型)。收录口径与
172
+ * LocalScanner 的本机扫描一致:只认三种插件命名前缀,monorepo
173
+ * 根(@scope/monorepo 形态)与普通库不进列表;收录键
174
+ * ./<相对路径>(loader 的相对路径解析原生支持任意深度)。
175
+ */
176
+ private async collectWorkspacePlugins(
177
+ addPath: (path: string, warm: boolean) => Promise<void>,
178
+ ) {
179
+ const baseDir = this.scanner.baseDir;
180
+ const patterns = readWorkspacePatterns(baseDir);
181
+ // 目录通配 → 清单文件锚点(去尾斜杠后拼接;负向模式去掉 ! 前缀)
182
+ const anchor = (pattern: string) =>
183
+ `${pattern.replace(/\/+$/, "")}/package.json`;
184
+ const excluded = new Set<string>();
185
+ for (const pattern of patterns) {
186
+ if (!pattern.startsWith("!")) continue;
187
+ for (const rel of new Bun.Glob(
188
+ anchor(pattern.slice(1)),
189
+ ).scanSync({ cwd: baseDir })) {
190
+ excluded.add(normalizeSlashes(rel));
191
+ }
192
+ }
193
+ const seen = new Set<string>();
194
+ for (const pattern of patterns) {
195
+ if (pattern.startsWith("!")) continue;
196
+ for (const rel of new Bun.Glob(
197
+ anchor(pattern),
198
+ ).scanSync({ cwd: baseDir })) {
199
+ const manifestPath = normalizeSlashes(rel);
200
+ if (
201
+ excluded.has(manifestPath) ||
202
+ seen.has(manifestPath)
203
+ )
204
+ continue;
205
+ seen.add(manifestPath);
206
+ try {
207
+ const manifest = JSON.parse(
208
+ readFileSync(
209
+ join(baseDir, manifestPath),
210
+ "utf8",
211
+ ),
212
+ ) as { name?: unknown };
213
+ if (
214
+ typeof manifest.name !== "string" ||
215
+ !PLUGIN_NAME_PATTERN.test(manifest.name)
216
+ )
217
+ continue;
218
+ await addPath(
219
+ `./${dirname(manifestPath)}`,
220
+ false,
221
+ );
222
+ } catch (error) {
223
+ logger.warn(error);
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
229
+
230
+ /**
231
+ * 读取宿主 package.json 的 workspaces 通配声明;缺失、非数组(如 yarn
232
+ * no-hoist 的对象形态)或为空时回退模板默认约定——旧项目与 prod 形态
233
+ * (workspaces 已被脚手架删除)的一级目录可见性不回退。
234
+ */
235
+ export function readWorkspacePatterns(
236
+ baseDir: string,
237
+ ): string[] {
238
+ try {
239
+ const manifest = JSON.parse(
240
+ readFileSync(join(baseDir, "package.json"), "utf8"),
241
+ ) as { workspaces?: unknown };
242
+ if (Array.isArray(manifest.workspaces)) {
243
+ const patterns = manifest.workspaces.filter(
244
+ (pattern): pattern is string =>
245
+ typeof pattern === "string",
246
+ );
247
+ if (patterns.length) return patterns;
248
+ }
249
+ } catch {
250
+ // 无宿主清单(异常环境):走兜底约定
251
+ }
252
+ return FALLBACK_WORKSPACES;
253
+ }
254
+
255
+ /** Bun.Glob 的 scan 在 win32 返回反斜杠分隔的相对路径,统一归一为 / */
256
+ function normalizeSlashes(path: string): string {
257
+ return path.split("\\").join("/");
137
258
  }
138
259
 
139
260
  /**
@@ -8,11 +8,15 @@
8
8
  * 前端据此判断某插件"必需/可选注入的服务"是否已可用。
9
9
  *
10
10
  * 遍历方式:从根上下文的 internal 对象出发沿原型链逐层收集类型为
11
- * "service" 的属性,再通过 Context.current 访问器反查服务实例所属的
11
+ * "service" 的属性,再经 getServiceContext 反查服务实例所属的
12
12
  * 上下文,取其作用域 uid。
13
13
  */
14
14
  import { DataService } from "@koishi-ce/console";
15
- import { Context, type Dict } from "@koishi-ce/koishi";
15
+ import {
16
+ Context,
17
+ type Dict,
18
+ getServiceContext,
19
+ } from "@koishi-ce/koishi";
16
20
 
17
21
  export class ServiceProvider extends DataService<
18
22
  Dict<number>
@@ -41,12 +45,10 @@ export class ServiceProvider extends DataService<
41
45
  if (type !== "service") continue;
42
46
  const instance = this.ctx.get(key);
43
47
  if (!(instance instanceof Object)) continue;
44
- // Context.current 是个访问器属性,其值即服务实例所属的上下文
45
- const ctx: Context =
46
- Reflect.getOwnPropertyDescriptor(
47
- instance,
48
- Context.current,
49
- )?.value;
48
+ // 归属反查见 getServiceContext:provide() 形态(loader /
49
+ // hmr 的 watcher)只有 tracker 符号,descriptor 落空须
50
+ // 属性访问兜底,否则配置页恒显示「未加载」
51
+ const ctx = getServiceContext(instance);
50
52
  if (!ctx) continue;
51
53
  // 服务内部名形如 __foo__,对外展示时去掉首尾下划线
52
54
  const name = key