@tinycloud/web-sdk 3.8.0-beta.9 → 3.8.1-beta.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/dist/modals.cjs CHANGED
@@ -1 +1 @@
1
- (()=>{"use strict";var e={d:(t,n)=>{for(var i in n)e.o(n,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:n[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{ModalManager:()=>re,showPermissionRequestModal:()=>le,showSpaceCreationModal:()=>ce});let n=0,i="";function s(){let e=!0;return 0===n&&(i=document.body.style.overflow),n+=1,document.body.style.overflow="hidden",()=>{e&&(e=!1,n-=1,0===n&&(document.body.style.overflow=i))}}function o(){let e=document.activeElement;for(;e instanceof HTMLElement&&e.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e instanceof HTMLElement?e:null}function a(e){return Array.from(e.querySelectorAll("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href]"))}function r(e,t,n){return 0===e?-1:n?t<=0?e-1:t-1:t<0||t>=e-1?0:t+1}function c(e,t){if(!e?.isConnected)return;const n=o();(null===n||n===document.body||t.contains(n))&&e.focus()}function l(e){const t=e.getBoundingClientRect();if(0===t.width||0===t.height)return!1;const n=e.getRootNode();if("function"!=typeof n.elementFromPoint)return!1;const i=n.elementFromPoint.call(n,t.left+t.width/2,t.top+t.height/2);return null!==i&&e.contains(i)}class d extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.isCreating=!1,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"!==e.key||this.isCreating?"Tab"===e.key&&this.trapTab(e):this.dismiss())},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-space-title" aria-describedby="tc-space-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n <div class="modal-icon">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5"/>\n <path d="M8 12l2 2 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n </div>\n <h2 class="modal-title" id="tc-space-title">Create Your TinyCloud Space</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-space-description">\n A TinyCloud Space is your personal data container where only you control access.\n Your signature is required for any operations on the data stored there.\n </p>\n <p class="modal-subdescription">\n Sign a message to create your space and start storing data.\n </p>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="learn-more">\n Learn More\n </button>\n <button class="modal-button modal-button--primary" data-action="create">\n <span class="button-text">Create TinyCloud Space</span>\n <span class="button-spinner" data-state="hidden">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" opacity="0.25"/>\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" stroke-dashoffset="9.42">\n <animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>\n </circle>\n </svg>\n </span>\n </button>\n </div>\n </div>\n </div>\n </div>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n\n .modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n overflow-y: auto;\n padding: 20px;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n\n .modal-backdrop[data-state="visible"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .modal-backdrop[data-state="visible"] .modal-container {\n transform: scale(1) translateY(0);\n }\n\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow:\n 0 25px 50px -12px rgba(0, 0, 0, 0.25),\n 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box;\n max-width: 480px;\n width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex;\n align-items: flex-start;\n gap: 16px;\n position: relative;\n }\n\n .modal-icon {\n flex-shrink: 0;\n width: 48px;\n height: 48px;\n background: var(--modal-accent);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n margin-top: 4px;\n }\n\n .modal-title {\n flex: 1;\n min-width: 0;\n font-size: 20px;\n font-weight: 600;\n line-height: 1.3;\n margin: 0;\n margin-top: 8px;\n }\n\n .modal-close {\n position: absolute;\n top: 0;\n right: 0;\n background: transparent;\n border: none;\n color: var(--modal-muted);\n cursor: pointer;\n padding: 8px;\n border-radius: 6px;\n opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .modal-close:hover {\n opacity: 1;\n background: var(--modal-secondary);\n color: var(--modal-foreground);\n }\n\n .modal-body {\n padding: 24px;\n padding-top: 20px;\n }\n\n .modal-description {\n font-size: 16px;\n line-height: 1.5;\n margin: 0 0 12px 0;\n color: var(--modal-foreground);\n }\n\n .modal-subdescription {\n font-size: 14px;\n line-height: 1.4;\n margin: 0;\n color: var(--modal-muted);\n }\n\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n }\n\n .modal-button {\n padding: 12px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 100px;\n justify-content: center;\n position: relative;\n }\n\n .modal-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n\n .modal-button--secondary:hover:not(:disabled) {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n\n .modal-button--primary {\n background: var(--modal-accent);\n color: white;\n }\n\n .modal-button--primary:hover:not(:disabled) {\n background: var(--modal-accent-hover);\n transform: translateY(-1px);\n }\n\n .modal-button--primary:active:not(:disabled) {\n transform: translateY(0);\n }\n\n .button-spinner {\n display: none;\n }\n\n .button-spinner[data-state="visible"] {\n display: flex;\n }\n\n .modal-button[data-state="loading"] .button-text {\n opacity: 0;\n }\n\n .modal-button[data-state="loading"] .button-spinner {\n display: flex;\n position: absolute;\n }\n\n @media (max-width: 640px) {\n .modal-content {\n max-width: 100%;\n margin: 0 16px;\n border-radius: 12px;\n }\n\n .modal-header {\n padding: 20px 20px 0 20px;\n }\n\n .modal-body {\n padding: 20px;\n padding-top: 16px;\n }\n\n .modal-actions {\n padding: 0 20px 20px 20px;\n flex-direction: column-reverse;\n }\n\n .modal-button {\n width: 100%;\n }\n }\n .modal-container:focus { outline: none; }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="learn-more"]'),i=this.shadowRoot.querySelector('[data-action="create"]');e?.addEventListener("click",t=>{t.target!==e||this.isCreating||this.dismiss()}),t?.addEventListener("click",()=>{this.isCreating||this.dismiss()}),n?.addEventListener("click",()=>{window.open("https://tinycloud.xyz","_blank","noopener,noreferrer")}),i?.addEventListener("click",()=>{!this.isCreating&&i&&l(i)&&this.handleCreateSpace()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=a(t),i=n.indexOf(t.activeElement);e.preventDefault();const s=r(n.length,i,e.shiftKey);s>=0&&n[s].focus()}async handleCreateSpace(){this.isCreating=!0;const e=this.shadowRoot.querySelector('[data-action="create"]');e.setAttribute("data-state","loading"),e.setAttribute("disabled","true");try{await this.options.onCreateSpace(),this.resolveResult?.({success:!0,dismissed:!1}),this.hide()}catch(e){console.error("[TinyCloud] Space creation failed:",e)}finally{this.isCreating=!1,e.removeAttribute("data-state"),e.removeAttribute("disabled")}}show(){this.isVisible||(this.opener=o()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=s()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,c(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({success:!1,dismissed:!0}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}customElements.get("tinycloud-space-modal")||customElements.define("tinycloud-space-modal",d);var p=[{urn:"tinycloud.kv/get",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/list",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/metadata",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/put",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/del",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/delete",service:"tinycloud.kv",status:"deprecated-alias",aliasOf:"tinycloud.kv/del"},{urn:"tinycloud.kv/sync",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/retain",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.sql/read",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/select",service:"tinycloud.sql",status:"deprecated-alias",aliasOf:"tinycloud.sql/read"},{urn:"tinycloud.sql/write",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/schema",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/admin",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/schema"]},{urn:"tinycloud.sql/*",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/read","tinycloud.sql/write","tinycloud.sql/schema","tinycloud.sql/admin"]},{urn:"tinycloud.duckdb/read",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/write",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/admin",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/import",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/export",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/select",service:"tinycloud.duckdb",status:"deprecated-alias",aliasOf:"tinycloud.duckdb/read"},{urn:"tinycloud.duckdb/*",service:"tinycloud.duckdb",status:"active",implies:["tinycloud.duckdb/read","tinycloud.duckdb/write","tinycloud.duckdb/admin","tinycloud.duckdb/import","tinycloud.duckdb/export"]},{urn:"tinycloud.capabilities/read",service:"tinycloud.capabilities",status:"active"},{urn:"tinycloud.delegation/status",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.delegation/list",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.hooks/subscribe",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/register",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/list",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/unregister",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.encryption/decrypt",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.create",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.revoke",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.space/host",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/create",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/list",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/info",service:"tinycloud.space",status:"active"},{urn:"tinycloud.vfs/get",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/list",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/metadata",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/put",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/delete",service:"tinycloud.vfs",status:"reserved"}];function u(e){return"*"===e?"ALL":e.toUpperCase().replace(/[^A-Z0-9]/g,"_")}function m(e){return e.slice(10,e.indexOf("/"))}function h(e){return e.slice(e.indexOf("/")+1)}function y(e){const t={};for(const n of p)m(n.urn)===e&&(t[u(h(n.urn))]=n.urn);return t}var v=y("kv"),b={...y("sql"),EXECUTE:"tinycloud.sql/execute",EXPORT:"tinycloud.sql/export"},f={...y("duckdb"),DESCRIBE:"tinycloud.duckdb/describe",EXECUTE:"tinycloud.duckdb/execute"},g=y("capabilities"),k=y("hooks"),w=y("encryption"),x=y("space"),S="applications",A="account",E="applications/",L="secrets",R="default",T="public",D="agents",C="tinycloud.encryption",q=w.NETWORK_CREATE,M=[R,S,A,L,T,D],P={app_id:"xyz.tinycloud.default",name:"TinyCloud Default Space",space:R,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:R,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:R,path:"",actions:["read","write"]}]},$={app_id:"xyz.tinycloud.applications",name:"TinyCloud Applications Space",space:S,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:S,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:S,path:"",actions:["read","write"]}]},z={app_id:"xyz.tinycloud.account",name:"TinyCloud Account Registry",space:A,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:A,path:"applications/",actions:["get","put","list"]},{service:"tinycloud.kv",space:A,path:"spaces/",actions:["get","put","list"]},{service:"tinycloud.sql",space:A,path:"account",actions:["read","write","schema"]}]},O={app_id:"xyz.tinycloud.secrets",name:"TinyCloud Secrets",space:L,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.vault",space:L,path:"secrets/",actions:["read"],skipPrefix:!0},{service:"tinycloud.kv",space:L,path:"variables",actions:["list","metadata"],skipPrefix:!0},{service:"tinycloud.kv",space:L,path:"variables/",actions:["get","put","del","list","metadata"],skipPrefix:!0},{service:"tinycloud.sql",space:L,path:"default",actions:["read","write","schema"],skipPrefix:!0},{service:"tinycloud.capabilities",space:L,path:"",actions:["read"],skipPrefix:!0}]},B={app_id:"xyz.tinycloud.public",name:"TinyCloud Public Space",space:T,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:T,path:"",actions:["get","list","metadata"]}]},j={app_id:"xyz.tinycloud.agents",name:"TinyCloud Agents Space",space:D,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:D,path:"",actions:["get","put","del","list","metadata"]}]},I={[R]:P,[S]:$,[A]:z,[L]:O,[T]:B,[D]:j};function W(e){return{...e,permissions:e.permissions?.map(e=>({...e,actions:[...e.actions]}))}}function Q(e,t,n){return!0===n||""===e||""===t?t:"/"===t?`${e}/`:`${e.endsWith("/")?e.slice(0,-1):e}/${t.startsWith("/")?t.slice(1):t}`}function _(e,t,n){if("tinycloud.vault"===e.service)return function(e){return e.actions.map(t=>{const n=t.startsWith("tinycloud.vault/")?t.slice(16):t.startsWith("tinycloud.kv/")?t.slice(13):t,i="read"===n||"get"===n?v.GET:"write"===n||"put"===n?v.PUT:"delete"===n||"del"===n?v.DEL:"list"===n?v.LIST:"metadata"===n?v.METADATA:void 0;if(void 0===i)throw new Error(`unknown vault action ${JSON.stringify(t)}`);const s=e.path.startsWith("/")?e.path.slice(1):e.path;return{...e,service:"tinycloud.kv",path:`vault/${s}`,actions:[i],skipPrefix:!0}})}(e).flatMap(e=>_(e,t,n));const i=!0===e.skipPrefix||e.service===C;return[{service:e.service,space:e.service===C?"encryption":e.space??n,path:Q(t,e.path,i),actions:e.actions.map(t=>function(e,t){return t.includes("/")?t:`${e}/${t}`}(e.service,t)),...void 0!==e.description?{description:e.description}:{}}]}function F(e){const t=new Set(e.filter(e=>e.service!==C).map(e=>e.space));return function(e){const t=new Map;for(const n of e){const e=`${n.service}\0${n.space}\0${n.path}`,i=t.get(e);if(i)for(const e of n.actions)i.actions.includes(e)||i.actions.push(e);else t.set(e,{...n,actions:[...n.actions]})}return[...t.values()]}([...e,...[...t].map(e=>({service:"tinycloud.capabilities",space:e,path:"",actions:[g.READ]}))])}function U(e){return function(e,t={}){if(!Array.isArray(e)||0===e.length)throw new Error("composeManifestRequest requires at least one manifest");const n=t.includeAccountRegistryPermissions??!0,i=e.map(W),s=i.flatMap(e=>{const t=e.prefix??e.app_id,n=e.space??S;return(e.permissions??[]).flatMap(e=>_(e,t,n))});n&&s.push(...K.map(e=>({...e,actions:[...e.actions]})));const o=new Map;for(const e of i){const t=o.get(e.app_id)??[];t.push(W(e)),o.set(e.app_id,t)}return{manifests:i,resources:F(s),delegationTargets:[],registryRecords:n?[...o.entries()].map(([e,t])=>({key:`${E}${e}`,app_id:e,manifests:t})):[],expiryMs:2592e6,includePublicSpace:i.some(e=>e.includePublicSpace??!0)}}([I[e]],{includeAccountRegistryPermissions:!1})}M.map(e=>({name:e,manifest:I[e],persistedAsApplication:e===L}));var K=Object.freeze([{service:"tinycloud.kv",space:A,path:E,actions:[v.GET,v.PUT,v.LIST]},{service:"tinycloud.kv",space:A,path:"spaces/",actions:[v.GET,v.PUT,v.LIST]},{service:"tinycloud.kv",space:A,path:"system/bootstrap/complete",actions:[v.GET,v.PUT]},{service:"tinycloud.delegation",space:A,path:"",actions:["tinycloud.delegation/list"]},{service:"tinycloud.sql",space:A,path:"account",actions:[b.READ,b.WRITE,b.SCHEMA]},{service:"tinycloud.capabilities",space:A,path:"",actions:[g.READ]}]),V=U(R),Y={...V,resources:[...V.resources,...K.map(e=>({...e,actions:[...e.actions]}))]},H=Object.freeze(Object.fromEntries(M.map(e=>[e,e===R?Y:U(e)]))),N=Object.freeze([{service:"tinycloud.encryption",resource:"urn:tinycloud:encryption:{ownerDid}:default",actions:[q]}]);Object.freeze(M.flatMap(e=>[{kind:"session",service:"tinycloud.session",space:e,actions:["siwe"],resources:H[e].resources,...e===A?{rawAbilities:N}:{}},{kind:"space/host",service:"tinycloud.space",space:e,actions:[x.HOST]}]));class G extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"===e.key?this.dismiss():"Tab"===e.key&&this.trapTab(e))},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){const e=this.options.additional.map(e=>this.renderEntry(e)).join(""),t=te(this.options.appName),n=this.options.appIcon?`<img class="app-icon" src="${i=this.options.appIcon,te(i)}" alt="" />`:'<div class="app-icon app-icon--placeholder">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>\n </svg>\n </div>';var i;this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-permission-title" aria-describedby="tc-permission-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n ${n}\n <h2 class="modal-title" id="tc-permission-title">${t} is requesting additional permissions</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-permission-description">\n This app is asking you to grant scoped permission. Review what it will be allowed to do before approving.\n </p>\n <ul class="permission-list">${e}</ul>\n <label class="suppression-option">\n <input type="checkbox" class="suppression-checkbox" data-role="suppress-prompt" />\n <span>Do not show me again for 30 days</span>\n </label>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="decline">\n Decline\n </button>\n <button class="modal-button modal-button--primary" data-action="approve">\n Approve\n </button>\n </div>\n </div>\n </div>\n </div>\n `}renderEntry(e){const t=Z(e),n=""===e.path?"/":e.path,i=e.actions.map(t=>function(e,t){const n=function(e,t){return t.includes("/")?t:t.startsWith(`${e}.`)?`${e}/${t.slice(e.length+1)}`:`${e}/${t}`}(e.service,t),i=function(e,t){return t.startsWith(`${e}/`)||t.startsWith(`${e}.`)?t.slice(e.length+1):t}(e.service,t),s=function(e,t){if("tinycloud.kv"!==e.service||"secrets"!==e.space)return;const n=function(e){const t="vault/secrets/";if("vault/secrets"===e)return"all secrets";if(!e.startsWith(t))return;const n=e.slice(14);if(""===n)return"all secrets";const i="scoped/";if(n.startsWith(i)){const[e,...t]=n.slice(7).split("/"),i=t.join("/");if(""!==e&&""!==i)return`the scoped secret ${i} in ${e}`}return`the secret ${n}`}(e.path);if(void 0!==n)switch(t){case"tinycloud.kv/get":return{title:"Read an encrypted secret",description:`Allows this app to read the encrypted secret record for ${n}. Decrypting it still requires encryption-network decrypt permission.`};case"tinycloud.kv/put":return{title:"Write a secret",description:`Allows this app to create or update the encrypted secret record for ${n}.`};case"tinycloud.kv/del":return{title:"Delete a secret",description:`Allows this app to remove the encrypted secret record for ${n}.`};case"tinycloud.kv/list":return{title:"List secrets",description:`Allows this app to list encrypted secret records under ${n}.`};case"tinycloud.kv/metadata":return{title:"Read secret metadata",description:`Allows this app to inspect metadata for encrypted secret records under ${n}.`};default:return}}(e,n)??J[n]??function(e,t){return{title:`Use ${ee(e).replace(/ permission$/,"")}`,description:`Allows this app to perform ${t} on the resource shown below.`}}(e.service,i);return{canonicalAction:n,displayAction:i,...s}}(e,t)),s=i.map(e=>`\n <li class="permission-summary-item">\n <div class="permission-summary-row">\n <span class="permission-summary-label">Permission</span>\n <strong class="permission-summary-title">${te(e.title)}</strong>\n </div>\n <p class="permission-summary-description">${te(e.description)}</p>\n </li>\n `).join(""),o=[["Service",e.service],["Scope",t],["Resource",n],["Actions",i.map(e=>e.canonicalAction).join(", ")]].map(([e,t])=>`\n <div class="dictionary-row">\n <dt>${te(e)}</dt>\n <dd>${te(t)}</dd>\n </div>\n `).join("");return`\n <li class="permission-entry">\n <ul class="permission-summary-list">${s}</ul>\n <details class="technical-details">\n <summary>Show technical details</summary>\n <p class="entry-summary">${te(function(e){if("tinycloud.encryption"===e.service)return"This permission applies to an encryption network, not a TinyCloud data space.";return`This permission applies to the ${Z(e)} TinyCloud space and the resource path shown below.`}(e))}</p>\n <dl class="permission-dictionary">${o}</dl>\n </details>\n </li>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n .modal-backdrop {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex; align-items: flex-start; justify-content: center; overflow-y: auto;\n padding: 20px; opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n .modal-backdrop[data-state="visible"] { opacity: 1; pointer-events: auto; }\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .modal-container:focus { outline: none; }\n .modal-backdrop[data-state="visible"] .modal-container { transform: scale(1) translateY(0); }\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box; max-width: 520px; width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex; align-items: flex-start; gap: 16px; position: relative;\n }\n .app-icon {\n flex-shrink: 0; width: 48px; height: 48px;\n border-radius: 12px; object-fit: cover;\n background: var(--modal-secondary);\n }\n .app-icon--placeholder {\n display: flex; align-items: center; justify-content: center;\n background: var(--modal-accent); color: white;\n }\n .modal-title {\n flex: 1; min-width: 0; font-size: 18px; font-weight: 600; line-height: 1.3;\n margin: 0; margin-top: 4px;\n }\n .modal-close {\n position: absolute; top: 0; right: 0;\n background: transparent; border: none;\n color: var(--modal-muted); cursor: pointer;\n padding: 8px; border-radius: 6px; opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex; align-items: center; justify-content: center;\n }\n .modal-close:hover { opacity: 1; background: var(--modal-secondary); color: var(--modal-foreground); }\n .modal-body { padding: 20px 24px; }\n .modal-description {\n font-size: 14px; line-height: 1.5; margin: 0 0 16px 0; color: var(--modal-muted);\n }\n .permission-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n max-height: 320px; overflow-y: auto;\n }\n .permission-entry {\n background: var(--modal-secondary);\n border: 1px solid var(--modal-border);\n border-radius: 10px; padding: 14px;\n }\n .suppression-option {\n display: flex; align-items: center; gap: 10px;\n margin-top: 16px; color: var(--modal-muted);\n font-size: 13px; line-height: 1.4; cursor: pointer;\n }\n .suppression-checkbox {\n width: 16px; height: 16px; flex: 0 0 auto;\n margin: 0; accent-color: var(--modal-accent);\n }\n .suppression-option:hover { color: var(--modal-foreground); }\n .entry-summary {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 10px 0;\n }\n .permission-summary-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n }\n .permission-summary-item {\n display: flex; flex-direction: column; gap: 6px;\n }\n .permission-summary-row {\n display: grid; grid-template-columns: 88px minmax(0, 1fr);\n gap: 10px; align-items: baseline;\n }\n .permission-summary-label {\n font-size: 12px; color: var(--modal-muted);\n }\n .permission-summary-title {\n font-size: 15px; font-weight: 600; color: var(--modal-foreground);\n line-height: 1.35;\n }\n .permission-summary-description {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 0;\n }\n .technical-details {\n margin-top: 12px;\n border-top: 1px solid var(--modal-border);\n padding-top: 10px;\n }\n .technical-details summary {\n cursor: pointer; color: var(--modal-muted); font-size: 12px;\n user-select: none;\n }\n .technical-details summary:hover { color: var(--modal-foreground); }\n .technical-details[open] summary { margin-bottom: 8px; }\n .permission-dictionary {\n margin: 0; padding: 0;\n border: 1px solid var(--modal-border);\n border-radius: 8px; overflow: hidden;\n }\n .dictionary-row {\n display: grid; grid-template-columns: 86px 1fr;\n border-top: 1px solid var(--modal-border);\n }\n .dictionary-row:first-child { border-top: 0; }\n .dictionary-row dt,\n .dictionary-row dd {\n margin: 0; padding: 8px 10px; font-size: 12px; line-height: 1.4;\n }\n .dictionary-row dt {\n color: var(--modal-muted); background: var(--modal-bg);\n }\n .dictionary-row dd {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", monospace;\n color: var(--modal-foreground); word-break: break-all;\n }\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex; gap: 12px; justify-content: flex-end;\n }\n .modal-button {\n padding: 12px 20px; border-radius: 8px;\n font-size: 14px; font-weight: 500; cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex; align-items: center; gap: 8px;\n min-width: 100px; justify-content: center;\n }\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n .modal-button--secondary:hover {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n .modal-button--primary { background: var(--modal-accent); color: white; }\n .modal-button--primary:hover { background: var(--modal-accent-hover); transform: translateY(-1px); }\n .modal-button--primary:active { transform: translateY(0); }\n @media (max-width: 640px) {\n .modal-content { max-width: 100%; margin: 0 16px; border-radius: 12px; }\n .modal-header { padding: 20px 20px 0 20px; }\n .modal-body { padding: 16px 20px; }\n .modal-actions { padding: 0 20px 20px 20px; flex-direction: column-reverse; }\n .modal-button { width: 100%; }\n .permission-summary-row { grid-template-columns: 1fr; gap: 3px; }\n .dictionary-row { grid-template-columns: 1fr; }\n .dictionary-row dd { padding-top: 0; }\n }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="decline"]'),i=this.shadowRoot.querySelector('[data-action="approve"]');e?.addEventListener("click",t=>{t.target===e&&this.dismiss()}),t?.addEventListener("click",()=>this.dismiss()),n?.addEventListener("click",()=>this.dismiss()),i?.addEventListener("click",()=>{i&&l(i)&&this.handleApprove()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=a(t),i=n.indexOf(t.activeElement);e.preventDefault();const s=r(n.length,i,e.shiftKey);s>=0&&n[s].focus()}handleApprove(){const e=this.shadowRoot.querySelector('[data-role="suppress-prompt"]');this.resolveResult?.({approved:!0,suppressPromptFor30Days:!0===e?.checked}),this.hide()}show(){this.isVisible||(this.opener=o()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=s()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,c(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({approved:!1}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}const X={"tinycloud.encryption":"Encryption network permission","tinycloud.kv":"Key-value storage permission","tinycloud.sql":"SQL storage permission","tinycloud.duckdb":"DuckDB storage permission","tinycloud.capabilities":"Capability registry permission","tinycloud.hooks":"Hook permission"},J={[w.DECRYPT]:{title:"Decrypt data keys for this network",description:"Allows this app to ask a TinyCloud node to unwrap encrypted data keys for data encrypted to this network. The app still needs separate data-read permission to fetch encrypted records."},[w.NETWORK_CREATE]:{title:"Create an encryption network",description:"Allows this app to create or initialize an encryption network that data can be encrypted to. The network key is managed by TinyCloud nodes and used later for delegated decrypt requests."},[w.NETWORK_REVOKE]:{title:"Revoke an encryption network",description:"Allows this app to revoke or disable an encryption network so future decrypt requests for that network are denied."},[v.GET]:{title:"Read key-value data",description:"Allows this app to read values at the selected key or key prefix in the requested space."},[v.PUT]:{title:"Write key-value data",description:"Allows this app to create or replace values at the selected key or key prefix in the requested space."},[v.DEL]:{title:"Delete key-value data",description:"Allows this app to remove values at the selected key or key prefix in the requested space."},[v.LIST]:{title:"List key-value entries",description:"Allows this app to list keys under the selected key-value prefix in the requested space."},[v.METADATA]:{title:"Read key-value metadata",description:"Allows this app to inspect metadata for keys without necessarily reading the stored value."},[b.READ]:{title:"Read SQL data",description:"Allows this app to read rows and query results from the selected SQL database in the requested space."},[b.WRITE]:{title:"Write SQL data",description:"Allows this app to insert, update, or delete rows in the selected SQL database."},[b.SCHEMA]:{title:"Change SQL schema",description:"Allows this app to create, alter, or drop SQL tables, indexes, and schema objects."},[b.ADMIN]:{title:"Administer SQL storage",description:"Allows this app to perform administrative SQL operations for the selected database."},[b.SELECT]:{title:"Select SQL rows",description:"Allows this app to run SELECT queries against the selected SQL database."},[b.EXECUTE]:{title:"Execute SQL statements",description:"Allows this app to execute SQL statements against the selected database."},[b.EXPORT]:{title:"Export SQL data",description:"Allows this app to export data from the selected SQL database."},[b.ALL]:{title:"Full SQL access",description:"Allows this app to read, write, administer, and export the selected SQL database."},[f.READ]:{title:"Read DuckDB data",description:"Allows this app to read data and query results from the selected DuckDB database."},[f.WRITE]:{title:"Write DuckDB data",description:"Allows this app to write data to the selected DuckDB database."},[f.ADMIN]:{title:"Administer DuckDB storage",description:"Allows this app to perform administrative operations for the selected DuckDB database."},[f.DESCRIBE]:{title:"Describe DuckDB data",description:"Allows this app to inspect tables, columns, and metadata in the selected DuckDB database."},[f.EXPORT]:{title:"Export DuckDB data",description:"Allows this app to export data from the selected DuckDB database."},[f.IMPORT]:{title:"Import DuckDB data",description:"Allows this app to import data into the selected DuckDB database."},[f.EXECUTE]:{title:"Execute DuckDB statements",description:"Allows this app to execute statements against the selected DuckDB database."},[f.ALL]:{title:"Full DuckDB access",description:"Allows this app to read, write, administer, import, export, and execute statements on the selected DuckDB database."},[g.READ]:{title:"Read delegated capabilities",description:"Allows this app to read capability and delegation records for the selected scope."},[k.SUBSCRIBE]:{title:"Subscribe to hooks",description:"Allows this app to receive hook events for the selected hook scope."},[k.REGISTER]:{title:"Register hooks",description:"Allows this app to register hook handlers for the selected hook scope."},[k.LIST]:{title:"List hooks",description:"Allows this app to list registered hooks for the selected hook scope."},[k.UNREGISTER]:{title:"Unregister hooks",description:"Allows this app to remove registered hook handlers for the selected hook scope."}};function Z(e){return void 0!==e.space&&""!==e.space?e.space:"tinycloud.encryption"===e.service?"network-scoped":"unscoped"}function ee(e){return X[e]??`${e} permission`}function te(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var ne;"undefined"==typeof customElements||customElements.get("tinycloud-permission-request-modal")||customElements.define("tinycloud-permission-request-modal",G);const ie=Symbol.for("tinycloud.web-sdk.prompt-state"),se=(ne=globalThis)[ie]??(ne[ie]={open:new Set,layer:void 0});const oe=Symbol.for("tinycloud.web-sdk.modals"),ae={uiEnabled:!0};class re{constructor(){this.activeModal=null,this.releaseActivePrompt=null}static getInstance(){return re.instance||(re.instance=new re),re.instance}showSpaceCreationModal(e,t=ae){this.displaceActiveModal();const n=new d({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({success:!1,dismissed:!0}))}showPermissionRequestModal(e,t=ae){this.displaceActiveModal();const n=new G({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({approved:!1}))}present(e,t,n){const i=function(e,t,n){const i=function(e){return function(){try{return"undefined"!=typeof window&&"undefined"!=typeof document&&window.top===window&&"null"!==window.location.origin}catch{return!1}}()?e.uiEnabled&&void 0!==se.layer?void 0:"ui-disabled":"ui-unavailable-frame"}(e);if(void 0!==i)return console.warn(`[tinycloud] ${i}`),{refusal:i};se.layer?.appendChild(t);const s={scope:e,dismiss:n,element:t};return se.open.add(s),{release:()=>{se.open.delete(s),se.layer?.dispatchEvent(new Event("tinycloud-prompt-settled"))}}}(t,e,n);return!("refusal"in i)&&(this.activeModal=e,this.releaseActivePrompt=i.release,!0)}clearActive(e){this.activeModal===e&&(this.releaseActivePrompt?.(),this.releaseActivePrompt=null,this.activeModal=null)}displaceActiveModal(){try{this.closeActiveModal()}catch(e){queueMicrotask(()=>{throw e})}}closeActiveModal(){const e=this.activeModal;if(e)try{e.isOpen&&e.dismiss()}finally{e.remove(),this.clearActive(e)}}hasActiveModal(){return null!==this.activeModal}}const ce=(e,t)=>re.getInstance().showSpaceCreationModal(e,t),le=(e,t)=>re.getInstance().showPermissionRequestModal(e,t);globalThis[oe]={showPermissionRequestModal:le,showSpaceCreationModal:ce,closeActiveModal:()=>re.getInstance().closeActiveModal()},module.exports=t})();
1
+ (()=>{"use strict";var e={d:(t,n)=>{for(var i in n)e.o(n,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:n[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{ModalManager:()=>re,showPermissionRequestModal:()=>le,showSpaceCreationModal:()=>ce});let n=0,i="";function s(){let e=!0;return 0===n&&(i=document.body.style.overflow),n+=1,document.body.style.overflow="hidden",()=>{e&&(e=!1,n-=1,0===n&&(document.body.style.overflow=i))}}function o(){let e=document.activeElement;for(;e instanceof HTMLElement&&e.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e instanceof HTMLElement?e:null}function a(e){return Array.from(e.querySelectorAll("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href]"))}function r(e,t,n){return 0===e?-1:n?t<=0?e-1:t-1:t<0||t>=e-1?0:t+1}function c(e,t){if(!e?.isConnected)return;const n=o();(null===n||n===document.body||t.contains(n))&&e.focus()}function l(e){const t=e.getBoundingClientRect();if(0===t.width||0===t.height)return!1;const n=e.getRootNode();if("function"!=typeof n.elementFromPoint)return!1;const i=n.elementFromPoint.call(n,t.left+t.width/2,t.top+t.height/2);return null!==i&&e.contains(i)}class d extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.isCreating=!1,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"!==e.key||this.isCreating?"Tab"===e.key&&this.trapTab(e):this.dismiss())},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-space-title" aria-describedby="tc-space-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n <div class="modal-icon">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5"/>\n <path d="M8 12l2 2 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n </div>\n <h2 class="modal-title" id="tc-space-title">Create Your TinyCloud Space</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-space-description">\n A TinyCloud Space is your personal data container where only you control access.\n Your signature is required for any operations on the data stored there.\n </p>\n <p class="modal-subdescription">\n Sign a message to create your space and start storing data.\n </p>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="learn-more">\n Learn More\n </button>\n <button class="modal-button modal-button--primary" data-action="create">\n <span class="button-text">Create TinyCloud Space</span>\n <span class="button-spinner" data-state="hidden">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" opacity="0.25"/>\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" stroke-dashoffset="9.42">\n <animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>\n </circle>\n </svg>\n </span>\n </button>\n </div>\n </div>\n </div>\n </div>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n\n .modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n overflow-y: auto;\n padding: 20px;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n\n .modal-backdrop[data-state="visible"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .modal-backdrop[data-state="visible"] .modal-container {\n transform: scale(1) translateY(0);\n }\n\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow:\n 0 25px 50px -12px rgba(0, 0, 0, 0.25),\n 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box;\n max-width: 480px;\n width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex;\n align-items: flex-start;\n gap: 16px;\n position: relative;\n }\n\n .modal-icon {\n flex-shrink: 0;\n width: 48px;\n height: 48px;\n background: var(--modal-accent);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n margin-top: 4px;\n }\n\n .modal-title {\n flex: 1;\n min-width: 0;\n font-size: 20px;\n font-weight: 600;\n line-height: 1.3;\n margin: 0;\n margin-top: 8px;\n }\n\n .modal-close {\n position: absolute;\n top: 0;\n right: 0;\n background: transparent;\n border: none;\n color: var(--modal-muted);\n cursor: pointer;\n padding: 8px;\n border-radius: 6px;\n opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .modal-close:hover {\n opacity: 1;\n background: var(--modal-secondary);\n color: var(--modal-foreground);\n }\n\n .modal-body {\n padding: 24px;\n padding-top: 20px;\n }\n\n .modal-description {\n font-size: 16px;\n line-height: 1.5;\n margin: 0 0 12px 0;\n color: var(--modal-foreground);\n }\n\n .modal-subdescription {\n font-size: 14px;\n line-height: 1.4;\n margin: 0;\n color: var(--modal-muted);\n }\n\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n }\n\n .modal-button {\n padding: 12px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 100px;\n justify-content: center;\n position: relative;\n }\n\n .modal-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n\n .modal-button--secondary:hover:not(:disabled) {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n\n .modal-button--primary {\n background: var(--modal-accent);\n color: white;\n }\n\n .modal-button--primary:hover:not(:disabled) {\n background: var(--modal-accent-hover);\n transform: translateY(-1px);\n }\n\n .modal-button--primary:active:not(:disabled) {\n transform: translateY(0);\n }\n\n .button-spinner {\n display: none;\n }\n\n .button-spinner[data-state="visible"] {\n display: flex;\n }\n\n .modal-button[data-state="loading"] .button-text {\n opacity: 0;\n }\n\n .modal-button[data-state="loading"] .button-spinner {\n display: flex;\n position: absolute;\n }\n\n @media (max-width: 640px) {\n .modal-content {\n max-width: 100%;\n margin: 0 16px;\n border-radius: 12px;\n }\n\n .modal-header {\n padding: 20px 20px 0 20px;\n }\n\n .modal-body {\n padding: 20px;\n padding-top: 16px;\n }\n\n .modal-actions {\n padding: 0 20px 20px 20px;\n flex-direction: column-reverse;\n }\n\n .modal-button {\n width: 100%;\n }\n }\n .modal-container:focus { outline: none; }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="learn-more"]'),i=this.shadowRoot.querySelector('[data-action="create"]');e?.addEventListener("click",t=>{t.target!==e||this.isCreating||this.dismiss()}),t?.addEventListener("click",()=>{this.isCreating||this.dismiss()}),n?.addEventListener("click",()=>{window.open("https://tinycloud.xyz","_blank","noopener,noreferrer")}),i?.addEventListener("click",()=>{!this.isCreating&&i&&l(i)&&this.handleCreateSpace()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=a(t),i=n.indexOf(t.activeElement);e.preventDefault();const s=r(n.length,i,e.shiftKey);s>=0&&n[s].focus()}async handleCreateSpace(){this.isCreating=!0;const e=this.shadowRoot.querySelector('[data-action="create"]');e.setAttribute("data-state","loading"),e.setAttribute("disabled","true");try{await this.options.onCreateSpace(),this.resolveResult?.({success:!0,dismissed:!1}),this.hide()}catch(e){console.error("[TinyCloud] Space creation failed:",e)}finally{this.isCreating=!1,e.removeAttribute("data-state"),e.removeAttribute("disabled")}}show(){this.isVisible||(this.opener=o()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=s()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,c(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({success:!1,dismissed:!0}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}customElements.get("tinycloud-space-modal")||customElements.define("tinycloud-space-modal",d);var p=[{urn:"tinycloud.kv/get",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/list",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/metadata",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/put",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/del",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/delete",service:"tinycloud.kv",status:"deprecated-alias",aliasOf:"tinycloud.kv/del"},{urn:"tinycloud.kv/sync",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/retain",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.sql/read",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/select",service:"tinycloud.sql",status:"deprecated-alias",aliasOf:"tinycloud.sql/read"},{urn:"tinycloud.sql/write",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/schema",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/admin",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/schema"]},{urn:"tinycloud.sql/*",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/read","tinycloud.sql/write","tinycloud.sql/schema","tinycloud.sql/admin"]},{urn:"tinycloud.duckdb/read",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/write",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/admin",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/import",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/export",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/select",service:"tinycloud.duckdb",status:"deprecated-alias",aliasOf:"tinycloud.duckdb/read"},{urn:"tinycloud.duckdb/*",service:"tinycloud.duckdb",status:"active",implies:["tinycloud.duckdb/read","tinycloud.duckdb/write","tinycloud.duckdb/admin","tinycloud.duckdb/import","tinycloud.duckdb/export"]},{urn:"tinycloud.capabilities/read",service:"tinycloud.capabilities",status:"active"},{urn:"tinycloud.delegation/status",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.delegation/list",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.hooks/subscribe",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/register",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/list",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/unregister",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.encryption/decrypt",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.create",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.revoke",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.space/host",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/create",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/list",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/info",service:"tinycloud.space",status:"active"},{urn:"tinycloud.vfs/get",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/list",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/metadata",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/put",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/delete",service:"tinycloud.vfs",status:"reserved"}];function u(e){return"*"===e?"ALL":e.toUpperCase().replace(/[^A-Z0-9]/g,"_")}function m(e){return e.slice(10,e.indexOf("/"))}function h(e){return e.slice(e.indexOf("/")+1)}function y(e){const t={};for(const n of p)m(n.urn)===e&&(t[u(h(n.urn))]=n.urn);return t}var v=y("kv"),b={...y("sql"),EXECUTE:"tinycloud.sql/execute",EXPORT:"tinycloud.sql/export"},f={...y("duckdb"),DESCRIBE:"tinycloud.duckdb/describe",EXECUTE:"tinycloud.duckdb/execute"},g=y("capabilities"),k=y("hooks"),w=y("encryption"),x=y("space"),S="applications",A="account",E="applications/",L="secrets",R="default",T="public",D="agents",C="tinycloud.encryption",q=w.NETWORK_CREATE,M=[R,S,A,L,T,D],P={app_id:"xyz.tinycloud.default",name:"TinyCloud Default Space",space:R,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:R,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:R,path:"",actions:["read","write"]}]},$={app_id:"xyz.tinycloud.applications",name:"TinyCloud Applications Space",space:S,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:S,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:S,path:"",actions:["read","write"]}]},z={app_id:"xyz.tinycloud.account",name:"TinyCloud Account Registry",space:A,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:A,path:"applications/",actions:["get","put","list"]},{service:"tinycloud.kv",space:A,path:"spaces/",actions:["get","put","list"]},{service:"tinycloud.sql",space:A,path:"account",actions:["read","write","schema"]}]},O={app_id:"xyz.tinycloud.secrets",name:"TinyCloud Secrets",space:L,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.vault",space:L,path:"secrets/",actions:["read"],skipPrefix:!0},{service:"tinycloud.kv",space:L,path:"variables",actions:["list","metadata"],skipPrefix:!0},{service:"tinycloud.kv",space:L,path:"variables/",actions:["get","put","del","list","metadata"],skipPrefix:!0},{service:"tinycloud.sql",space:L,path:"default",actions:["read","write","schema"],skipPrefix:!0},{service:"tinycloud.capabilities",space:L,path:"",actions:["read"],skipPrefix:!0}]},B={app_id:"xyz.tinycloud.public",name:"TinyCloud Public Space",space:T,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:T,path:"",actions:["get","list","metadata"]}]},j={app_id:"xyz.tinycloud.agents",name:"TinyCloud Agents Space",space:D,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:D,path:"",actions:["get","put","del","list","metadata"]}]},I={[R]:P,[S]:$,[A]:z,[L]:O,[T]:B,[D]:j};function W(e){return{...e,permissions:e.permissions?.map(e=>({...e,actions:[...e.actions]}))}}function Q(e,t,n){return!0===n||""===e||""===t?t:"/"===t?`${e}/`:`${e.endsWith("/")?e.slice(0,-1):e}/${t.startsWith("/")?t.slice(1):t}`}function _(e,t,n){if("tinycloud.vault"===e.service)return function(e){return e.actions.map(t=>{const n=t.startsWith("tinycloud.vault/")?t.slice(16):t.startsWith("tinycloud.kv/")?t.slice(13):t,i="read"===n||"get"===n?v.GET:"write"===n||"put"===n?v.PUT:"delete"===n||"del"===n?v.DEL:"list"===n?v.LIST:"metadata"===n?v.METADATA:void 0;if(void 0===i)throw new Error(`unknown vault action ${JSON.stringify(t)}`);const s=e.path.startsWith("/")?e.path.slice(1):e.path;return{...e,service:"tinycloud.kv",path:`vault/${s}`,actions:[i],skipPrefix:!0}})}(e).flatMap(e=>_(e,t,n));const i=!0===e.skipPrefix||e.service===C;return[{service:e.service,space:e.service===C?"encryption":e.space??n,path:Q(t,e.path,i),actions:e.actions.map(t=>function(e,t){return t.includes("/")?t:`${e}/${t}`}(e.service,t)),...void 0!==e.description?{description:e.description}:{}}]}function F(e){const t=new Set(e.filter(e=>e.service!==C).map(e=>e.space));return function(e){const t=new Map;for(const n of e){const e=`${n.service}\0${n.space}\0${n.path}`,i=t.get(e);if(i)for(const e of n.actions)i.actions.includes(e)||i.actions.push(e);else t.set(e,{...n,actions:[...n.actions]})}return[...t.values()]}([...e,...[...t].map(e=>({service:"tinycloud.capabilities",space:e,path:"",actions:[g.READ]}))])}function K(e){return function(e,t={}){if(!Array.isArray(e)||0===e.length)throw new Error("composeManifestRequest requires at least one manifest");const n=t.includeAccountRegistryPermissions??!0,i=e.map(W),s=i.flatMap(e=>{const t=e.prefix??e.app_id,n=e.space??S;return(e.permissions??[]).flatMap(e=>_(e,t,n))});n&&s.push(...U.map(e=>({...e,actions:[...e.actions]})));const o=new Map;for(const e of i){const t=o.get(e.app_id)??[];t.push(W(e)),o.set(e.app_id,t)}return{manifests:i,resources:F(s),delegationTargets:[],registryRecords:n?[...o.entries()].map(([e,t])=>({key:`${E}${e}`,app_id:e,manifests:t})):[],expiryMs:2592e6,includePublicSpace:i.some(e=>e.includePublicSpace??!0)}}([I[e]],{includeAccountRegistryPermissions:!1})}M.map(e=>({name:e,manifest:I[e],persistedAsApplication:e===L}));var U=Object.freeze([{service:"tinycloud.kv",space:A,path:E,actions:[v.GET,v.PUT,v.LIST]},{service:"tinycloud.kv",space:A,path:"spaces/",actions:[v.GET,v.PUT,v.LIST]},{service:"tinycloud.kv",space:A,path:"system/bootstrap/complete",actions:[v.GET,v.PUT]},{service:"tinycloud.delegation",space:A,path:"",actions:["tinycloud.delegation/list"]},{service:"tinycloud.sql",space:A,path:"account",actions:[b.READ,b.WRITE,b.SCHEMA]},{service:"tinycloud.capabilities",space:A,path:"",actions:[g.READ]}]),V=K(R),Y={...V,resources:[...V.resources,...U.map(e=>({...e,actions:[...e.actions]}))]},H=Object.freeze(Object.fromEntries(M.map(e=>[e,e===R?Y:K(e)]))),N=Object.freeze([{service:"tinycloud.encryption",resource:"urn:tinycloud:encryption:{ownerDid}:default",actions:[q]}]);Object.freeze(M.flatMap(e=>[{kind:"session",service:"tinycloud.session",space:e,actions:["siwe"],resources:H[e].resources,...e===A?{rawAbilities:N}:{}},{kind:"space/host",service:"tinycloud.space",space:e,actions:[x.HOST]}]));class G extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"===e.key?this.dismiss():"Tab"===e.key&&this.trapTab(e))},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){const e=this.options.additional.map(e=>this.renderEntry(e)).join(""),t=te(this.options.appName),n=this.options.appIcon?`<img class="app-icon" src="${i=this.options.appIcon,te(i)}" alt="" />`:'<div class="app-icon app-icon--placeholder">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>\n </svg>\n </div>';var i;this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-permission-title" aria-describedby="tc-permission-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n ${n}\n <h2 class="modal-title" id="tc-permission-title">${t} is requesting additional permissions</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-permission-description">\n This app is asking you to grant scoped permission. Review what it will be allowed to do before approving.\n </p>\n <ul class="permission-list">${e}</ul>\n <label class="suppression-option">\n <input type="checkbox" class="suppression-checkbox" data-role="suppress-prompt" />\n <span>Do not show me again for 30 days</span>\n </label>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="decline">\n Decline\n </button>\n <button class="modal-button modal-button--primary" data-action="approve">\n Approve\n </button>\n </div>\n </div>\n </div>\n </div>\n `}renderEntry(e){const t=Z(e),n=""===e.path?"/":e.path,i=e.actions.map(t=>function(e,t){const n=function(e,t){return t.includes("/")?t:t.startsWith(`${e}.`)?`${e}/${t.slice(e.length+1)}`:`${e}/${t}`}(e.service,t),i=function(e,t){return t.startsWith(`${e}/`)||t.startsWith(`${e}.`)?t.slice(e.length+1):t}(e.service,t),s=function(e,t){if("tinycloud.kv"!==e.service||"secrets"!==e.space)return;const n=function(e){const t="vault/secrets/";if("vault/secrets"===e)return"all secrets";if(!e.startsWith(t))return;const n=e.slice(14);if(""===n)return"all secrets";const i="scoped/";if(n.startsWith(i)){const[e,...t]=n.slice(7).split("/"),i=t.join("/");if(""!==e&&""!==i)return`the scoped secret ${i} in ${e}`}return`the secret ${n}`}(e.path);if(void 0!==n)switch(t){case"tinycloud.kv/get":return{title:"Read an encrypted secret",description:`Allows this app to read the encrypted secret record for ${n}. Decrypting it still requires encryption-network decrypt permission.`};case"tinycloud.kv/put":return{title:"Write a secret",description:`Allows this app to create or update the encrypted secret record for ${n}.`};case"tinycloud.kv/del":return{title:"Delete a secret",description:`Allows this app to remove the encrypted secret record for ${n}.`};case"tinycloud.kv/list":return{title:"List secrets",description:`Allows this app to list encrypted secret records under ${n}.`};case"tinycloud.kv/metadata":return{title:"Read secret metadata",description:`Allows this app to inspect metadata for encrypted secret records under ${n}.`};default:return}}(e,n)??J[n]??function(e,t){return{title:"tinycloud.delegation"===e?"Manage your delegations"+(""===t?"":` (${t})`):`Use ${ee(e).replace(/ permission$/,"")}`,description:`Allows this app to perform ${t} on the resource shown below.`}}(e.service,i);return{canonicalAction:n,displayAction:i,...s}}(e,t)),s=i.map(e=>`\n <li class="permission-summary-item">\n <div class="permission-summary-row">\n <span class="permission-summary-label">Permission</span>\n <strong class="permission-summary-title">${te(e.title)}</strong>\n </div>\n <p class="permission-summary-description">${te(e.description)}</p>\n </li>\n `).join(""),o=[["Service",e.service],["Scope",t],["Resource",n],["Actions",i.map(e=>e.canonicalAction).join(", ")]].map(([e,t])=>`\n <div class="dictionary-row">\n <dt>${te(e)}</dt>\n <dd>${te(t)}</dd>\n </div>\n `).join("");return`\n <li class="permission-entry">\n <ul class="permission-summary-list">${s}</ul>\n <details class="technical-details">\n <summary>Show technical details</summary>\n <p class="entry-summary">${te(function(e){if("tinycloud.encryption"===e.service)return"This permission applies to an encryption network, not a TinyCloud data space.";return`This permission applies to the ${Z(e)} TinyCloud space and the resource path shown below.`}(e))}</p>\n <dl class="permission-dictionary">${o}</dl>\n </details>\n </li>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n .modal-backdrop {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex; align-items: flex-start; justify-content: center; overflow-y: auto;\n padding: 20px; opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n .modal-backdrop[data-state="visible"] { opacity: 1; pointer-events: auto; }\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .modal-container:focus { outline: none; }\n .modal-backdrop[data-state="visible"] .modal-container { transform: scale(1) translateY(0); }\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box; max-width: 520px; width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex; align-items: flex-start; gap: 16px; position: relative;\n }\n .app-icon {\n flex-shrink: 0; width: 48px; height: 48px;\n border-radius: 12px; object-fit: cover;\n background: var(--modal-secondary);\n }\n .app-icon--placeholder {\n display: flex; align-items: center; justify-content: center;\n background: var(--modal-accent); color: white;\n }\n .modal-title {\n flex: 1; min-width: 0; font-size: 18px; font-weight: 600; line-height: 1.3;\n margin: 0; margin-top: 4px;\n }\n .modal-close {\n position: absolute; top: 0; right: 0;\n background: transparent; border: none;\n color: var(--modal-muted); cursor: pointer;\n padding: 8px; border-radius: 6px; opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex; align-items: center; justify-content: center;\n }\n .modal-close:hover { opacity: 1; background: var(--modal-secondary); color: var(--modal-foreground); }\n .modal-body { padding: 20px 24px; }\n .modal-description {\n font-size: 14px; line-height: 1.5; margin: 0 0 16px 0; color: var(--modal-muted);\n }\n .permission-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n max-height: 320px; overflow-y: auto;\n }\n .permission-entry {\n background: var(--modal-secondary);\n border: 1px solid var(--modal-border);\n border-radius: 10px; padding: 14px;\n }\n .suppression-option {\n display: flex; align-items: center; gap: 10px;\n margin-top: 16px; color: var(--modal-muted);\n font-size: 13px; line-height: 1.4; cursor: pointer;\n }\n .suppression-checkbox {\n width: 16px; height: 16px; flex: 0 0 auto;\n margin: 0; accent-color: var(--modal-accent);\n }\n .suppression-option:hover { color: var(--modal-foreground); }\n .entry-summary {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 10px 0;\n }\n .permission-summary-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n }\n .permission-summary-item {\n display: flex; flex-direction: column; gap: 6px;\n }\n .permission-summary-row {\n display: grid; grid-template-columns: 88px minmax(0, 1fr);\n gap: 10px; align-items: baseline;\n }\n .permission-summary-label {\n font-size: 12px; color: var(--modal-muted);\n }\n .permission-summary-title {\n font-size: 15px; font-weight: 600; color: var(--modal-foreground);\n line-height: 1.35;\n }\n .permission-summary-description {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 0;\n }\n .technical-details {\n margin-top: 12px;\n border-top: 1px solid var(--modal-border);\n padding-top: 10px;\n }\n .technical-details summary {\n cursor: pointer; color: var(--modal-muted); font-size: 12px;\n user-select: none;\n }\n .technical-details summary:hover { color: var(--modal-foreground); }\n .technical-details[open] summary { margin-bottom: 8px; }\n .permission-dictionary {\n margin: 0; padding: 0;\n border: 1px solid var(--modal-border);\n border-radius: 8px; overflow: hidden;\n }\n .dictionary-row {\n display: grid; grid-template-columns: 86px 1fr;\n border-top: 1px solid var(--modal-border);\n }\n .dictionary-row:first-child { border-top: 0; }\n .dictionary-row dt,\n .dictionary-row dd {\n margin: 0; padding: 8px 10px; font-size: 12px; line-height: 1.4;\n }\n .dictionary-row dt {\n color: var(--modal-muted); background: var(--modal-bg);\n }\n .dictionary-row dd {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", monospace;\n color: var(--modal-foreground); word-break: break-all;\n }\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex; gap: 12px; justify-content: flex-end;\n }\n .modal-button {\n padding: 12px 20px; border-radius: 8px;\n font-size: 14px; font-weight: 500; cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex; align-items: center; gap: 8px;\n min-width: 100px; justify-content: center;\n }\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n .modal-button--secondary:hover {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n .modal-button--primary { background: var(--modal-accent); color: white; }\n .modal-button--primary:hover { background: var(--modal-accent-hover); transform: translateY(-1px); }\n .modal-button--primary:active { transform: translateY(0); }\n @media (max-width: 640px) {\n .modal-content { max-width: 100%; margin: 0 16px; border-radius: 12px; }\n .modal-header { padding: 20px 20px 0 20px; }\n .modal-body { padding: 16px 20px; }\n .modal-actions { padding: 0 20px 20px 20px; flex-direction: column-reverse; }\n .modal-button { width: 100%; }\n .permission-summary-row { grid-template-columns: 1fr; gap: 3px; }\n .dictionary-row { grid-template-columns: 1fr; }\n .dictionary-row dd { padding-top: 0; }\n }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="decline"]'),i=this.shadowRoot.querySelector('[data-action="approve"]');e?.addEventListener("click",t=>{t.target===e&&this.dismiss()}),t?.addEventListener("click",()=>this.dismiss()),n?.addEventListener("click",()=>this.dismiss()),i?.addEventListener("click",()=>{i&&l(i)&&this.handleApprove()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=a(t),i=n.indexOf(t.activeElement);e.preventDefault();const s=r(n.length,i,e.shiftKey);s>=0&&n[s].focus()}handleApprove(){const e=this.shadowRoot.querySelector('[data-role="suppress-prompt"]');this.resolveResult?.({approved:!0,suppressPromptFor30Days:!0===e?.checked}),this.hide()}show(){this.isVisible||(this.opener=o()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=s()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,c(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({approved:!1}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}const X={"tinycloud.encryption":"Encryption network permission","tinycloud.kv":"Key-value storage permission","tinycloud.sql":"SQL storage permission","tinycloud.duckdb":"DuckDB storage permission","tinycloud.capabilities":"Capability registry permission","tinycloud.hooks":"Hook permission"},J={[w.DECRYPT]:{title:"Decrypt data keys for this network",description:"Allows this app to ask a TinyCloud node to unwrap encrypted data keys for data encrypted to this network. The app still needs separate data-read permission to fetch encrypted records."},[w.NETWORK_CREATE]:{title:"Create an encryption network",description:"Allows this app to create or initialize an encryption network that data can be encrypted to. The network key is managed by TinyCloud nodes and used later for delegated decrypt requests."},[w.NETWORK_REVOKE]:{title:"Revoke an encryption network",description:"Allows this app to revoke or disable an encryption network so future decrypt requests for that network are denied."},[v.GET]:{title:"Read key-value data",description:"Allows this app to read values at the selected key or key prefix in the requested space."},[v.PUT]:{title:"Write key-value data",description:"Allows this app to create or replace values at the selected key or key prefix in the requested space."},[v.DEL]:{title:"Delete key-value data",description:"Allows this app to remove values at the selected key or key prefix in the requested space."},[v.LIST]:{title:"List key-value entries",description:"Allows this app to list keys under the selected key-value prefix in the requested space."},[v.METADATA]:{title:"Read key-value metadata",description:"Allows this app to inspect metadata for keys without necessarily reading the stored value."},[b.READ]:{title:"Read SQL data",description:"Allows this app to read rows and query results from the selected SQL database in the requested space."},[b.WRITE]:{title:"Write SQL data",description:"Allows this app to insert, update, or delete rows in the selected SQL database."},[b.SCHEMA]:{title:"Change SQL schema",description:"Allows this app to create, alter, or drop SQL tables, indexes, and schema objects."},[b.ADMIN]:{title:"Administer SQL storage",description:"Allows this app to perform administrative SQL operations for the selected database."},[b.SELECT]:{title:"Select SQL rows",description:"Allows this app to run SELECT queries against the selected SQL database."},[b.EXECUTE]:{title:"Execute SQL statements",description:"Allows this app to execute SQL statements against the selected database."},[b.EXPORT]:{title:"Export SQL data",description:"Allows this app to export data from the selected SQL database."},[b.ALL]:{title:"Full SQL access",description:"Allows this app to read, write, administer, and export the selected SQL database."},[f.READ]:{title:"Read DuckDB data",description:"Allows this app to read data and query results from the selected DuckDB database."},[f.WRITE]:{title:"Write DuckDB data",description:"Allows this app to write data to the selected DuckDB database."},[f.ADMIN]:{title:"Administer DuckDB storage",description:"Allows this app to perform administrative operations for the selected DuckDB database."},[f.DESCRIBE]:{title:"Describe DuckDB data",description:"Allows this app to inspect tables, columns, and metadata in the selected DuckDB database."},[f.EXPORT]:{title:"Export DuckDB data",description:"Allows this app to export data from the selected DuckDB database."},[f.IMPORT]:{title:"Import DuckDB data",description:"Allows this app to import data into the selected DuckDB database."},[f.EXECUTE]:{title:"Execute DuckDB statements",description:"Allows this app to execute statements against the selected DuckDB database."},[f.ALL]:{title:"Full DuckDB access",description:"Allows this app to read, write, administer, import, export, and execute statements on the selected DuckDB database."},[g.READ]:{title:"Read delegated capabilities",description:"Allows this app to read capability and delegation records for the selected scope."},"tinycloud.delegation/list":{title:"See which apps and links can reach your data, and the names of what each can reach",description:"See who has access to your data, what it reaches (by name) and when it ends. Keys and contents aren't shown."},[k.SUBSCRIBE]:{title:"Subscribe to hooks",description:"Allows this app to receive hook events for the selected hook scope."},[k.REGISTER]:{title:"Register hooks",description:"Allows this app to register hook handlers for the selected hook scope."},[k.LIST]:{title:"List hooks",description:"Allows this app to list registered hooks for the selected hook scope."},[k.UNREGISTER]:{title:"Unregister hooks",description:"Allows this app to remove registered hook handlers for the selected hook scope."}};function Z(e){return void 0!==e.space&&""!==e.space?e.space:"tinycloud.encryption"===e.service?"network-scoped":"unscoped"}function ee(e){return X[e]??`${e} permission`}function te(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var ne;"undefined"==typeof customElements||customElements.get("tinycloud-permission-request-modal")||customElements.define("tinycloud-permission-request-modal",G);const ie=Symbol.for("tinycloud.web-sdk.prompt-state"),se=(ne=globalThis)[ie]??(ne[ie]={open:new Set,layer:void 0});const oe=Symbol.for("tinycloud.web-sdk.modals"),ae={uiEnabled:!0};class re{constructor(){this.activeModal=null,this.releaseActivePrompt=null}static getInstance(){return re.instance||(re.instance=new re),re.instance}showSpaceCreationModal(e,t=ae){this.displaceActiveModal();const n=new d({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({success:!1,dismissed:!0}))}showPermissionRequestModal(e,t=ae){this.displaceActiveModal();const n=new G({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({approved:!1}))}present(e,t,n){const i=function(e,t,n){const i=function(e){return function(){try{return"undefined"!=typeof window&&"undefined"!=typeof document&&window.top===window&&"null"!==window.location.origin}catch{return!1}}()?e.uiEnabled&&void 0!==se.layer?void 0:"ui-disabled":"ui-unavailable-frame"}(e);if(void 0!==i)return console.warn(`[tinycloud] ${i}`),{refusal:i};se.layer?.appendChild(t);const s={scope:e,dismiss:n,element:t};return se.open.add(s),{release:()=>{se.open.delete(s),se.layer?.dispatchEvent(new Event("tinycloud-prompt-settled"))}}}(t,e,n);return!("refusal"in i)&&(this.activeModal=e,this.releaseActivePrompt=i.release,!0)}clearActive(e){this.activeModal===e&&(this.releaseActivePrompt?.(),this.releaseActivePrompt=null,this.activeModal=null)}displaceActiveModal(){try{this.closeActiveModal()}catch(e){queueMicrotask(()=>{throw e})}}closeActiveModal(){const e=this.activeModal;if(e)try{e.isOpen&&e.dismiss()}finally{e.remove(),this.clearActive(e)}}hasActiveModal(){return null!==this.activeModal}}const ce=(e,t)=>re.getInstance().showSpaceCreationModal(e,t),le=(e,t)=>re.getInstance().showPermissionRequestModal(e,t);globalThis[oe]={showPermissionRequestModal:le,showSpaceCreationModal:ce,closeActiveModal:()=>re.getInstance().closeActiveModal()},module.exports=t})();
package/dist/modals.mjs CHANGED
@@ -1 +1 @@
1
- let e=0,t="";function n(){let n=!0;return 0===e&&(t=document.body.style.overflow),e+=1,document.body.style.overflow="hidden",()=>{n&&(n=!1,e-=1,0===e&&(document.body.style.overflow=t))}}function i(){let e=document.activeElement;for(;e instanceof HTMLElement&&e.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e instanceof HTMLElement?e:null}function s(e){return Array.from(e.querySelectorAll("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href]"))}function o(e,t,n){return 0===e?-1:n?t<=0?e-1:t-1:t<0||t>=e-1?0:t+1}function a(e,t){if(!e?.isConnected)return;const n=i();(null===n||n===document.body||t.contains(n))&&e.focus()}function r(e){const t=e.getBoundingClientRect();if(0===t.width||0===t.height)return!1;const n=e.getRootNode();if("function"!=typeof n.elementFromPoint)return!1;const i=n.elementFromPoint.call(n,t.left+t.width/2,t.top+t.height/2);return null!==i&&e.contains(i)}class c extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.isCreating=!1,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"!==e.key||this.isCreating?"Tab"===e.key&&this.trapTab(e):this.dismiss())},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-space-title" aria-describedby="tc-space-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n <div class="modal-icon">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5"/>\n <path d="M8 12l2 2 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n </div>\n <h2 class="modal-title" id="tc-space-title">Create Your TinyCloud Space</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-space-description">\n A TinyCloud Space is your personal data container where only you control access.\n Your signature is required for any operations on the data stored there.\n </p>\n <p class="modal-subdescription">\n Sign a message to create your space and start storing data.\n </p>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="learn-more">\n Learn More\n </button>\n <button class="modal-button modal-button--primary" data-action="create">\n <span class="button-text">Create TinyCloud Space</span>\n <span class="button-spinner" data-state="hidden">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" opacity="0.25"/>\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" stroke-dashoffset="9.42">\n <animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>\n </circle>\n </svg>\n </span>\n </button>\n </div>\n </div>\n </div>\n </div>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n\n .modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n overflow-y: auto;\n padding: 20px;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n\n .modal-backdrop[data-state="visible"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .modal-backdrop[data-state="visible"] .modal-container {\n transform: scale(1) translateY(0);\n }\n\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow:\n 0 25px 50px -12px rgba(0, 0, 0, 0.25),\n 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box;\n max-width: 480px;\n width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex;\n align-items: flex-start;\n gap: 16px;\n position: relative;\n }\n\n .modal-icon {\n flex-shrink: 0;\n width: 48px;\n height: 48px;\n background: var(--modal-accent);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n margin-top: 4px;\n }\n\n .modal-title {\n flex: 1;\n min-width: 0;\n font-size: 20px;\n font-weight: 600;\n line-height: 1.3;\n margin: 0;\n margin-top: 8px;\n }\n\n .modal-close {\n position: absolute;\n top: 0;\n right: 0;\n background: transparent;\n border: none;\n color: var(--modal-muted);\n cursor: pointer;\n padding: 8px;\n border-radius: 6px;\n opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .modal-close:hover {\n opacity: 1;\n background: var(--modal-secondary);\n color: var(--modal-foreground);\n }\n\n .modal-body {\n padding: 24px;\n padding-top: 20px;\n }\n\n .modal-description {\n font-size: 16px;\n line-height: 1.5;\n margin: 0 0 12px 0;\n color: var(--modal-foreground);\n }\n\n .modal-subdescription {\n font-size: 14px;\n line-height: 1.4;\n margin: 0;\n color: var(--modal-muted);\n }\n\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n }\n\n .modal-button {\n padding: 12px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 100px;\n justify-content: center;\n position: relative;\n }\n\n .modal-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n\n .modal-button--secondary:hover:not(:disabled) {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n\n .modal-button--primary {\n background: var(--modal-accent);\n color: white;\n }\n\n .modal-button--primary:hover:not(:disabled) {\n background: var(--modal-accent-hover);\n transform: translateY(-1px);\n }\n\n .modal-button--primary:active:not(:disabled) {\n transform: translateY(0);\n }\n\n .button-spinner {\n display: none;\n }\n\n .button-spinner[data-state="visible"] {\n display: flex;\n }\n\n .modal-button[data-state="loading"] .button-text {\n opacity: 0;\n }\n\n .modal-button[data-state="loading"] .button-spinner {\n display: flex;\n position: absolute;\n }\n\n @media (max-width: 640px) {\n .modal-content {\n max-width: 100%;\n margin: 0 16px;\n border-radius: 12px;\n }\n\n .modal-header {\n padding: 20px 20px 0 20px;\n }\n\n .modal-body {\n padding: 20px;\n padding-top: 16px;\n }\n\n .modal-actions {\n padding: 0 20px 20px 20px;\n flex-direction: column-reverse;\n }\n\n .modal-button {\n width: 100%;\n }\n }\n .modal-container:focus { outline: none; }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="learn-more"]'),i=this.shadowRoot.querySelector('[data-action="create"]');e?.addEventListener("click",t=>{t.target!==e||this.isCreating||this.dismiss()}),t?.addEventListener("click",()=>{this.isCreating||this.dismiss()}),n?.addEventListener("click",()=>{window.open("https://tinycloud.xyz","_blank","noopener,noreferrer")}),i?.addEventListener("click",()=>{!this.isCreating&&i&&r(i)&&this.handleCreateSpace()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=s(t),i=n.indexOf(t.activeElement);e.preventDefault();const a=o(n.length,i,e.shiftKey);a>=0&&n[a].focus()}async handleCreateSpace(){this.isCreating=!0;const e=this.shadowRoot.querySelector('[data-action="create"]');e.setAttribute("data-state","loading"),e.setAttribute("disabled","true");try{await this.options.onCreateSpace(),this.resolveResult?.({success:!0,dismissed:!1}),this.hide()}catch(e){console.error("[TinyCloud] Space creation failed:",e)}finally{this.isCreating=!1,e.removeAttribute("data-state"),e.removeAttribute("disabled")}}show(){this.isVisible||(this.opener=i()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=n()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,a(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({success:!1,dismissed:!0}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}customElements.get("tinycloud-space-modal")||customElements.define("tinycloud-space-modal",c);var l=[{urn:"tinycloud.kv/get",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/list",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/metadata",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/put",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/del",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/delete",service:"tinycloud.kv",status:"deprecated-alias",aliasOf:"tinycloud.kv/del"},{urn:"tinycloud.kv/sync",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/retain",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.sql/read",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/select",service:"tinycloud.sql",status:"deprecated-alias",aliasOf:"tinycloud.sql/read"},{urn:"tinycloud.sql/write",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/schema",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/admin",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/schema"]},{urn:"tinycloud.sql/*",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/read","tinycloud.sql/write","tinycloud.sql/schema","tinycloud.sql/admin"]},{urn:"tinycloud.duckdb/read",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/write",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/admin",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/import",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/export",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/select",service:"tinycloud.duckdb",status:"deprecated-alias",aliasOf:"tinycloud.duckdb/read"},{urn:"tinycloud.duckdb/*",service:"tinycloud.duckdb",status:"active",implies:["tinycloud.duckdb/read","tinycloud.duckdb/write","tinycloud.duckdb/admin","tinycloud.duckdb/import","tinycloud.duckdb/export"]},{urn:"tinycloud.capabilities/read",service:"tinycloud.capabilities",status:"active"},{urn:"tinycloud.delegation/status",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.delegation/list",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.hooks/subscribe",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/register",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/list",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/unregister",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.encryption/decrypt",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.create",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.revoke",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.space/host",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/create",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/list",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/info",service:"tinycloud.space",status:"active"},{urn:"tinycloud.vfs/get",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/list",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/metadata",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/put",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/delete",service:"tinycloud.vfs",status:"reserved"}];function d(e){return"*"===e?"ALL":e.toUpperCase().replace(/[^A-Z0-9]/g,"_")}function p(e){return e.slice(10,e.indexOf("/"))}function u(e){return e.slice(e.indexOf("/")+1)}function m(e){const t={};for(const n of l)p(n.urn)===e&&(t[d(u(n.urn))]=n.urn);return t}var h=m("kv"),y={...m("sql"),EXECUTE:"tinycloud.sql/execute",EXPORT:"tinycloud.sql/export"},v={...m("duckdb"),DESCRIBE:"tinycloud.duckdb/describe",EXECUTE:"tinycloud.duckdb/execute"},b=m("capabilities"),f=m("hooks"),g=m("encryption"),k=m("space"),w="applications",x="account",A="applications/",E="secrets",S="default",L="public",R="agents",T="tinycloud.encryption",D=g.NETWORK_CREATE,C=[S,w,x,E,L,R],q={app_id:"xyz.tinycloud.default",name:"TinyCloud Default Space",space:S,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:S,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:S,path:"",actions:["read","write"]}]},M={app_id:"xyz.tinycloud.applications",name:"TinyCloud Applications Space",space:w,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:w,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:w,path:"",actions:["read","write"]}]},P={app_id:"xyz.tinycloud.account",name:"TinyCloud Account Registry",space:x,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:x,path:"applications/",actions:["get","put","list"]},{service:"tinycloud.kv",space:x,path:"spaces/",actions:["get","put","list"]},{service:"tinycloud.sql",space:x,path:"account",actions:["read","write","schema"]}]},$={app_id:"xyz.tinycloud.secrets",name:"TinyCloud Secrets",space:E,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.vault",space:E,path:"secrets/",actions:["read"],skipPrefix:!0},{service:"tinycloud.kv",space:E,path:"variables",actions:["list","metadata"],skipPrefix:!0},{service:"tinycloud.kv",space:E,path:"variables/",actions:["get","put","del","list","metadata"],skipPrefix:!0},{service:"tinycloud.sql",space:E,path:"default",actions:["read","write","schema"],skipPrefix:!0},{service:"tinycloud.capabilities",space:E,path:"",actions:["read"],skipPrefix:!0}]},z={app_id:"xyz.tinycloud.public",name:"TinyCloud Public Space",space:L,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:L,path:"",actions:["get","list","metadata"]}]},B={app_id:"xyz.tinycloud.agents",name:"TinyCloud Agents Space",space:R,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:R,path:"",actions:["get","put","del","list","metadata"]}]},I={[S]:q,[w]:M,[x]:P,[E]:$,[L]:z,[R]:B};function O(e){return{...e,permissions:e.permissions?.map(e=>({...e,actions:[...e.actions]}))}}function j(e,t,n){return!0===n||""===e||""===t?t:"/"===t?`${e}/`:`${e.endsWith("/")?e.slice(0,-1):e}/${t.startsWith("/")?t.slice(1):t}`}function W(e,t,n){if("tinycloud.vault"===e.service)return function(e){return e.actions.map(t=>{const n=t.startsWith("tinycloud.vault/")?t.slice(16):t.startsWith("tinycloud.kv/")?t.slice(13):t,i="read"===n||"get"===n?h.GET:"write"===n||"put"===n?h.PUT:"delete"===n||"del"===n?h.DEL:"list"===n?h.LIST:"metadata"===n?h.METADATA:void 0;if(void 0===i)throw new Error(`unknown vault action ${JSON.stringify(t)}`);const s=e.path.startsWith("/")?e.path.slice(1):e.path;return{...e,service:"tinycloud.kv",path:`vault/${s}`,actions:[i],skipPrefix:!0}})}(e).flatMap(e=>W(e,t,n));const i=!0===e.skipPrefix||e.service===T;return[{service:e.service,space:e.service===T?"encryption":e.space??n,path:j(t,e.path,i),actions:e.actions.map(t=>function(e,t){return t.includes("/")?t:`${e}/${t}`}(e.service,t)),...void 0!==e.description?{description:e.description}:{}}]}function Q(e){const t=new Set(e.filter(e=>e.service!==T).map(e=>e.space));return function(e){const t=new Map;for(const n of e){const e=`${n.service}\0${n.space}\0${n.path}`,i=t.get(e);if(i)for(const e of n.actions)i.actions.includes(e)||i.actions.push(e);else t.set(e,{...n,actions:[...n.actions]})}return[...t.values()]}([...e,...[...t].map(e=>({service:"tinycloud.capabilities",space:e,path:"",actions:[b.READ]}))])}function F(e){return function(e,t={}){if(!Array.isArray(e)||0===e.length)throw new Error("composeManifestRequest requires at least one manifest");const n=t.includeAccountRegistryPermissions??!0,i=e.map(O),s=i.flatMap(e=>{const t=e.prefix??e.app_id,n=e.space??w;return(e.permissions??[]).flatMap(e=>W(e,t,n))});n&&s.push(...U.map(e=>({...e,actions:[...e.actions]})));const o=new Map;for(const e of i){const t=o.get(e.app_id)??[];t.push(O(e)),o.set(e.app_id,t)}return{manifests:i,resources:Q(s),delegationTargets:[],registryRecords:n?[...o.entries()].map(([e,t])=>({key:`${A}${e}`,app_id:e,manifests:t})):[],expiryMs:2592e6,includePublicSpace:i.some(e=>e.includePublicSpace??!0)}}([I[e]],{includeAccountRegistryPermissions:!1})}C.map(e=>({name:e,manifest:I[e],persistedAsApplication:e===E}));var U=Object.freeze([{service:"tinycloud.kv",space:x,path:A,actions:[h.GET,h.PUT,h.LIST]},{service:"tinycloud.kv",space:x,path:"spaces/",actions:[h.GET,h.PUT,h.LIST]},{service:"tinycloud.kv",space:x,path:"system/bootstrap/complete",actions:[h.GET,h.PUT]},{service:"tinycloud.delegation",space:x,path:"",actions:["tinycloud.delegation/list"]},{service:"tinycloud.sql",space:x,path:"account",actions:[y.READ,y.WRITE,y.SCHEMA]},{service:"tinycloud.capabilities",space:x,path:"",actions:[b.READ]}]),K=F(S),V={...K,resources:[...K.resources,...U.map(e=>({...e,actions:[...e.actions]}))]},_=Object.freeze(Object.fromEntries(C.map(e=>[e,e===S?V:F(e)]))),Y=Object.freeze([{service:"tinycloud.encryption",resource:"urn:tinycloud:encryption:{ownerDid}:default",actions:[D]}]);Object.freeze(C.flatMap(e=>[{kind:"session",service:"tinycloud.session",space:e,actions:["siwe"],resources:_[e].resources,...e===x?{rawAbilities:Y}:{}},{kind:"space/host",service:"tinycloud.space",space:e,actions:[k.HOST]}]));class H extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"===e.key?this.dismiss():"Tab"===e.key&&this.trapTab(e))},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){const e=this.options.additional.map(e=>this.renderEntry(e)).join(""),t=Z(this.options.appName),n=this.options.appIcon?`<img class="app-icon" src="${i=this.options.appIcon,Z(i)}" alt="" />`:'<div class="app-icon app-icon--placeholder">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>\n </svg>\n </div>';var i;this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-permission-title" aria-describedby="tc-permission-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n ${n}\n <h2 class="modal-title" id="tc-permission-title">${t} is requesting additional permissions</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-permission-description">\n This app is asking you to grant scoped permission. Review what it will be allowed to do before approving.\n </p>\n <ul class="permission-list">${e}</ul>\n <label class="suppression-option">\n <input type="checkbox" class="suppression-checkbox" data-role="suppress-prompt" />\n <span>Do not show me again for 30 days</span>\n </label>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="decline">\n Decline\n </button>\n <button class="modal-button modal-button--primary" data-action="approve">\n Approve\n </button>\n </div>\n </div>\n </div>\n </div>\n `}renderEntry(e){const t=X(e),n=""===e.path?"/":e.path,i=e.actions.map(t=>function(e,t){const n=function(e,t){return t.includes("/")?t:t.startsWith(`${e}.`)?`${e}/${t.slice(e.length+1)}`:`${e}/${t}`}(e.service,t),i=function(e,t){return t.startsWith(`${e}/`)||t.startsWith(`${e}.`)?t.slice(e.length+1):t}(e.service,t),s=function(e,t){if("tinycloud.kv"!==e.service||"secrets"!==e.space)return;const n=function(e){const t="vault/secrets/";if("vault/secrets"===e)return"all secrets";if(!e.startsWith(t))return;const n=e.slice(14);if(""===n)return"all secrets";const i="scoped/";if(n.startsWith(i)){const[e,...t]=n.slice(7).split("/"),i=t.join("/");if(""!==e&&""!==i)return`the scoped secret ${i} in ${e}`}return`the secret ${n}`}(e.path);if(void 0!==n)switch(t){case"tinycloud.kv/get":return{title:"Read an encrypted secret",description:`Allows this app to read the encrypted secret record for ${n}. Decrypting it still requires encryption-network decrypt permission.`};case"tinycloud.kv/put":return{title:"Write a secret",description:`Allows this app to create or update the encrypted secret record for ${n}.`};case"tinycloud.kv/del":return{title:"Delete a secret",description:`Allows this app to remove the encrypted secret record for ${n}.`};case"tinycloud.kv/list":return{title:"List secrets",description:`Allows this app to list encrypted secret records under ${n}.`};case"tinycloud.kv/metadata":return{title:"Read secret metadata",description:`Allows this app to inspect metadata for encrypted secret records under ${n}.`};default:return}}(e,n)??G[n]??function(e,t){return{title:`Use ${J(e).replace(/ permission$/,"")}`,description:`Allows this app to perform ${t} on the resource shown below.`}}(e.service,i);return{canonicalAction:n,displayAction:i,...s}}(e,t)),s=i.map(e=>`\n <li class="permission-summary-item">\n <div class="permission-summary-row">\n <span class="permission-summary-label">Permission</span>\n <strong class="permission-summary-title">${Z(e.title)}</strong>\n </div>\n <p class="permission-summary-description">${Z(e.description)}</p>\n </li>\n `).join(""),o=[["Service",e.service],["Scope",t],["Resource",n],["Actions",i.map(e=>e.canonicalAction).join(", ")]].map(([e,t])=>`\n <div class="dictionary-row">\n <dt>${Z(e)}</dt>\n <dd>${Z(t)}</dd>\n </div>\n `).join("");return`\n <li class="permission-entry">\n <ul class="permission-summary-list">${s}</ul>\n <details class="technical-details">\n <summary>Show technical details</summary>\n <p class="entry-summary">${Z(function(e){if("tinycloud.encryption"===e.service)return"This permission applies to an encryption network, not a TinyCloud data space.";return`This permission applies to the ${X(e)} TinyCloud space and the resource path shown below.`}(e))}</p>\n <dl class="permission-dictionary">${o}</dl>\n </details>\n </li>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n .modal-backdrop {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex; align-items: flex-start; justify-content: center; overflow-y: auto;\n padding: 20px; opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n .modal-backdrop[data-state="visible"] { opacity: 1; pointer-events: auto; }\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .modal-container:focus { outline: none; }\n .modal-backdrop[data-state="visible"] .modal-container { transform: scale(1) translateY(0); }\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box; max-width: 520px; width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex; align-items: flex-start; gap: 16px; position: relative;\n }\n .app-icon {\n flex-shrink: 0; width: 48px; height: 48px;\n border-radius: 12px; object-fit: cover;\n background: var(--modal-secondary);\n }\n .app-icon--placeholder {\n display: flex; align-items: center; justify-content: center;\n background: var(--modal-accent); color: white;\n }\n .modal-title {\n flex: 1; min-width: 0; font-size: 18px; font-weight: 600; line-height: 1.3;\n margin: 0; margin-top: 4px;\n }\n .modal-close {\n position: absolute; top: 0; right: 0;\n background: transparent; border: none;\n color: var(--modal-muted); cursor: pointer;\n padding: 8px; border-radius: 6px; opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex; align-items: center; justify-content: center;\n }\n .modal-close:hover { opacity: 1; background: var(--modal-secondary); color: var(--modal-foreground); }\n .modal-body { padding: 20px 24px; }\n .modal-description {\n font-size: 14px; line-height: 1.5; margin: 0 0 16px 0; color: var(--modal-muted);\n }\n .permission-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n max-height: 320px; overflow-y: auto;\n }\n .permission-entry {\n background: var(--modal-secondary);\n border: 1px solid var(--modal-border);\n border-radius: 10px; padding: 14px;\n }\n .suppression-option {\n display: flex; align-items: center; gap: 10px;\n margin-top: 16px; color: var(--modal-muted);\n font-size: 13px; line-height: 1.4; cursor: pointer;\n }\n .suppression-checkbox {\n width: 16px; height: 16px; flex: 0 0 auto;\n margin: 0; accent-color: var(--modal-accent);\n }\n .suppression-option:hover { color: var(--modal-foreground); }\n .entry-summary {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 10px 0;\n }\n .permission-summary-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n }\n .permission-summary-item {\n display: flex; flex-direction: column; gap: 6px;\n }\n .permission-summary-row {\n display: grid; grid-template-columns: 88px minmax(0, 1fr);\n gap: 10px; align-items: baseline;\n }\n .permission-summary-label {\n font-size: 12px; color: var(--modal-muted);\n }\n .permission-summary-title {\n font-size: 15px; font-weight: 600; color: var(--modal-foreground);\n line-height: 1.35;\n }\n .permission-summary-description {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 0;\n }\n .technical-details {\n margin-top: 12px;\n border-top: 1px solid var(--modal-border);\n padding-top: 10px;\n }\n .technical-details summary {\n cursor: pointer; color: var(--modal-muted); font-size: 12px;\n user-select: none;\n }\n .technical-details summary:hover { color: var(--modal-foreground); }\n .technical-details[open] summary { margin-bottom: 8px; }\n .permission-dictionary {\n margin: 0; padding: 0;\n border: 1px solid var(--modal-border);\n border-radius: 8px; overflow: hidden;\n }\n .dictionary-row {\n display: grid; grid-template-columns: 86px 1fr;\n border-top: 1px solid var(--modal-border);\n }\n .dictionary-row:first-child { border-top: 0; }\n .dictionary-row dt,\n .dictionary-row dd {\n margin: 0; padding: 8px 10px; font-size: 12px; line-height: 1.4;\n }\n .dictionary-row dt {\n color: var(--modal-muted); background: var(--modal-bg);\n }\n .dictionary-row dd {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", monospace;\n color: var(--modal-foreground); word-break: break-all;\n }\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex; gap: 12px; justify-content: flex-end;\n }\n .modal-button {\n padding: 12px 20px; border-radius: 8px;\n font-size: 14px; font-weight: 500; cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex; align-items: center; gap: 8px;\n min-width: 100px; justify-content: center;\n }\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n .modal-button--secondary:hover {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n .modal-button--primary { background: var(--modal-accent); color: white; }\n .modal-button--primary:hover { background: var(--modal-accent-hover); transform: translateY(-1px); }\n .modal-button--primary:active { transform: translateY(0); }\n @media (max-width: 640px) {\n .modal-content { max-width: 100%; margin: 0 16px; border-radius: 12px; }\n .modal-header { padding: 20px 20px 0 20px; }\n .modal-body { padding: 16px 20px; }\n .modal-actions { padding: 0 20px 20px 20px; flex-direction: column-reverse; }\n .modal-button { width: 100%; }\n .permission-summary-row { grid-template-columns: 1fr; gap: 3px; }\n .dictionary-row { grid-template-columns: 1fr; }\n .dictionary-row dd { padding-top: 0; }\n }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="decline"]'),i=this.shadowRoot.querySelector('[data-action="approve"]');e?.addEventListener("click",t=>{t.target===e&&this.dismiss()}),t?.addEventListener("click",()=>this.dismiss()),n?.addEventListener("click",()=>this.dismiss()),i?.addEventListener("click",()=>{i&&r(i)&&this.handleApprove()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=s(t),i=n.indexOf(t.activeElement);e.preventDefault();const a=o(n.length,i,e.shiftKey);a>=0&&n[a].focus()}handleApprove(){const e=this.shadowRoot.querySelector('[data-role="suppress-prompt"]');this.resolveResult?.({approved:!0,suppressPromptFor30Days:!0===e?.checked}),this.hide()}show(){this.isVisible||(this.opener=i()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=n()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,a(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({approved:!1}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}const N={"tinycloud.encryption":"Encryption network permission","tinycloud.kv":"Key-value storage permission","tinycloud.sql":"SQL storage permission","tinycloud.duckdb":"DuckDB storage permission","tinycloud.capabilities":"Capability registry permission","tinycloud.hooks":"Hook permission"},G={[g.DECRYPT]:{title:"Decrypt data keys for this network",description:"Allows this app to ask a TinyCloud node to unwrap encrypted data keys for data encrypted to this network. The app still needs separate data-read permission to fetch encrypted records."},[g.NETWORK_CREATE]:{title:"Create an encryption network",description:"Allows this app to create or initialize an encryption network that data can be encrypted to. The network key is managed by TinyCloud nodes and used later for delegated decrypt requests."},[g.NETWORK_REVOKE]:{title:"Revoke an encryption network",description:"Allows this app to revoke or disable an encryption network so future decrypt requests for that network are denied."},[h.GET]:{title:"Read key-value data",description:"Allows this app to read values at the selected key or key prefix in the requested space."},[h.PUT]:{title:"Write key-value data",description:"Allows this app to create or replace values at the selected key or key prefix in the requested space."},[h.DEL]:{title:"Delete key-value data",description:"Allows this app to remove values at the selected key or key prefix in the requested space."},[h.LIST]:{title:"List key-value entries",description:"Allows this app to list keys under the selected key-value prefix in the requested space."},[h.METADATA]:{title:"Read key-value metadata",description:"Allows this app to inspect metadata for keys without necessarily reading the stored value."},[y.READ]:{title:"Read SQL data",description:"Allows this app to read rows and query results from the selected SQL database in the requested space."},[y.WRITE]:{title:"Write SQL data",description:"Allows this app to insert, update, or delete rows in the selected SQL database."},[y.SCHEMA]:{title:"Change SQL schema",description:"Allows this app to create, alter, or drop SQL tables, indexes, and schema objects."},[y.ADMIN]:{title:"Administer SQL storage",description:"Allows this app to perform administrative SQL operations for the selected database."},[y.SELECT]:{title:"Select SQL rows",description:"Allows this app to run SELECT queries against the selected SQL database."},[y.EXECUTE]:{title:"Execute SQL statements",description:"Allows this app to execute SQL statements against the selected database."},[y.EXPORT]:{title:"Export SQL data",description:"Allows this app to export data from the selected SQL database."},[y.ALL]:{title:"Full SQL access",description:"Allows this app to read, write, administer, and export the selected SQL database."},[v.READ]:{title:"Read DuckDB data",description:"Allows this app to read data and query results from the selected DuckDB database."},[v.WRITE]:{title:"Write DuckDB data",description:"Allows this app to write data to the selected DuckDB database."},[v.ADMIN]:{title:"Administer DuckDB storage",description:"Allows this app to perform administrative operations for the selected DuckDB database."},[v.DESCRIBE]:{title:"Describe DuckDB data",description:"Allows this app to inspect tables, columns, and metadata in the selected DuckDB database."},[v.EXPORT]:{title:"Export DuckDB data",description:"Allows this app to export data from the selected DuckDB database."},[v.IMPORT]:{title:"Import DuckDB data",description:"Allows this app to import data into the selected DuckDB database."},[v.EXECUTE]:{title:"Execute DuckDB statements",description:"Allows this app to execute statements against the selected DuckDB database."},[v.ALL]:{title:"Full DuckDB access",description:"Allows this app to read, write, administer, import, export, and execute statements on the selected DuckDB database."},[b.READ]:{title:"Read delegated capabilities",description:"Allows this app to read capability and delegation records for the selected scope."},[f.SUBSCRIBE]:{title:"Subscribe to hooks",description:"Allows this app to receive hook events for the selected hook scope."},[f.REGISTER]:{title:"Register hooks",description:"Allows this app to register hook handlers for the selected hook scope."},[f.LIST]:{title:"List hooks",description:"Allows this app to list registered hooks for the selected hook scope."},[f.UNREGISTER]:{title:"Unregister hooks",description:"Allows this app to remove registered hook handlers for the selected hook scope."}};function X(e){return void 0!==e.space&&""!==e.space?e.space:"tinycloud.encryption"===e.service?"network-scoped":"unscoped"}function J(e){return N[e]??`${e} permission`}function Z(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var ee;"undefined"==typeof customElements||customElements.get("tinycloud-permission-request-modal")||customElements.define("tinycloud-permission-request-modal",H);const te=Symbol.for("tinycloud.web-sdk.prompt-state"),ne=(ee=globalThis)[te]??(ee[te]={open:new Set,layer:void 0});const ie=Symbol.for("tinycloud.web-sdk.modals"),se={uiEnabled:!0};class oe{constructor(){this.activeModal=null,this.releaseActivePrompt=null}static getInstance(){return oe.instance||(oe.instance=new oe),oe.instance}showSpaceCreationModal(e,t=se){this.displaceActiveModal();const n=new c({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({success:!1,dismissed:!0}))}showPermissionRequestModal(e,t=se){this.displaceActiveModal();const n=new H({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({approved:!1}))}present(e,t,n){const i=function(e,t,n){const i=function(e){return function(){try{return"undefined"!=typeof window&&"undefined"!=typeof document&&window.top===window&&"null"!==window.location.origin}catch{return!1}}()?e.uiEnabled&&void 0!==ne.layer?void 0:"ui-disabled":"ui-unavailable-frame"}(e);if(void 0!==i)return console.warn(`[tinycloud] ${i}`),{refusal:i};ne.layer?.appendChild(t);const s={scope:e,dismiss:n,element:t};return ne.open.add(s),{release:()=>{ne.open.delete(s),ne.layer?.dispatchEvent(new Event("tinycloud-prompt-settled"))}}}(t,e,n);return!("refusal"in i)&&(this.activeModal=e,this.releaseActivePrompt=i.release,!0)}clearActive(e){this.activeModal===e&&(this.releaseActivePrompt?.(),this.releaseActivePrompt=null,this.activeModal=null)}displaceActiveModal(){try{this.closeActiveModal()}catch(e){queueMicrotask(()=>{throw e})}}closeActiveModal(){const e=this.activeModal;if(e)try{e.isOpen&&e.dismiss()}finally{e.remove(),this.clearActive(e)}}hasActiveModal(){return null!==this.activeModal}}const ae=(e,t)=>oe.getInstance().showSpaceCreationModal(e,t),re=(e,t)=>oe.getInstance().showPermissionRequestModal(e,t);globalThis[ie]={showPermissionRequestModal:re,showSpaceCreationModal:ae,closeActiveModal:()=>oe.getInstance().closeActiveModal()};export{oe as ModalManager,re as showPermissionRequestModal,ae as showSpaceCreationModal};
1
+ let e=0,t="";function n(){let n=!0;return 0===e&&(t=document.body.style.overflow),e+=1,document.body.style.overflow="hidden",()=>{n&&(n=!1,e-=1,0===e&&(document.body.style.overflow=t))}}function i(){let e=document.activeElement;for(;e instanceof HTMLElement&&e.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e instanceof HTMLElement?e:null}function s(e){return Array.from(e.querySelectorAll("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href]"))}function o(e,t,n){return 0===e?-1:n?t<=0?e-1:t-1:t<0||t>=e-1?0:t+1}function a(e,t){if(!e?.isConnected)return;const n=i();(null===n||n===document.body||t.contains(n))&&e.focus()}function r(e){const t=e.getBoundingClientRect();if(0===t.width||0===t.height)return!1;const n=e.getRootNode();if("function"!=typeof n.elementFromPoint)return!1;const i=n.elementFromPoint.call(n,t.left+t.width/2,t.top+t.height/2);return null!==i&&e.contains(i)}class c extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.isCreating=!1,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"!==e.key||this.isCreating?"Tab"===e.key&&this.trapTab(e):this.dismiss())},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-space-title" aria-describedby="tc-space-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n <div class="modal-icon">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5"/>\n <path d="M8 12l2 2 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n </div>\n <h2 class="modal-title" id="tc-space-title">Create Your TinyCloud Space</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-space-description">\n A TinyCloud Space is your personal data container where only you control access.\n Your signature is required for any operations on the data stored there.\n </p>\n <p class="modal-subdescription">\n Sign a message to create your space and start storing data.\n </p>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="learn-more">\n Learn More\n </button>\n <button class="modal-button modal-button--primary" data-action="create">\n <span class="button-text">Create TinyCloud Space</span>\n <span class="button-spinner" data-state="hidden">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" opacity="0.25"/>\n <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="9.42 9.42" stroke-dashoffset="9.42">\n <animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>\n </circle>\n </svg>\n </span>\n </button>\n </div>\n </div>\n </div>\n </div>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n\n .modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n overflow-y: auto;\n padding: 20px;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n\n .modal-backdrop[data-state="visible"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .modal-backdrop[data-state="visible"] .modal-container {\n transform: scale(1) translateY(0);\n }\n\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow:\n 0 25px 50px -12px rgba(0, 0, 0, 0.25),\n 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box;\n max-width: 480px;\n width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex;\n align-items: flex-start;\n gap: 16px;\n position: relative;\n }\n\n .modal-icon {\n flex-shrink: 0;\n width: 48px;\n height: 48px;\n background: var(--modal-accent);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n margin-top: 4px;\n }\n\n .modal-title {\n flex: 1;\n min-width: 0;\n font-size: 20px;\n font-weight: 600;\n line-height: 1.3;\n margin: 0;\n margin-top: 8px;\n }\n\n .modal-close {\n position: absolute;\n top: 0;\n right: 0;\n background: transparent;\n border: none;\n color: var(--modal-muted);\n cursor: pointer;\n padding: 8px;\n border-radius: 6px;\n opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .modal-close:hover {\n opacity: 1;\n background: var(--modal-secondary);\n color: var(--modal-foreground);\n }\n\n .modal-body {\n padding: 24px;\n padding-top: 20px;\n }\n\n .modal-description {\n font-size: 16px;\n line-height: 1.5;\n margin: 0 0 12px 0;\n color: var(--modal-foreground);\n }\n\n .modal-subdescription {\n font-size: 14px;\n line-height: 1.4;\n margin: 0;\n color: var(--modal-muted);\n }\n\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n }\n\n .modal-button {\n padding: 12px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 100px;\n justify-content: center;\n position: relative;\n }\n\n .modal-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n\n .modal-button--secondary:hover:not(:disabled) {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n\n .modal-button--primary {\n background: var(--modal-accent);\n color: white;\n }\n\n .modal-button--primary:hover:not(:disabled) {\n background: var(--modal-accent-hover);\n transform: translateY(-1px);\n }\n\n .modal-button--primary:active:not(:disabled) {\n transform: translateY(0);\n }\n\n .button-spinner {\n display: none;\n }\n\n .button-spinner[data-state="visible"] {\n display: flex;\n }\n\n .modal-button[data-state="loading"] .button-text {\n opacity: 0;\n }\n\n .modal-button[data-state="loading"] .button-spinner {\n display: flex;\n position: absolute;\n }\n\n @media (max-width: 640px) {\n .modal-content {\n max-width: 100%;\n margin: 0 16px;\n border-radius: 12px;\n }\n\n .modal-header {\n padding: 20px 20px 0 20px;\n }\n\n .modal-body {\n padding: 20px;\n padding-top: 16px;\n }\n\n .modal-actions {\n padding: 0 20px 20px 20px;\n flex-direction: column-reverse;\n }\n\n .modal-button {\n width: 100%;\n }\n }\n .modal-container:focus { outline: none; }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="learn-more"]'),i=this.shadowRoot.querySelector('[data-action="create"]');e?.addEventListener("click",t=>{t.target!==e||this.isCreating||this.dismiss()}),t?.addEventListener("click",()=>{this.isCreating||this.dismiss()}),n?.addEventListener("click",()=>{window.open("https://tinycloud.xyz","_blank","noopener,noreferrer")}),i?.addEventListener("click",()=>{!this.isCreating&&i&&r(i)&&this.handleCreateSpace()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=s(t),i=n.indexOf(t.activeElement);e.preventDefault();const a=o(n.length,i,e.shiftKey);a>=0&&n[a].focus()}async handleCreateSpace(){this.isCreating=!0;const e=this.shadowRoot.querySelector('[data-action="create"]');e.setAttribute("data-state","loading"),e.setAttribute("disabled","true");try{await this.options.onCreateSpace(),this.resolveResult?.({success:!0,dismissed:!1}),this.hide()}catch(e){console.error("[TinyCloud] Space creation failed:",e)}finally{this.isCreating=!1,e.removeAttribute("data-state"),e.removeAttribute("disabled")}}show(){this.isVisible||(this.opener=i()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=n()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,a(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({success:!1,dismissed:!0}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}customElements.get("tinycloud-space-modal")||customElements.define("tinycloud-space-modal",c);var l=[{urn:"tinycloud.kv/get",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/list",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/metadata",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/put",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/del",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/delete",service:"tinycloud.kv",status:"deprecated-alias",aliasOf:"tinycloud.kv/del"},{urn:"tinycloud.kv/sync",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.kv/retain",service:"tinycloud.kv",status:"active"},{urn:"tinycloud.sql/read",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/select",service:"tinycloud.sql",status:"deprecated-alias",aliasOf:"tinycloud.sql/read"},{urn:"tinycloud.sql/write",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/schema",service:"tinycloud.sql",status:"active"},{urn:"tinycloud.sql/admin",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/schema"]},{urn:"tinycloud.sql/*",service:"tinycloud.sql",status:"active",implies:["tinycloud.sql/read","tinycloud.sql/write","tinycloud.sql/schema","tinycloud.sql/admin"]},{urn:"tinycloud.duckdb/read",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/write",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/admin",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/import",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/export",service:"tinycloud.duckdb",status:"active"},{urn:"tinycloud.duckdb/select",service:"tinycloud.duckdb",status:"deprecated-alias",aliasOf:"tinycloud.duckdb/read"},{urn:"tinycloud.duckdb/*",service:"tinycloud.duckdb",status:"active",implies:["tinycloud.duckdb/read","tinycloud.duckdb/write","tinycloud.duckdb/admin","tinycloud.duckdb/import","tinycloud.duckdb/export"]},{urn:"tinycloud.capabilities/read",service:"tinycloud.capabilities",status:"active"},{urn:"tinycloud.delegation/status",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.delegation/list",service:"tinycloud.delegation",status:"active"},{urn:"tinycloud.hooks/subscribe",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/register",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/list",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.hooks/unregister",service:"tinycloud.hooks",status:"active"},{urn:"tinycloud.encryption/decrypt",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.create",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.encryption/network.revoke",service:"tinycloud.encryption",status:"active"},{urn:"tinycloud.space/host",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/create",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/list",service:"tinycloud.space",status:"active"},{urn:"tinycloud.space/info",service:"tinycloud.space",status:"active"},{urn:"tinycloud.vfs/get",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/list",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/metadata",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/put",service:"tinycloud.vfs",status:"reserved"},{urn:"tinycloud.vfs/delete",service:"tinycloud.vfs",status:"reserved"}];function d(e){return"*"===e?"ALL":e.toUpperCase().replace(/[^A-Z0-9]/g,"_")}function p(e){return e.slice(10,e.indexOf("/"))}function u(e){return e.slice(e.indexOf("/")+1)}function m(e){const t={};for(const n of l)p(n.urn)===e&&(t[d(u(n.urn))]=n.urn);return t}var h=m("kv"),y={...m("sql"),EXECUTE:"tinycloud.sql/execute",EXPORT:"tinycloud.sql/export"},v={...m("duckdb"),DESCRIBE:"tinycloud.duckdb/describe",EXECUTE:"tinycloud.duckdb/execute"},b=m("capabilities"),f=m("hooks"),g=m("encryption"),k=m("space"),w="applications",x="account",A="applications/",S="secrets",E="default",L="public",R="agents",T="tinycloud.encryption",D=g.NETWORK_CREATE,C=[E,w,x,S,L,R],q={app_id:"xyz.tinycloud.default",name:"TinyCloud Default Space",space:E,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:E,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:E,path:"",actions:["read","write"]}]},M={app_id:"xyz.tinycloud.applications",name:"TinyCloud Applications Space",space:w,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:w,path:"",actions:["get","put","del","list","metadata"]},{service:"tinycloud.sql",space:w,path:"",actions:["read","write"]}]},P={app_id:"xyz.tinycloud.account",name:"TinyCloud Account Registry",space:x,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:x,path:"applications/",actions:["get","put","list"]},{service:"tinycloud.kv",space:x,path:"spaces/",actions:["get","put","list"]},{service:"tinycloud.sql",space:x,path:"account",actions:["read","write","schema"]}]},$={app_id:"xyz.tinycloud.secrets",name:"TinyCloud Secrets",space:S,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.vault",space:S,path:"secrets/",actions:["read"],skipPrefix:!0},{service:"tinycloud.kv",space:S,path:"variables",actions:["list","metadata"],skipPrefix:!0},{service:"tinycloud.kv",space:S,path:"variables/",actions:["get","put","del","list","metadata"],skipPrefix:!0},{service:"tinycloud.sql",space:S,path:"default",actions:["read","write","schema"],skipPrefix:!0},{service:"tinycloud.capabilities",space:S,path:"",actions:["read"],skipPrefix:!0}]},z={app_id:"xyz.tinycloud.public",name:"TinyCloud Public Space",space:L,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:L,path:"",actions:["get","list","metadata"]}]},B={app_id:"xyz.tinycloud.agents",name:"TinyCloud Agents Space",space:R,prefix:"",defaults:!1,includePublicSpace:!1,permissions:[{service:"tinycloud.kv",space:R,path:"",actions:["get","put","del","list","metadata"]}]},I={[E]:q,[w]:M,[x]:P,[S]:$,[L]:z,[R]:B};function O(e){return{...e,permissions:e.permissions?.map(e=>({...e,actions:[...e.actions]}))}}function j(e,t,n){return!0===n||""===e||""===t?t:"/"===t?`${e}/`:`${e.endsWith("/")?e.slice(0,-1):e}/${t.startsWith("/")?t.slice(1):t}`}function W(e,t,n){if("tinycloud.vault"===e.service)return function(e){return e.actions.map(t=>{const n=t.startsWith("tinycloud.vault/")?t.slice(16):t.startsWith("tinycloud.kv/")?t.slice(13):t,i="read"===n||"get"===n?h.GET:"write"===n||"put"===n?h.PUT:"delete"===n||"del"===n?h.DEL:"list"===n?h.LIST:"metadata"===n?h.METADATA:void 0;if(void 0===i)throw new Error(`unknown vault action ${JSON.stringify(t)}`);const s=e.path.startsWith("/")?e.path.slice(1):e.path;return{...e,service:"tinycloud.kv",path:`vault/${s}`,actions:[i],skipPrefix:!0}})}(e).flatMap(e=>W(e,t,n));const i=!0===e.skipPrefix||e.service===T;return[{service:e.service,space:e.service===T?"encryption":e.space??n,path:j(t,e.path,i),actions:e.actions.map(t=>function(e,t){return t.includes("/")?t:`${e}/${t}`}(e.service,t)),...void 0!==e.description?{description:e.description}:{}}]}function Q(e){const t=new Set(e.filter(e=>e.service!==T).map(e=>e.space));return function(e){const t=new Map;for(const n of e){const e=`${n.service}\0${n.space}\0${n.path}`,i=t.get(e);if(i)for(const e of n.actions)i.actions.includes(e)||i.actions.push(e);else t.set(e,{...n,actions:[...n.actions]})}return[...t.values()]}([...e,...[...t].map(e=>({service:"tinycloud.capabilities",space:e,path:"",actions:[b.READ]}))])}function F(e){return function(e,t={}){if(!Array.isArray(e)||0===e.length)throw new Error("composeManifestRequest requires at least one manifest");const n=t.includeAccountRegistryPermissions??!0,i=e.map(O),s=i.flatMap(e=>{const t=e.prefix??e.app_id,n=e.space??w;return(e.permissions??[]).flatMap(e=>W(e,t,n))});n&&s.push(...K.map(e=>({...e,actions:[...e.actions]})));const o=new Map;for(const e of i){const t=o.get(e.app_id)??[];t.push(O(e)),o.set(e.app_id,t)}return{manifests:i,resources:Q(s),delegationTargets:[],registryRecords:n?[...o.entries()].map(([e,t])=>({key:`${A}${e}`,app_id:e,manifests:t})):[],expiryMs:2592e6,includePublicSpace:i.some(e=>e.includePublicSpace??!0)}}([I[e]],{includeAccountRegistryPermissions:!1})}C.map(e=>({name:e,manifest:I[e],persistedAsApplication:e===S}));var K=Object.freeze([{service:"tinycloud.kv",space:x,path:A,actions:[h.GET,h.PUT,h.LIST]},{service:"tinycloud.kv",space:x,path:"spaces/",actions:[h.GET,h.PUT,h.LIST]},{service:"tinycloud.kv",space:x,path:"system/bootstrap/complete",actions:[h.GET,h.PUT]},{service:"tinycloud.delegation",space:x,path:"",actions:["tinycloud.delegation/list"]},{service:"tinycloud.sql",space:x,path:"account",actions:[y.READ,y.WRITE,y.SCHEMA]},{service:"tinycloud.capabilities",space:x,path:"",actions:[b.READ]}]),U=F(E),V={...U,resources:[...U.resources,...K.map(e=>({...e,actions:[...e.actions]}))]},_=Object.freeze(Object.fromEntries(C.map(e=>[e,e===E?V:F(e)]))),Y=Object.freeze([{service:"tinycloud.encryption",resource:"urn:tinycloud:encryption:{ownerDid}:default",actions:[D]}]);Object.freeze(C.flatMap(e=>[{kind:"session",service:"tinycloud.session",space:e,actions:["siwe"],resources:_[e].resources,...e===x?{rawAbilities:Y}:{}},{kind:"space/host",service:"tinycloud.space",space:e,actions:[k.HOST]}]));class H extends HTMLElement{constructor(e){super(),this.isVisible=!1,this.releaseScrollLock=null,this.opener=null,this.resolveResult=null,this.handleKeyDown=e=>{this.isVisible&&("Escape"===e.key?this.dismiss():"Tab"===e.key&&this.trapTab(e))},this.options=e,this.attachShadow({mode:"open"}),this.render(),this.completionPromise=new Promise(e=>{this.resolveResult=e})}getCompletionPromise(){return this.completionPromise}connectedCallback(){this.show(),this.setupEventListeners()}disconnectedCallback(){this.isVisible=!1,this.restoreFocus(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)}render(){const e=this.options.additional.map(e=>this.renderEntry(e)).join(""),t=Z(this.options.appName),n=this.options.appIcon?`<img class="app-icon" src="${i=this.options.appIcon,Z(i)}" alt="" />`:'<div class="app-icon app-icon--placeholder">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>\n </svg>\n </div>';var i;this.shadowRoot.innerHTML=`\n <style>${this.getModalStyles()}</style>\n <div class="modal-backdrop" data-state="hidden">\n <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="tc-permission-title" aria-describedby="tc-permission-description" tabindex="-1">\n <div class="modal-content">\n <div class="modal-header">\n ${n}\n <h2 class="modal-title" id="tc-permission-title">${t} is requesting additional permissions</h2>\n <button class="modal-close" aria-label="Close">\n <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path d="M1 1L13 13M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n </button>\n </div>\n\n <div class="modal-body">\n <p class="modal-description" id="tc-permission-description">\n This app is asking you to grant scoped permission. Review what it will be allowed to do before approving.\n </p>\n <ul class="permission-list">${e}</ul>\n <label class="suppression-option">\n <input type="checkbox" class="suppression-checkbox" data-role="suppress-prompt" />\n <span>Do not show me again for 30 days</span>\n </label>\n </div>\n\n <div class="modal-actions">\n <button class="modal-button modal-button--secondary" data-action="decline">\n Decline\n </button>\n <button class="modal-button modal-button--primary" data-action="approve">\n Approve\n </button>\n </div>\n </div>\n </div>\n </div>\n `}renderEntry(e){const t=X(e),n=""===e.path?"/":e.path,i=e.actions.map(t=>function(e,t){const n=function(e,t){return t.includes("/")?t:t.startsWith(`${e}.`)?`${e}/${t.slice(e.length+1)}`:`${e}/${t}`}(e.service,t),i=function(e,t){return t.startsWith(`${e}/`)||t.startsWith(`${e}.`)?t.slice(e.length+1):t}(e.service,t),s=function(e,t){if("tinycloud.kv"!==e.service||"secrets"!==e.space)return;const n=function(e){const t="vault/secrets/";if("vault/secrets"===e)return"all secrets";if(!e.startsWith(t))return;const n=e.slice(14);if(""===n)return"all secrets";const i="scoped/";if(n.startsWith(i)){const[e,...t]=n.slice(7).split("/"),i=t.join("/");if(""!==e&&""!==i)return`the scoped secret ${i} in ${e}`}return`the secret ${n}`}(e.path);if(void 0!==n)switch(t){case"tinycloud.kv/get":return{title:"Read an encrypted secret",description:`Allows this app to read the encrypted secret record for ${n}. Decrypting it still requires encryption-network decrypt permission.`};case"tinycloud.kv/put":return{title:"Write a secret",description:`Allows this app to create or update the encrypted secret record for ${n}.`};case"tinycloud.kv/del":return{title:"Delete a secret",description:`Allows this app to remove the encrypted secret record for ${n}.`};case"tinycloud.kv/list":return{title:"List secrets",description:`Allows this app to list encrypted secret records under ${n}.`};case"tinycloud.kv/metadata":return{title:"Read secret metadata",description:`Allows this app to inspect metadata for encrypted secret records under ${n}.`};default:return}}(e,n)??G[n]??function(e,t){return{title:"tinycloud.delegation"===e?"Manage your delegations"+(""===t?"":` (${t})`):`Use ${J(e).replace(/ permission$/,"")}`,description:`Allows this app to perform ${t} on the resource shown below.`}}(e.service,i);return{canonicalAction:n,displayAction:i,...s}}(e,t)),s=i.map(e=>`\n <li class="permission-summary-item">\n <div class="permission-summary-row">\n <span class="permission-summary-label">Permission</span>\n <strong class="permission-summary-title">${Z(e.title)}</strong>\n </div>\n <p class="permission-summary-description">${Z(e.description)}</p>\n </li>\n `).join(""),o=[["Service",e.service],["Scope",t],["Resource",n],["Actions",i.map(e=>e.canonicalAction).join(", ")]].map(([e,t])=>`\n <div class="dictionary-row">\n <dt>${Z(e)}</dt>\n <dd>${Z(t)}</dd>\n </div>\n `).join("");return`\n <li class="permission-entry">\n <ul class="permission-summary-list">${s}</ul>\n <details class="technical-details">\n <summary>Show technical details</summary>\n <p class="entry-summary">${Z(function(e){if("tinycloud.encryption"===e.service)return"This permission applies to an encryption network, not a TinyCloud data space.";return`This permission applies to the ${X(e)} TinyCloud space and the resource path shown below.`}(e))}</p>\n <dl class="permission-dictionary">${o}</dl>\n </details>\n </li>\n `}getModalStyles(){return'\n :host {\n --modal-bg: hsl(0 0% 3.9%);\n --modal-border: hsl(0 0% 14.9%);\n --modal-foreground: hsl(0 0% 98%);\n --modal-muted: hsl(0 0% 63.9%);\n --modal-accent: hsl(217 91% 60%);\n --modal-accent-hover: hsl(217 91% 65%);\n --modal-secondary: hsl(0 0% 10%);\n --modal-secondary-hover: hsl(0 0% 15%);\n --modal-overlay: rgba(0, 0, 0, 0.5);\n }\n .modal-backdrop {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: var(--modal-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n z-index: 2000000;\n display: flex; align-items: flex-start; justify-content: center; overflow-y: auto;\n padding: 20px; opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n .modal-backdrop[data-state="visible"] { opacity: 1; pointer-events: auto; }\n .modal-container {\n margin: auto;\n transform: scale(0.95) translateY(10px);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .modal-container:focus { outline: none; }\n .modal-backdrop[data-state="visible"] .modal-container { transform: scale(1) translateY(0); }\n .modal-content {\n background: var(--modal-bg);\n border: 1px solid var(--modal-border);\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05);\n box-sizing: border-box; max-width: 520px; width: 100%;\n overflow-wrap: anywhere;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n color: var(--modal-foreground);\n }\n .modal-header {\n padding: 24px 24px 0 24px;\n display: flex; align-items: flex-start; gap: 16px; position: relative;\n }\n .app-icon {\n flex-shrink: 0; width: 48px; height: 48px;\n border-radius: 12px; object-fit: cover;\n background: var(--modal-secondary);\n }\n .app-icon--placeholder {\n display: flex; align-items: center; justify-content: center;\n background: var(--modal-accent); color: white;\n }\n .modal-title {\n flex: 1; min-width: 0; font-size: 18px; font-weight: 600; line-height: 1.3;\n margin: 0; margin-top: 4px;\n }\n .modal-close {\n position: absolute; top: 0; right: 0;\n background: transparent; border: none;\n color: var(--modal-muted); cursor: pointer;\n padding: 8px; border-radius: 6px; opacity: 0.7;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex; align-items: center; justify-content: center;\n }\n .modal-close:hover { opacity: 1; background: var(--modal-secondary); color: var(--modal-foreground); }\n .modal-body { padding: 20px 24px; }\n .modal-description {\n font-size: 14px; line-height: 1.5; margin: 0 0 16px 0; color: var(--modal-muted);\n }\n .permission-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n max-height: 320px; overflow-y: auto;\n }\n .permission-entry {\n background: var(--modal-secondary);\n border: 1px solid var(--modal-border);\n border-radius: 10px; padding: 14px;\n }\n .suppression-option {\n display: flex; align-items: center; gap: 10px;\n margin-top: 16px; color: var(--modal-muted);\n font-size: 13px; line-height: 1.4; cursor: pointer;\n }\n .suppression-checkbox {\n width: 16px; height: 16px; flex: 0 0 auto;\n margin: 0; accent-color: var(--modal-accent);\n }\n .suppression-option:hover { color: var(--modal-foreground); }\n .entry-summary {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 10px 0;\n }\n .permission-summary-list {\n list-style: none; padding: 0; margin: 0;\n display: flex; flex-direction: column; gap: 10px;\n }\n .permission-summary-item {\n display: flex; flex-direction: column; gap: 6px;\n }\n .permission-summary-row {\n display: grid; grid-template-columns: 88px minmax(0, 1fr);\n gap: 10px; align-items: baseline;\n }\n .permission-summary-label {\n font-size: 12px; color: var(--modal-muted);\n }\n .permission-summary-title {\n font-size: 15px; font-weight: 600; color: var(--modal-foreground);\n line-height: 1.35;\n }\n .permission-summary-description {\n font-size: 13px; line-height: 1.45; color: var(--modal-muted);\n margin: 0;\n }\n .technical-details {\n margin-top: 12px;\n border-top: 1px solid var(--modal-border);\n padding-top: 10px;\n }\n .technical-details summary {\n cursor: pointer; color: var(--modal-muted); font-size: 12px;\n user-select: none;\n }\n .technical-details summary:hover { color: var(--modal-foreground); }\n .technical-details[open] summary { margin-bottom: 8px; }\n .permission-dictionary {\n margin: 0; padding: 0;\n border: 1px solid var(--modal-border);\n border-radius: 8px; overflow: hidden;\n }\n .dictionary-row {\n display: grid; grid-template-columns: 86px 1fr;\n border-top: 1px solid var(--modal-border);\n }\n .dictionary-row:first-child { border-top: 0; }\n .dictionary-row dt,\n .dictionary-row dd {\n margin: 0; padding: 8px 10px; font-size: 12px; line-height: 1.4;\n }\n .dictionary-row dt {\n color: var(--modal-muted); background: var(--modal-bg);\n }\n .dictionary-row dd {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", monospace;\n color: var(--modal-foreground); word-break: break-all;\n }\n .modal-actions {\n padding: 0 24px 24px 24px;\n display: flex; gap: 12px; justify-content: flex-end;\n }\n .modal-button {\n padding: 12px 20px; border-radius: 8px;\n font-size: 14px; font-weight: 500; cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n border: 1px solid transparent;\n display: flex; align-items: center; gap: 8px;\n min-width: 100px; justify-content: center;\n }\n .modal-button--secondary {\n background: var(--modal-secondary);\n border-color: var(--modal-border);\n color: var(--modal-foreground);\n }\n .modal-button--secondary:hover {\n background: var(--modal-secondary-hover);\n border-color: hsl(0 0% 25%);\n transform: translateY(-1px);\n }\n .modal-button--primary { background: var(--modal-accent); color: white; }\n .modal-button--primary:hover { background: var(--modal-accent-hover); transform: translateY(-1px); }\n .modal-button--primary:active { transform: translateY(0); }\n @media (max-width: 640px) {\n .modal-content { max-width: 100%; margin: 0 16px; border-radius: 12px; }\n .modal-header { padding: 20px 20px 0 20px; }\n .modal-body { padding: 16px 20px; }\n .modal-actions { padding: 0 20px 20px 20px; flex-direction: column-reverse; }\n .modal-button { width: 100%; }\n .permission-summary-row { grid-template-columns: 1fr; gap: 3px; }\n .dictionary-row { grid-template-columns: 1fr; }\n .dictionary-row dd { padding-top: 0; }\n }\n '}setupEventListeners(){const e=this.shadowRoot.querySelector(".modal-backdrop"),t=this.shadowRoot.querySelector(".modal-close"),n=this.shadowRoot.querySelector('[data-action="decline"]'),i=this.shadowRoot.querySelector('[data-action="approve"]');e?.addEventListener("click",t=>{t.target===e&&this.dismiss()}),t?.addEventListener("click",()=>this.dismiss()),n?.addEventListener("click",()=>this.dismiss()),i?.addEventListener("click",()=>{i&&r(i)&&this.handleApprove()}),document.addEventListener("keydown",this.handleKeyDown)}trapTab(e){if(!e.composedPath().includes(this))return;const t=this.shadowRoot,n=s(t),i=n.indexOf(t.activeElement);e.preventDefault();const a=o(n.length,i,e.shiftKey);a>=0&&n[a].focus()}handleApprove(){const e=this.shadowRoot.querySelector('[data-role="suppress-prompt"]');this.resolveResult?.({approved:!0,suppressPromptFor30Days:!0===e?.checked}),this.hide()}show(){this.isVisible||(this.opener=i()),this.isVisible=!0,this.releaseScrollLock||(this.releaseScrollLock=n()),this.shadowRoot.querySelector(".modal-container").focus(),requestAnimationFrame(()=>{const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","visible")})}hide(){const e=this.shadowRoot.querySelector(".modal-backdrop");e?.setAttribute("data-state","hidden"),this.restoreFocus(),setTimeout(()=>{this.remove(),this.releaseLock(),document.removeEventListener("keydown",this.handleKeyDown)},200),this.isVisible=!1}restoreFocus(){const e=this.opener;this.opener=null,a(e,this.shadowRoot)}releaseLock(){this.releaseScrollLock?.(),this.releaseScrollLock=null}dismiss(){try{this.resolveResult?.({approved:!1}),this.options.onDismiss?.()}finally{this.hide()}}get isOpen(){return this.isVisible}}const N={"tinycloud.encryption":"Encryption network permission","tinycloud.kv":"Key-value storage permission","tinycloud.sql":"SQL storage permission","tinycloud.duckdb":"DuckDB storage permission","tinycloud.capabilities":"Capability registry permission","tinycloud.hooks":"Hook permission"},G={[g.DECRYPT]:{title:"Decrypt data keys for this network",description:"Allows this app to ask a TinyCloud node to unwrap encrypted data keys for data encrypted to this network. The app still needs separate data-read permission to fetch encrypted records."},[g.NETWORK_CREATE]:{title:"Create an encryption network",description:"Allows this app to create or initialize an encryption network that data can be encrypted to. The network key is managed by TinyCloud nodes and used later for delegated decrypt requests."},[g.NETWORK_REVOKE]:{title:"Revoke an encryption network",description:"Allows this app to revoke or disable an encryption network so future decrypt requests for that network are denied."},[h.GET]:{title:"Read key-value data",description:"Allows this app to read values at the selected key or key prefix in the requested space."},[h.PUT]:{title:"Write key-value data",description:"Allows this app to create or replace values at the selected key or key prefix in the requested space."},[h.DEL]:{title:"Delete key-value data",description:"Allows this app to remove values at the selected key or key prefix in the requested space."},[h.LIST]:{title:"List key-value entries",description:"Allows this app to list keys under the selected key-value prefix in the requested space."},[h.METADATA]:{title:"Read key-value metadata",description:"Allows this app to inspect metadata for keys without necessarily reading the stored value."},[y.READ]:{title:"Read SQL data",description:"Allows this app to read rows and query results from the selected SQL database in the requested space."},[y.WRITE]:{title:"Write SQL data",description:"Allows this app to insert, update, or delete rows in the selected SQL database."},[y.SCHEMA]:{title:"Change SQL schema",description:"Allows this app to create, alter, or drop SQL tables, indexes, and schema objects."},[y.ADMIN]:{title:"Administer SQL storage",description:"Allows this app to perform administrative SQL operations for the selected database."},[y.SELECT]:{title:"Select SQL rows",description:"Allows this app to run SELECT queries against the selected SQL database."},[y.EXECUTE]:{title:"Execute SQL statements",description:"Allows this app to execute SQL statements against the selected database."},[y.EXPORT]:{title:"Export SQL data",description:"Allows this app to export data from the selected SQL database."},[y.ALL]:{title:"Full SQL access",description:"Allows this app to read, write, administer, and export the selected SQL database."},[v.READ]:{title:"Read DuckDB data",description:"Allows this app to read data and query results from the selected DuckDB database."},[v.WRITE]:{title:"Write DuckDB data",description:"Allows this app to write data to the selected DuckDB database."},[v.ADMIN]:{title:"Administer DuckDB storage",description:"Allows this app to perform administrative operations for the selected DuckDB database."},[v.DESCRIBE]:{title:"Describe DuckDB data",description:"Allows this app to inspect tables, columns, and metadata in the selected DuckDB database."},[v.EXPORT]:{title:"Export DuckDB data",description:"Allows this app to export data from the selected DuckDB database."},[v.IMPORT]:{title:"Import DuckDB data",description:"Allows this app to import data into the selected DuckDB database."},[v.EXECUTE]:{title:"Execute DuckDB statements",description:"Allows this app to execute statements against the selected DuckDB database."},[v.ALL]:{title:"Full DuckDB access",description:"Allows this app to read, write, administer, import, export, and execute statements on the selected DuckDB database."},[b.READ]:{title:"Read delegated capabilities",description:"Allows this app to read capability and delegation records for the selected scope."},"tinycloud.delegation/list":{title:"See which apps and links can reach your data, and the names of what each can reach",description:"See who has access to your data, what it reaches (by name) and when it ends. Keys and contents aren't shown."},[f.SUBSCRIBE]:{title:"Subscribe to hooks",description:"Allows this app to receive hook events for the selected hook scope."},[f.REGISTER]:{title:"Register hooks",description:"Allows this app to register hook handlers for the selected hook scope."},[f.LIST]:{title:"List hooks",description:"Allows this app to list registered hooks for the selected hook scope."},[f.UNREGISTER]:{title:"Unregister hooks",description:"Allows this app to remove registered hook handlers for the selected hook scope."}};function X(e){return void 0!==e.space&&""!==e.space?e.space:"tinycloud.encryption"===e.service?"network-scoped":"unscoped"}function J(e){return N[e]??`${e} permission`}function Z(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var ee;"undefined"==typeof customElements||customElements.get("tinycloud-permission-request-modal")||customElements.define("tinycloud-permission-request-modal",H);const te=Symbol.for("tinycloud.web-sdk.prompt-state"),ne=(ee=globalThis)[te]??(ee[te]={open:new Set,layer:void 0});const ie=Symbol.for("tinycloud.web-sdk.modals"),se={uiEnabled:!0};class oe{constructor(){this.activeModal=null,this.releaseActivePrompt=null}static getInstance(){return oe.instance||(oe.instance=new oe),oe.instance}showSpaceCreationModal(e,t=se){this.displaceActiveModal();const n=new c({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({success:!1,dismissed:!0}))}showPermissionRequestModal(e,t=se){this.displaceActiveModal();const n=new H({...e,onDismiss:()=>{this.clearActive(n),e.onDismiss?.()}});return this.present(n,t,()=>n.dismiss())?n.getCompletionPromise().finally(()=>{this.clearActive(n)}):(e.onDismiss?.(),Promise.resolve({approved:!1}))}present(e,t,n){const i=function(e,t,n){const i=function(e){return function(){try{return"undefined"!=typeof window&&"undefined"!=typeof document&&window.top===window&&"null"!==window.location.origin}catch{return!1}}()?e.uiEnabled&&void 0!==ne.layer?void 0:"ui-disabled":"ui-unavailable-frame"}(e);if(void 0!==i)return console.warn(`[tinycloud] ${i}`),{refusal:i};ne.layer?.appendChild(t);const s={scope:e,dismiss:n,element:t};return ne.open.add(s),{release:()=>{ne.open.delete(s),ne.layer?.dispatchEvent(new Event("tinycloud-prompt-settled"))}}}(t,e,n);return!("refusal"in i)&&(this.activeModal=e,this.releaseActivePrompt=i.release,!0)}clearActive(e){this.activeModal===e&&(this.releaseActivePrompt?.(),this.releaseActivePrompt=null,this.activeModal=null)}displaceActiveModal(){try{this.closeActiveModal()}catch(e){queueMicrotask(()=>{throw e})}}closeActiveModal(){const e=this.activeModal;if(e)try{e.isOpen&&e.dismiss()}finally{e.remove(),this.clearActive(e)}}hasActiveModal(){return null!==this.activeModal}}const ae=(e,t)=>oe.getInstance().showSpaceCreationModal(e,t),re=(e,t)=>oe.getInstance().showPermissionRequestModal(e,t);globalThis[ie]={showPermissionRequestModal:re,showSpaceCreationModal:ae,closeActiveModal:()=>oe.getInstance().closeActiveModal()};export{oe as ModalManager,re as showPermissionRequestModal,ae as showSpaceCreationModal};
@@ -36,10 +36,10 @@ export declare const SessionConfigSchema: z.ZodObject<{
36
36
  domain?: string;
37
37
  actions?: Record<string, Record<string, string[]>>;
38
38
  spaceId?: string;
39
+ jwk?: Record<string, unknown>;
39
40
  address?: string;
40
41
  chainId?: number;
41
42
  issuedAt?: string;
42
- jwk?: Record<string, unknown>;
43
43
  notBefore?: string;
44
44
  expirationTime?: string;
45
45
  parents?: string[];
@@ -47,10 +47,10 @@ export declare const SessionConfigSchema: z.ZodObject<{
47
47
  domain?: string;
48
48
  actions?: Record<string, Record<string, string[]>>;
49
49
  spaceId?: string;
50
+ jwk?: Record<string, unknown>;
50
51
  address?: string;
51
52
  chainId?: number;
52
53
  issuedAt?: string;
53
- jwk?: Record<string, unknown>;
54
54
  notBefore?: string;
55
55
  expirationTime?: string;
56
56
  parents?: string[];
@@ -10,11 +10,13 @@
10
10
  *
11
11
  * @packageDocumentation
12
12
  */
13
- import type { Manifest, PermissionEntry } from "@tinycloud/sdk-core";
13
+ import { type Manifest, type PermissionEntry } from "@tinycloud/sdk-core";
14
14
  import type { PortableDelegation } from "@tinycloud/node-sdk/core";
15
15
  export interface RequestPermissionsCoreDeps {
16
16
  /** Current stored manifest. Must be defined — caller validates first. */
17
17
  manifest: Manifest;
18
+ /** Authenticated owner from the current node, never from a requested resource. */
19
+ ownerDid?: string;
18
20
  /**
19
21
  * Show the permission request modal. Resolves with `{ approved }`
20
22
  * once the user interacts. In tests this is a stub; in production it
@@ -49,6 +51,11 @@ export interface RequestPermissionsOptions {
49
51
  * the same error text.
50
52
  */
51
53
  export declare function validateAdditionalPermissions(additional: PermissionEntry[]): void;
54
+ /**
55
+ * Normalize only encryption key creation here. Other runtime entries retain
56
+ * their existing Web consent representation; the node validates them.
57
+ */
58
+ export declare function normalizeRuntimeKeyCreatePermissions(additional: PermissionEntry[], ownerDid?: string): PermissionEntry[];
52
59
  /**
53
60
  * Core escalation flow. See the TinyCloudWeb.requestPermissions JSDoc
54
61
  * for the full spec-level description — this function is the plumbing.
@@ -0,0 +1,41 @@
1
+ import type { PortableDelegation } from "@tinycloud/node-sdk/core";
2
+ export interface RuntimeGrantStore {
3
+ loadRuntimeGrants(address: string): Promise<PortableDelegation[]>;
4
+ saveRuntimeGrants(address: string, grants: readonly PortableDelegation[]): Promise<void>;
5
+ }
6
+ /** How long a restore waits for stored grants to be activated before it carries on without them. */
7
+ export declare const RESTORE_GRANT_BUDGET_MS = 4000;
8
+ interface Resource {
9
+ readonly service: string;
10
+ readonly space: string;
11
+ readonly path: string;
12
+ readonly actions: readonly string[];
13
+ readonly caveats?: readonly unknown[] | undefined;
14
+ }
15
+ /**
16
+ * True when `resources` are exactly one tab's entry for this owner: that service, the resolved space, that path,
17
+ * exactly those actions, no caveats. A grant that carries anything more (an app's larger grant) is not a panel grant.
18
+ */
19
+ export declare function isPanelResourceSet(resources: readonly Resource[], owner: {
20
+ readonly address: string;
21
+ readonly chainId: number;
22
+ }): boolean;
23
+ /** True when `grant` is exactly a panel grant of the session owned by `address`. */
24
+ export declare function isPanelGrant(grant: PortableDelegation, address: string): boolean;
25
+ /**
26
+ * Stores the panel's own grants among `grants` for `address`, replacing a stored grant with the same cid. Other
27
+ * delegations (an app's larger grant that already covered the request) are never stored.
28
+ */
29
+ export declare function rememberRuntimeGrants(store: RuntimeGrantStore, address: string, grants: readonly PortableDelegation[]): Promise<void>;
30
+ /**
31
+ * Activates each stored grant through `use` (the SDK's validated activation). A grant refused for good is removed from
32
+ * storage; the others stay, including one whose activation failed for a transient reason. Waits at most `budgetMs`;
33
+ * activations still running then finish in the background. Never throws. `isCurrent` false (signed out meanwhile)
34
+ * stops any write to storage.
35
+ */
36
+ export declare function reuseRuntimeGrants(store: RuntimeGrantStore, address: string, use: (grant: PortableDelegation) => Promise<unknown>, options?: {
37
+ readonly budgetMs?: number;
38
+ readonly isCurrent?: () => boolean;
39
+ }): Promise<void>;
40
+ export {};
41
+ //# sourceMappingURL=runtimeGrantPersistence.d.ts.map