@nimblebrain/synapse 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -4
- package/dist/{chunk-JVKQNMAP.js → chunk-5AR3IROW.js} +11 -6
- package/dist/chunk-5AR3IROW.js.map +1 -0
- package/dist/{chunk-XGJAADVV.js → chunk-AGN3NDOK.js} +25 -36
- package/dist/chunk-AGN3NDOK.js.map +1 -0
- package/dist/chunk-DOKVNVTI.js +162 -0
- package/dist/chunk-DOKVNVTI.js.map +1 -0
- package/dist/chunk-IA2M4GTK.cjs +170 -0
- package/dist/chunk-IA2M4GTK.cjs.map +1 -0
- package/dist/{chunk-A4ENX6XW.cjs → chunk-QHS7BJLI.cjs} +28 -39
- package/dist/chunk-QHS7BJLI.cjs.map +1 -0
- package/dist/{chunk-ANUZ7WFQ.cjs → chunk-VMUZMVKW.cjs} +11 -6
- package/dist/chunk-VMUZMVKW.cjs.map +1 -0
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/connect.iife.global.js +25 -25
- package/dist/{detect-DSnLT-_m.d.ts → detect-C_qmYsh9.d.cts} +6 -0
- package/dist/{detect-DSnLT-_m.d.cts → detect-Dfd0upoz.d.ts} +6 -0
- package/dist/host/index.cjs +14 -14
- package/dist/host/index.d.cts +10 -3
- package/dist/host/index.d.ts +10 -3
- package/dist/host/index.js +2 -2
- package/dist/index.cjs +13 -9
- package/dist/index.d.cts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +3 -3
- package/dist/react/index.cjs +5 -5
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/synapse-runtime.iife.global.js +25 -25
- package/dist/synapse-ui.iife.global.js +1 -1
- package/dist/{types-C9utGXv_.d.cts → types-BSZasM8q.d.cts} +39 -1
- package/dist/{types-C9utGXv_.d.ts → types-BSZasM8q.d.ts} +39 -1
- package/dist/ui/index.cjs +34 -1
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +34 -1
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -6
- package/dist/chunk-A4ENX6XW.cjs.map +0 -1
- package/dist/chunk-ANUZ7WFQ.cjs.map +0 -1
- package/dist/chunk-JVKQNMAP.js.map +0 -1
- package/dist/chunk-KTXASIFJ.cjs +0 -75
- package/dist/chunk-KTXASIFJ.cjs.map +0 -1
- package/dist/chunk-VXTMI266.js +0 -73
- package/dist/chunk-VXTMI266.js.map +0 -1
- package/dist/chunk-XGJAADVV.js.map +0 -1
- package/dist/ui/fonts.cjs +0 -19
- package/dist/ui/fonts.cjs.map +0 -1
- package/dist/ui/fonts.d.cts +0 -30
- package/dist/ui/fonts.d.ts +0 -30
- package/dist/ui/fonts.js +0 -17
- package/dist/ui/fonts.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){'use strict';var
|
|
1
|
+
(function(){'use strict';var he=new Set(["auto","block","swap","fallback","optional"]),Te={"--color-background-primary":"#ffffff","--color-background-secondary":"#fafafa","--color-background-tertiary":"#f3f4f6","--color-text-primary":"#111827","--color-text-secondary":"#6b7280","--color-text-tertiary":"#9ca3af","--color-text-accent":"#2563eb","--nb-color-accent-foreground":"#ffffff","--color-border-primary":"#e5e7eb","--color-border-secondary":"#d1d5db","--color-ring-primary":"#2563eb","--nb-color-danger":"#dc2626","--nb-color-success":"#059669","--nb-color-warning":"#f59e0b","--nb-color-warm":"#d4620a","--nb-color-warm-light":"#fef5ee","--nb-color-processing":"#7c3aed","--nb-color-processing-light":"#f3eeff","--nb-color-info-light":"#eef4ff"},be={"--color-background-primary":"#18181b","--color-background-secondary":"#27272a","--color-background-tertiary":"#2f2f34","--color-text-primary":"#fafafa","--color-text-secondary":"#a1a1aa","--color-text-tertiary":"#71717a","--color-text-accent":"#818cf8","--nb-color-accent-foreground":"#ffffff","--color-border-primary":"#3f3f46","--color-border-secondary":"#52525b","--color-ring-primary":"#818cf8","--nb-color-danger":"#f87171","--nb-color-success":"#34d399","--nb-color-warning":"#fbbf24","--nb-color-warm":"#fb923c","--nb-color-warm-light":"#3a2a1e","--nb-color-processing":"#a78bfa","--nb-color-processing-light":"#2a2440","--nb-color-info-light":"#1e2a44"},ke={light:Te,dark:be};function Pe(e,n){if(typeof document>"u")return;let o=document.documentElement.style;for(let[s,a]of Object.entries(ke[e]))o.setProperty(s,a);if(n&&typeof n=="object")for(let[s,a]of Object.entries(n))typeof s=="string"&&typeof a=="string"&&o.setProperty(s,a);}var S=new Set,G="";function A(e){return Array.isArray(e)?JSON.stringify(e.map(n=>[n.family,n.src,n.weight,n.style,n.display])):""}function x(e){if(!Array.isArray(e))return;let n=e.filter(we);if(!(e.length>0&&n.length===0))return n}function Ie(e){let n=x(e);if(n===void 0||typeof document>"u"||!document.fonts||typeof FontFace>"u")return;let o=A(n);if(o!==G){G=o;for(let s of S)try{document.fonts.delete(s);}catch{}S.clear();for(let s of n)try{let a=new FontFace(s.family,s.src,{weight:s.weight,style:s.style,display:s.display&&he.has(s.display)?s.display:"swap"});document.fonts.add(a),S.add(a);}catch{}}}function we(e){if(!e||typeof e!="object")return false;let n=e;return typeof n.family=="string"&&typeof n.src=="string"}function W(e,n,o){Pe(e,n),Ie(o);}function R(e){typeof document<"u"&&document.documentElement.setAttribute("data-theme",e.mode),W(e.mode,e.tokens,e.fontFaces);}function O(e){try{if(e?.matchMedia?.("(prefers-color-scheme: dark)").matches)return "dark"}catch{}return "light"}function h(e,n){return e==="light"||e==="dark"?e:n}var T=class extends Error{constructor(n,o){super(`"${n}" is not supported by the "${o}" host`),this.name="HostUnsupportedError";}},_="synapse-ui-data",V="ui-lifecycle-iframe-ready",Z="ui-lifecycle-iframe-render-data",Y="ui-size-change",q="link",B="prompt",U="openai:set_globals",Q="2026-01-26",X="ui/initialize",$="ui/notifications/initialized",J="ui/notifications/tool-result",ee="ui/notifications/host-context-changed",te="ui/notifications/size-changed",ne="ui/open-link",oe="ui/message",re="ui/resource-teardown",se="tools/call";function ie(e,n){let o=()=>e.openai,s=o()?.toolOutput??null,a={mode:h(o()?.theme,"light"),tokens:{}},c=new Set,u=new Set,f=false,m=i=>{if(f)return;let l=i.detail?.globals;if(l){if("toolOutput"in l&&l.toolOutput!=null){s=l.toolOutput;for(let p of c)p(s);}if("theme"in l&&l.theme!=null){let p=h(l.theme,a.mode);if(p!==a.mode){a={mode:p,tokens:{}};for(let b of u)b(a);}}}};return {host:"chatgpt",getData:()=>s,onData(i){return c.add(i),()=>c.delete(i)},getTheme:()=>a,onTheme(i){return u.add(i),()=>u.delete(i)},async callTool(i,l){let p=o()?.callTool;if(!p)throw new T("callTool","chatgpt");return await p(i,l??{})},sendPrompt(i){let l=o();(l?.sendFollowUpMessage??l?.sendFollowupMessage)?.({prompt:i});},openLink(i){let l=o()?.openExternal;l?l({href:i}):e.open(i,"_blank","noopener,noreferrer");},resize(){},capabilities(){let i=o();return {pull:typeof i?.callTool=="function",sendPrompt:typeof i?.sendFollowUpMessage=="function"||typeof i?.sendFollowupMessage=="function",openLink:true}},start(){e.addEventListener(U,m,{passive:true}),s=o()?.toolOutput??s;},destroy(){f||(f=true,e.removeEventListener(U,m),c.clear(),u.clear());}}}function M(e,n){if(!e)return null;let s=e.getElementById(n)?.textContent;if(!s)return null;try{return JSON.parse(s)??null}catch{return null}}function L(e){if(e==null||typeof e!="object")return null;let n=e,o=n.renderData,s=o!=null&&typeof o=="object"?o:n;return s.toolOutput!=null?s.toolOutput:s.structuredContent!=null?s.structuredContent:s}function ae(e,n){let o=n.dataElementId??_,s=null,a={mode:O(e),tokens:{}},c=new Set,u=false,f=null,m=i=>{if(u)return;let l=h(i.matches?"dark":"light",a.mode);if(l!==a.mode){a={mode:l,tokens:{}};for(let p of c)p(a);}};return {host:"generic",getData:()=>s,onData(){return ()=>{}},getTheme:()=>a,onTheme(i){return c.add(i),()=>c.delete(i)},async callTool(i){throw new T("callTool","generic")},sendPrompt(){},openLink(i){e.open(i,"_blank","noopener,noreferrer");},resize(){},capabilities(){return {pull:false,sendPrompt:false,openLink:true}},start(){s=M(e.document,o);try{f=e.matchMedia?.("(prefers-color-scheme: dark)")??null,f?.addEventListener?.("change",m);}catch{f=null;}},destroy(){u||(u=true,f?.removeEventListener?.("change",m),f=null,c.clear());}}}var Ee="synapse/fontFaces";function Ce(e){return x(e?.[Ee])}function ce(e,n){return Ce(n)??e}var ve=3e4;function le(e,n){let o=n.dataElementId??_,s=n.autoResize!==false,a=null,c={mode:O(e),tokens:{}},u=new Set,f=new Set,m=false,i=false,l=1,p=new Map,b=-1,E=null,k=null,H=()=>e.parent??e;function P(t){H().postMessage(t,"*");}function F(t){i||P(t);}function N(t,r){P({jsonrpc:"2.0",method:t,params:r??{}});}function C(t,r){let d=l++;return new Promise((g,y)=>{let w=setTimeout(()=>{p.delete(d),y(new Error(`"${t}" timed out`));},ve);p.set(d,{resolve:g,reject:y,timer:w}),P({jsonrpc:"2.0",id:d,method:t,params:r??{}});})}function z(t){if(t!=null){a=t;for(let r of u)r(t);}}function D(t){if(!t||typeof t!="object")return;let{mode:r,tokens:d,fontFaces:g}=c,y=false;if(t.theme!=null){let v=h(t.theme,r);v!==r&&(r=v,y=true);}let w=t.styles;w?.variables&&typeof w.variables=="object"&&(d={...d,...w.variables},y=true);let K=ce(g,t);if(A(K)!==A(g)&&(g=K,y=true),y){c={mode:r,tokens:d,...g?{fontFaces:g}:{}};for(let v of f)v(c);}}function I(t){if(m)return;let r=typeof t=="number"?t:Math.ceil(e.document.body.scrollHeight);r!==b&&(b=r,N(te,{height:r}),F({type:Y,payload:{height:r}}));}function ue(t){let r=Number(t.id),d=p.get(r);if(d)if(p.delete(r),clearTimeout(d.timer),t.error!=null){let g=t.error;d.reject(new Error(g.message??"request failed"));}else d.resolve(t.result);}function fe(t,r){if(t===J){let d=r.structuredContent;z(d??L(r));}else t===ee&&D(r);}function me(t){t.method===re&&P({jsonrpc:"2.0",id:t.id,result:{}});}function ge(t){if(t.type===Z||t.type==="renderData"){let{theme:r,...d}=t.payload??{};r!=null&&D({theme:r}),Object.keys(d).length>0&&z(L(d));}}let j=t=>{if(m||t.source&&t.source!==H())return;let r=t.data;if(!(!r||typeof r!="object")){if(r.jsonrpc!=="2.0"){ge(r);return}r.id!=null&&("result"in r||"error"in r)?ue(r):typeof r.method=="string"&&(r.id!=null?me(r):fe(r.method,r.params??{}));}};function ye(){k=()=>I(),e.addEventListener("resize",k),typeof e.ResizeObserver<"u"&&(E=new e.ResizeObserver(()=>I()),E.observe(e.document.body));}return {host:"claude",getData:()=>a,onData(t){return u.add(t),()=>u.delete(t)},getTheme:()=>c,onTheme(t){return f.add(t),()=>f.delete(t)},async callTool(t,r){return await C(se,{name:t,arguments:r??{}})},sendPrompt(t){C(oe,{role:"user",content:[{type:"text",text:t}]}).catch(()=>{}),F({type:B,payload:{prompt:t}});},openLink(t){C(ne,{url:t}).catch(()=>{}),F({type:q,payload:{url:t}});},resize(t){I(t);},capabilities(){return {pull:true,sendPrompt:true,openLink:true}},start(){e.addEventListener("message",j),a=M(e.document,o),s&&ye(),P({type:V}),C(X,{appInfo:{name:n.name??"synapse-ui",version:n.version??"0.0.0"},appCapabilities:{availableDisplayModes:["inline"]},protocolVersion:Q}).then(t=>{m||(i=true,D(t?.hostContext),N($,{}),b=-1,I());}).catch(()=>{}),I();},destroy(){if(!m){m=true,e.removeEventListener("message",j),k&&e.removeEventListener("resize",k),k=null,E?.disconnect(),E=null;for(let t of p.values())clearTimeout(t.timer),t.reject(new Error("adapter destroyed"));p.clear(),u.clear(),f.clear();}}}}function Ae(e){if(e.openai!=null)return "chatgpt";try{if(e.parent!=null&&e.parent!==e)return "claude"}catch{return "claude"}return "generic"}function Oe(e,n,o){switch(e){case "chatgpt":return ie(n);case "claude":case "nimblebrain":return le(n,o);default:return ae(n,o)}}function de(e,n){let o=n.host??Ae(e);return Oe(o,e,n)}function pe(e={}){let n=e.window??globalThis,o=de(n,e);R(o.getTheme());let s=o.onTheme(R);o.start();let a=false;return {data:()=>o.getData(),onData:c=>o.onData(c),theme:()=>o.getTheme(),onTheme:c=>o.onTheme(c),callTool:(c,u)=>o.callTool(c,u),sendPrompt:c=>o.sendPrompt(c),openLink:c=>o.openLink(c),resize:c=>o.resize(c),capabilities:()=>o.capabilities(),host:()=>o.host,destroy(){a||(a=true,s(),o.destroy());}}}globalThis.SynapseUI={connect:pe};})();
|
|
@@ -113,10 +113,46 @@ interface SynapseOptions {
|
|
|
113
113
|
/** Key combos to forward from iframe to host. Default: all Ctrl/Cmd combos + Escape. */
|
|
114
114
|
forwardKeys?: KeyForwardConfig[];
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* One `@font-face` a host asks the app to load.
|
|
118
|
+
*
|
|
119
|
+
* The token contract can name a font (`--font-sans: 'Hanken Grotesk', system-ui`)
|
|
120
|
+
* but cannot *load* one — a CSS custom property carries a family name, never the
|
|
121
|
+
* `@font-face` rule behind it. An app iframe is its own document and inherits no
|
|
122
|
+
* `@font-face` from the host page, so a host that only sends tokens is naming a
|
|
123
|
+
* family the app has no way to render. This descriptor is the missing half.
|
|
124
|
+
*
|
|
125
|
+
* The SDK ships **no font data** — it applies what the host sends. A host that
|
|
126
|
+
* sends none leaves the web-safe fallbacks in `ui/tokens.ts` in force.
|
|
127
|
+
*/
|
|
128
|
+
interface FontFaceDescriptor {
|
|
129
|
+
/** Family name, matching the one used in the host's `--font-*` token value. */
|
|
130
|
+
family: string;
|
|
131
|
+
/**
|
|
132
|
+
* CSS `src` descriptor. Local and absolute URLs work identically —
|
|
133
|
+
* `url('/fonts/x.woff2') format('woff2')` or `url('https://cdn.example/x.woff2')`.
|
|
134
|
+
* Whatever origin this names must satisfy the app iframe's `font-src` CSP.
|
|
135
|
+
*/
|
|
136
|
+
src: string;
|
|
137
|
+
/** `font-weight` descriptor — a single weight (`400`) or a variable range (`400 700`). */
|
|
138
|
+
weight?: string;
|
|
139
|
+
/** `font-style` descriptor (`normal`, `italic`, …). */
|
|
140
|
+
style?: string;
|
|
141
|
+
/** `font-display` descriptor. Defaults to `swap` so text paints in the fallback first. */
|
|
142
|
+
display?: FontDisplayValue;
|
|
143
|
+
}
|
|
144
|
+
/** The CSS `font-display` values. Closed set — anything else is ignored. */
|
|
145
|
+
type FontDisplayValue = "auto" | "block" | "swap" | "fallback" | "optional";
|
|
116
146
|
interface SynapseTheme {
|
|
117
147
|
mode: "light" | "dark";
|
|
118
148
|
primaryColor: string;
|
|
119
149
|
tokens: Record<string, string>;
|
|
150
|
+
/**
|
|
151
|
+
* Font faces the host wants loaded into the app document. Optional — a host
|
|
152
|
+
* that omits them leaves the web-safe fallbacks in force. Arrives over the
|
|
153
|
+
* wire as the `synapse/fontFaces` host-context extension.
|
|
154
|
+
*/
|
|
155
|
+
fontFaces?: FontFaceDescriptor[];
|
|
120
156
|
}
|
|
121
157
|
interface DataChangedEvent {
|
|
122
158
|
source: "agent";
|
|
@@ -384,6 +420,8 @@ interface ConnectOptions {
|
|
|
384
420
|
interface Theme {
|
|
385
421
|
mode: "light" | "dark";
|
|
386
422
|
tokens: Record<string, string>;
|
|
423
|
+
/** Font faces the host wants loaded. See {@link FontFaceDescriptor}. */
|
|
424
|
+
fontFaces?: FontFaceDescriptor[];
|
|
387
425
|
}
|
|
388
426
|
interface Dimensions {
|
|
389
427
|
width?: number;
|
|
@@ -430,4 +468,4 @@ interface App {
|
|
|
430
468
|
destroy(): void;
|
|
431
469
|
}
|
|
432
470
|
|
|
433
|
-
export type { App as A, BuiltinActionType as B, ConnectOptions as C, DataChangedEvent as D,
|
|
471
|
+
export type { App as A, BuiltinActionType as B, ConnectOptions as C, DataChangedEvent as D, FontFaceDescriptor as F, HostInfo as H, KeyForwardConfig as K, NavigatePayload as N, RequestFileOptions as R, SynapseOptions as S, ToolDefinition as T, VisibleState as V, Synapse as a, ActionReducer as b, StoreConfig as c, Store as d, AgentAction as e, AppEventName as f, CallToolAsTaskOptions as g, Dimensions as h, FileResult as i, FontDisplayValue as j, NotifyPayload as k, StateAcknowledgement as l, StoreDispatch as m, SynapseTheme as n, TaskHandle as o, TasksCapability as p, Theme as q, ToolCallResult as r, ToolResultData as s };
|
|
@@ -113,10 +113,46 @@ interface SynapseOptions {
|
|
|
113
113
|
/** Key combos to forward from iframe to host. Default: all Ctrl/Cmd combos + Escape. */
|
|
114
114
|
forwardKeys?: KeyForwardConfig[];
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* One `@font-face` a host asks the app to load.
|
|
118
|
+
*
|
|
119
|
+
* The token contract can name a font (`--font-sans: 'Hanken Grotesk', system-ui`)
|
|
120
|
+
* but cannot *load* one — a CSS custom property carries a family name, never the
|
|
121
|
+
* `@font-face` rule behind it. An app iframe is its own document and inherits no
|
|
122
|
+
* `@font-face` from the host page, so a host that only sends tokens is naming a
|
|
123
|
+
* family the app has no way to render. This descriptor is the missing half.
|
|
124
|
+
*
|
|
125
|
+
* The SDK ships **no font data** — it applies what the host sends. A host that
|
|
126
|
+
* sends none leaves the web-safe fallbacks in `ui/tokens.ts` in force.
|
|
127
|
+
*/
|
|
128
|
+
interface FontFaceDescriptor {
|
|
129
|
+
/** Family name, matching the one used in the host's `--font-*` token value. */
|
|
130
|
+
family: string;
|
|
131
|
+
/**
|
|
132
|
+
* CSS `src` descriptor. Local and absolute URLs work identically —
|
|
133
|
+
* `url('/fonts/x.woff2') format('woff2')` or `url('https://cdn.example/x.woff2')`.
|
|
134
|
+
* Whatever origin this names must satisfy the app iframe's `font-src` CSP.
|
|
135
|
+
*/
|
|
136
|
+
src: string;
|
|
137
|
+
/** `font-weight` descriptor — a single weight (`400`) or a variable range (`400 700`). */
|
|
138
|
+
weight?: string;
|
|
139
|
+
/** `font-style` descriptor (`normal`, `italic`, …). */
|
|
140
|
+
style?: string;
|
|
141
|
+
/** `font-display` descriptor. Defaults to `swap` so text paints in the fallback first. */
|
|
142
|
+
display?: FontDisplayValue;
|
|
143
|
+
}
|
|
144
|
+
/** The CSS `font-display` values. Closed set — anything else is ignored. */
|
|
145
|
+
type FontDisplayValue = "auto" | "block" | "swap" | "fallback" | "optional";
|
|
116
146
|
interface SynapseTheme {
|
|
117
147
|
mode: "light" | "dark";
|
|
118
148
|
primaryColor: string;
|
|
119
149
|
tokens: Record<string, string>;
|
|
150
|
+
/**
|
|
151
|
+
* Font faces the host wants loaded into the app document. Optional — a host
|
|
152
|
+
* that omits them leaves the web-safe fallbacks in force. Arrives over the
|
|
153
|
+
* wire as the `synapse/fontFaces` host-context extension.
|
|
154
|
+
*/
|
|
155
|
+
fontFaces?: FontFaceDescriptor[];
|
|
120
156
|
}
|
|
121
157
|
interface DataChangedEvent {
|
|
122
158
|
source: "agent";
|
|
@@ -384,6 +420,8 @@ interface ConnectOptions {
|
|
|
384
420
|
interface Theme {
|
|
385
421
|
mode: "light" | "dark";
|
|
386
422
|
tokens: Record<string, string>;
|
|
423
|
+
/** Font faces the host wants loaded. See {@link FontFaceDescriptor}. */
|
|
424
|
+
fontFaces?: FontFaceDescriptor[];
|
|
387
425
|
}
|
|
388
426
|
interface Dimensions {
|
|
389
427
|
width?: number;
|
|
@@ -430,4 +468,4 @@ interface App {
|
|
|
430
468
|
destroy(): void;
|
|
431
469
|
}
|
|
432
470
|
|
|
433
|
-
export type { App as A, BuiltinActionType as B, ConnectOptions as C, DataChangedEvent as D,
|
|
471
|
+
export type { App as A, BuiltinActionType as B, ConnectOptions as C, DataChangedEvent as D, FontFaceDescriptor as F, HostInfo as H, KeyForwardConfig as K, NavigatePayload as N, RequestFileOptions as R, SynapseOptions as S, ToolDefinition as T, VisibleState as V, Synapse as a, ActionReducer as b, StoreConfig as c, Store as d, AgentAction as e, AppEventName as f, CallToolAsTaskOptions as g, Dimensions as h, FileResult as i, FontDisplayValue as j, NotifyPayload as k, StateAcknowledgement as l, StoreDispatch as m, SynapseTheme as n, TaskHandle as o, TasksCapability as p, Theme as q, ToolCallResult as r, ToolResultData as s };
|
package/dist/ui/index.cjs
CHANGED
|
@@ -333,6 +333,18 @@ var RULES3 = `
|
|
|
333
333
|
.nb-drawer-iconbtn { min-width: 44px; min-height: 44px; }
|
|
334
334
|
}
|
|
335
335
|
`;
|
|
336
|
+
function inClosedDetails(el) {
|
|
337
|
+
const details = el.closest("details:not([open])");
|
|
338
|
+
if (!details) return false;
|
|
339
|
+
const summary = details.querySelector("summary");
|
|
340
|
+
return !summary || !summary.contains(el);
|
|
341
|
+
}
|
|
342
|
+
var CANDIDATES = "a[href], button, input, select, textarea, summary, [tabindex]";
|
|
343
|
+
function tabbables(panel) {
|
|
344
|
+
return Array.from(panel.querySelectorAll(CANDIDATES)).filter(
|
|
345
|
+
(el) => el.tabIndex >= 0 && !el.matches(":disabled") && !el.closest("[hidden]") && !inClosedDetails(el) && el.type !== "hidden"
|
|
346
|
+
);
|
|
347
|
+
}
|
|
336
348
|
var DrawerContext = react.createContext(null);
|
|
337
349
|
function DrawerRoot({
|
|
338
350
|
open,
|
|
@@ -361,12 +373,33 @@ function DrawerRoot({
|
|
|
361
373
|
}, [open, onEscape, onClose]);
|
|
362
374
|
react.useEffect(() => {
|
|
363
375
|
if (!open) return;
|
|
376
|
+
const panel = panelRef.current;
|
|
364
377
|
const previouslyFocused = document.activeElement;
|
|
365
|
-
|
|
378
|
+
panel?.focus();
|
|
366
379
|
const { body } = document;
|
|
367
380
|
const prevOverflow = body.style.overflow;
|
|
368
381
|
body.style.overflow = "hidden";
|
|
382
|
+
const onKeyDown = (e) => {
|
|
383
|
+
if (e.key !== "Tab" || !panel) return;
|
|
384
|
+
const focusables = tabbables(panel);
|
|
385
|
+
if (focusables.length === 0) {
|
|
386
|
+
e.preventDefault();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const first = focusables[0];
|
|
390
|
+
const last = focusables[focusables.length - 1];
|
|
391
|
+
const active = document.activeElement;
|
|
392
|
+
if (e.shiftKey && (active === first || active === panel)) {
|
|
393
|
+
e.preventDefault();
|
|
394
|
+
last.focus();
|
|
395
|
+
} else if (!e.shiftKey && active === last) {
|
|
396
|
+
e.preventDefault();
|
|
397
|
+
first.focus();
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
panel?.addEventListener("keydown", onKeyDown);
|
|
369
401
|
return () => {
|
|
402
|
+
panel?.removeEventListener("keydown", onKeyDown);
|
|
370
403
|
body.style.overflow = prevOverflow;
|
|
371
404
|
previouslyFocused?.focus?.();
|
|
372
405
|
};
|