diff --git a/.changeset/skill-group-selector.md b/.changeset/skill-group-selector.md new file mode 100644 index 0000000000..6f8c7a1e9a --- /dev/null +++ b/.changeset/skill-group-selector.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Add hierarchical group navigation selector for the /skill command. diff --git a/.github/workflows/fork-update-rebase.yml b/.github/workflows/fork-update-rebase.yml new file mode 100644 index 0000000000..39dcba4cca --- /dev/null +++ b/.github/workflows/fork-update-rebase.yml @@ -0,0 +1,51 @@ +name: Fork Update & Automated Rebase + +on: + schedule: + # Run daily at midnight + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + sync-and-rebase: + name: Sync Fork with Upstream and Rebase PRs + runs-on: ubuntu-latest + + steps: + - name: Checkout Fork Main + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git User + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch Upstream + run: | + git remote add upstream https://github.com/MoonshotAI/kimi-code.git || true + git fetch upstream main + + - name: Sync Main Branch + run: | + git checkout main + git reset --hard upstream/main + git push origin main --force + + - name: Setup Node & pnpm + uses: pnpm/action-setup@v6 + + - uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Install & Verify Build + run: | + pnpm install --no-frozen-lockfile + pnpm run build diff --git a/.github/workflows/pr-conflict-check.yml b/.github/workflows/pr-conflict-check.yml new file mode 100644 index 0000000000..69bdde252c --- /dev/null +++ b/.github/workflows/pr-conflict-check.yml @@ -0,0 +1,51 @@ +name: PR Conflict & Sanity Check + +on: + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +permissions: + contents: read + +jobs: + check-conflict-markers: + name: Check Conflict Markers & Code Health + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Check for Unresolved Conflict Markers + run: | + echo "Searching for unresolved conflict markers (<<<<<<<, =======, >>>>>>>)..." + FOUND=$(grep -rnE "^(<<<<<<<|=======|>>>>>>>)" --exclude-dir={node_modules,.git,dist} . || true) + if [ -n "$FOUND" ]; then + echo "::error::Unresolved conflict markers found in files:" + echo "$FOUND" + exit 1 + else + echo "No conflict markers found." + fi + + - name: Check for Accidental Root Configuration Files + run: | + if [ -f "mcp.json" ]; then + echo "::error::Accidental mcp.json found in repository root!" + exit 1 + fi + + - name: Setup Node & pnpm + uses: pnpm/action-setup@v6 + + - uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck & Build Sanity Check + run: pnpm run build diff --git a/.kimi-code/mcp.json b/.kimi-code/mcp.json new file mode 100644 index 0000000000..6799b58803 --- /dev/null +++ b/.kimi-code/mcp.json @@ -0,0 +1,31 @@ +{ + "mcpServers": { + "mcpjungle-localfiles-rag": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "http://127.0.0.1:8090/v0/groups/localfiles-rag/mcp", + "--allow-http" + ] + }, + "mcpjungle-coding-architecture": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "http://127.0.0.1:8090/v0/groups/coding-architecture/mcp", + "--allow-http" + ] + }, + "mcpjungle-docker-devops": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "http://127.0.0.1:8090/v0/groups/docker-devops/mcp", + "--allow-http" + ] + } + } +} diff --git a/apps/kimi-code/dist-web/assets/CodeBlockNode-BZ3nIcUJ.js b/apps/kimi-code/dist-web/assets/CodeBlockNode-BZ3nIcUJ.js new file mode 100644 index 0000000000..bbdd9045bc --- /dev/null +++ b/apps/kimi-code/dist-web/assets/CodeBlockNode-BZ3nIcUJ.js @@ -0,0 +1,22 @@ +import{bR as ao,ci as uo,bQ as Bl,M as El,bl as so,af as gl,bZ as co,c0 as Ol,aU as P,bT as fo,bE as q,az as vo,c1 as wl,c2 as mo,aI as Ll,aL as Xe,s as Nn,bk as Et,cj as bl,u as Sl,I as po,A as ho,bJ as Ln,aY as bt,bL as yo,v as A,aw as St,au as Hn,t as kt,bB as go,bb as Ot,q as w,ck as wo,c4 as bo,c5 as So,c8 as ko,a0 as Co,c3 as kl,aD as xo,bO as Mo,T as Po,as as V,b$ as Bo,cl as Eo}from"./index-DtRfJ0vw.js";import{i as Z,t as Oo}from"./safeRaf-DGuzXxDK.js";import{V as Lo}from"./CodeBlockShell.vue_vue_type_style_index_0_lang-CdMaGbWN.js";var Fn=(x,k,M)=>new Promise((i,de)=>{var Oe=B=>{try{ee(M.next(B))}catch(L){de(L)}},K=B=>{try{ee(M.throw(B))}catch(L){de(L)}},ee=B=>B.done?i(B.value):Promise.resolve(B.value).then(Oe,K);ee((M=M.apply(x,k)).next())});let Cl=!1,fn=null,vn=null,Ct=!1,mn=null;function Fo(){return Fn(this,null,function*(){if(mn)return mn;mn=Fn(null,null,function*(){if(!vn){if(Ct)return null;try{if(vn=(function(x){const k=x;if(typeof k?.useMonaco=="function")return k;const M=x?.default;return typeof M?.useMonaco=="function"?M:null})(yield ao(()=>import("./__vite-optional-peer-dep_stream-monaco_markstream-vue-BIHI7g3E.js"),[])),!vn)return Ct=!0,null}catch{return Ct=!0,null}}try{return yield(function(x){return Fn(this,null,function*(){return Cl?void 0:fn||(fn=Fn(null,null,function*(){const M=globalThis?.MonacoEnvironment;M&&(typeof M.getWorker=="function"||typeof M.getWorkerUrl=="function")||typeof x?.preloadMonacoWorkers!="function"||(yield x.preloadMonacoWorkers()),Cl=!0}).finally(()=>{fn=null}),fn)})})(vn),uo(),vn}catch{return null}});try{return yield mn}finally{mn=null}})}var $o=Object.defineProperty,No=Object.defineProperties,Ho=Object.getOwnPropertyDescriptors,xl=Object.getOwnPropertySymbols,zo=Object.prototype.hasOwnProperty,To=Object.prototype.propertyIsEnumerable,Ml=(x,k,M)=>k in x?$o(x,k,{enumerable:!0,configurable:!0,writable:!0,value:M}):x[k]=M,_=(x,k)=>{for(var M in k||(k={}))zo.call(k,M)&&Ml(x,M,k[M]);if(xl)for(var M of xl(k))To.call(k,M)&&Ml(x,M,k[M]);return x},ye=(x,k)=>No(x,Ho(k)),H=(x,k,M)=>new Promise((i,de)=>{var Oe=B=>{try{ee(M.next(B))}catch(L){de(L)}},K=B=>{try{ee(M.throw(B))}catch(L){de(L)}},ee=B=>B.done?i(B.value):Promise.resolve(B.value).then(Oe,K);ee((M=M.apply(x,k)).next())});const Ro={class:"html-preview-frame__header"},_o={class:"html-preview-frame__title"},Do={class:"html-preview-frame__label"},Wo=["sandbox","srcdoc"],Ao=Bl(El({__name:"HtmlPreviewFrame",props:{code:{},isDark:{type:Boolean},htmlPreviewAllowScripts:{type:Boolean},htmlPreviewSandbox:{},onClose:{type:Function},title:{}},setup(x){const k=x,M=import.meta!==void 0&&!1;let i=null;const{t:de}=Ol(),Oe=w(()=>{const B=k.code||"",L=B.trim().toLowerCase();return L.startsWith(" + +
+ + + + + + ${B} + +`}),K=w(()=>{return B=k.htmlPreviewSandbox,L=k.htmlPreviewAllowScripts,typeof B=="string"?((function(ke){if(!M||typeof console>"u"||i===ke)return;const ne=(function(S){return new Set(S.trim().toLowerCase().split(/\s+/).filter(Boolean))})(ke);ne.has("allow-scripts")&&ne.has("allow-same-origin")&&(i=ke,console.warn("[markstream-vue] htmlPreviewSandbox contains both allow-scripts and allow-same-origin. Use this only for fully trusted content served from an isolated origin."))})(B),B):B!==void 0?"":L===!0?"allow-scripts":"";var B,L});function ee(B){var L;B.key!=="Escape"&&B.key!=="Esc"||(L=k.onClose)==null||L.call(k)}return xo(()=>{typeof window<"u"&&window.addEventListener("keydown",ee)}),Ll(()=>{typeof window<"u"&&window.removeEventListener("keydown",ee)}),(B,L)=>(Xe(),Nn(Po,{to:"body"},[A("div",{class:Hn(["markstream-vue",{dark:k.isDark}])},[A("div",{class:"html-preview-frame__backdrop",onClick:L[2]||(L[2]=ke=>{var ne;return(ne=k.onClose)==null?void 0:ne.call(k)})},[A("div",{class:"html-preview-frame",onClick:L[1]||(L[1]=Mo(()=>{},["stop"]))},[A("div",Ro,[A("div",_o,[L[3]||(L[3]=A("span",{class:"html-preview-frame__dot"},null,-1)),A("span",Do,Ot(k.title||Et(de)("common.preview")||"Preview"),1)]),A("button",{type:"button",class:"html-preview-frame__close",onClick:L[0]||(L[0]=ke=>{var ne;return(ne=k.onClose)==null?void 0:ne.call(k)})}," × ")]),A("iframe",{class:"html-preview-frame__iframe",sandbox:K.value,referrerpolicy:"no-referrer",srcdoc:Oe.value},null,8,Wo)])])],2)]))}}),[["__scopeId","data-v-24e66176"]]);let Ye=null,xt=null,pn=null,Qe=null,Mt=null,Pt=null;const Bt=new WeakMap;let Io=0;const qo=["data-markstream-enhanced","data-markstream-enhancement-state","data-markstream-pending"],jo={class:"code-header-main"},Uo=["innerHTML"],Vo={class:"code-header-copy"},Go={class:"code-header-title"},Jo={key:0,class:"code-header-caption"},Ko={class:"code-editor-layer"},Pl="__markstreamMonacoPassiveTouchState__",$n=1.5,Zo=Bl(El({__name:"CodeBlockNode",props:{node:{},isDark:{type:Boolean,default:!1},loading:{type:Boolean,default:!0},stream:{type:Boolean,default:!0},theme:{},darkTheme:{default:"vitesse-dark"},lightTheme:{default:"vitesse-light"},isShowPreview:{type:Boolean,default:!0},monacoOptions:{},enableFontSizeControl:{type:Boolean,default:!0},minWidth:{default:void 0},maxWidth:{default:void 0},themes:{},showPreviewButton:{type:Boolean,default:!0},showCollapseButton:{type:Boolean,default:!0},showFontSizeButtons:{type:Boolean,default:!0},showTooltips:{type:Boolean},htmlPreviewAllowScripts:{type:Boolean},htmlPreviewSandbox:{},customId:{},showHeader:{type:Boolean,default:!0},showCopyButton:{type:Boolean,default:!0},showExpandButton:{type:Boolean,default:!0},estimatedHeightPx:{},estimatedContentHeightPx:{}},emits:["previewCode","copy"],setup(x,{emit:k}){var M;const i=x,de=k,Oe=so(),K=gl(co,null),ee=gl("markstreamHostScrollManaged",null),B=w(()=>Bo(i,Oe)),L=Co(),ke=w(()=>{const e=L?.vnode.props;return!(!e||!e.onPreviewCode&&!e.onPreviewCode)}),{t:ne}=Ol(),S=P(null),ge=P(null),zn=P(!1),ce=P(al(i.node.language,i.node.code,ft())),fe=w(()=>wo(ce.value)),Lt=w(()=>fe.value==="plaintext"),ve=P(!1),Ce=P(!1),j=P(!1),me=P(!1),I=P(!1),De=P(!1);let F=!1,We=null,xe="";const Le=P(null),Fe=P(null);let hn=0,Tn=0;const Fl=fo(),Ze=P(null),Y=P(typeof window>"u");typeof window<"u"&&q(()=>ge.value,e=>{var n;if((n=Ze.value)==null||n.destroy(),Ze.value=null,!e)return void(Y.value=!1);const t=window.__MARKSTREAM_CODE_BLOCK_VIEWPORT_ROOT_MARGIN__,l=Fl(e,{rootMargin:typeof t=="string"?t:"400px"});Ze.value=l,Y.value=l.isVisible.value,l.whenVisible.then(()=>{Y.value=!0})},{immediate:!0}),vo(()=>{var e;F=!0,(function(){const n=xe;K&&n&&(xe="",K.markSettled(n))})(),(e=Ze.value)==null||e.destroy(),Ze.value=null});let G=null,yn=null,gn=()=>{},wn=()=>{},en=()=>null,te=()=>({getModel:()=>({getLineCount:()=>1}),getOption:()=>14,updateOptions:()=>{}}),W=()=>({getModel:()=>({getLineCount:()=>1}),getOption:()=>14,updateOptions:()=>{}}),Rn=()=>{},Ae=()=>{},_n=()=>{},we=null,nn=null,tn=null,Ft=()=>{var e;return String((e=i.node.language)!=null?e:"plaintext")},Dn=()=>H(null,null,function*(){}),$t=!1;const Ie=[],Wn=[];let be=null;function bn(e){var n;return((n=String(e??"").trim().split(/\s+/,1)[0])!=null?n:"")==="diff"}const m=w(()=>{var e;return i.node.diff===!0||bn(i.node.language)||bn(mt(String((e=i.node.raw)!=null?e:"")))}),$e=P({removed:0,added:0}),$l=w(()=>`-${$e.value.removed} +${$e.value.added}`),ln=Object.freeze({enabled:!0,contextLineCount:2,minimumLineCount:4,revealLineCount:5}),Nt=Object.freeze(ye(_({},ln),{enabled:!1,revealLineCount:0}));function An(e){var n;if(typeof e=="boolean")return e;if(e&&typeof e=="object"){const t=e;return ye(_(_({},ln),t),{enabled:(n=t.enabled)==null||n})}return _({},ln)}function Ht(e){var n,t,l;if(e.renderSideBySide===!1)return!0;if(e.useInlineViewWhenSpaceIsLimited!==!0)return!1;const o=e.renderSideBySideInlineBreakpoint,a=typeof o=="number"&&Number.isFinite(o)?o:900,r=((t=(n=ge.value)==null?void 0:n.getBoundingClientRect)==null?void 0:t.call(n).width)||((l=ge.value)==null?void 0:l.clientWidth)||(typeof window>"u"?0:window.innerWidth);return r>0&&r<=a}function In(e,n){return{original:Sn(e,!1),updated:Sn(n,!1)}}function Sn(e,n){const t=String(e??"");return n?t:t.replace(/\r\n$|\n$|\r$/,"")}function on(e){var n;return String((n=e?.message)!=null?n:e).includes("no diff result available")}function qe(){try{const e=_n();e&&typeof e.catch=="function"&&e.catch(n=>{on(n)})}catch(e){on(e)}}const Q=w(()=>{var e,n,t,l;const o=i.monacoOptions?_({},i.monacoOptions):{};if(!m.value)return o;const a=o.diffHideUnchangedRegions===void 0?_({},ln):An(o.diffHideUnchangedRegions),r=o.hideUnchangedRegions===void 0?void 0:An(o.hideUnchangedRegions),u=i.stream!==!1&&i.loading!==!1,s=u?_({},Nt):a,c=u?_({},Nt):r,d=(function(v){return v.diffWordWrap!==void 0?v.diffWordWrap:"off"})(o),f=_({},(e=o.experimental)!=null?e:{}),y=(n=o.diffUnchangedRegionStyle)!=null?n:"line-info",g=(function(v){if(!Ht(v))return;const h=v.scrollbar&&typeof v.scrollbar=="object"?v.scrollbar:{};return ye(_({},h),{horizontal:"hidden",horizontalScrollbarSize:0})})(o),p={maxComputationTime:0,diffAlgorithm:"legacy",ignoreTrimWhitespace:!1,renderIndicators:!0,diffUpdateThrottleMs:120,renderLineHighlight:"none",renderLineHighlightOnlyWhenFocus:!0,selectionHighlight:!1,occurrencesHighlight:"off",matchBrackets:"never",lineDecorationsWidth:4,lineNumbersMinChars:2,glyphMargin:!1,minimap:{enabled:!1},renderOverviewRuler:!1,overviewRulerBorder:!1,hideCursorInOverviewRuler:!0,scrollBeyondLastLine:!1,diffWordWrap:d,renderSideBySide:(t=o.renderSideBySide)==null||t,diffHideUnchangedRegions:s,useInlineViewWhenSpaceIsLimited:(l=o.useInlineViewWhenSpaceIsLimited)!=null&&l,diffLineStyle:"background",diffAppearance:"auto",diffUnchangedRegionStyle:y,diffHunkActionsOnHover:!1,experimental:f};return _(ye(_(ye(_(_({},p),o),{experimental:f}),c===void 0?{}:{hideUnchangedRegions:c}),{diffHideUnchangedRegions:s,diffWordWrap:d}),g===void 0?{}:{scrollbar:g})}),qn=w(()=>m.value?"diff":"single"),Nl=P(qn.value),ae=P(!1),T=P(!1),ue=P(!1),Ne=P(null),jn=P(0),zt=P(0),le=P(!1),oe=P(!1);let rn=null,an=null,je=!1,Un=null;const Hl=w(()=>{var e,n,t;if(m.value){const o=(e=Q.value)==null?void 0:e.diffWordWrap;if(o==="inherit"){const a=(n=i.monacoOptions)==null?void 0:n.wordWrap;return a==null||String(a)!=="off"}return o==="on"}const l=(t=i.monacoOptions)==null?void 0:t.wordWrap;return l==null||String(l)!=="off"}),kn=w(()=>{var e;return!!m.value&&Ht((e=Q.value)!=null?e:{})});function Tt(e){return ee?.value===!0||!!e&&(!!e.closest('[data-markstream-virtual-timeline="1"], .markstream-virtual-timeline')||!!e.closest(".vue-recycle-scroller, [data-virtualizer], [data-virtual-scroll-root]"))}const Rt=w(()=>Tt(ge.value)),Se=w(()=>!ae.value&&(!!ue.value||(m.value||Rt.value||!Eo())&&!T.value)),_t=w(()=>Se.value||le.value),zl=w(()=>Se.value&&!le.value),Tl=w(()=>!ae.value&&!ue.value&&(Se.value||le.value||oe.value)),Rl=w(()=>T.value&&!ae.value?"ready":ue.value?"fallback":"pending"),Cn=P(!1),Ue=w(()=>Sn(i.node.code,i.node.loading===!0)),Dt=w(()=>m.value?i.node.diff===!0?i.node:ye(_({},i.node),{diff:!0}):Ue.value===i.node.code?i.node:ye(_({},i.node),{code:Ue.value})),ie=P(typeof((M=i.monacoOptions)==null?void 0:M.fontSize)=="number"?i.monacoOptions.fontSize:Number.NaN),z=P(ie.value),Vn=P(null),xn=P(null),_l=w(()=>{const e=ie.value,n=z.value;return typeof e=="number"&&Number.isFinite(e)&&e>0&&typeof n=="number"&&Number.isFinite(n)&&n>0}),Gn=w(()=>{var e;const n=Vn.value;if(typeof n=="number"&&Number.isFinite(n)&&n>0)return n;const t=(e=i.monacoOptions)==null?void 0:e.fontSize;if(typeof t=="number"&&Number.isFinite(t)&&t>0)return t;const l=z.value;return typeof l=="number"&&Number.isFinite(l)&&l>0?l:12}),Wt=w(()=>{var e;const n=xn.value;if(typeof n=="number"&&Number.isFinite(n)&&n>0)return n;const t=(e=i.monacoOptions)==null?void 0:e.lineHeight;return typeof t=="number"&&Number.isFinite(t)&&t>0?t:Gn.value===12?18:Math.max(12,Math.round(1.5*Gn.value))}),Jn=w(()=>Wt.value);function At(e){const n=String(e??"");return n?Math.max(1,n.split(/\r\n|\n|\r/).length):1}function It(e){return e.startsWith("-")&&!e.startsWith("---")}function qt(e){return e.startsWith("+")&&!e.startsWith("+++")}function jt(e){var n;const t=e.some(o=>It(o)),l=e.some(o=>qt(o));return t&&l||bn(i.node.language)&&(t||l)||bn(mt(String((n=i.node.raw)!=null?n:"")))&&(t||l)}const Dl=w(()=>kn.value?(function(){var e,n,t;const l=String((e=i.node.code)!=null?e:"").split(/\r\n|\n|\r/);if(jt(l))return Math.max(1,l.length);const o=String((n=i.node.originalCode)!=null?n:"").split(/\r\n|\n|\r/),a=String((t=i.node.updatedCode)!=null?t:"").split(/\r\n|\n|\r/);let r=0,u=o.length-1,s=a.length-1;for(;r<=u&&r<=s&&o[r]===a[r];)r++;let c=0;for(;u>=r&&s>=r&&o[u]===a[s];)c++,u--,s--;const d=Math.max(0,u-r+1),f=Math.max(0,s-r+1);return Math.max(1,r+d+f+c)})():(function(){var e;const n=String((e=i.node.code)!=null?e:"").split(/\r\n|\n|\r/);if(!jt(n)&&(i.node.originalCode!=null||i.node.updatedCode!=null))return Math.max(At(i.node.originalCode),At(i.node.updatedCode));let t=0,l=0;for(const o of n)It(o)?t++:(qt(o)||t++,l++);return Math.max(1,t,l)})()),Wl=w(()=>{var e;const n=(e=i.monacoOptions)==null?void 0:e.tabSize;return typeof n=="number"&&Number.isFinite(n)&&n>0?n:4}),Kn=w(()=>{var e;const n=(e=i.monacoOptions)==null?void 0:e.padding;return{top:typeof n?.top=="number"&&Number.isFinite(n.top)&&n.top>0?n.top:0,bottom:typeof n?.bottom=="number"&&Number.isFinite(n.bottom)&&n.bottom>0?n.bottom:0}}),Mn=w(()=>{const e=i.estimatedContentHeightPx;return typeof e=="number"&&Number.isFinite(e)&&e>0?e:null});function Pn(e){if(e==null)return null;const n=Math.ceil(e);return!Number.isFinite(n)||n<=0?null:Math.min(n,Math.ceil(Je()))}const un=w(()=>m.value?Math.ceil(Dl.value*Jn.value):Mn.value!=null?null:Math.ceil((e=>{const n=String(e??"");return n?Math.max(1,n.split(/\r\n|\n|\r/).length):1})(Ue.value)*(Wt.value+$n)+1)),Yn=w(()=>{if(m.value){const n=un.value,t=et();return Pn(t!=null&&n!=null?Math.max(n,t):t??n)}const e=Mn.value;return Pn(e??un.value)}),Al=w(()=>{const e=i.estimatedHeightPx;return typeof e=="number"&&Number.isFinite(e)&&e>0?e:null}),Qn=P(null),Xn=P(null);function Me(){const e=Qn.value;return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.round(e):null}function Zn(){return m.value&&i.stream!==!1&&(i.loading!==!1||Se.value||le.value||oe.value)}function et(){if(!Zn())return null;const e=Xn.value;return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.round(e):null}function Ut(e){if(!Zn()||e==null)return;const n=Math.min(Math.ceil(e),Math.ceil(Je()));if(!Number.isFinite(n)||n<=0)return;const t=et();Xn.value=t==null?n:Math.max(t,n)}const nt=w(()=>{const e=Me();return e??(Se.value||!T.value?Yn.value:null)}),Il=w(()=>{var e;const n=(e=i.monacoOptions)==null?void 0:e.fontFamily,t=Pn(Mn.value),l=Pn(un.value),o=_(_({fontSize:`${Gn.value}px`,lineHeight:`${Jn.value}px`,tabSize:Wl.value,boxSizing:"border-box",maxHeight:`${Je()}px`,overflow:"auto",paddingTop:`${Kn.value.top}px`,paddingBottom:`${Kn.value.bottom}px`},m.value?(function(a){if(a==null)return{};const r=Math.ceil(a),u=Math.ceil(Je()),s=Math.min(r,u);return r>=u?{height:`${s}px`,minHeight:`${s}px`}:{minHeight:`${r}px`}})(Yn.value):t!=null?{height:`${t}px`,minHeight:`${t}px`}:l!=null?{minHeight:`${l}px`}:{}),typeof n=="string"&&n.trim()?{"--markstream-code-font-family":n.trim()}:{});return o["--markstream-pre-line-number-top"]=`${Kn.value.top}px`,o["--markstream-code-padding-left"]="62px",o["--markstream-pre-line-number-width"]="36px",o["--markstream-pre-line-number-gap"]="0px",m.value&&(o["--markstream-pre-diff-line-height"]=`${Jn.value}px`,o["--markstream-pre-diff-pane-bottom-padding"]="10px"),o}),Vt=w(()=>nt.value!=null&&(!T.value||Me()!=null)),ql=w(()=>{const e=nt.value;if(e==null)return null;if(m.value)return Math.ceil(e);const n=Al.value,t=Mn.value;if(n==null||t==null)return Math.ceil(e);const l=Math.max(0,Math.ceil(n)-Math.ceil(t));return Math.ceil(e+l)}),jl=w(()=>{if(m.value&&Se.value)return;const e=nt.value;return Vt.value&&e!=null?{minHeight:`${e}px`}:void 0});function tt(){rn!=null&&typeof window<"u"&&window.clearTimeout(rn),rn=null}function Gt(){if(!m.value||!I.value||!T.value||le.value||oe.value)return!1;const e=S.value;return!!e&&el(e)}function Jt(e,n=!1,t={}){const l=Math.ceil(e),o=Me();if(o==null)return l;const a=t.allowBelowEstimatedFloor===!0||Gt();return l>=o||a?((n||a)&&I.value&&(Qn.value=null),l):o}function pe(){return new Promise(e=>{Z(()=>e())})}function Kt(){try{const e=S.value;if(!e)return null;const n=e.querySelector(".view-lines .view-line");if(n){const t=Math.ceil(n.getBoundingClientRect().height);if(t>0)return t}}catch{}return null}function lt(){var e,n,t,l,o;try{const a=m.value?(t=(n=(e=W())==null?void 0:e.getModifiedEditor)==null?void 0:n.call(e))!=null?t:W():te(),r=en(),u=(l=r?.EditorOption)==null?void 0:l.fontInfo;if(a&&u!=null){const s=(o=a.getOption)==null?void 0:o.call(a,u),c=s?.fontSize;if(typeof c=="number"&&Number.isFinite(c)&&c>0)return c}}catch{}try{const a=S.value;if(a){const r=a.querySelector(".view-lines .view-line");if(r)try{if(typeof window<"u"&&typeof window.getComputedStyle=="function"){const u=window.getComputedStyle(r).fontSize,s=u&&u.match(/^(\d+(?:\.\d+)?)/);if(s)return Number.parseFloat(s[1])}}catch{}}}catch{}return null}function Ve(e){var n,t;try{const a=en(),r=(n=a?.EditorOption)==null?void 0:n.lineHeight;if(r!=null){const u=(t=e?.getOption)==null?void 0:t.call(e,r);if(typeof u=="number"&&u>0)return u}}catch{}const l=Kt();if(l&&l>0)return l;const o=Number.isFinite(z.value)&&z.value>0?z.value:14;return Math.max(12,Math.round(1.35*o))}function Yt(e){var n,t,l;try{const u=en(),s=(n=u?.EditorOption)==null?void 0:n.padding;if(s!=null){const c=(t=e?.getOption)==null?void 0:t.call(e,s),d=typeof c?.top=="number"?c.top:0,f=typeof c?.bottom=="number"?c.bottom:0;if(d>0||f>0)return d+f}}catch{}const o=(l=Q.value)==null?void 0:l.padding,a=typeof o?.top=="number"?o.top:0,r=typeof o?.bottom=="number"?o.bottom:0;return a>0||r>0?a+r:m.value?24:0}function Qt(e,n){return typeof e!="number"||typeof n!="number"||e<1||nThis document defines the visual language and component specification for Kimi Web — design tokens, component primitives, the chat interface, theming, and style rules. All UI work is grounded in it: unified, restrained, token-driven, and themeable.
Every UI decision traces back to the following principles. Kimi Web is a local Agent tool for developers: quick scanning, long stretches of staring, often in the dark — the design serves the task, and is restrained, clinical, and density-first.
Collapse every visual decision into tokens. Color tokens keep the existing short names and fill out the semantics (lowering migration cost), while spacing, z-index, motion, and font-weight fill in the scales that are currently missing. Every token has: name, light value, dark value, and usage.
--<category>-<role>-<state>. For example --color-text-muted, --radius-md, --space-4. To reduce churn, the existing short names (--bg / --ink / --line / --blue …) are kept as compatibility aliases for one release cycle. Semantic-first, in three layers: background / text / border + accent + status colors. All colors are defined in light / dark pairs, with contrast ≥ 4.5:1.
--color-accent comes from --accent-primary, and --color-bg comes from the current light / dark surface. The semantic status colors (success / warning / danger / info) are independent palettes paired with the seeds, one set each for light / dark; they are not auto-derived from the seeds. Day-to-day reskinning usually only needs the 4 seeds, with the status colors fine-tuned as needed.| Token | Light | Dark | Usage |
|---|---|---|---|
| --color-bg | #ffffff | #0d1117 | Page background |
| --color-surface | #fafbfc | #161b22 | Panel / sidebar / card head |
| --color-surface-raised | #ffffff | #1c2128 | Raised card / dialog / input |
| --color-text | #14171c | #e8eaed | Body text / headings |
| --color-text-muted | #6b7280 | #9aa0a8 | Secondary text / placeholder |
| --color-line | #e7eaee | #2d333b | Divider / card border |
| --color-selected | #00000014 | #ffffff14 | Neutral selected fill (sidebar rows, list pickers) — translucent, never accent-tinted |
| --color-hover | #0000000d | #ffffff0d | Row hover wash — lighter than the selected fill (hover < selected); translucent, sits on any surface |
| --color-media-alpha-bg-1 | ≈#858585 | ≈#676b72 | Checkerboard square A of the <img> alpha canvas — color-mix of --color-bg/--color-text (52/48); applied via --media-alpha-canvas (16px period) |
| --color-media-alpha-bg-2 | ≈#6b6b6b | ≈#7a7e85 | Checkerboard square B (42/58) — both squares stay ≥3:1 against white and black; opaque images cover the canvas |
| --color-sidebar-bg | #fbfaf9 | #181817 | Sidebar surface — one step off --color-bg so the session column reads as its own plane |
| --color-accent | #1783ff | #58a6ff | Primary action / link / focus |
| --color-success | #0e7a38 | #3fb950 | Success / pass |
| --color-warning | #a9610a | #d29922 | Warning / pending |
| --color-danger | #c0392b | #f85149 | Danger / error / abort |
The four surface layers each have a role — choose by "raised layer / default flat layer / sunken layer / page background", and avoid treating --p-surface-raised as a universal background.
| Token | Light | Dark | Usage |
|---|---|---|---|
| --p-surface-raised | #ffffff | #1c2128 | Raised card / dialog / input (raised layer) |
| --p-surface | #fafbfc | #161b22 | Panel / sidebar / card head (default flat layer) |
| --p-surface-sunken | #f3f5f8 | #0d1117 | Code block / inline input / recessed area (sunken layer) |
| --p-bg | #ffffff | #0d1117 | Page background |
All focusable controls (button, input, link, menu item, switch, checkbox) use the focus-ring token uniformly; do not hand-write a box-shadow focus ring.
| Token | Value | Usage |
|---|---|---|
| --p-focus-ring | 0 0 0 3px var(--p-accent-soft) | Default focus ring (link, menu item, switch, checkbox) |
| --p-focus-ring-strong | 0 0 0 3px var(--p-accent-soft), 0 0 0 1px var(--p-accent) | Strong focus ring (button, primary action) |
The text-selection color uses --p-selection uniformly (light rgba(23,131,255,.18) / dark rgba(88,166,255,.32)), applied by the global ::selection rule; do not set a separate highlight background.
All disabled controls use opacity:.5 + cursor:not-allowed uniformly; do not separately grey out or recolor.
Kimi Web uses two font families: --font-ui (UI and body, Inter first) and --font-mono (code and monospace). Components always reference the variables; do not hard-code font names.
Body and UI use self-hosted Inter as the primary face. CJK and platform system UI fonts sit late in the fallback chain so Latin glyphs resolve to Inter while Chinese text can fall through to native CJK fonts:
--font-ui: "Inter Variable", "Inter", "Helvetica Neue", Arial, + "PingFang SC", "Microsoft YaHei", "Noto Sans SC", + -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, Ubuntu, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
Code, tool names, line numbers, diffs, etc. use JetBrains Mono (a self-hosted variable font), falling back to the system monospace:
--font-mono: "JetBrains Mono Variable", "JetBrains Mono", + ui-monospace, "SF Mono", Menlo, Consolas, monospace;
| Font | Source | Bundled | Usage |
|---|---|---|---|
| JetBrains Mono | @fontsource-variable/jetbrains-mono | ✓ self-hosted | monospace / code (--font-mono) |
| Inter | @fontsource-variable/inter/opsz.css + opsz-italic.css | ✓ self-hosted | UI / body / display (--font-ui, --font-display), wght 100-900, opsz 14-32, normal + italic |
| System UI / CJK fonts | operating system | — | late fallback for UI / body, not bundled |
var(--font-ui) / var(--font-mono); do not hard-code font names like 'Inter' / 'JetBrains Mono'.--font-ui (Inter first); code / monospace use --font-mono (JetBrains Mono).font-optical-sizing: auto is enabled globally.--font-ui fallback chain, after Inter and Western fallbacks.The user font-size preference writes --base-ui-font-size. Compact UI chrome and the sidebar follow it through --ui-font-size, while chat reading surfaces derive one readable step above it through --content-font-size.
The fixed product type tokens still define component defaults: UI controls / buttons / forms use --text-base (14px); reading body — including chat Markdown, message bubbles, etc. stays one step larger than compact chrome for readability; the sidebar session list follows that same readable step while keeping list density. Drop stray font-weight: 650 / 750; converge on two weights, 400 / 500 (regular / emphasis).
| Token | Value | Usage |
|---|---|---|
| --font-ui | "Inter Variable", "Inter", "Helvetica Neue", Arial… | UI & body (Inter first) |
| --font-mono | JetBrains Mono… | code, tool names, line numbers, diffs |
| --base-ui-font-size | 14px user preference | root setting that drives UI, reading body, and sidebar font sizes |
| --content-font-size | calc(base + 1px) | chat Markdown, message bubbles, composer |
| --leading-tight/normal/relaxed | 1.25 / 1.5 / 1.7 | headings / UI / long text |
| --weight-regular/medium | 400 / 500 | body / emphasis |
Icons use three size tokens uniformly. The global .p-ic default is 16px (--p-ic-md); components pick as needed, and random pixel sizes are forbidden.
| Token | Value | Usage |
|---|---|---|
| --p-ic-sm | 14px | small button, badge, menu item, inline link icon |
| --p-ic-md | 16px | default (button, icon button, toolbar) |
| --p-ic-lg | 20px | Toast status icon, empty-state illustration |
Icons always come from the centralized registry lib/icons.ts: in templates use the <Icon name size /> component (components/ui/Icon.vue); for v-html contexts (such as a tool glyph) use iconSvg(name, size). Do not hand-write <svg> — the scripts/check-style.mjs icon-from-registry rule flags stray SVGs. Icons come from Remix Icon (Apache-2.0), uniformly in a fill style (fill="currentColor", 24×24 source grid), with color following the text; size uses the three tokens below. The registry is bundled on demand by unplugin-icons at build time from @iconify-json/ri — only icons imported in lib/icons.ts end up in the production bundle, fully offline and tree-shaken. The whole site uses only this one icon family; do not mix in other icon libraries, and never hand-write SVG paths. When an icon is missing, add it to the registry — two static ~icons/ri/* imports (component + ?raw string) plus one entry in ICONS in lib/icons.ts; the import names (e.g. RiFolderOpenLine / RawFolderOpenLine) show the ri: icon id. Do not draw it in a component.
Do not use emoji as functional icons (the sole exception is the moon phases 🌑…🌘, used only in the "waiting for the Agent to respond" chat state). The Kimi brand mark (the 32×22 eye logo) is a brand asset and is not part of this icon system.
A few special graphics are not in the registry; each has a dedicated component maintained in one place, and must not be copied by hand: <ContextRing :pct /> (the Composer context progress ring, data-driven), <AuthStateIcon kind /> (the success / expired / error colored illustrations in the login flow), <Spinner /> (loading state). Status dots (such as in the Provider list) always use CSS dots (border-radius:50%), not SVG. The scripts/check-style.mjs icon-from-registry rule exempts the above and the brand mark; all other hand-written <svg> is flagged.
A 4px base grid. All spacing, gaps, and padding inside and outside components come from this scale — no arbitrary pixels.
High-density navigation lists like the sidebar share one rhythm, all on the 4px grid: in-row vertical padding --space-1 (4px), no margin between rows (the hover pill provides the separation); section gap (between logo / search / action buttons / group title / list) uniformly --space-2 (8px); between groups --space-2; the brand header is slightly looser at the top (--space-3). When building similar lists, reuse this scale — do not hand-write 1/6/7/10px.
Merge the existing 14 values into the nearest of 7 scale steps. Rule: the component type determines the radius, not the author's feel.
| Token | Value | Usage | Merged from |
|---|---|---|---|
| --radius-xs | 4px | small badge, inline tag | 2/3/4px → |
| --radius-sm | 6px | small button, icon button, menu item | 5/6px → |
| --radius-md | 8px | button, input, badge, card | 7/8/9px → |
| --radius-lg | 12px | dropdown panel | 10/12px → |
| --radius-xl | 16px | dialog, bottom Sheet, Composer | 14/16px → |
| --radius-2xl | 20px | accent container / large panel | 20px |
| --radius-full | 999px | pill badge, avatar, send button | 999px / 50% |
Shadows express only "elevation", never decoration (no colored glow). z-index is unified into a scale, eradicating 9999-style one-upping.
| Z-index Token | Value | Usage |
|---|---|---|
| --z-base | 0 | normal flow |
| --z-sticky | 100 | sticky header / sidebar |
| --z-dropdown | 200 | dropdown menu / tooltip |
| --z-overlay | 300 | overlay / bottom Sheet |
| --z-modal | 400 | dialog |
| --z-toast | 600 | toast |
| --z-max | 9999 | reserved: only this tier for extreme fallback |
| Token | Value | Usage |
|---|---|---|
| --ease-out | cubic-bezier(0.16, 1, 0.3, 1) | enter, hover, expand |
| --ease-in-out | cubic-bezier(0.4, 0, 0.2, 1) | panel width, layout changes |
| --duration-fast | 120ms | press, focus |
| --duration-base | 160ms | hover, show/hide |
| --duration-slow | 260ms | dialog, Sheet, layout |
@media (prefers-reduced-motion: reduce), all animation and transition durations drop to about 0.001ms (effectively off), and the MoonSpinner moon phase pauses on the current frame. Components should not check this individually; it is handled uniformly in the global styles. Layout sizes and responsive breakpoints are tokenized too: sidebar width, content reading-column width, and two global breakpoints. Components should not hard-code pixels.
| Token | Value | Usage |
|---|---|---|
| --p-sidebar-w | 264px | left session sidebar width |
| --p-content-max | 760px | chat reading-column max width (regular chat prose) |
| --p-content-wide | 920px | wide content (settings / panel) |
| --p-table-max | 1040px | desktop wide-table max width (see §04) |
| --p-table-cell-max | 700px | max width of a single table column; longer cell content wraps (see §04) |
| --p-bp-sm | 640px | mobile / desktop boundary |
| --p-bp-md | 980px | narrow / wide screen boundary |
Component primitives are the "smallest correct units" of the site UI. Each primitive exposes variants along only two dimensions — variant / size — with appearance driven by tokens, so it naturally supports light / dark mode and customizable theme colors.
| Scenario | Use |
|---|---|
| Primary action (submit / confirm) | Button variant=primary |
| Secondary action / cancel | Button secondary / ghost |
| Destructive action (delete / abort) | Button danger / danger-soft |
| Status marker | Badge |
| Toolbar filter / model switch | Pill |
| 2–4 mutually exclusive options | SegmentedControl |
| Top tabs | Tabs |
| Switch / multi-select | Switch / Checkbox |
| Floating content card / list action menu | Card / Menu |
| Inline notice / global toast | Banner / Toast |
| Dialog / confirmation · bottom panel (mobile) | Dialog / Sheet |
4 semantic variants × 3 sizes. The primary action primary takes its color from the current theme color (§05 can switch between the blue and black families). Radius uses --radius-md uniformly (small size --radius-sm), weight 600, with a visible focus ring.
<Button variant="primary" size="md" :loading="submitting">Save</Button> + // variant: primary | secondary | ghost | danger | danger-soft + // size: sm | md | lg
Unified into three sizes — 26 / 32 / 44px — with a light-grey hover background and a visible focus ring. Replaces the ad-hoc icon + click areas scattered across components today.
sm 26 / md 32; on touch devices the tap target should be ≥ 44px, so use lg 44px, satisfying the §01 accessibility principle (the mobile three-piece set uses lg). Collapsed into two kinds: Badge (status badge, with an optional status dot) and Pill (the clickable pill in the composer toolbar). Radius, font size, and padding are all unified.
Kbd renders a shortcut as keycaps — one block per key, never inline text like (⌘K). Caps are 18px tall (Badge sm rhythm): sunken surface, 1px border with a 2px bottom edge, 11px UI font, muted text. Typical placement: pushed to the row's trailing edge, opposite the label (e.g. the sidebar search row).
All cards across the site share one shell: flat, 1px border, --radius-md radius, no shadow. The structure is split into three parts — head / body / foot. Cards differ only in the head — in two tiers by visual weight, while the shell stays consistent:
accent-soft / warning-soft) to stand out from the message stream, signaling that the user must step in. The shell is exactly the same as the operation card.border-top hairlines, rather than each row being its own card.Unified 38px height (32px small), --radius-md radius, --color-surface-raised background, and a unified blue focus ring (0 0 0 3px accent-soft).
Inline code, code blocks, and diffs all use the monospace font (--p-font-mono). Code blocks have a filename title bar and a copy button. Diffs use + / - row colors to express additions and deletions — additions use a success light background, deletions use a danger light background, with no gradients.
jwt.verify(token) to verify the signature, returning 401 on failure.import { verify } from './jwt';
+
+ export function auth(token: string) {
+ return verify(token, process.env.JWT_SECRET!);
+ }One dialog primitive replaces 6 hand-written implementations: unified --radius-xl radius, --shadow-xl shadow, 20px head padding, right-aligned footer actions, and an IconButton close button.
md 440 / lg 640 / xl 760 (--p-content-max) — chosen by content weight. Height comes in two kinds: auto (default, grows with content up to max-height) and fixed (constant height min(680px, 100vh - 64px), with overflow scrolled inside the body). Content / multi-tab dialogs (settings, model picker, provider manager, folder browser) always use fixed so the frame size stays constant and doesn't jump when switching tabs or content length; short confirmation dialogs keep auto. Unified information architecture: status icon + title + description. The status color appears only on the icon, avoiding large colored areas that create visual noise.
Loaders fall into two categories by scenario — do not mix them:
MoonSpinner.vue and ActivityNotice.vue — and should be merged into a single MoonSpinner component, sized via tokens and supporting prefers-reduced-motion. All other loading states use the plain Spinner.Inline text link: the default is the accent color with no underline; on hover it shows an underline and darkens. The .muted variant uses the secondary text color. Used for in-text jumps, external links, "view all", and other lightweight actions.
Dropdown menu panel: raised surface + border + light shadow (--shadow-sm, flat-leaning). Menu items support icons, the current (active) state, the danger state, and the disabled state, with separators grouping items. On touch / mobile, use lg (≥44px row height) for menu items.
Mutually exclusive short option groups, commonly used for 2–4 option switches such as "light / dark / follow system". The current item is highlighted with a raised surface + subtle shadow.
Tabs with a bottom hairline, used for grouping and switching sibling content. The current tab is marked with accent text + an accent underline.
A two-state switch for settings that take effect immediately. 36×20 track with full radius, 16px knob; when on, the track turns accent and the knob slides right, with the transition driven by tokens.
A 17×17 checkbox. When checked it fills with the accent color and shows a white tick (inline SVG). Often paired with a text label.
A 32px default avatar with md radius; .sm is 24px. Can hold an initial or an icon; falls back to this placeholder when there is no image.
A centered placeholder for empty lists / panels: a 48px faint icon + title + hint, avoiding blank pages.
A 1px horizontal divider (--p-line); .p-divider-v is the vertical divider, used between inline elements.
A CSS-only hover hint, wrapped in .p-tip. Inverted background (--p-text / --p-bg), single line, no wrapping — carries only short notes.
An inline notice bar placed at the top of a content area. Three states — .info / .warning / .danger — each with a matching 18px icon.
A mobile bottom slide-up panel: xl top radius + drag handle, xl shadow. At ≤640px, dialogs become bottom-anchored Sheets.
A placeholder for loading content, using a breathing opacity animation (no gradients), following the no-gradient-text rule. Composed into titles / text lines / avatars.
An inline combination of "primary action + command text + copy", sitting between a button and a code block — used for install / onboarding / one-click execution. The primary action reuses Button primary; the command area uses a mono light-grey background.
The application top bar. Solid by default; the .frost variant is translucent + background blur, used only for sticky navigation bars, and is the sole exception to the no-glassmorphism rule (see §06).
A small group title for sidebar lists, used to section the content below (such as Workspaces in the sidebar). Spec: 13px / 700 / uppercase / letter-spacing .08em, color --color-fg-faint; left-aligned to the row's starting padding (--sb-pad-x), keeping the same indent as the group rows below. For scripts without case (such as Chinese), text-transform:uppercase simply has no effect — no special handling needed.
The message stream is the core of Kimi Web. The goal of the overhaul: have the 6 card types (Agent / Tool / Question / Approval / Swarm / Todo) share one card skeleton, distinguished only by the head icon and semantic color; and collapse the Composer into a single rounded container.
I looked at the structure of src/auth; it is currently based on a session cookie. The scope of the change is below — once you confirm, I'll start.
src/auth/middleware.ts, 42 lines changed. Allow?Wide markdown tables (desktop): regular chat prose stays within the 760px reading column (--p-content-max). On desktop a wide table may grow naturally with its content up to 1040px (--p-table-max), centred within the conversation pane; beyond that the excess scrolls horizontally inside the table's own wrapper — the page and the chat area never scroll sideways. A single column is capped at 700px (--p-table-cell-max), so long cell content wraps inside the cell instead of stretching the table. The conversation outline (TOC) keeps its usual position just outside the reading column; when a table grows past it and scrolls under the rail, the TOC is hidden temporarily and returns as soon as the table leaves, without touching the user's TOC setting. On mobile a table never breaks out of the reading column.
High-frequency calls like read_file / bash / grep are "operational noise" — if each one took a full card, parallel triggers would quickly drown out the conversation. The new strategy splits tool calls into three tiers by visual weight, pushing them as light as possible:
src/auth/middleware.ts, 42 lines changed.N tool calls · status).Question (needs an answer) and Approval (needs authorization) — they genuinely need the user's attention.Unified into a single rounded container: --radius-xl, with the whole border turning blue + a soft focus ring on focus. Toolbar controls all use the Pill / IconButton primitives, and the send button is a 32px circle.
--radius-xl · 16px) and one height; toolbar controls all use the Pill / IconButton primitives, and the send button is a 32px circle — it no longer drifts with the theme. See §02 --p-bp-sm for the breakpoint. This section only gives mobile-adaptation pointers for the chat interface; a full mobile mockup is out of scope for this spec.
Kimi Web uses one unified theme: the same components, fonts, radii, shadows, and surfaces — "reskinning" only changes colors. Colors are collapsed into 4 seed tokens — two theme colors + one light surface + one dark surface; the neutrals and accent are derived from them, and the semantic status colors (success / warning / danger) ship as independent palettes paired with the seeds, one set each for light / dark.
Day-to-day customization only needs these 4 seeds; the whole site's neutrals and accent change with them:
Within one theme, the theme color (accent) can switch among several color families. Two parallel families are provided today: blue (default, brand blue, carrying semantic emphasis) and black (neutral black, carrying the most restrained strong action). Both share the same components, fonts, radii, and surfaces — switching families only swaps the accent token set, with zero structural change; more families (green / purple, etc.) can be added later. The two cards below show the same primary button under the two families.
Driven by the two surfaces --surface-light / --surface-dark: whichever surface is current derives the corresponding foreground, border, shadow, and status colors. Switching light / dark simply swaps between these two sets of derived tokens, with zero structural change.
Anti-pattern rules that all UI code must follow. These rules are also the basis of the check-style detection script, one-to-one with a warning.
| Rule ID | What it detects | Action |
|---|---|---|
| no-gradient-text | gradient text / gradient background | Forbidden |
| no-glassmorphism | backdrop-filter: blur (TopBar sticky nav bar is the sole exception) | TopBar exempt |
| no-color-glow | colored / large-radius box-shadow glow | Forbidden |
| no-emoji-icon | using emoji as a functional icon (the moon phases 🌑…🌘 are the sole exception, and only in the "waiting for the Agent to respond" chat state; all other loading states use the plain Spinner) | Moon phase exempt |
| no-hardcoded-hex | unregistered hex color inside a component <style> | Warning |
| no-hardcoded-font | hard-coded font-family in a component (e.g. 'Inter') instead of var(--font-ui) | Warning |
| radius-from-scale | radius value not in {4,6,8,12,16,20,999} | Warning |
| z-from-scale | z-index using an unregistered large number | Warning |
| weight-from-scale | font-weight not in {400,500} | Warning |
Every interactive primitive should define the following states where applicable; missing ones are flagged by the style rules. focus-visible always uses --p-focus-ring (appears only on keyboard focus, see §08); disabled is uniformly opacity:.5.
| State | Button | Input | Card | Menu item | Switch |
|---|---|---|---|---|---|
| default | ✓ | ✓ | ✓ | ✓ | ✓ |
| hover | ✓ | ✓ | ✓ | ✓ | — |
| active / pressed | ✓ | — | — | — | — |
| focus-visible | ✓ | ✓ | — | — | ✓ |
| disabled | ✓ | ✓ | — | ✓ | — |
| loading | ✓ | — | — | — | — |
| selected / active | — | — | — | ✓ | ✓ |
| error | — | ✓ | — | — | — |
| readonly | — | ✓ | — | — | — |
MoonSpinner component; waiting states such as ActivityNotice reuse it rather than implementing their own moon phase. It is the sole exception to the no-emoji-icon rule; all other loading states use the plain Spinner. backdrop-filter: blur is banned site-wide, with the sole exception of the .frost variant of TopBar — and only in the one place of the "sticky navigation bar", used to stay readable over scrolling content. No other component (card, dialog, Toast, panel) may use glassmorphism; violations are flagged under no-glassmorphism. The structural spec for the app shell (three-column grid + right preview panel) and the left session sidebar. These are business-agnostic "skeletons" — components, fonts, radii, and surfaces are reused from §02 / §03, but layout and alignment have their own conventions.
On desktop it is a single-row 5-track grid: the sidebar and the right panel each occupy a permanent auto track, with the conversation column in the middle; two 0-width tracks are for the ResizeHandles.
grid-template-columns: auto 0 minmax(0, 1fr) 0 auto; + /* sidebar ↑ ↑handle ↑conversation ↑handle ↑right panel (auto) */
| Token | Value | Usage |
|---|---|---|
| sidebar width | 270px default (adjustable) | expanded sidebar width, changed by dragging the ResizeHandle; should approach §02's --p-sidebar-w (264px) |
| --preview-w | 460px | width of the right preview panel when open |
| --panel-head-h | 48px | unified height for all right panel heads + the conversation column head, so the hairline runs as one line |
| --p-bp-sm | 640px | ≤640 switches to a mobile single column (top bar + conversation), no sidebar / handle / right panel |
0 ↔ var(--preview-w) (when open it squeezes the conversation column, rather than switching templates).min-height:0; min-width:0, so only the inner scroll containers scroll and the page itself does not scroll.--sb-*)All sidebar rows (group head, session row, New chat button) share 4 custom properties, so the "session title" aligns precisely under the "workspace name".
| Token | Value | Usage |
|---|---|---|
| --sb-inset | 12px | row box (hover/selected pill) inset from the sidebar edges — matches the brand header's 12px padding |
| --sb-pad-x | 20px | content start x (= --sb-inset + 8px row padding) |
| --sb-gutter | 16px | leading icon slot width — matches the workspace folder icon so the session title aligns under the workspace name |
| --sb-gap | 6px | gap between the icon slot and the text |
--sb-pad-x + --sb-gutter + --sb-gap. The group head has a folder icon and the session row has a status slot; both icons are the same width and position, so the titles align naturally. The sidebar from top to bottom: brand header → New chat → search → grouped list (workspace head + session rows) → settings footer. Controls reuse the §03 primitives as much as possible. The sidebar sits on --color-sidebar-bg (one step off --color-bg: warm off-white in light, near-black in dark — the session column reads as its own plane; the hairline still separates it from the conversation pane). Vertical rhythm: the brand header keeps 12px padding (on macOS desktop the left padding grows to 80px to clear the traffic lights); rows inside the actions group (New chat + search) stack flush (0 gap, same rhythm as the list rows); adjacent groups are separated by 12px. Row hover uses --sb-hover (= the global --color-hover wash); the selected row uses --color-selected — neutral, never the accent.
| Block | Use | Note |
|---|---|---|
| Brand header | logo + name + collapse IconButton (right-aligned) | on Windows / web the brand is left and the collapse IconButton sm is right-aligned inside the header; the logo is animated (a blinking eye). On macOS desktop the header is a bare drag strip (brand hidden, traffic lights + resident floating toggle over it) |
| New chat | full-width left-aligned button (custom) | same rhythm as the session rows in the list (left-aligned, hover = --sb-hover). Do not use Button (centered, breaks the rhythm) |
| Search | bare search row (custom) | no border, hover/focus shows a sunken background; icon + label, with the Kbd keycaps (⌘K / Ctrl K) pushed to the trailing edge — label and shortcut are justified apart. Do not use Input (the 38px bordered version is too heavy). Last fixed row above the list — its wrapper carries the scroll-linked seam |
| Section label | .p-section-label | uppercase muted small titles like "Workspaces" |
| Workspace head / session row | see next two sections | share --sb-* alignment |
| Settings footer | full-width left-aligned button (custom) | pinned row under the session list, separated by a 1px --line top border; icon + label, same list-style family as New chat |
A session row is an inset rounded pill, structured as: status slot → title → time → attention Badge → kebab.
| Part | Rule |
|---|---|
| Container | padding: 8px 8px inside the list's --sb-inset gutter, radius-sm; no fixed/min height — row height is font-driven (title line-height: --leading-tight, ≈16px) → ≈32px total, the sidebar-wide row rhythm. The hover kebab is absolutely positioned so it never forces the row taller (no hover jitter). hover = --sb-hover (the global --color-hover wash); active = --color-selected — neutral, no accent tint, no border, no weight change |
| Status slot (lead) | fixed --sb-gutter width; running = Spinner sm, otherwise unread = 7px accent dot |
| Title | flex:1 with truncation; double-click enters inline rename (compact input, not Input) |
| Time | mono xs, fg-faint; yields to the kebab on hover |
| Attention Badge | Badge sm: info (needs answer) / warning (needs approval) / danger (aborted) |
| kebab | IconButton sm, shown on hover; dropdown uses Menu/MenuItem |
| Archive confirmation | replaces the title area, Button sm (danger confirm / secondary cancel) |
The group head and session rows share --sb-*: folder icon (open/closed) → name, with the kebab and "+" revealed on hover.
--sb-gap before the name — it does not pad out the --sb-gutter slot.--color-text-muted, one step lighter than session titles), so group heads read as grouping labels. No path subtitle; hovering the name shows the full root path in a Tooltip.IconButton sm inside a floating actions layer anchored to the row's right edge — no reserved layout space, so the name uses the full row width when idle. Shown on hover, keyboard focus, or while the menu is open; the layer backs itself with the sidebar surface (container background) plus the row hover wash (an ::after shown only while the row is hovered), so its color exactly equals the row's current background and the overlapped name tail doesn't bleed through (hidden via opacity:0, staying in the tab order).The "load more / show less" control at the bottom of each workspace group is a session-row-shaped compact list control (same family as search, New chat, inline rename — not a Button). It doubles as the pagination trigger and the in-group expand / collapse toggle.
| Part | Rule |
|---|---|
| Container | session-row pill: display:flex; gap:--sb-gap; padding:8px …, no fixed/min height (font-driven, ≈32px like a session row), same padding as a session row, radius-sm; hover = --sb-hover (no text recolor); :focus-visible uses --p-focus-ring |
| Lead slot | empty, --sb-gutter wide, so the label's start x aligns with the session titles (--sb-pad-x + --sb-gutter + --sb-gap) |
| Label | font-ui, text-xs, --color-text; flex:1, truncated |
| Behavior | "Load more" fetches the next page and auto-expands; once more than the first page is loaded, "Show less" appears and collapses back to the first page (view-layer trim — data is kept, no refetch); "Show all" re-expands |
A 4px vertical drag bar, layered over the 1px column border (margin: 0 -2px makes the whole 4px grabbable), turning accent on hover / drag.
| Rule | Value |
|---|---|
| Width / cursor | 4px / col-resize |
| Normal / active | transparent / accent fill |
| Layer | --z-dropdown, above pane-level sticky chrome (chat dock at --z-sticky) so the overhang stays visible and grabbable |
| Behavior | panel width follows the pointer 1:1 while dragging (the parent disables transitions to avoid lag); on release it is persisted to localStorage |
The right panels (file preview / Diff / thinking / sub-agent / side chat) share one track and one head primitive.
PanelHeader primitive (48px = --panel-head-h), the same height as the conversation column head, so the hairline runs as one line.0 → var(--preview-w), smoothly squeezing the conversation column.position:fixed; inset:0).Kimi Web is a local developer tool; it does not target a specific WCAG conformance level, nor maintain a full screen-reader QA matrix. This section collects only the rules that are "low-cost, don't hurt the look, and directly benefit keyboard-heavy users", as the baseline contract for each primitive; the more expensive, lower-ROI parts (such as real-time announcement orchestration for streaming output) are not mandatory for now.
:focus-visible (not :focus). It appears only on keyboard focus; mouse clicks don't trigger it, so it doesn't pollute the mouse-driven visual; the ring's strength is tuned uniformly with --p-focus-ring, not overridden per place. Anything doable with a mouse must also be doable with a keyboard; Tab order follows the DOM, with no invented skipping. Composite controls define their keyboard model per the table below; a missing model is treated as incomplete:
| Control | Keyboard behavior |
|---|---|
| Dialog | Tab cycles within the dialog (focus trap); Esc closes; focus returns to the trigger element after closing. |
| Menu | ↑ / ↓ move the highlight, Enter selects, Esc closes. |
| Tabs | ← / → switch tabs (roving tabindex); only the current tab is in the Tab sequence. |
| Switch / Segmented | ← / → or Space / Enter to toggle. |
:focus-visible + --p-focus-ring (primary actions may use --p-focus-ring-strong).outline: none is forbidden. To remove the default outline, you must provide an equivalent replacement style.aria-label — IconButton already enforces this with a required label prop.role="dialog" + aria-modal="true", with the title as the dialog's accessible name.aria-hidden="true" to avoid being read out by screen readers.Desktop click targets ≥ 32px; touch devices ≥ 44px (consistent with the §01 principle and the IconButton lg tier).
Handled uniformly in the global styles per §02's @media (prefers-reduced-motion: reduce); components do not check this individually. The MoonSpinner moon phase pauses on the current frame.
Screen-reader announcements are not a mandatory contract in this product. Short hints like Toast can use role="status" / aria-live; chat streaming output is currently not announced word-by-word, which is an acceptable trade-off, to be added later if a real need arises.
0&&(R=p+n.verticalPadding-n.rowHeight[I]);var W;n.width-M>=m+n.horizontalPadding?W=(n.height+R)/(M+m+n.horizontalPadding):W=(n.height+R)/n.width,R=p+n.verticalPadding;var x;return n.width{var f=i(548),r=i(140).CoSELayout,v=i(140).CoSENode,t=i(140).layoutBase.PointD,s=i(140).layoutBase.DimensionD,o=i(140).layoutBase.LayoutConstants,c=i(140).layoutBase.FDLayoutConstants,l=i(140).CoSEConstants,T=function(d,N){var b=d.cy,A=d.eles,S=A.nodes(),V=A.edges(),X=void 0,Z=void 0,D=void 0,_={};d.randomize&&(X=N.nodeIndexes,Z=N.xCoords,D=N.yCoords);var n=function(x){return typeof x=="function"},m=function(x,Q){return n(x)?x(Q):x},p=f.calcParentsWithoutChildren(b,A),E=function W(x,Q,z,Y){for(var rt=Q.length,$=0;$
\` around edge labels.
+ *
+ * TODO: We should probably remove this in a future release.
+ */
+ p {
+ margin: 0;
+ padding: 0;
+ display: inline;
+ }
+ rect {
+ opacity: 0.5;
+ background-color: ${e.edgeLabelBackground};
+ fill: ${e.edgeLabelBackground};
+ }
+ text-align: center;
+ }
+
+ /* For html labels only */
+ .labelBkg {
+ background-color: ${e.edgeLabelBackground};
+ }
+
+ .node .cluster {
+ // fill: ${bt(e.mainBkg,.5)};
+ fill: ${bt(e.clusterBkg,.5)};
+ stroke: ${bt(e.clusterBorder,.2)};
+ box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
+ stroke-width: 1px;
+ }
+
+ .cluster text {
+ fill: ${e.titleColor};
+ }
+
+ .cluster span,p {
+ color: ${e.titleColor};
+ }
+ /* .cluster div {
+ color: ${e.titleColor};
+ } */
+
+ div.mermaidTooltip {
+ position: absolute;
+ text-align: center;
+ max-width: 200px;
+ padding: 2px;
+ font-family: ${e.fontFamily};
+ font-size: 12px;
+ background: ${e.tertiaryColor};
+ border: 1px solid ${e.border2};
+ border-radius: 2px;
+ pointer-events: none;
+ z-index: 100;
+ }
+
+ .flowchartTitleText {
+ text-anchor: middle;
+ font-size: 18px;
+ fill: ${e.textColor};
+ }
+ ${pe()}
+`,"getStyles"),Tr=_r,Dr=d((e,t,a,s)=>{t.forEach(i=>{Pr[i](e,a,s)})},"insertMarkers"),Br=d((e,t,a)=>{k.trace("Making markers for ",a),e.append("defs").append("marker").attr("id",a+"_"+t+"-extensionStart").attr("class","marker extension "+t).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),e.append("defs").append("marker").attr("id",a+"_"+t+"-extensionEnd").attr("class","marker extension "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},"extension"),Nr=d((e,t,a)=>{e.append("defs").append("marker").attr("id",a+"_"+t+"-compositionStart").attr("class","marker composition "+t).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",a+"_"+t+"-compositionEnd").attr("class","marker composition "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"composition"),Ir=d((e,t,a)=>{e.append("defs").append("marker").attr("id",a+"_"+t+"-aggregationStart").attr("class","marker aggregation "+t).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",a+"_"+t+"-aggregationEnd").attr("class","marker aggregation "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"aggregation"),Cr=d((e,t,a)=>{e.append("defs").append("marker").attr("id",a+"_"+t+"-dependencyStart").attr("class","marker dependency "+t).attr("refX",6).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",a+"_"+t+"-dependencyEnd").attr("class","marker dependency "+t).attr("refX",13).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"dependency"),Or=d((e,t,a)=>{e.append("defs").append("marker").attr("id",a+"_"+t+"-lollipopStart").attr("class","marker lollipop "+t).attr("refX",13).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("circle").attr("stroke","black").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6),e.append("defs").append("marker").attr("id",a+"_"+t+"-lollipopEnd").attr("class","marker lollipop "+t).attr("refX",1).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("circle").attr("stroke","black").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6)},"lollipop"),Rr=d((e,t,a)=>{e.append("marker").attr("id",a+"_"+t+"-pointEnd").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",6).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),e.append("marker").attr("id",a+"_"+t+"-pointStart").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",4.5).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},"point"),Ar=d((e,t,a)=>{e.append("marker").attr("id",a+"_"+t+"-circleEnd").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),e.append("marker").attr("id",a+"_"+t+"-circleStart").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},"circle"),zr=d((e,t,a)=>{e.append("marker").attr("id",a+"_"+t+"-crossEnd").attr("class","marker cross "+t).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),e.append("marker").attr("id",a+"_"+t+"-crossStart").attr("class","marker cross "+t).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},"cross"),Mr=d((e,t,a)=>{e.append("defs").append("marker").attr("id",a+"_"+t+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")},"barb"),Pr={extension:Br,composition:Nr,aggregation:Ir,dependency:Cr,lollipop:Or,point:Rr,circle:Ar,cross:zr,barb:Mr},Fr=Dr,C=A()?.block?.padding??8;function St(e,t){if(e===0||!Number.isInteger(e))throw new Error("Columns must be an integer !== 0.");if(t<0||!Number.isInteger(t))throw new Error("Position must be a non-negative integer."+t);if(e<0)return{px:t,py:0};if(e===1)return{px:0,py:t};const a=t%e,s=Math.floor(t/e);return{px:a,py:s}}d(St,"calculateBlockPosition");var Wr=d(e=>{let t=0,a=0;for(const s of e.children){const{width:i,height:c,x:r,y:n}=s.size??{width:0,height:0,x:0,y:0};if(k.debug("getMaxChildSize abc95 child:",s.id,"width:",i,"height:",c,"x:",r,"y:",n,s.type),s.type==="space")continue;const l=i/(s.widthInColumns??1);l>t&&(t=l),c>a&&(a=c)}return{width:t,height:a}},"getMaxChildSize");function ot(e,t,a=0,s=0){k.debug("setBlockSizes abc95 (start)",e.id,e?.size?.x,"block width =",e?.size,"siblingWidth",a),e?.size?.width||(e.size={width:a,height:s,x:0,y:0});let i=0,c=0;if(e.children?.length>0){for(const x of e.children)ot(x,t);const r=Wr(e);i=r.width,c=r.height,k.debug("setBlockSizes abc95 maxWidth of",e.id,":s children is ",i,c);for(const x of e.children)x.size&&(k.debug(`abc95 Setting size of children of ${e.id} id=${x.id} ${i} ${c} ${JSON.stringify(x.size)}`),x.size.width=i*(x.widthInColumns??1)+C*((x.widthInColumns??1)-1),x.size.height=c,x.size.x=0,x.size.y=0,k.debug(`abc95 updating size of ${e.id} children child:${x.id} maxWidth:${i} maxHeight:${c}`));for(const x of e.children)ot(x,t,i,c);const n=e.columns??-1;let l=0;for(const x of e.children)l+=x.widthInColumns??1;let g=e.children.length;n>0&&n l?(e-i)*(e-i)+(t-s)*(t-s):v-c},Er=function(e,t,a){for(var n,i,s,o,u,l=0,v=0;v
"):p,t.labelStyle,!0,!0);if(P(A())){const v=x.children[0],S=D(x);o=v.getBoundingClientRect(),S.attr("width",o.width),S.attr("height",o.height)}const w=t.padding/2;return D(x).attr("transform","translate( "+(o.width>b.width?0:(b.width-o.width)/2)+", "+(b.height+w+5)+")"),D(g).attr("transform","translate( "+(o.width1&&arguments[1]!==void 0?arguments[1]:0,a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,n=1/0,i=t;i1&&arguments[1]!==void 0?arguments[1]:0,a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,n=-1/0,i=t;i1&&arguments[1]!==void 0?arguments[1]:0,a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,n=0,i=0,s=t;s1&&arguments[1]!==void 0?arguments[1]:0,a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,n=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,s=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0;n?e=e.slice(t,a):(a1&&arguments[1]!==void 0?arguments[1]:!0,a=this[0],n=a.cy();if(n.styleEnabled()&&a){a._private.styleDirty&&(a._private.styleDirty=!1,n.style().apply(a));var i=a._private.style[e];return i??(t?n.style().getDefaultProperty(e):null)}},numericStyle:function(e){var t=this[0];if(t.cy().styleEnabled()&&t){var a=t.pstyle(e);return a.pfValue!==void 0?a.pfValue:a.value}},numericStyleUnits:function(e){var t=this[0];if(t.cy().styleEnabled()&&t)return t.pstyle(e).units},renderedStyle:function(e){var t=this.cy();if(!t.styleEnabled())return this;var a=this[0];if(a)return t.style().getRenderedStyle(a,e)},style:function(e,t){var a=this.cy();if(!a.styleEnabled())return this;var n=!1,i=a.style();if(Me(e)){var s=e;i.applyBypass(this,s,n),this.emitAndNotify("style")}else if(he(e))if(t===void 0){var o=this[0];return o?i.getStylePropertyValue(o,e):void 0}else i.applyBypass(this,e,t,n),this.emitAndNotify("style");else if(e===void 0){var u=this[0];return u?i.getRawStyle(u):void 0}return this},removeStyle:function(e){var t=this.cy();if(!t.styleEnabled())return this;var a=!1,n=t.style(),i=this;if(e===void 0)for(var s=0;s