@tinycloud/web-sdk 3.8.0 → 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/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/modals.cjs +1 -1
- package/dist/modals.mjs +1 -1
- package/dist/modules/tcw.d.ts +24 -0
- package/package.json +2 -2
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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};
|
package/dist/modules/tcw.d.ts
CHANGED
|
@@ -25,6 +25,10 @@ declare global {
|
|
|
25
25
|
ethereum?: any;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
/** Signs a message for a runtime permission grant; see `TinyCloudWeb.setRuntimePermissionSigner`. */
|
|
29
|
+
export interface RuntimePermissionSigner {
|
|
30
|
+
signMessage(message: string): Promise<string>;
|
|
31
|
+
}
|
|
28
32
|
/**
|
|
29
33
|
* Configuration for TinyCloudWeb.
|
|
30
34
|
*
|
|
@@ -206,6 +210,8 @@ export declare class TinyCloudWeb {
|
|
|
206
210
|
private _sessionRestoreStatus;
|
|
207
211
|
/** Incremented by signOut; a sign-in that started before it does not complete. */
|
|
208
212
|
private signOutCount;
|
|
213
|
+
/** The signer set with `setRuntimePermissionSigner`, with the session it was set for. */
|
|
214
|
+
private runtimeSigner?;
|
|
209
215
|
private signOutInFlight;
|
|
210
216
|
private persistedRestoreBlocked;
|
|
211
217
|
private signOutCleanupFailed;
|
|
@@ -475,6 +481,24 @@ export declare class TinyCloudWeb {
|
|
|
475
481
|
* `expiryMs` does not apply to them.
|
|
476
482
|
*/
|
|
477
483
|
requestPermissions(additional: PermissionEntry[], options?: RequestPermissionsOptions): Promise<RequestPermissionsResult>;
|
|
484
|
+
/**
|
|
485
|
+
* Signs the SIWE of a runtime permission grant. Precedence: the signer set with `setRuntimePermissionSigner`, then
|
|
486
|
+
* the OpenKey client the session was established with (its byte-exact `signMessage`; an OpenKey session's wallet
|
|
487
|
+
* provider does not implement `personal_sign`), then undefined: the connected wallet signs. Only
|
|
488
|
+
* `requestPermissions` calls this; sign-in and session setup never do.
|
|
489
|
+
*/
|
|
490
|
+
private runtimeMessageSigner;
|
|
491
|
+
/**
|
|
492
|
+
* Sets the signer for runtime permission grants (`requestPermissions`, and the panel's "allow" buttons), or clears
|
|
493
|
+
* it with no argument. For apps whose sign-in is not the SDK's OpenKey flow, for example a restored session whose
|
|
494
|
+
* wallet provider cannot sign: the signer signs each grant's SIWE as an EIP-191 message (`personal_sign`), and the
|
|
495
|
+
* grant is refused unless the signature recovers to the session's account.
|
|
496
|
+
*
|
|
497
|
+
* Call it after the session is established; it throws without one. It is bound to that session: sign-out, a new
|
|
498
|
+
* sign-in, a restore, or a session for another account clear it. It is never used for sign-in or session setup.
|
|
499
|
+
* Without it, grants sign through the `openkey` client when configured, else the connected wallet.
|
|
500
|
+
*/
|
|
501
|
+
setRuntimePermissionSigner(signer?: RuntimePermissionSigner): void;
|
|
478
502
|
/** True when the panel may ask for a permission: a manifest, a page signer and a live session exist. */
|
|
479
503
|
private canRequestPanelPermissions;
|
|
480
504
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinycloud/web-sdk",
|
|
3
|
-
"version": "3.8.0",
|
|
3
|
+
"version": "3.8.1-beta.0",
|
|
4
4
|
"description": "A set of tools and utilities to help you build your app with TinyCloud.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@multiformats/multiaddr": "^13.0.1",
|
|
67
67
|
"@multiformats/multiaddr-to-uri": "^12.0.0",
|
|
68
68
|
"@noble/curves": "^1.9.7",
|
|
69
|
-
"@tinycloud/node-sdk": "3.8.0",
|
|
69
|
+
"@tinycloud/node-sdk": "3.8.1-beta.0",
|
|
70
70
|
"@tinycloud/replica": "0.4.0",
|
|
71
71
|
"@tinycloud/sdk-core": "3.8.0",
|
|
72
72
|
"@tinycloud/share-envelope": "1.4.0",
|