dolphin-client 1.0.9 → 1.1.1

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.
@@ -390,6 +390,34 @@
390
390
  {
391
391
  "name": "data-rt-sort",
392
392
  "description": "Sorts the rendered data array (e.g., price.asc or popular)."
393
+ },
394
+ {
395
+ "name": "data-api-toast",
396
+ "description": "Inline success message to show as toast after a successful API request."
397
+ },
398
+ {
399
+ "name": "data-api-error-toast",
400
+ "description": "Inline error message to show as toast after a failed API request."
401
+ },
402
+ {
403
+ "name": "data-rt-api-success",
404
+ "description": "ID of a hidden element whose text content is shown as a success toast after a successful API request. Example: data-rt-api-success=\"my-success-msg\""
405
+ },
406
+ {
407
+ "name": "data-rt-api-error",
408
+ "description": "ID of a hidden element whose text content is shown as an error toast after a failed API request. Example: data-rt-api-error=\"my-error-msg\""
409
+ },
410
+ {
411
+ "name": "data-rt-api-success-show",
412
+ "description": "ID of an inline alert element to reveal (remove hidden) on API success. Pair with data-rt-alert-duration for auto-hide. Example: data-rt-api-success-show=\"success-alert\""
413
+ },
414
+ {
415
+ "name": "data-rt-api-error-show",
416
+ "description": "ID of an inline alert element to reveal (remove hidden) on API error. Pair with data-rt-alert-duration for auto-hide. Example: data-rt-api-error-show=\"error-alert\""
417
+ },
418
+ {
419
+ "name": "data-rt-alert-duration",
420
+ "description": "Duration in milliseconds before the inline alert auto-hides. Use with data-rt-api-success-show or data-rt-api-error-show. Example: data-rt-alert-duration=\"3000\""
393
421
  }
394
422
  ],
395
423
  "valueSets": [
@@ -41,7 +41,7 @@ var DolphinModule = (() => {
41
41
  target.patch = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PATCH", pathOrBody, bodyOrOptions, options) : this.request("PATCH", joined, pathOrBody, bodyOrOptions);
42
42
  target.del = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("DELETE", pathOrOptions, null, options) : this.request("DELETE", joined, null, pathOrOptions);
43
43
  target.request = (method, subPath, body, options) => {
44
- const finalPath = subPath ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : joined;
44
+ const finalPath = subPath ? joined ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : subPath : joined;
45
45
  return this.request(method, finalPath, body, options);
46
46
  };
47
47
  target.requestDirect = (method, path, body, options) => {
@@ -225,8 +225,9 @@ var DolphinModule = (() => {
225
225
  const _isRetry = options._isRetry === true;
226
226
  let finalMethod = method.toUpperCase();
227
227
  let finalBody = body;
228
+ const hasBody = !["GET", "HEAD"].includes(finalMethod);
228
229
  const headers = {
229
- "Content-Type": "application/json",
230
+ ...hasBody ? { "Content-Type": "application/json" } : {},
230
231
  ...options.headers || {}
231
232
  };
232
233
  if (["PUT", "PATCH", "DELETE"].includes(finalMethod)) {
@@ -1376,6 +1377,86 @@ var DolphinModule = (() => {
1376
1377
  console.error("%cFailed Expression:", "color: #3b82f6; font-style: italic;", expression);
1377
1378
  }
1378
1379
  };
1380
+ clientProto._showAlert = function(id, duration) {
1381
+ if (typeof document === "undefined") return;
1382
+ const el = document.getElementById(id) || document.querySelector(`[id="${id}"]`);
1383
+ if (!el) return;
1384
+ el.removeAttribute("hidden");
1385
+ el.style.display = "";
1386
+ if (duration && duration > 0) {
1387
+ setTimeout(() => {
1388
+ el.setAttribute("hidden", "");
1389
+ }, duration);
1390
+ }
1391
+ };
1392
+ clientProto._showToast = function(message, type = "success") {
1393
+ if (typeof document === "undefined") return;
1394
+ const colors = {
1395
+ success: { bg: "rgba(16,185,129,0.15)", border: "#10b981", icon: "\u2705" },
1396
+ error: { bg: "rgba(239,68,68,0.15)", border: "#ef4444", icon: "\u274C" },
1397
+ info: { bg: "rgba(59,130,246,0.15)", border: "#3b82f6", icon: "\u2139\uFE0F" }
1398
+ };
1399
+ const c = colors[type] || colors.success;
1400
+ const toast = document.createElement("div");
1401
+ toast.setAttribute("data-dolphin-toast", "");
1402
+ const existing = document.querySelectorAll("[data-dolphin-toast]");
1403
+ const offset = existing.length * 72;
1404
+ toast.style.cssText = [
1405
+ "position:fixed",
1406
+ `bottom:${24 + offset}px`,
1407
+ "right:24px",
1408
+ "z-index:2147483647",
1409
+ `background:${c.bg}`,
1410
+ `border:1px solid ${c.border}`,
1411
+ "color:#fff",
1412
+ "padding:14px 20px",
1413
+ "border-radius:14px",
1414
+ "font-size:14px",
1415
+ "font-weight:600",
1416
+ "font-family:system-ui,sans-serif",
1417
+ "box-shadow:0 8px 32px rgba(0,0,0,0.4)",
1418
+ "display:flex",
1419
+ "align-items:center",
1420
+ "gap:10px",
1421
+ "max-width:380px",
1422
+ "word-break:break-word",
1423
+ "transform:translateY(80px)",
1424
+ "opacity:0",
1425
+ "transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1),opacity 0.3s ease",
1426
+ "pointer-events:auto",
1427
+ "backdrop-filter:blur(12px)"
1428
+ ].join(";");
1429
+ toast.innerHTML = `<span style="font-size:18px">${c.icon}</span><span>${message}</span>`;
1430
+ document.body.appendChild(toast);
1431
+ const restack = () => {
1432
+ const remaining = document.querySelectorAll("[data-dolphin-toast]");
1433
+ remaining.forEach((el, i) => {
1434
+ el.style.bottom = `${24 + i * 72}px`;
1435
+ });
1436
+ };
1437
+ const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
1438
+ raf(() => raf(() => {
1439
+ toast.style.transform = "translateY(0)";
1440
+ toast.style.opacity = "1";
1441
+ }));
1442
+ const removeToast = () => {
1443
+ clearTimeout(toast._removeTimer);
1444
+ if (toast.parentNode) {
1445
+ toast.parentNode.removeChild(toast);
1446
+ restack();
1447
+ }
1448
+ };
1449
+ const hideTimer = setTimeout(() => {
1450
+ toast.style.transform = "translateY(80px)";
1451
+ toast.style.opacity = "0";
1452
+ toast._removeTimer = setTimeout(removeToast, 400);
1453
+ }, 3500);
1454
+ toast._hideTimer = hideTimer;
1455
+ toast.addEventListener("click", () => {
1456
+ clearTimeout(toast._hideTimer);
1457
+ removeToast();
1458
+ }, { once: true });
1459
+ };
1379
1460
  clientProto._initDOMBinding = function() {
1380
1461
  if (this._domInitialized) return;
1381
1462
  this._domInitialized = true;
@@ -1474,6 +1555,11 @@ var DolphinModule = (() => {
1474
1555
  resolvedTopic = resolvedTopic.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1475
1556
  }
1476
1557
  this.publish(resolvedTopic, data);
1558
+ const rtSuccessId = e.target.getAttribute("data-rt-api-success");
1559
+ if (rtSuccessId) {
1560
+ const rtSuccessEl = document.getElementById(rtSuccessId);
1561
+ if (rtSuccessEl) this._showToast(rtSuccessEl.textContent || rtSuccessEl.innerText || "", "success");
1562
+ }
1477
1563
  } else if (apiTarget) {
1478
1564
  let resolvedTarget = apiTarget;
1479
1565
  for (const k in parentCtx) {
@@ -1490,16 +1576,40 @@ var DolphinModule = (() => {
1490
1576
  const result = await this.api.request(method, path, data);
1491
1577
  const resultBind = e.target.getAttribute("data-api-result");
1492
1578
  if (resultBind) this._updateDOM(resultBind, result);
1579
+ const successToast = e.target.getAttribute("data-api-toast");
1580
+ if (successToast) this._showToast(successToast, "success");
1581
+ const successElId = e.target.getAttribute("data-rt-api-success");
1582
+ if (successElId) {
1583
+ const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
1584
+ this._showAlert(successElId, duration);
1585
+ const errElId = e.target.getAttribute("data-rt-api-error");
1586
+ if (errElId) {
1587
+ const errEl = document.getElementById(errElId);
1588
+ if (errEl) errEl.setAttribute("hidden", "");
1589
+ }
1590
+ }
1493
1591
  const redirect = e.target.getAttribute("data-api-redirect");
1494
1592
  if (redirect) window.location.href = redirect;
1495
1593
  if (e.target.hasAttribute("data-api-reload")) window.location.reload();
1496
1594
  } catch (err) {
1497
1595
  console.error("[Dolphin] API Submit Error:", err);
1596
+ const errorToast = e.target.getAttribute("data-api-error-toast");
1597
+ if (errorToast) this._showToast(errorToast, "error");
1598
+ const errorElId = e.target.getAttribute("data-rt-api-error");
1599
+ if (errorElId) {
1600
+ const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
1601
+ this._showAlert(errorElId, duration);
1602
+ const sucElId = e.target.getAttribute("data-rt-api-success");
1603
+ if (sucElId) {
1604
+ const sucEl = document.getElementById(sucElId);
1605
+ if (sucEl) sucEl.setAttribute("hidden", "");
1606
+ }
1607
+ }
1498
1608
  }
1499
1609
  }
1500
1610
  }
1501
1611
  });
1502
- const INTERACTION_EVENTS = ["click", "change", "submit", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1612
+ const INTERACTION_EVENTS = ["click", "change", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1503
1613
  INTERACTION_EVENTS.forEach((evtName) => {
1504
1614
  this.addDomListener(document, evtName, async (e) => {
1505
1615
  if (!e.target || !e.target.closest) return;
@@ -1530,7 +1640,12 @@ var DolphinModule = (() => {
1530
1640
  const apiTarget = apiBtn.getAttribute(`data-api-${evtName}`);
1531
1641
  const actionData = apiBtn.getAttribute("data-api-payload");
1532
1642
  const parentCtx = this.getClosestContext(apiBtn) || {};
1533
- const parts = apiTarget.trim().split(" ");
1643
+ let resolvedApiTarget = apiTarget;
1644
+ for (const k in parentCtx) {
1645
+ const escapedK = escapeRegExp(k);
1646
+ resolvedApiTarget = resolvedApiTarget.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1647
+ }
1648
+ const parts = resolvedApiTarget.trim().split(" ");
1534
1649
  const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
1535
1650
  const path = parts.length > 1 ? parts[1] : parts[0];
1536
1651
  let payload = null;
@@ -1550,11 +1665,35 @@ var DolphinModule = (() => {
1550
1665
  const result = await this.api.request(method, path, payload);
1551
1666
  const resultBind = apiBtn.getAttribute("data-api-result");
1552
1667
  if (resultBind) this._updateDOM(resultBind, result);
1668
+ const successToast = apiBtn.getAttribute("data-api-toast");
1669
+ if (successToast) this._showToast(successToast, "success");
1670
+ const successElId = apiBtn.getAttribute("data-rt-api-success");
1671
+ if (successElId) {
1672
+ const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
1673
+ this._showAlert(successElId, duration);
1674
+ const errElId = apiBtn.getAttribute("data-rt-api-error");
1675
+ if (errElId) {
1676
+ const errEl = document.getElementById(errElId);
1677
+ if (errEl) errEl.setAttribute("hidden", "");
1678
+ }
1679
+ }
1553
1680
  const redirect = apiBtn.getAttribute("data-api-redirect");
1554
1681
  if (redirect) window.location.href = redirect;
1555
1682
  if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
1556
1683
  } catch (err) {
1557
1684
  console.error(`[Dolphin] API ${evtName} Error:`, err);
1685
+ const errorToast = apiBtn.getAttribute("data-api-error-toast");
1686
+ if (errorToast) this._showToast(errorToast, "error");
1687
+ const errorElId = apiBtn.getAttribute("data-rt-api-error");
1688
+ if (errorElId) {
1689
+ const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
1690
+ this._showAlert(errorElId, duration);
1691
+ const sucElId = apiBtn.getAttribute("data-rt-api-success");
1692
+ if (sucElId) {
1693
+ const sucEl = document.getElementById(sucElId);
1694
+ if (sucEl) sucEl.setAttribute("hidden", "");
1695
+ }
1696
+ }
1558
1697
  }
1559
1698
  }
1560
1699
  const storeActionBtn = e.target.closest(`[data-store-${evtName}]`);
@@ -1,29 +1,29 @@
1
- var DolphinModule=(()=>{var R=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var tt=Object.prototype.hasOwnProperty;var et=(A,t)=>{for(var e in t)R(A,e,{get:t[e],enumerable:!0})},nt=(A,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Z(t))!tt.call(A,s)&&s!==e&&R(A,s,{get:()=>t[s],enumerable:!(n=Y(t,s))||n.enumerable});return A};var st=A=>nt(R({},"__esModule",{value:!0}),A);var rt={};et(rt,{DolphinClient:()=>C});var x=class{client;constructor(t){return this.client=t,this._createProxy([])}_createProxy(t){let e=t.join("/"),n=i=>this.request("GET",e,null,i);n.get=(i,a)=>typeof i=="string"?this.request("GET",i,null,a):this.request("GET",e,null,i),n.post=(i,a,l)=>typeof i=="string"?this.request("POST",i,a,l):this.request("POST",e,i,a),n.put=(i,a,l)=>typeof i=="string"?this.request("PUT",i,a,l):this.request("PUT",e,i,a),n.patch=(i,a,l)=>typeof i=="string"?this.request("PATCH",i,a,l):this.request("PATCH",e,i,a),n.del=(i,a)=>typeof i=="string"?this.request("DELETE",i,null,a):this.request("DELETE",e,null,i),n.request=(i,a,l,_)=>{let v=a?`${e}/${a.startsWith("/")?a.slice(1):a}`:e;return this.request(i,v,l,_)},n.requestDirect=(i,a,l,_)=>this.requestDirect(i,a,l,_),n._findCSRFToken=()=>this._findCSRFToken(),n._resolveBaseUrl=i=>this._resolveBaseUrl(i),n._normalizeValidationErrors=i=>this._normalizeValidationErrors(i);let s=["get","post","put","patch","del","request","requestDirect","_findCSRFToken","_resolveBaseUrl","_normalizeValidationErrors"];return new Proxy(n,{get:(i,a)=>typeof a=="string"&&!s.includes(a)?this._createProxy([...t,a]):i[a]})}_findCSRFToken(){if(typeof document>"u")return null;let t=["csrf-token","_csrf","xsrf-token","csrf_token"];for(let i of t){let a=document.querySelector(`meta[name="${i}"], meta[content][name$="${i}"]`);if(a){let l=a.getAttribute("content");if(l)return l}}let e=["_csrfToken","_token","_csrf","csrf_token"];for(let i of e){let a=document.querySelector(`input[type="hidden"][name="${i}"]`);if(a&&a.value)return a.value}let n=["csrfToken","XSRF-TOKEN","_csrf","csrf_token"];for(let i of n){let a=document.cookie.match(new RegExp("(^|;\\s*)"+i+"=([^;]*)"));if(a)return decodeURIComponent(a[2])}let s=typeof window<"u"&&window.wpApiSettings?.nonce;return s||null}_resolveBaseUrl(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;let e=this.client.httpUrl;if(typeof document<"u"){let s=document.querySelector("base[href]");if(s){let i=s.getAttribute("href")||"";if(i&&i!=="/"){let a=i.endsWith("/")?i.slice(0,-1):i;e=`${this.client.httpUrl}${a.startsWith("/")?a:"/"+a}`}}else{let i=document.querySelector('meta[name="base-path"]');if(i){let a=i.getAttribute("content")||"";if(a&&a!=="/"){let l=a.endsWith("/")?a.slice(0,-1):a;e=`${this.client.httpUrl}${l.startsWith("/")?l:"/"+l}`}}}}let n=t.startsWith("/")?t:"/"+t;return`${e}${n}`}_normalizeValidationErrors(t){let e={};if(!t||typeof t!="object")return e;let n=t.errors||t.validationErrors||t;if(Array.isArray(n)){for(let s of n)if(s&&typeof s=="object"){let i=s.path||s.param||s.field||s.property,a=s.msg||s.message||s.error;i&&a&&(e[i]=Array.isArray(a)?a[0]:a)}return e}if(typeof n=="object"&&n!==null)for(let s in n){let i=n[s];if(i)if(Array.isArray(i))i.length>0&&(e[s]=String(i[0]));else if(typeof i=="object"){let a=Object.keys(i);if(a.length>0){let l=a[0];e[s]=String(i[l])}}else e[s]=String(i)}return e}async request(t,e,n=null,s={}){if(this.client.offline){let i=this.client.offline.isOnline,a=`${t.toUpperCase()}:${e}`;if(t.toUpperCase()==="GET")if(i)try{let l=await this.requestDirect(t,e,n,s);return await this.client.offline.setCache(a,l),l}catch(l){let _=await this.client.offline.getCache(a);if(_!=null)return _;throw l}else{let l=await this.client.offline.getCache(a);if(l!=null)return l;throw{status:503,data:{error:"Offline, no cache available"}}}else return i?this.requestDirect(t,e,n,s):(await this.client.offline.queueMutation(t,e,n),this.client._dispatch("offline:queued",{method:t,path:e,body:n}),{success:!0,offline:!0,message:"Mutation queued offline"})}return this.requestDirect(t,e,n,s)}async requestDirect(t,e,n=null,s={}){let i=s._isRetry===!0,a=t.toUpperCase(),l=n,_={"Content-Type":"application/json",...s.headers||{}};["PUT","PATCH","DELETE"].includes(a)&&(this.client.options.methodSpoofing||s.methodSpoofing)&&(_["X-HTTP-Method-Override"]=a,l instanceof FormData?l.append("_method",a):l&&typeof l=="object"?l={...l,_method:a}:l||(l={_method:a}),a="POST");let v=this._resolveBaseUrl(e);this.client.options.debug&&console.log("%c\u{1F680} [Dolphin API Request]:","color: #3b82f6; font-weight: bold;",t.toUpperCase(),e,l||"");let k=new AbortController,h=setTimeout(()=>k.abort(),this.client.options.timeout);if(this.client.accessToken&&(_.Authorization=`Bearer ${this.client.accessToken}`),["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let c=this._findCSRFToken();c&&(_["X-CSRF-Token"]=c,_["X-XSRF-TOKEN"]=c,_["X-CSRFToken"]=c,_["X-WP-Nonce"]=c,l&&typeof l=="object"&&!l._csrfToken&&!l._token&&!l._csrf&&(l={...l,_csrfToken:c,_token:c,_csrf:c}))}let d={...s};delete d._isRetry,delete d.methodSpoofing;try{let c=await fetch(v,{method:a,headers:_,signal:k.signal,...l?{body:JSON.stringify(l)}:{},...d});if(clearTimeout(h),c.status===401&&!i&&this.client.options.autoRefreshToken&&await this.client.auth._silentRefresh())return this.request(t,e,n,{...s,_isRetry:!0});let o=(c.headers.get("content-type")||"").includes("application/json")?await c.json():await c.text();if(!c.ok)throw{status:c.status,data:o};if(this.client.options.debug&&console.log("%c\u2705 [Dolphin API Success]:","color: #10b981; font-weight: bold;",t.toUpperCase(),e,o),o&&typeof o=="object"&&o.accessToken&&(this.client.setToken(o.accessToken),o.user&&(this.client.auth.user=o.user)),this.client.options.autoBroadcast&&["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let u=e.startsWith("/")?e.substring(1):e;this.client.publish(u,{method:t.toUpperCase(),payload:n,result:o})}return o}catch(c){if(clearTimeout(h),this.client.options.debug&&console.error("%c\u274C [Dolphin API Error]:","color: #ef4444; font-weight: bold;",t.toUpperCase(),e,c),c&&typeof c=="object"&&c.data){let r=this._normalizeValidationErrors(c.data);if(Object.keys(r).length>0)for(let o in r)this.client.publish(`errors/${o}`,r[o])}throw c.name==="AbortError"?{status:408,data:{error:"Request timed out"}}:c}}};var $=class{client;user;_refreshing;constructor(t){this.client=t,this.user=null,this._refreshing=!1}async login(t,e){let n=await this.client.api.post("/api/auth/login",{email:t,password:e});return n.accessToken&&(this.client.setToken(n.accessToken),this.user=n.user||null),n}async register(t){return this.client.api.post("/api/auth/register",t)}async me(){let t=await this.client.api.get("/api/auth/me");return t.success&&(this.user=t.data),t}async logout(){try{await this.client.api.post("/api/auth/logout")}catch{}this.client.setToken(null),this.user=null}async refresh(){return this._silentRefresh()}async _silentRefresh(){if(this._refreshing)return!1;this._refreshing=!0;try{let t=await this.client.api.post("/api/auth/refresh",null,{_isRetry:!0});return t.accessToken?(this.client.setToken(t.accessToken),!0):!1}catch{return this.client.setToken(null),!1}finally{this._refreshing=!1}}async verify2FA(t,e){let n={code:t,email:e||this.user?.email},s=await this.client.api.post("/api/auth/2fa/verify",n);return s.accessToken&&(this.client.setToken(s.accessToken),s.user&&(this.user=s.user)),s}async enable2FA(){return this.client.api.post("/api/auth/2fa/enable")}async disable2FA(t){return this.client.api.post("/api/auth/2fa/disable",{code:t})}async forgotPassword(t){return this.client.api.post("/api/auth/forgot-password",{email:t})}async resetPassword(t,e){return this.client.api.post("/api/auth/reset-password",{token:t,newPassword:e})}};var P=class{client;data;listeners;subscribed;_unsubscribers;constructor(t){return this.client=t,this.data=new Map,this.listeners=new Set,this.subscribed=new Set,this._unsubscribers=new Map,new Proxy(this,{get:(e,n)=>{if(n in e)return e[n];if(typeof n=="string")return this._getCollection(n)}})}_getCollection(t){if(!this.data.has(t)){let e={_rawItems:[],items:[],loading:!0,error:null,success:!1,_filter:null,_sort:null,where:n=>(e._filter=n,this._applyTransform(e),e),orderBy:(n,s="asc")=>(e._sort={key:n,direction:s},this._applyTransform(e),e),reset:()=>(e._filter=null,e._sort=null,this._applyTransform(e),e)};this.data.set(t,e),this._fetchAndSync(t)}return this.data.get(t)}async _fetchAndSync(t){let e=this.data.get(t);try{let n=await this.client.api.get(`/${t.toLowerCase()}`);if(e._rawItems=Array.isArray(n)?n:n.data||[],e.loading=!1,e.success=!0,e.error=null,this._applyTransform(e),!this.subscribed.has(t)){let s=()=>this.client.unsubscribe(`db:sync/${t.toLowerCase()}`,i),i=a=>{this._handleRemoteUpdate(t,a)};this.client.subscribe(`db:sync/${t.toLowerCase()}`,i),this._unsubscribers.set(t,s),this.subscribed.add(t)}}catch(n){e.loading=!1,e.success=!1,e.error=n.data?.error||n.message||"Fetch failed",this._notify()}}_applyTransform(t){let e=[...t._rawItems];if(t._filter&&(e=e.filter(t._filter)),t._sort){let{key:n,direction:s}=t._sort;e.sort((i,a)=>i[n]===a[n]?0:(i[n]>a[n]?1:-1)*(s==="asc"?1:-1))}t.items=e,this._notify()}_handleRemoteUpdate(t,e){let n=this.data.get(t);if(!n)return;let{type:s,data:i}=e,a=n._rawItems;s==="create"?a=[...a,i]:s==="update"?a=a.map(l=>l.id===i.id||l._id===i._id?{...l,...i}:l):s==="delete"&&(a=a.filter(l=>!(i.id!=null&&l.id===i.id||i._id!=null&&l._id===i._id))),n._rawItems=a,this._applyTransform(n)}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}getSnapshot(t){return this.data.get(t)||{items:[],loading:!1,error:null,success:!1}}_notify(){this.listeners.forEach(t=>t())}destroy(){this._unsubscribers.forEach(t=>{try{t()}catch{}}),this._unsubscribers.clear(),this.subscribed.clear(),this.listeners.clear(),this.data.clear()}};var C=class{host;httpUrl;deviceId;options;socket;storage;accessToken;api;auth;store;handlers;signalHandlers;fileHandlers;_offlineQueue;reconnectAttempts;_reconnectTimer;_attachedListeners;constructor(t="",e="",n={}){!t&&typeof window<"u"&&(t=window.location.host);let s="http:";t.startsWith("https://")?s="https:":t.startsWith("http://")?s="http:":typeof window<"u"&&(s=window.location.protocol),this.host=(t||"localhost").replace(/\/$/,"").replace(/^https?:\/\//,""),this.httpUrl=`${s}//${this.host}`,this.deviceId=e||(typeof crypto<"u"&&typeof crypto.randomUUID=="function"?"web_"+crypto.randomUUID().replace(/-/g,"").slice(0,8):"web_"+Math.random().toString(36).slice(2,10)),this.options={timeout:15e3,chunkSize:65536,maxReconnect:5,autoRefreshToken:!0,debug:!1,methodSpoofing:!1,routerViewport:"main, #viewport, body",routerTransitions:!0,...n},this.socket=null,this.storage=typeof localStorage<"u"?localStorage:{getItem:()=>null,setItem:()=>{},removeItem:()=>{}},this.accessToken=this.storage.getItem("dolphin_token"),this.api=new x(this),this.auth=new $(this),this.store=new P(this),this.handlers=new Map,this.signalHandlers=new Set,this.fileHandlers=new Set,this._offlineQueue=[],this.reconnectAttempts=0,this._reconnectTimer=null,this._attachedListeners=[],typeof window<"u"&&typeof this._initDOMBinding=="function"&&this._initDOMBinding(),typeof this._initOffline=="function"&&this._initOffline(),typeof this._initA11y=="function"&&this._initA11y(),typeof this._initI18n=="function"&&this._initI18n(),typeof this._initDragDrop=="function"&&this._initDragDrop(),typeof this._initCollab=="function"&&this._initCollab()}setToken(t){this.accessToken=t,t?this.storage.setItem("dolphin_token",t):this.storage.removeItem("dolphin_token")}async connect(){return this.socket&&(this.socket.readyState===WebSocket.OPEN||this.socket.readyState===WebSocket.CONNECTING)?Promise.resolve():new Promise((t,e)=>{let s=`${this.httpUrl.startsWith("https")?"wss:":"ws:"}//${this.host}/realtime?deviceId=${this.deviceId}`;console.log(`[Dolphin] Connecting to ${s}...`),this.socket=new WebSocket(s),this.socket.onopen=()=>{console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`),this.reconnectAttempts=0,this._flushOfflineQueue(),t()},this.socket.onmessage=i=>this._handleMessage(i.data),this.socket.onclose=()=>{console.warn("[Dolphin] Connection closed"),this._maybeReconnect()},this.socket.onerror=i=>{console.error("[Dolphin] WebSocket error:",i),e(i)}})}disconnect(){this._reconnectTimer!==null&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this.socket&&(this.socket.onclose=null,this.socket.close(),this.socket=null),typeof this._collabCleanup=="function"&&this._collabCleanup(),this.cleanupDomListeners()}_handleMessage(t){try{let e=JSON.parse(t);this.options.debug&&console.log("%c\u{1F4E5} [Dolphin WS Incoming]:","color: #eab308; font-weight: bold;",e),e.type&&e.from&&(e.to===this.deviceId||e.to==="all")&&(e.msgId&&e.type!=="ACK"&&this._sendAck(e.from,e.msgId),this.signalHandlers.forEach(n=>n(e))),e.type==="FILE_AVAILABLE"&&this.fileHandlers.forEach(n=>n(e)),e.type==="FILE_CHUNK"&&(this.saveFileProgress(e.fileId,e.chunkIndex),this._dispatch("file:chunk",e),this._dispatch(`file:chunk/${e.fileId}`,e)),e.type==="FILE_UPLOAD_ACK"&&this._dispatch(`file:upload:ack/${e.fileId}`,e),e.type==="PULL_RESPONSE"&&(this._dispatch("pull:response",e.payload,e.topic),this._dispatch(`pull:response/${e.topic}`,e.payload,e.topic)),e.topic&&e.payload!==void 0&&this.handlers.forEach((n,s)=>{this._matchTopic(s,e.topic)&&n.forEach(i=>i(e.payload,e.topic))})}catch{this._dispatch("raw",t)}}_dispatch(t,e,n){let s=this.handlers.get(t);s&&s.forEach(i=>i(e,n||t))}_sendRaw(t){this.options.debug&&console.log("%c\u{1F4E4} [Dolphin WS Outgoing]:","color: #8b5cf6; font-weight: bold;",t);let e=typeof t=="string"?t:JSON.stringify(t);this.socket&&this.socket.readyState===WebSocket.OPEN?this.socket.send(e):this._offlineQueue.length<100&&this._offlineQueue.push(e)}_flushOfflineQueue(){for(;this._offlineQueue.length>0;){let t=this._offlineQueue.shift();this.socket&&this.socket.readyState===WebSocket.OPEN&&this.socket.send(t)}}_sendAck(t,e){this._sendRaw({type:"ACK",from:this.deviceId,to:t,data:{ackId:e},timestamp:Date.now()})}_matchTopic(t,e){if(t===e||t==="#")return!0;let n=t.split("/"),s=e.split("/");if(n.length!==s.length&&!t.includes("#"))return!1;for(let i=0;i<n.length;i++){if(n[i]==="#")return!0;if(n[i]!=="+"&&n[i]!==s[i])return!1}return n.length===s.length}_maybeReconnect(){if(this.reconnectAttempts<this.options.maxReconnect){this.reconnectAttempts++;let t=Math.pow(2,this.reconnectAttempts)*1e3;console.log(`[Dolphin] Reconnecting in ${t/1e3}s (attempt ${this.reconnectAttempts})...`),this._reconnectTimer=setTimeout(()=>{this._reconnectTimer=null,this.connect().catch(()=>{})},t)}else console.error("[Dolphin] Max reconnect attempts reached.")}subscribe(t,e){this.handlers.has(t)||(this.handlers.set(t,new Set),this._sendRaw({type:"sub",topic:t})),this.handlers.get(t).add(e)}unsubscribe(t,e){if(this.handlers.has(t)){let n=this.handlers.get(t);n.delete(e),n.size===0&&(this.handlers.delete(t),this._sendRaw({type:"unsub",topic:t}))}}publish(t,e){this._sendRaw({topic:t,payload:e})}pubPush(t,e){this._sendRaw({type:"pub",topic:t,payload:e})}subPull(t,e=10){this._sendRaw({type:"PULL_REQUEST",topic:t,count:e})}async pubFile(t,e,n="",s){let i;e instanceof Blob?i=await e.arrayBuffer():e instanceof ArrayBuffer?i=e:i=e.buffer||e;let a=new Uint8Array(i),l=this.options.chunkSize,_=Math.ceil(a.length/l);this._sendRaw({type:"FILE_UPLOAD_START",fileId:t,name:n,size:a.length,totalChunks:_,chunkSize:l});for(let v=0;v<_;v++){let k=a.slice(v*l,(v+1)*l),h=this._uint8ToBase64(k);this._sendRaw({type:"FILE_UPLOAD_CHUNK",fileId:t,chunkIndex:v,totalChunks:_,data:h}),s&&s(Math.round((v+1)/_*100)),v%10===0&&await new Promise(d=>setTimeout(d,0))}this._sendRaw({type:"FILE_UPLOAD_DONE",fileId:t})}_uint8ToBase64(t){let e="";for(let n=0;n<t.length;n++)e+=String.fromCharCode(t[n]);return typeof btoa<"u"?btoa(e):Buffer.from(e,"binary").toString("base64")}subFile(t,e=0){this._sendRaw({type:"FILE_REQUEST",fileId:t,startChunk:e})}resumeFile(t){let e=parseInt(this.storage.getItem(`dolphin_file_${t}`)||"-1");this.subFile(t,e+1)}saveFileProgress(t,e){this.storage.setItem(`dolphin_file_${t}`,e.toString())}onSignal(t){this.signalHandlers.add(t)}offSignal(t){this.signalHandlers.delete(t)}onFileAvailable(t){this.fileHandlers.add(t)}offFileAvailable(t){this.fileHandlers.delete(t)}addDomListener(t,e,n){t&&(t.addEventListener(e,n),this._attachedListeners=this._attachedListeners||[],this._attachedListeners.push({target:t,event:e,cb:n}))}cleanupDomListeners(){this._attachedListeners&&(this._attachedListeners.forEach(({target:t,event:e,cb:n})=>{try{t.removeEventListener(e,n)}catch{}}),this._attachedListeners=[])}};function U(A){let t=new Map;function e(h){return h.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function n(h){let d=h.getAttribute("data-rt-template");if(!d)return null;if(typeof document<"u"&&!d.includes("<"))try{let c=document.querySelector(d);if(c)return c.innerHTML}catch{}return d}function s(h,d){if(!h.includes("{#if")&&!h.includes("{#each")){let c=h;for(let r in d){let o=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");c=c.replace(new RegExp(`\\{\\{${o}\\}\\}`,"g"),d[r]!==void 0&&d[r]!==null?d[r]:"")}return c}try{let c=p=>p.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"),r=`let out = "";
2
- `,o=0,u=/(\{\{([\s\S]*?)\}\}|\{#if\s+([\s\S]*?)\}|\{:else\s+if\s+([\s\S]*?)\}|\{:else\}|\{\/if\}|\{#each\s+([\s\S]*?)\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\}|\{\/each\}|\{([^{}]+?)\})/g,b=[],f;for(;(f=u.exec(h))!==null;){let p=h.slice(o,f.index);p&&(r+=`out += "${c(p)}";
3
- `);let y=f[0];if(y.startsWith("{{")){let T=f[2];r+=`out += (${T} !== undefined && ${T} !== null ? ${T} : "");
4
- `}else if(y.startsWith("{#if")){let T=f[3];r+=`if (${T}) {
5
- `}else if(y.startsWith("{:else if")){let T=f[4];r+=`} else if (${T}) {
6
- `}else if(y.startsWith("{:else}"))r+=`} else {
7
- `;else if(y.startsWith("{/if}"))r+=`}
8
- `;else if(y.startsWith("{#each")){let T=f[5],D=f[6],E=f[7];b.push({indexVar:E}),r+=`if (typeof ${T} !== "undefined" && ${T} !== null && Array.isArray(${T})) {
9
- `,E&&(r+=` let ${E} = 0;
10
- `),r+=` for (let ${D} of ${T}) {
11
- `}else if(y.startsWith("{/each}")){let T=b.pop();T&&T.indexVar&&(r+=` ${T.indexVar}++;
12
- `),r+=` }
1
+ var DolphinModule=(()=>{var U=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var tt=Object.prototype.hasOwnProperty;var et=(A,t)=>{for(var e in t)U(A,e,{get:t[e],enumerable:!0})},nt=(A,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Z(t))!tt.call(A,i)&&i!==e&&U(A,i,{get:()=>t[i],enumerable:!(s=G(t,i))||s.enumerable});return A};var st=A=>nt(U({},"__esModule",{value:!0}),A);var rt={};et(rt,{DolphinClient:()=>C});var N=class{client;constructor(t){return this.client=t,this._createProxy([])}_createProxy(t){let e=t.join("/"),s=r=>this.request("GET",e,null,r);s.get=(r,a)=>typeof r=="string"?this.request("GET",r,null,a):this.request("GET",e,null,r),s.post=(r,a,d)=>typeof r=="string"?this.request("POST",r,a,d):this.request("POST",e,r,a),s.put=(r,a,d)=>typeof r=="string"?this.request("PUT",r,a,d):this.request("PUT",e,r,a),s.patch=(r,a,d)=>typeof r=="string"?this.request("PATCH",r,a,d):this.request("PATCH",e,r,a),s.del=(r,a)=>typeof r=="string"?this.request("DELETE",r,null,a):this.request("DELETE",e,null,r),s.request=(r,a,d,E)=>{let S=a?e?`${e}/${a.startsWith("/")?a.slice(1):a}`:a:e;return this.request(r,S,d,E)},s.requestDirect=(r,a,d,E)=>this.requestDirect(r,a,d,E),s._findCSRFToken=()=>this._findCSRFToken(),s._resolveBaseUrl=r=>this._resolveBaseUrl(r),s._normalizeValidationErrors=r=>this._normalizeValidationErrors(r);let i=["get","post","put","patch","del","request","requestDirect","_findCSRFToken","_resolveBaseUrl","_normalizeValidationErrors"];return new Proxy(s,{get:(r,a)=>typeof a=="string"&&!i.includes(a)?this._createProxy([...t,a]):r[a]})}_findCSRFToken(){if(typeof document>"u")return null;let t=["csrf-token","_csrf","xsrf-token","csrf_token"];for(let r of t){let a=document.querySelector(`meta[name="${r}"], meta[content][name$="${r}"]`);if(a){let d=a.getAttribute("content");if(d)return d}}let e=["_csrfToken","_token","_csrf","csrf_token"];for(let r of e){let a=document.querySelector(`input[type="hidden"][name="${r}"]`);if(a&&a.value)return a.value}let s=["csrfToken","XSRF-TOKEN","_csrf","csrf_token"];for(let r of s){let a=document.cookie.match(new RegExp("(^|;\\s*)"+r+"=([^;]*)"));if(a)return decodeURIComponent(a[2])}let i=typeof window<"u"&&window.wpApiSettings?.nonce;return i||null}_resolveBaseUrl(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;let e=this.client.httpUrl;if(typeof document<"u"){let i=document.querySelector("base[href]");if(i){let r=i.getAttribute("href")||"";if(r&&r!=="/"){let a=r.endsWith("/")?r.slice(0,-1):r;e=`${this.client.httpUrl}${a.startsWith("/")?a:"/"+a}`}}else{let r=document.querySelector('meta[name="base-path"]');if(r){let a=r.getAttribute("content")||"";if(a&&a!=="/"){let d=a.endsWith("/")?a.slice(0,-1):a;e=`${this.client.httpUrl}${d.startsWith("/")?d:"/"+d}`}}}}let s=t.startsWith("/")?t:"/"+t;return`${e}${s}`}_normalizeValidationErrors(t){let e={};if(!t||typeof t!="object")return e;let s=t.errors||t.validationErrors||t;if(Array.isArray(s)){for(let i of s)if(i&&typeof i=="object"){let r=i.path||i.param||i.field||i.property,a=i.msg||i.message||i.error;r&&a&&(e[r]=Array.isArray(a)?a[0]:a)}return e}if(typeof s=="object"&&s!==null)for(let i in s){let r=s[i];if(r)if(Array.isArray(r))r.length>0&&(e[i]=String(r[0]));else if(typeof r=="object"){let a=Object.keys(r);if(a.length>0){let d=a[0];e[i]=String(r[d])}}else e[i]=String(r)}return e}async request(t,e,s=null,i={}){if(this.client.offline){let r=this.client.offline.isOnline,a=`${t.toUpperCase()}:${e}`;if(t.toUpperCase()==="GET")if(r)try{let d=await this.requestDirect(t,e,s,i);return await this.client.offline.setCache(a,d),d}catch(d){let E=await this.client.offline.getCache(a);if(E!=null)return E;throw d}else{let d=await this.client.offline.getCache(a);if(d!=null)return d;throw{status:503,data:{error:"Offline, no cache available"}}}else return r?this.requestDirect(t,e,s,i):(await this.client.offline.queueMutation(t,e,s),this.client._dispatch("offline:queued",{method:t,path:e,body:s}),{success:!0,offline:!0,message:"Mutation queued offline"})}return this.requestDirect(t,e,s,i)}async requestDirect(t,e,s=null,i={}){let r=i._isRetry===!0,a=t.toUpperCase(),d=s,S={...!["GET","HEAD"].includes(a)?{"Content-Type":"application/json"}:{},...i.headers||{}};["PUT","PATCH","DELETE"].includes(a)&&(this.client.options.methodSpoofing||i.methodSpoofing)&&(S["X-HTTP-Method-Override"]=a,d instanceof FormData?d.append("_method",a):d&&typeof d=="object"?d={...d,_method:a}:d||(d={_method:a}),a="POST");let k=this._resolveBaseUrl(e);this.client.options.debug&&console.log("%c\u{1F680} [Dolphin API Request]:","color: #3b82f6; font-weight: bold;",t.toUpperCase(),e,d||"");let f=new AbortController,u=setTimeout(()=>f.abort(),this.client.options.timeout);if(this.client.accessToken&&(S.Authorization=`Bearer ${this.client.accessToken}`),["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let n=this._findCSRFToken();n&&(S["X-CSRF-Token"]=n,S["X-XSRF-TOKEN"]=n,S["X-CSRFToken"]=n,S["X-WP-Nonce"]=n,d&&typeof d=="object"&&!d._csrfToken&&!d._token&&!d._csrf&&(d={...d,_csrfToken:n,_token:n,_csrf:n}))}let l={...i};delete l._isRetry,delete l.methodSpoofing;try{let n=await fetch(k,{method:a,headers:S,signal:f.signal,...d?{body:JSON.stringify(d)}:{},...l});if(clearTimeout(u),n.status===401&&!r&&this.client.options.autoRefreshToken&&await this.client.auth._silentRefresh())return this.request(t,e,s,{...i,_isRetry:!0});let c=(n.headers.get("content-type")||"").includes("application/json")?await n.json():await n.text();if(!n.ok)throw{status:n.status,data:c};if(this.client.options.debug&&console.log("%c\u2705 [Dolphin API Success]:","color: #10b981; font-weight: bold;",t.toUpperCase(),e,c),c&&typeof c=="object"&&c.accessToken&&(this.client.setToken(c.accessToken),c.user&&(this.client.auth.user=c.user)),this.client.options.autoBroadcast&&["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let y=e.startsWith("/")?e.substring(1):e;this.client.publish(y,{method:t.toUpperCase(),payload:s,result:c})}return c}catch(n){if(clearTimeout(u),this.client.options.debug&&console.error("%c\u274C [Dolphin API Error]:","color: #ef4444; font-weight: bold;",t.toUpperCase(),e,n),n&&typeof n=="object"&&n.data){let o=this._normalizeValidationErrors(n.data);if(Object.keys(o).length>0)for(let c in o)this.client.publish(`errors/${c}`,o[c])}throw n.name==="AbortError"?{status:408,data:{error:"Request timed out"}}:n}}};var q=class{client;user;_refreshing;constructor(t){this.client=t,this.user=null,this._refreshing=!1}async login(t,e){let s=await this.client.api.post("/api/auth/login",{email:t,password:e});return s.accessToken&&(this.client.setToken(s.accessToken),this.user=s.user||null),s}async register(t){return this.client.api.post("/api/auth/register",t)}async me(){let t=await this.client.api.get("/api/auth/me");return t.success&&(this.user=t.data),t}async logout(){try{await this.client.api.post("/api/auth/logout")}catch{}this.client.setToken(null),this.user=null}async refresh(){return this._silentRefresh()}async _silentRefresh(){if(this._refreshing)return!1;this._refreshing=!0;try{let t=await this.client.api.post("/api/auth/refresh",null,{_isRetry:!0});return t.accessToken?(this.client.setToken(t.accessToken),!0):!1}catch{return this.client.setToken(null),!1}finally{this._refreshing=!1}}async verify2FA(t,e){let s={code:t,email:e||this.user?.email},i=await this.client.api.post("/api/auth/2fa/verify",s);return i.accessToken&&(this.client.setToken(i.accessToken),i.user&&(this.user=i.user)),i}async enable2FA(){return this.client.api.post("/api/auth/2fa/enable")}async disable2FA(t){return this.client.api.post("/api/auth/2fa/disable",{code:t})}async forgotPassword(t){return this.client.api.post("/api/auth/forgot-password",{email:t})}async resetPassword(t,e){return this.client.api.post("/api/auth/reset-password",{token:t,newPassword:e})}};var O=class{client;data;listeners;subscribed;_unsubscribers;constructor(t){return this.client=t,this.data=new Map,this.listeners=new Set,this.subscribed=new Set,this._unsubscribers=new Map,new Proxy(this,{get:(e,s)=>{if(s in e)return e[s];if(typeof s=="string")return this._getCollection(s)}})}_getCollection(t){if(!this.data.has(t)){let e={_rawItems:[],items:[],loading:!0,error:null,success:!1,_filter:null,_sort:null,where:s=>(e._filter=s,this._applyTransform(e),e),orderBy:(s,i="asc")=>(e._sort={key:s,direction:i},this._applyTransform(e),e),reset:()=>(e._filter=null,e._sort=null,this._applyTransform(e),e)};this.data.set(t,e),this._fetchAndSync(t)}return this.data.get(t)}async _fetchAndSync(t){let e=this.data.get(t);try{let s=await this.client.api.get(`/${t.toLowerCase()}`);if(e._rawItems=Array.isArray(s)?s:s.data||[],e.loading=!1,e.success=!0,e.error=null,this._applyTransform(e),!this.subscribed.has(t)){let i=()=>this.client.unsubscribe(`db:sync/${t.toLowerCase()}`,r),r=a=>{this._handleRemoteUpdate(t,a)};this.client.subscribe(`db:sync/${t.toLowerCase()}`,r),this._unsubscribers.set(t,i),this.subscribed.add(t)}}catch(s){e.loading=!1,e.success=!1,e.error=s.data?.error||s.message||"Fetch failed",this._notify()}}_applyTransform(t){let e=[...t._rawItems];if(t._filter&&(e=e.filter(t._filter)),t._sort){let{key:s,direction:i}=t._sort;e.sort((r,a)=>r[s]===a[s]?0:(r[s]>a[s]?1:-1)*(i==="asc"?1:-1))}t.items=e,this._notify()}_handleRemoteUpdate(t,e){let s=this.data.get(t);if(!s)return;let{type:i,data:r}=e,a=s._rawItems;i==="create"?a=[...a,r]:i==="update"?a=a.map(d=>d.id===r.id||d._id===r._id?{...d,...r}:d):i==="delete"&&(a=a.filter(d=>!(r.id!=null&&d.id===r.id||r._id!=null&&d._id===r._id))),s._rawItems=a,this._applyTransform(s)}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}getSnapshot(t){return this.data.get(t)||{items:[],loading:!1,error:null,success:!1}}_notify(){this.listeners.forEach(t=>t())}destroy(){this._unsubscribers.forEach(t=>{try{t()}catch{}}),this._unsubscribers.clear(),this.subscribed.clear(),this.listeners.clear(),this.data.clear()}};var C=class{host;httpUrl;deviceId;options;socket;storage;accessToken;api;auth;store;handlers;signalHandlers;fileHandlers;_offlineQueue;reconnectAttempts;_reconnectTimer;_attachedListeners;constructor(t="",e="",s={}){!t&&typeof window<"u"&&(t=window.location.host);let i="http:";t.startsWith("https://")?i="https:":t.startsWith("http://")?i="http:":typeof window<"u"&&(i=window.location.protocol),this.host=(t||"localhost").replace(/\/$/,"").replace(/^https?:\/\//,""),this.httpUrl=`${i}//${this.host}`,this.deviceId=e||(typeof crypto<"u"&&typeof crypto.randomUUID=="function"?"web_"+crypto.randomUUID().replace(/-/g,"").slice(0,8):"web_"+Math.random().toString(36).slice(2,10)),this.options={timeout:15e3,chunkSize:65536,maxReconnect:5,autoRefreshToken:!0,debug:!1,methodSpoofing:!1,routerViewport:"main, #viewport, body",routerTransitions:!0,...s},this.socket=null,this.storage=typeof localStorage<"u"?localStorage:{getItem:()=>null,setItem:()=>{},removeItem:()=>{}},this.accessToken=this.storage.getItem("dolphin_token"),this.api=new N(this),this.auth=new q(this),this.store=new O(this),this.handlers=new Map,this.signalHandlers=new Set,this.fileHandlers=new Set,this._offlineQueue=[],this.reconnectAttempts=0,this._reconnectTimer=null,this._attachedListeners=[],typeof window<"u"&&typeof this._initDOMBinding=="function"&&this._initDOMBinding(),typeof this._initOffline=="function"&&this._initOffline(),typeof this._initA11y=="function"&&this._initA11y(),typeof this._initI18n=="function"&&this._initI18n(),typeof this._initDragDrop=="function"&&this._initDragDrop(),typeof this._initCollab=="function"&&this._initCollab()}setToken(t){this.accessToken=t,t?this.storage.setItem("dolphin_token",t):this.storage.removeItem("dolphin_token")}async connect(){return this.socket&&(this.socket.readyState===WebSocket.OPEN||this.socket.readyState===WebSocket.CONNECTING)?Promise.resolve():new Promise((t,e)=>{let i=`${this.httpUrl.startsWith("https")?"wss:":"ws:"}//${this.host}/realtime?deviceId=${this.deviceId}`;console.log(`[Dolphin] Connecting to ${i}...`),this.socket=new WebSocket(i),this.socket.onopen=()=>{console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`),this.reconnectAttempts=0,this._flushOfflineQueue(),t()},this.socket.onmessage=r=>this._handleMessage(r.data),this.socket.onclose=()=>{console.warn("[Dolphin] Connection closed"),this._maybeReconnect()},this.socket.onerror=r=>{console.error("[Dolphin] WebSocket error:",r),e(r)}})}disconnect(){this._reconnectTimer!==null&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this.socket&&(this.socket.onclose=null,this.socket.close(),this.socket=null),typeof this._collabCleanup=="function"&&this._collabCleanup(),this.cleanupDomListeners()}_handleMessage(t){try{let e=JSON.parse(t);this.options.debug&&console.log("%c\u{1F4E5} [Dolphin WS Incoming]:","color: #eab308; font-weight: bold;",e),e.type&&e.from&&(e.to===this.deviceId||e.to==="all")&&(e.msgId&&e.type!=="ACK"&&this._sendAck(e.from,e.msgId),this.signalHandlers.forEach(s=>s(e))),e.type==="FILE_AVAILABLE"&&this.fileHandlers.forEach(s=>s(e)),e.type==="FILE_CHUNK"&&(this.saveFileProgress(e.fileId,e.chunkIndex),this._dispatch("file:chunk",e),this._dispatch(`file:chunk/${e.fileId}`,e)),e.type==="FILE_UPLOAD_ACK"&&this._dispatch(`file:upload:ack/${e.fileId}`,e),e.type==="PULL_RESPONSE"&&(this._dispatch("pull:response",e.payload,e.topic),this._dispatch(`pull:response/${e.topic}`,e.payload,e.topic)),e.topic&&e.payload!==void 0&&this.handlers.forEach((s,i)=>{this._matchTopic(i,e.topic)&&s.forEach(r=>r(e.payload,e.topic))})}catch{this._dispatch("raw",t)}}_dispatch(t,e,s){let i=this.handlers.get(t);i&&i.forEach(r=>r(e,s||t))}_sendRaw(t){this.options.debug&&console.log("%c\u{1F4E4} [Dolphin WS Outgoing]:","color: #8b5cf6; font-weight: bold;",t);let e=typeof t=="string"?t:JSON.stringify(t);this.socket&&this.socket.readyState===WebSocket.OPEN?this.socket.send(e):this._offlineQueue.length<100&&this._offlineQueue.push(e)}_flushOfflineQueue(){for(;this._offlineQueue.length>0;){let t=this._offlineQueue.shift();this.socket&&this.socket.readyState===WebSocket.OPEN&&this.socket.send(t)}}_sendAck(t,e){this._sendRaw({type:"ACK",from:this.deviceId,to:t,data:{ackId:e},timestamp:Date.now()})}_matchTopic(t,e){if(t===e||t==="#")return!0;let s=t.split("/"),i=e.split("/");if(s.length!==i.length&&!t.includes("#"))return!1;for(let r=0;r<s.length;r++){if(s[r]==="#")return!0;if(s[r]!=="+"&&s[r]!==i[r])return!1}return s.length===i.length}_maybeReconnect(){if(this.reconnectAttempts<this.options.maxReconnect){this.reconnectAttempts++;let t=Math.pow(2,this.reconnectAttempts)*1e3;console.log(`[Dolphin] Reconnecting in ${t/1e3}s (attempt ${this.reconnectAttempts})...`),this._reconnectTimer=setTimeout(()=>{this._reconnectTimer=null,this.connect().catch(()=>{})},t)}else console.error("[Dolphin] Max reconnect attempts reached.")}subscribe(t,e){this.handlers.has(t)||(this.handlers.set(t,new Set),this._sendRaw({type:"sub",topic:t})),this.handlers.get(t).add(e)}unsubscribe(t,e){if(this.handlers.has(t)){let s=this.handlers.get(t);s.delete(e),s.size===0&&(this.handlers.delete(t),this._sendRaw({type:"unsub",topic:t}))}}publish(t,e){this._sendRaw({topic:t,payload:e})}pubPush(t,e){this._sendRaw({type:"pub",topic:t,payload:e})}subPull(t,e=10){this._sendRaw({type:"PULL_REQUEST",topic:t,count:e})}async pubFile(t,e,s="",i){let r;e instanceof Blob?r=await e.arrayBuffer():e instanceof ArrayBuffer?r=e:r=e.buffer||e;let a=new Uint8Array(r),d=this.options.chunkSize,E=Math.ceil(a.length/d);this._sendRaw({type:"FILE_UPLOAD_START",fileId:t,name:s,size:a.length,totalChunks:E,chunkSize:d});for(let S=0;S<E;S++){let k=a.slice(S*d,(S+1)*d),f=this._uint8ToBase64(k);this._sendRaw({type:"FILE_UPLOAD_CHUNK",fileId:t,chunkIndex:S,totalChunks:E,data:f}),i&&i(Math.round((S+1)/E*100)),S%10===0&&await new Promise(u=>setTimeout(u,0))}this._sendRaw({type:"FILE_UPLOAD_DONE",fileId:t})}_uint8ToBase64(t){let e="";for(let s=0;s<t.length;s++)e+=String.fromCharCode(t[s]);return typeof btoa<"u"?btoa(e):Buffer.from(e,"binary").toString("base64")}subFile(t,e=0){this._sendRaw({type:"FILE_REQUEST",fileId:t,startChunk:e})}resumeFile(t){let e=parseInt(this.storage.getItem(`dolphin_file_${t}`)||"-1");this.subFile(t,e+1)}saveFileProgress(t,e){this.storage.setItem(`dolphin_file_${t}`,e.toString())}onSignal(t){this.signalHandlers.add(t)}offSignal(t){this.signalHandlers.delete(t)}onFileAvailable(t){this.fileHandlers.add(t)}offFileAvailable(t){this.fileHandlers.delete(t)}addDomListener(t,e,s){t&&(t.addEventListener(e,s),this._attachedListeners=this._attachedListeners||[],this._attachedListeners.push({target:t,event:e,cb:s}))}cleanupDomListeners(){this._attachedListeners&&(this._attachedListeners.forEach(({target:t,event:e,cb:s})=>{try{t.removeEventListener(e,s)}catch{}}),this._attachedListeners=[])}};function B(A){let t=new Map;function e(f){return f.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function s(f){let u=f.getAttribute("data-rt-template");if(!u)return null;if(typeof document<"u"&&!u.includes("<"))try{let l=document.querySelector(u);if(l)return l.innerHTML}catch{}return u}function i(f,u){if(!f.includes("{#if")&&!f.includes("{#each")){let l=f;for(let n in u){let o=n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");l=l.replace(new RegExp(`\\{\\{${o}\\}\\}`,"g"),u[n]!==void 0&&u[n]!==null?u[n]:"")}return l}try{let l=p=>p.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"),n=`let out = "";
2
+ `,o=0,c=/(\{\{([\s\S]*?)\}\}|\{#if\s+([\s\S]*?)\}|\{:else\s+if\s+([\s\S]*?)\}|\{:else\}|\{\/if\}|\{#each\s+([\s\S]*?)\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\}|\{\/each\}|\{([^{}]+?)\})/g,y=[],h;for(;(h=c.exec(f))!==null;){let p=f.slice(o,h.index);p&&(n+=`out += "${l(p)}";
3
+ `);let b=h[0];if(b.startsWith("{{")){let T=h[2];n+=`out += (${T} !== undefined && ${T} !== null ? ${T} : "");
4
+ `}else if(b.startsWith("{#if")){let T=h[3];n+=`if (${T}) {
5
+ `}else if(b.startsWith("{:else if")){let T=h[4];n+=`} else if (${T}) {
6
+ `}else if(b.startsWith("{:else}"))n+=`} else {
7
+ `;else if(b.startsWith("{/if}"))n+=`}
8
+ `;else if(b.startsWith("{#each")){let T=h[5],x=h[6],v=h[7];y.push({indexVar:v}),n+=`if (typeof ${T} !== "undefined" && ${T} !== null && Array.isArray(${T})) {
9
+ `,v&&(n+=` let ${v} = 0;
10
+ `),n+=` for (let ${x} of ${T}) {
11
+ `}else if(b.startsWith("{/each}")){let T=y.pop();T&&T.indexVar&&(n+=` ${T.indexVar}++;
12
+ `),n+=` }
13
13
  }
14
- `}else if(y.startsWith("{")){let T=f[8];T&&(r+=`out += (${T} !== undefined && ${T} !== null ? ${T} : "");
15
- `)}o=u.lastIndex}let m=h.slice(o);m&&(r+=`out += "${c(m)}";
16
- `),r+=`return out;
14
+ `}else if(b.startsWith("{")){let T=h[8];T&&(n+=`out += (${T} !== undefined && ${T} !== null ? ${T} : "");
15
+ `)}o=c.lastIndex}let m=f.slice(o);m&&(n+=`out += "${l(m)}";
16
+ `),n+=`return out;
17
17
  `;let w=`
18
18
  with (context) {
19
19
  try {
20
- ${r}
20
+ ${n}
21
21
  } catch (innerErr) {
22
22
  console.warn('[Dolphin Template Eval Warning]:', innerErr);
23
23
  return '';
24
24
  }
25
25
  }
26
- `,S=d;return typeof Proxy<"u"&&d!==null&&typeof d=="object"&&(S=new Proxy(d,{has(p,y){return typeof y!="symbol"},get(p,y){if(y!==Symbol.unscopables){if(y in p)return p[y];if(typeof globalThis<"u"&&y in globalThis)return globalThis[y];if(typeof window<"u"&&y in window)return window[y]}}})),new Function("context",w)(S)}catch(c){console.error("[Dolphin Template Compiler Error]:",c);let r=h;for(let o in d){let u=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");r=r.replace(new RegExp(`\\{\\{${u}\\}\\}`,"g"),d[o]!==void 0&&d[o]!==null?d[o]:"")}return r}}function i(h){if(typeof document>"u")return h;try{let r=new DOMParser().parseFromString(h,"text/html").body,o=u=>{let b=u.tagName.toLowerCase();if(["script","iframe","object","embed","link","style","meta","applet","svg"].includes(b)){u.parentNode?.removeChild(u);return}let f=u.attributes;for(let m=f.length-1;m>=0;m--){let w=f[m].name.toLowerCase(),S=f[m].value.toLowerCase();(w.startsWith("on")||["src","href","data"].includes(w)&&(S.includes("javascript:")||S.includes("data:text/html")))&&u.removeAttribute(f[m].name)}Array.from(u.children).forEach(o)};return Array.from(r.children).forEach(o),r.innerHTML}catch{return h}}function a(h,d){if(h.nodeType!==d.nodeType){h.parentNode?.replaceChild(d.cloneNode(!0),h);return}if(h.nodeType===Node.TEXT_NODE){h.textContent!==d.textContent&&(h.textContent=d.textContent);return}if(h.nodeType===Node.ELEMENT_NODE){let c=h,r=d;if(c.tagName!==r.tagName){c.parentNode?.replaceChild(r.cloneNode(!0),c);return}let o=c.attributes,u=r.attributes;for(let g=o.length-1;g>=0;g--){let p=o[g].name;r.hasAttribute(p)||c.removeAttribute(p)}for(let g=0;g<u.length;g++){let p=u[g].name,y=u[g].value;c.getAttribute(p)!==y&&c.setAttribute(p,y)}c.tagName==="INPUT"||c.tagName==="TEXTAREA"?(c.value!==r.value&&(c.value=r.value),c.checked!==r.checked&&(c.checked=r.checked)):c.tagName==="SELECT"&&c.value!==r.value&&(c.value=r.value);let b=Array.from(c.childNodes),f=Array.from(r.childNodes),m=b.length,w=f.length,S=Math.max(m,w);for(let g=0;g<S;g++)g>=m?c.appendChild(f[g].cloneNode(!0)):g>=w?c.removeChild(b[g]):a(b[g],f[g])}}function l(h,d){if(typeof document>"u")return;let c=document.createElement(h.tagName);c.innerHTML=d;let r=Array.from(h.childNodes),o=Array.from(c.childNodes),u=r.length,b=o.length,f=Math.max(u,b);for(let m=0;m<f;m++)m>=u?h.appendChild(o[m].cloneNode(!0)):m>=b?h.removeChild(r[m]):a(r[m],o[m])}let _=new Map,v=!1;function k(h,d){_.set(h,d),v||(v=!0,(typeof requestAnimationFrame<"u"?requestAnimationFrame:r=>setTimeout(r,0))(()=>{_.forEach((r,o)=>{o.isConnected!==!1&&l(o,r)}),_.clear(),v=!1}))}A.setStoreState=function(h,d,c){this.uiStores=this.uiStores||new Map,this.uiStores.has(h)||this.uiStores.set(h,{});let r=this.uiStores.get(h);r[d]=c,this.options.debug&&console.log("%c\u{1F4BE} [Dolphin Store Update]:","color: #ec4899; font-weight: bold;",`${h}.${d}`,"=",c),typeof document<"u"&&document.querySelectorAll(`[data-store-read="${h}.${d}"]`).forEach(u=>{u.tagName==="INPUT"||u.tagName==="TEXTAREA"?u.type==="checkbox"?u.checked=!!c:u.value=c??"":u.textContent=c??""}),this.publish(`store/${h}`,r),typeof this._updateDOM=="function"&&this._updateDOM(`store/${h}`,r)},A.getStoreState=function(h,d){this.uiStores=this.uiStores||new Map;let c=this.uiStores.get(h);return c?c[d]:void 0},A._scanStoreBinds=function(){if(typeof document>"u")return;document.querySelectorAll("[data-store-write]").forEach(c=>{let r=c.getAttribute("data-store-write");if(r){let o=r.split(".");if(o.length===2){let u=o[0],b=o[1],f=c.type==="checkbox"?c.checked:c.value;this.uiStores=this.uiStores||new Map,this.uiStores.has(u)||this.uiStores.set(u,{});let m=this.uiStores.get(u);m[b]===void 0&&(m[b]=f)}}}),document.querySelectorAll("[data-store-read]").forEach(c=>{let r=c.getAttribute("data-store-read");if(r){let o=r.split(".");if(o.length===2){let u=o[0],b=o[1],f=this.getStoreState(u,b);f!=null&&(c.tagName==="INPUT"||c.tagName==="TEXTAREA"?c.type==="checkbox"?c.checked=!!f:c.value=f:c.textContent=f)}}})},A.getClosestContext=function(h,d){let c=h;for(;c;){if(c._rtContext){let r=c._rtContext;return d?r[d]:r}c=c.parentElement}return null},A._executeStoreAction=function(h,d){this.uiStores=this.uiStores||new Map;let c=new Proxy({},{has:(r,o)=>!0,get:(r,o)=>{if(typeof o=="string")return new Proxy({},{get:(u,b)=>{if(typeof b=="string")return this.getStoreState(o,b)},set:(u,b,f)=>typeof b=="string"?(this.setStoreState(o,b,f),!0):!1})}});try{new Function("ctx",`with(ctx) { ${h} }`)(c)}catch(r){console.error("%c[Dolphin Store Action Error]:","color: #ef4444; font-weight: bold;",r),d&&console.error("%cFailed Element:","color: #f97316; font-weight: bold;",d),console.error("%cFailed Expression:","color: #3b82f6; font-style: italic;",h)}},A._initDOMBinding=function(){if(this._domInitialized)return;this._domInitialized=!0;let h=["input","change","keyup","paste","blur"],d=new WeakMap;h.forEach(r=>{this.addDomListener(document,r,o=>{if(!o.target||!o.target.getAttribute)return;let u=o.target.getAttribute("data-store-write");if(u){let w=u.split(".");if(w.length===2){let S=w[0],g=w[1],p=o.target.type==="checkbox"?o.target.checked:o.target.value;this.setStoreState(S,g,p)}}let b=o.target.getAttribute("data-rt-validate"),f=o.target.name;if(b&&f&&typeof this.validateField=="function"){let w=o.target.closest("form"),S=w?Object.fromEntries(new FormData(w).entries()):{},g=this.validateField(o.target.value,b,S);g?(o.target.classList.add("invalid"),this.publish(`errors/${f}`,g)):(o.target.classList.remove("invalid"),this.publish(`errors/${f}`,""))}let m=o.target.getAttribute("data-rt-push");if(m){let w=o.target.getAttribute("data-rt-debounce"),S=w?parseInt(w,10):0,g=()=>{let p={name:o.target.name,value:o.target.value};this.pubPush(m,p)};if(S>0){d.has(o.target)&&clearTimeout(d.get(o.target));let p=setTimeout(g,S);d.set(o.target,p)}else g()}})}),this.addDomListener(document,"submit",async r=>{if(!r.target||!r.target.getAttribute)return;let o=r.target.getAttribute("data-rt-submit"),u=r.target.getAttribute("data-api-submit");if(o||u){r.target.querySelectorAll("[name]").forEach(p=>{let y=p.name;y&&(this.publish(`errors/${y}`,""),p.classList.remove("invalid"))});let f=r.target.querySelectorAll("[data-rt-validate]"),m=!0;if(f.length>0&&typeof this.validateField=="function"){let p=Object.fromEntries(new FormData(r.target).entries());f.forEach(y=>{let T=y.getAttribute("data-rt-validate"),D=y.name;if(T&&D){let E=this.validateField(y.value,T,p);E&&(m=!1,y.classList.add("invalid"),this.publish(`errors/${D}`,E))}})}if(!m){r.preventDefault(),r.stopPropagation();return}r.preventDefault();let w=this.getClosestContext(r.target)||{},S=new FormData(r.target),g=Object.fromEntries(S.entries());if(o){let p=o;for(let y in w){let T=e(y);p=p.replace(new RegExp(`\\{\\{${T}\\}\\}`,"g"),w[y]!==void 0&&w[y]!==null?w[y]:"")}this.publish(p,g)}else if(u){let p=u;for(let E in w){let L=e(E);p=p.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),w[E]!==void 0&&w[E]!==null?w[E]:"")}let y=p.trim().split(" "),T=y.length>1?y[0].toUpperCase():"POST",D=y.length>1?y[1]:y[0];g._method&&(T=String(g._method).toUpperCase());try{let E=await this.api.request(T,D,g),L=r.target.getAttribute("data-api-result");L&&this._updateDOM(L,E);let M=r.target.getAttribute("data-api-redirect");M&&(window.location.href=M),r.target.hasAttribute("data-api-reload")&&window.location.reload()}catch(E){console.error("[Dolphin] API Submit Error:",E)}}}}),["click","change","submit","input","keydown","keyup","dblclick","focus","blur","mouseenter","mouseleave"].forEach(r=>{this.addDomListener(document,r,async o=>{if(!o.target||!o.target.closest)return;let u=o.target.closest(`[data-rt-${r}]`),b=o.target.closest(`[data-api-${r}]`);if(u){r==="submit"&&o.preventDefault();let m=u.getAttribute(`data-rt-${r}`),w=u.getAttribute("data-rt-payload"),S=this.getClosestContext(u)||{},g={};if(w){let p=w;for(let y in S){let T=e(y);p=p.replace(new RegExp(`\\{\\{${T}\\}\\}`,"g"),S[y]!==void 0&&S[y]!==null?S[y]:"")}try{g=JSON.parse(p)}catch{g={}}}this.publish(m,g)}if(b){r==="submit"&&o.preventDefault();let m=b.getAttribute(`data-api-${r}`),w=b.getAttribute("data-api-payload"),S=this.getClosestContext(b)||{},g=m.trim().split(" "),p=g.length>1?g[0].toUpperCase():"POST",y=g.length>1?g[1]:g[0],T=null;if(w){let D=w;for(let E in S){let L=e(E);D=D.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),S[E]!==void 0&&S[E]!==null?S[E]:"")}try{T=JSON.parse(D)}catch{T=null}}try{let D=await this.api.request(p,y,T),E=b.getAttribute("data-api-result");E&&this._updateDOM(E,D);let L=b.getAttribute("data-api-redirect");L&&(window.location.href=L),b.hasAttribute("data-api-reload")&&window.location.reload()}catch(D){console.error(`[Dolphin] API ${r} Error:`,D)}}let f=o.target.closest(`[data-store-${r}]`);if(f){r==="submit"&&o.preventDefault();let m=f.getAttribute(`data-store-${r}`);m&&this._executeStoreAction(m,f)}})}),this.subscribe("#",(r,o)=>{this._updateDOM(o,r)}),this._scanAndFetchAPIBinds(),this._scanStoreBinds(),this._resolveImports(),this._initSPARouter()},A._scanAndFetchAPIBinds=async function(){if(typeof document>"u")return;let h=document.querySelectorAll("[data-api-get]");for(let d of Array.from(h)){let c=d.getAttribute("data-api-get");if(c&&!(typeof d.hasAttribute=="function"&&d.hasAttribute("data-api-initialized"))){typeof d.setAttribute=="function"&&d.setAttribute("data-api-initialized","true");try{let r=await this.api.get(c),o=d.getAttribute("data-api-store");if(o){let b=o.split(".");b.length===2&&this.setStoreState(b[0],b[1],r)}let u=d.getAttribute("data-rt-bind");if(u&&!o)this._updateDOM(u,r);else if(!o){let b=n(d);if(b&&typeof r=="object"&&r!==null){let f=this._applyDeclarativeDirectives(d,r);if(Array.isArray(f)){let m="";for(let w of f)m+=s(b,w);k(d,m)}else k(d,s(b,f))}else if(d.tagName==="INPUT"||d.tagName==="TEXTAREA")d.value=typeof r=="object"?r.value!==void 0?r.value:"":r;else{let f=typeof r=="object"?r.html||r.text||JSON.stringify(r):String(r);d.innerHTML=i(f)}}}catch(r){console.error("[Dolphin] API Get Error:",r)}}}},A._applyDeclarativeDirectives=function(h,d){let c=d;if(typeof d=="object"&&d!==null){let r=o=>{let u=[...o],b=h.getAttribute("data-rt-filter");if(b){let w=b.split("==");if(w.length===2){let S=w[0].trim(),g=w[1].trim(),p,y=g.split(".");y.length===2?p=this.getStoreState(y[0],y[1]):p=d[g]!==void 0?d[g]:this.getStoreState("app",g),p!=null&&p!==""&&(u=u.filter(T=>T[S]===p))}}let f=h.getAttribute("data-rt-search");if(f){let w=f.split("==");if(w.length===2){let S=w[0].trim(),g=w[1].trim(),p,y=g.split(".");if(y.length===2?p=this.getStoreState(y[0],y[1]):p=d[g]!==void 0?d[g]:this.getStoreState("app",g),p!=null&&p!==""){let T=String(p).toLowerCase();u=u.filter(D=>{let E=D[S];return E!=null&&String(E).toLowerCase().includes(T)})}}}let m=h.getAttribute("data-rt-sort");if(m){let w,S=m.split(".");if(S.length===2?w=this.getStoreState(S[0],S[1]):w=d[m]!==void 0?d[m]:this.getStoreState("app",m),w&&w!=="")if(w==="popular")u.sort((g,p)=>{let y=g.rating?.rate||g.rate||0;return(p.rating?.rate||p.rate||0)-y});else{let g="",p="asc";w.endsWith("-low")||w.endsWith("-asc")?(g=w.replace("-low","").replace("-asc",""),p="asc"):(w.endsWith("-high")||w.endsWith("-desc"))&&(g=w.replace("-high","").replace("-desc",""),p="desc"),g&&u.sort((y,T)=>{let D=(J,Q)=>Q.split(".").reduce((q,G)=>q&&q[G],J),E=D(y,g),L=D(T,g);if(E===void 0&&(E=y[g]),L===void 0&&(L=T[g]),typeof E=="string"&&typeof L=="string")return p==="asc"?E.localeCompare(L):L.localeCompare(E);let M=Number(E),I=Number(L);return!isNaN(M)&&!isNaN(I)?p==="asc"?M-I:I-M:0})}}return u};if(Array.isArray(d))c=r(d);else{let o="";for(let u in d)if(Array.isArray(d[u])){o=u;break}if(o){let u=r(d[o]);c={...d,[o]:u}}}}return c},A._updateDOM=function(h,d){if(typeof document>"u")return;document.querySelectorAll(`[data-rt-bind="${h}"]`).forEach(r=>{let o=this._applyDeclarativeDirectives(r,d);if(r.getAttribute("data-rt-type")==="context"&&typeof o=="object"&&o!==null){r._rtContext=o;let b=f=>{if(f.hasAttribute("data-rt-text")){let m=f.getAttribute("data-rt-text");m&&o[m]!==void 0&&o[m]!==null&&(f.textContent=o[m])}if(f.hasAttribute("data-rt-html")){let m=f.getAttribute("data-rt-html");m&&o[m]!==void 0&&o[m]!==null&&(f.innerHTML=i(o[m]))}if(f.hasAttribute("data-rt-attr")){let m=f.getAttribute("data-rt-attr");m&&m.split(",").forEach(w=>{let S=w.split(":");if(S.length===2){let g=S[0].trim(),p=S[1].trim();g&&p&&o[p]!==void 0&&o[p]!==null&&f.setAttribute(g,o[p])}})}if(f.hasAttribute("data-rt-class")){let m=f.getAttribute("data-rt-class");m&&m.split(",").forEach(w=>{let S=w.split(":");if(S.length===2){let g=S[0].trim(),p=S[1].trim(),y=g.split(/\s+/).filter(Boolean);o[p]?y.forEach(T=>f.classList.add(T)):y.forEach(T=>f.classList.remove(T))}})}if(f.hasAttribute("data-rt-if")){let m=f.getAttribute("data-rt-if");m&&(o[m]?f.style.display="":f.style.display="none")}if(f.hasAttribute("data-rt-hide")){let m=f.getAttribute("data-rt-hide");m&&(o[m]?f.style.display="none":f.style.display="")}};b(r),r.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(b);return}let u=n(r);if(u&&typeof o=="object"&&o!==null){if(Array.isArray(o)){let b="";for(let f of o)b+=s(u,f);k(r,b)}else k(r,s(u,o));return}if(r.tagName==="INPUT"||r.tagName==="TEXTAREA")r.value=typeof o=="object"?o.value!==void 0?o.value:"":o;else if(u)r.innerHTML=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);else{let b=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);r.innerHTML=i(b)}})},A._resolveImports=async function(h){if(typeof document>"u")return;let d=h||document.body||document;if(!d||typeof d.querySelectorAll!="function")return;let c=d.querySelectorAll("[data-import]");if(c.length===0)return;let r=async(u,b)=>{let f=u.getAttribute("data-import");if(!f)return;if(b.has(f)){console.warn(`[Dolphin Component Warning]: Circular import detected for "${f}". Skipping resolving.`),u.innerHTML=`<span style="color:red;font-weight:bold;">Circular import: ${f}</span>`;return}b.add(f);let m=t.get(f);m||(m=fetch(f).then(g=>{if(!g.ok)throw new Error(`HTTP ${g.status}`);return g.text()}),m.catch(()=>t.delete(f)),t.set(f,m));let w="";try{w=await m}catch(g){console.error(`[Dolphin Component Error]: Failed to fetch component "${f}":`,g),w=`<span style="color:red;font-weight:bold;">Failed to import ${f}</span>`}u.innerHTML=i(w),u.removeAttribute("data-import");let S=u.querySelectorAll("[data-import]");if(S.length>0){let g=Array.from(S).map(p=>r(p,new Set(b)));await Promise.all(g)}this._scanStoreBinds(),this._scanAndFetchAPIBinds()},o=Array.from(c).map(u=>r(u,new Set));await Promise.all(o)},A._initSPARouter=function(){if(typeof window>"u"||typeof document>"u"||this._routerInitialized)return;this._routerInitialized=!0;let h=null,d=()=>{let o=(this.options.routerViewport||"main, #viewport, body").split(",").map(u=>u.trim());for(let u of o){let b=document.querySelector(u);if(b)return b}return document.body},c=async(r,o=!0)=>{try{this.options.debug&&console.log(`%c\u{1F6E3}\uFE0F [Dolphin Router]: Navigating to ${r}...`,"color: #3b82f6; font-weight: bold;"),h&&h.abort(),h=new AbortController;let u=h.signal,b=d();this.options.routerTransitions&&b&&(b.classList.add("dolphin-fade-out"),await new Promise(y=>setTimeout(y,150)));let f=await fetch(r,{signal:u});if(!f.ok)throw new Error(`HTTP ${f.status}`);let m=await f.text();h=null;let S=new DOMParser().parseFromString(m,"text/html");S.title&&(document.title=S.title);let g=S.querySelector(this.options.routerViewport||"main, #viewport, body"),p=d();g&&p?(p.innerHTML=g.innerHTML,Array.from(g.attributes).forEach(y=>{p.setAttribute(y.name,y.value)})):p&&(p.innerHTML=S.body.innerHTML),o&&window.history.pushState({dolphinSpa:!0,url:r},"",r),this.options.routerTransitions&&p&&(p.classList.remove("dolphin-fade-out"),p.classList.add("dolphin-fade-in"),setTimeout(()=>p.classList.remove("dolphin-fade-in"),300)),await this._resolveImports(p),this._scanStoreBinds(),this._scanAndFetchAPIBinds()}catch(u){if(u&&u.name==="AbortError")return;console.error("[Dolphin Router Error]: Failed to route page:",u),window.location.href=r}};if(this.addDomListener(document,"click",r=>{let o=r.target.closest("a");if(!o||!o.hasAttribute("data-spa")&&o.getAttribute("data-spa")!=="true")return;let u=o.getAttribute("href");!u||u.startsWith("#")||u.startsWith("javascript:")||u.startsWith("mailto:")||u.startsWith("tel:")||new URL(u,window.location.href).origin!==window.location.origin||(r.preventDefault(),c(u))}),this.addDomListener(window,"popstate",r=>{r.state&&r.state.dolphinSpa?c(r.state.url,!1):r.state===null&&c(window.location.pathname,!1)}),this.options.routerTransitions){let r=document.createElement("style");r.innerHTML=`
26
+ `,_=u;return typeof Proxy<"u"&&u!==null&&typeof u=="object"&&(_=new Proxy(u,{has(p,b){return typeof b!="symbol"},get(p,b){if(b!==Symbol.unscopables){if(b in p)return p[b];if(typeof globalThis<"u"&&b in globalThis)return globalThis[b];if(typeof window<"u"&&b in window)return window[b]}}})),new Function("context",w)(_)}catch(l){console.error("[Dolphin Template Compiler Error]:",l);let n=f;for(let o in u){let c=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");n=n.replace(new RegExp(`\\{\\{${c}\\}\\}`,"g"),u[o]!==void 0&&u[o]!==null?u[o]:"")}return n}}function r(f){if(typeof document>"u")return f;try{let n=new DOMParser().parseFromString(f,"text/html").body,o=c=>{let y=c.tagName.toLowerCase();if(["script","iframe","object","embed","link","style","meta","applet","svg"].includes(y)){c.parentNode?.removeChild(c);return}let h=c.attributes;for(let m=h.length-1;m>=0;m--){let w=h[m].name.toLowerCase(),_=h[m].value.toLowerCase();(w.startsWith("on")||["src","href","data"].includes(w)&&(_.includes("javascript:")||_.includes("data:text/html")))&&c.removeAttribute(h[m].name)}Array.from(c.children).forEach(o)};return Array.from(n.children).forEach(o),n.innerHTML}catch{return f}}function a(f,u){if(f.nodeType!==u.nodeType){f.parentNode?.replaceChild(u.cloneNode(!0),f);return}if(f.nodeType===Node.TEXT_NODE){f.textContent!==u.textContent&&(f.textContent=u.textContent);return}if(f.nodeType===Node.ELEMENT_NODE){let l=f,n=u;if(l.tagName!==n.tagName){l.parentNode?.replaceChild(n.cloneNode(!0),l);return}let o=l.attributes,c=n.attributes;for(let g=o.length-1;g>=0;g--){let p=o[g].name;n.hasAttribute(p)||l.removeAttribute(p)}for(let g=0;g<c.length;g++){let p=c[g].name,b=c[g].value;l.getAttribute(p)!==b&&l.setAttribute(p,b)}l.tagName==="INPUT"||l.tagName==="TEXTAREA"?(l.value!==n.value&&(l.value=n.value),l.checked!==n.checked&&(l.checked=n.checked)):l.tagName==="SELECT"&&l.value!==n.value&&(l.value=n.value);let y=Array.from(l.childNodes),h=Array.from(n.childNodes),m=y.length,w=h.length,_=Math.max(m,w);for(let g=0;g<_;g++)g>=m?l.appendChild(h[g].cloneNode(!0)):g>=w?l.removeChild(y[g]):a(y[g],h[g])}}function d(f,u){if(typeof document>"u")return;let l=document.createElement(f.tagName);l.innerHTML=u;let n=Array.from(f.childNodes),o=Array.from(l.childNodes),c=n.length,y=o.length,h=Math.max(c,y);for(let m=0;m<h;m++)m>=c?f.appendChild(o[m].cloneNode(!0)):m>=y?f.removeChild(n[m]):a(n[m],o[m])}let E=new Map,S=!1;function k(f,u){E.set(f,u),S||(S=!0,(typeof requestAnimationFrame<"u"?requestAnimationFrame:n=>setTimeout(n,0))(()=>{E.forEach((n,o)=>{o.isConnected!==!1&&d(o,n)}),E.clear(),S=!1}))}A.setStoreState=function(f,u,l){this.uiStores=this.uiStores||new Map,this.uiStores.has(f)||this.uiStores.set(f,{});let n=this.uiStores.get(f);n[u]=l,this.options.debug&&console.log("%c\u{1F4BE} [Dolphin Store Update]:","color: #ec4899; font-weight: bold;",`${f}.${u}`,"=",l),typeof document<"u"&&document.querySelectorAll(`[data-store-read="${f}.${u}"]`).forEach(c=>{c.tagName==="INPUT"||c.tagName==="TEXTAREA"?c.type==="checkbox"?c.checked=!!l:c.value=l??"":c.textContent=l??""}),this.publish(`store/${f}`,n),typeof this._updateDOM=="function"&&this._updateDOM(`store/${f}`,n)},A.getStoreState=function(f,u){this.uiStores=this.uiStores||new Map;let l=this.uiStores.get(f);return l?l[u]:void 0},A._scanStoreBinds=function(){if(typeof document>"u")return;document.querySelectorAll("[data-store-write]").forEach(l=>{let n=l.getAttribute("data-store-write");if(n){let o=n.split(".");if(o.length===2){let c=o[0],y=o[1],h=l.type==="checkbox"?l.checked:l.value;this.uiStores=this.uiStores||new Map,this.uiStores.has(c)||this.uiStores.set(c,{});let m=this.uiStores.get(c);m[y]===void 0&&(m[y]=h)}}}),document.querySelectorAll("[data-store-read]").forEach(l=>{let n=l.getAttribute("data-store-read");if(n){let o=n.split(".");if(o.length===2){let c=o[0],y=o[1],h=this.getStoreState(c,y);h!=null&&(l.tagName==="INPUT"||l.tagName==="TEXTAREA"?l.type==="checkbox"?l.checked=!!h:l.value=h:l.textContent=h)}}})},A.getClosestContext=function(f,u){let l=f;for(;l;){if(l._rtContext){let n=l._rtContext;return u?n[u]:n}l=l.parentElement}return null},A._executeStoreAction=function(f,u){this.uiStores=this.uiStores||new Map;let l=new Proxy({},{has:(n,o)=>!0,get:(n,o)=>{if(typeof o=="string")return new Proxy({},{get:(c,y)=>{if(typeof y=="string")return this.getStoreState(o,y)},set:(c,y,h)=>typeof y=="string"?(this.setStoreState(o,y,h),!0):!1})}});try{new Function("ctx",`with(ctx) { ${f} }`)(l)}catch(n){console.error("%c[Dolphin Store Action Error]:","color: #ef4444; font-weight: bold;",n),u&&console.error("%cFailed Element:","color: #f97316; font-weight: bold;",u),console.error("%cFailed Expression:","color: #3b82f6; font-style: italic;",f)}},A._showAlert=function(f,u){if(typeof document>"u")return;let l=document.getElementById(f)||document.querySelector(`[id="${f}"]`);l&&(l.removeAttribute("hidden"),l.style.display="",u&&u>0&&setTimeout(()=>{l.setAttribute("hidden","")},u))},A._showToast=function(f,u="success"){if(typeof document>"u")return;let l={success:{bg:"rgba(16,185,129,0.15)",border:"#10b981",icon:"\u2705"},error:{bg:"rgba(239,68,68,0.15)",border:"#ef4444",icon:"\u274C"},info:{bg:"rgba(59,130,246,0.15)",border:"#3b82f6",icon:"\u2139\uFE0F"}},n=l[u]||l.success,o=document.createElement("div");o.setAttribute("data-dolphin-toast","");let y=document.querySelectorAll("[data-dolphin-toast]").length*72;o.style.cssText=["position:fixed",`bottom:${24+y}px`,"right:24px","z-index:2147483647",`background:${n.bg}`,`border:1px solid ${n.border}`,"color:#fff","padding:14px 20px","border-radius:14px","font-size:14px","font-weight:600","font-family:system-ui,sans-serif","box-shadow:0 8px 32px rgba(0,0,0,0.4)","display:flex","align-items:center","gap:10px","max-width:380px","word-break:break-word","transform:translateY(80px)","opacity:0","transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1),opacity 0.3s ease","pointer-events:auto","backdrop-filter:blur(12px)"].join(";"),o.innerHTML=`<span style="font-size:18px">${n.icon}</span><span>${f}</span>`,document.body.appendChild(o);let h=()=>{document.querySelectorAll("[data-dolphin-toast]").forEach((p,b)=>{p.style.bottom=`${24+b*72}px`})},m=typeof requestAnimationFrame<"u"?requestAnimationFrame:g=>setTimeout(g,0);m(()=>m(()=>{o.style.transform="translateY(0)",o.style.opacity="1"}));let w=()=>{clearTimeout(o._removeTimer),o.parentNode&&(o.parentNode.removeChild(o),h())},_=setTimeout(()=>{o.style.transform="translateY(80px)",o.style.opacity="0",o._removeTimer=setTimeout(w,400)},3500);o._hideTimer=_,o.addEventListener("click",()=>{clearTimeout(o._hideTimer),w()},{once:!0})},A._initDOMBinding=function(){if(this._domInitialized)return;this._domInitialized=!0;let f=["input","change","keyup","paste","blur"],u=new WeakMap;f.forEach(n=>{this.addDomListener(document,n,o=>{if(!o.target||!o.target.getAttribute)return;let c=o.target.getAttribute("data-store-write");if(c){let w=c.split(".");if(w.length===2){let _=w[0],g=w[1],p=o.target.type==="checkbox"?o.target.checked:o.target.value;this.setStoreState(_,g,p)}}let y=o.target.getAttribute("data-rt-validate"),h=o.target.name;if(y&&h&&typeof this.validateField=="function"){let w=o.target.closest("form"),_=w?Object.fromEntries(new FormData(w).entries()):{},g=this.validateField(o.target.value,y,_);g?(o.target.classList.add("invalid"),this.publish(`errors/${h}`,g)):(o.target.classList.remove("invalid"),this.publish(`errors/${h}`,""))}let m=o.target.getAttribute("data-rt-push");if(m){let w=o.target.getAttribute("data-rt-debounce"),_=w?parseInt(w,10):0,g=()=>{let p={name:o.target.name,value:o.target.value};this.pubPush(m,p)};if(_>0){u.has(o.target)&&clearTimeout(u.get(o.target));let p=setTimeout(g,_);u.set(o.target,p)}else g()}})}),this.addDomListener(document,"submit",async n=>{if(!n.target||!n.target.getAttribute)return;let o=n.target.getAttribute("data-rt-submit"),c=n.target.getAttribute("data-api-submit");if(o||c){n.target.querySelectorAll("[name]").forEach(p=>{let b=p.name;b&&(this.publish(`errors/${b}`,""),p.classList.remove("invalid"))});let h=n.target.querySelectorAll("[data-rt-validate]"),m=!0;if(h.length>0&&typeof this.validateField=="function"){let p=Object.fromEntries(new FormData(n.target).entries());h.forEach(b=>{let T=b.getAttribute("data-rt-validate"),x=b.name;if(T&&x){let v=this.validateField(b.value,T,p);v&&(m=!1,b.classList.add("invalid"),this.publish(`errors/${x}`,v))}})}if(!m){n.preventDefault(),n.stopPropagation();return}n.preventDefault();let w=this.getClosestContext(n.target)||{},_=new FormData(n.target),g=Object.fromEntries(_.entries());if(o){let p=o;for(let T in w){let x=e(T);p=p.replace(new RegExp(`\\{\\{${x}\\}\\}`,"g"),w[T]!==void 0&&w[T]!==null?w[T]:"")}this.publish(p,g);let b=n.target.getAttribute("data-rt-api-success");if(b){let T=document.getElementById(b);T&&this._showToast(T.textContent||T.innerText||"","success")}}else if(c){let p=c;for(let v in w){let D=e(v);p=p.replace(new RegExp(`\\{\\{${D}\\}\\}`,"g"),w[v]!==void 0&&w[v]!==null?w[v]:"")}let b=p.trim().split(" "),T=b.length>1?b[0].toUpperCase():"POST",x=b.length>1?b[1]:b[0];g._method&&(T=String(g._method).toUpperCase());try{let v=await this.api.request(T,x,g),D=n.target.getAttribute("data-api-result");D&&this._updateDOM(D,v);let L=n.target.getAttribute("data-api-toast");L&&this._showToast(L,"success");let M=n.target.getAttribute("data-rt-api-success");if(M){let I=parseInt(n.target.getAttribute("data-rt-alert-duration")||"0",10);this._showAlert(M,I);let P=n.target.getAttribute("data-rt-api-error");if(P){let R=document.getElementById(P);R&&R.setAttribute("hidden","")}}let $=n.target.getAttribute("data-api-redirect");$&&(window.location.href=$),n.target.hasAttribute("data-api-reload")&&window.location.reload()}catch(v){console.error("[Dolphin] API Submit Error:",v);let D=n.target.getAttribute("data-api-error-toast");D&&this._showToast(D,"error");let L=n.target.getAttribute("data-rt-api-error");if(L){let M=parseInt(n.target.getAttribute("data-rt-alert-duration")||"0",10);this._showAlert(L,M);let $=n.target.getAttribute("data-rt-api-success");if($){let I=document.getElementById($);I&&I.setAttribute("hidden","")}}}}}}),["click","change","input","keydown","keyup","dblclick","focus","blur","mouseenter","mouseleave"].forEach(n=>{this.addDomListener(document,n,async o=>{if(!o.target||!o.target.closest)return;let c=o.target.closest(`[data-rt-${n}]`),y=o.target.closest(`[data-api-${n}]`);if(c){n==="submit"&&o.preventDefault();let m=c.getAttribute(`data-rt-${n}`),w=c.getAttribute("data-rt-payload"),_=this.getClosestContext(c)||{},g={};if(w){let p=w;for(let b in _){let T=e(b);p=p.replace(new RegExp(`\\{\\{${T}\\}\\}`,"g"),_[b]!==void 0&&_[b]!==null?_[b]:"")}try{g=JSON.parse(p)}catch{g={}}}this.publish(m,g)}if(y){n==="submit"&&o.preventDefault();let m=y.getAttribute(`data-api-${n}`),w=y.getAttribute("data-api-payload"),_=this.getClosestContext(y)||{},g=m;for(let v in _){let D=e(v);g=g.replace(new RegExp(`\\{\\{${D}\\}\\}`,"g"),_[v]!==void 0&&_[v]!==null?_[v]:"")}let p=g.trim().split(" "),b=p.length>1?p[0].toUpperCase():"POST",T=p.length>1?p[1]:p[0],x=null;if(w){let v=w;for(let D in _){let L=e(D);v=v.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),_[D]!==void 0&&_[D]!==null?_[D]:"")}try{x=JSON.parse(v)}catch{x=null}}try{let v=await this.api.request(b,T,x),D=y.getAttribute("data-api-result");D&&this._updateDOM(D,v);let L=y.getAttribute("data-api-toast");L&&this._showToast(L,"success");let M=y.getAttribute("data-rt-api-success");if(M){let I=parseInt(y.getAttribute("data-rt-alert-duration")||"0",10);this._showAlert(M,I);let P=y.getAttribute("data-rt-api-error");if(P){let R=document.getElementById(P);R&&R.setAttribute("hidden","")}}let $=y.getAttribute("data-api-redirect");$&&(window.location.href=$),y.hasAttribute("data-api-reload")&&window.location.reload()}catch(v){console.error(`[Dolphin] API ${n} Error:`,v);let D=y.getAttribute("data-api-error-toast");D&&this._showToast(D,"error");let L=y.getAttribute("data-rt-api-error");if(L){let M=parseInt(y.getAttribute("data-rt-alert-duration")||"0",10);this._showAlert(L,M);let $=y.getAttribute("data-rt-api-success");if($){let I=document.getElementById($);I&&I.setAttribute("hidden","")}}}}let h=o.target.closest(`[data-store-${n}]`);if(h){n==="submit"&&o.preventDefault();let m=h.getAttribute(`data-store-${n}`);m&&this._executeStoreAction(m,h)}})}),this.subscribe("#",(n,o)=>{this._updateDOM(o,n)}),this._scanAndFetchAPIBinds(),this._scanStoreBinds(),this._resolveImports(),this._initSPARouter()},A._scanAndFetchAPIBinds=async function(){if(typeof document>"u")return;let f=document.querySelectorAll("[data-api-get]");for(let u of Array.from(f)){let l=u.getAttribute("data-api-get");if(l&&!(typeof u.hasAttribute=="function"&&u.hasAttribute("data-api-initialized"))){typeof u.setAttribute=="function"&&u.setAttribute("data-api-initialized","true");try{let n=await this.api.get(l),o=u.getAttribute("data-api-store");if(o){let y=o.split(".");y.length===2&&this.setStoreState(y[0],y[1],n)}let c=u.getAttribute("data-rt-bind");if(c&&!o)this._updateDOM(c,n);else if(!o){let y=s(u);if(y&&typeof n=="object"&&n!==null){let h=this._applyDeclarativeDirectives(u,n);if(Array.isArray(h)){let m="";for(let w of h)m+=i(y,w);k(u,m)}else k(u,i(y,h))}else if(u.tagName==="INPUT"||u.tagName==="TEXTAREA")u.value=typeof n=="object"?n.value!==void 0?n.value:"":n;else{let h=typeof n=="object"?n.html||n.text||JSON.stringify(n):String(n);u.innerHTML=r(h)}}}catch(n){console.error("[Dolphin] API Get Error:",n)}}}},A._applyDeclarativeDirectives=function(f,u){let l=u;if(typeof u=="object"&&u!==null){let n=o=>{let c=[...o],y=f.getAttribute("data-rt-filter");if(y){let w=y.split("==");if(w.length===2){let _=w[0].trim(),g=w[1].trim(),p,b=g.split(".");b.length===2?p=this.getStoreState(b[0],b[1]):p=u[g]!==void 0?u[g]:this.getStoreState("app",g),p!=null&&p!==""&&(c=c.filter(T=>T[_]===p))}}let h=f.getAttribute("data-rt-search");if(h){let w=h.split("==");if(w.length===2){let _=w[0].trim(),g=w[1].trim(),p,b=g.split(".");if(b.length===2?p=this.getStoreState(b[0],b[1]):p=u[g]!==void 0?u[g]:this.getStoreState("app",g),p!=null&&p!==""){let T=String(p).toLowerCase();c=c.filter(x=>{let v=x[_];return v!=null&&String(v).toLowerCase().includes(T)})}}}let m=f.getAttribute("data-rt-sort");if(m){let w,_=m.split(".");if(_.length===2?w=this.getStoreState(_[0],_[1]):w=u[m]!==void 0?u[m]:this.getStoreState("app",m),w&&w!=="")if(w==="popular")c.sort((g,p)=>{let b=g.rating?.rate||g.rate||0;return(p.rating?.rate||p.rate||0)-b});else{let g="",p="asc";w.endsWith("-low")||w.endsWith("-asc")?(g=w.replace("-low","").replace("-asc",""),p="asc"):(w.endsWith("-high")||w.endsWith("-desc"))&&(g=w.replace("-high","").replace("-desc",""),p="desc"),g&&c.sort((b,T)=>{let x=($,I)=>I.split(".").reduce((P,R)=>P&&P[R],$),v=x(b,g),D=x(T,g);if(v===void 0&&(v=b[g]),D===void 0&&(D=T[g]),typeof v=="string"&&typeof D=="string")return p==="asc"?v.localeCompare(D):D.localeCompare(v);let L=Number(v),M=Number(D);return!isNaN(L)&&!isNaN(M)?p==="asc"?L-M:M-L:0})}}return c};if(Array.isArray(u))l=n(u);else{let o="";for(let c in u)if(Array.isArray(u[c])){o=c;break}if(o){let c=n(u[o]);l={...u,[o]:c}}}}return l},A._updateDOM=function(f,u){if(typeof document>"u")return;document.querySelectorAll(`[data-rt-bind="${f}"]`).forEach(n=>{let o=this._applyDeclarativeDirectives(n,u);if(n.getAttribute("data-rt-type")==="context"&&typeof o=="object"&&o!==null){n._rtContext=o;let y=h=>{if(h.hasAttribute("data-rt-text")){let m=h.getAttribute("data-rt-text");m&&o[m]!==void 0&&o[m]!==null&&(h.textContent=o[m])}if(h.hasAttribute("data-rt-html")){let m=h.getAttribute("data-rt-html");m&&o[m]!==void 0&&o[m]!==null&&(h.innerHTML=r(o[m]))}if(h.hasAttribute("data-rt-attr")){let m=h.getAttribute("data-rt-attr");m&&m.split(",").forEach(w=>{let _=w.split(":");if(_.length===2){let g=_[0].trim(),p=_[1].trim();g&&p&&o[p]!==void 0&&o[p]!==null&&h.setAttribute(g,o[p])}})}if(h.hasAttribute("data-rt-class")){let m=h.getAttribute("data-rt-class");m&&m.split(",").forEach(w=>{let _=w.split(":");if(_.length===2){let g=_[0].trim(),p=_[1].trim(),b=g.split(/\s+/).filter(Boolean);o[p]?b.forEach(T=>h.classList.add(T)):b.forEach(T=>h.classList.remove(T))}})}if(h.hasAttribute("data-rt-if")){let m=h.getAttribute("data-rt-if");m&&(o[m]?h.style.display="":h.style.display="none")}if(h.hasAttribute("data-rt-hide")){let m=h.getAttribute("data-rt-hide");m&&(o[m]?h.style.display="none":h.style.display="")}};y(n),n.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(y);return}let c=s(n);if(c&&typeof o=="object"&&o!==null){if(Array.isArray(o)){let y="";for(let h of o)y+=i(c,h);k(n,y)}else k(n,i(c,o));return}if(n.tagName==="INPUT"||n.tagName==="TEXTAREA")n.value=typeof o=="object"?o.value!==void 0?o.value:"":o;else if(c)n.innerHTML=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);else{let y=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);n.innerHTML=r(y)}})},A._resolveImports=async function(f){if(typeof document>"u")return;let u=f||document.body||document;if(!u||typeof u.querySelectorAll!="function")return;let l=u.querySelectorAll("[data-import]");if(l.length===0)return;let n=async(c,y)=>{let h=c.getAttribute("data-import");if(!h)return;if(y.has(h)){console.warn(`[Dolphin Component Warning]: Circular import detected for "${h}". Skipping resolving.`),c.innerHTML=`<span style="color:red;font-weight:bold;">Circular import: ${h}</span>`;return}y.add(h);let m=t.get(h);m||(m=fetch(h).then(g=>{if(!g.ok)throw new Error(`HTTP ${g.status}`);return g.text()}),m.catch(()=>t.delete(h)),t.set(h,m));let w="";try{w=await m}catch(g){console.error(`[Dolphin Component Error]: Failed to fetch component "${h}":`,g),w=`<span style="color:red;font-weight:bold;">Failed to import ${h}</span>`}c.innerHTML=r(w),c.removeAttribute("data-import");let _=c.querySelectorAll("[data-import]");if(_.length>0){let g=Array.from(_).map(p=>n(p,new Set(y)));await Promise.all(g)}this._scanStoreBinds(),this._scanAndFetchAPIBinds()},o=Array.from(l).map(c=>n(c,new Set));await Promise.all(o)},A._initSPARouter=function(){if(typeof window>"u"||typeof document>"u"||this._routerInitialized)return;this._routerInitialized=!0;let f=null,u=()=>{let o=(this.options.routerViewport||"main, #viewport, body").split(",").map(c=>c.trim());for(let c of o){let y=document.querySelector(c);if(y)return y}return document.body},l=async(n,o=!0)=>{try{this.options.debug&&console.log(`%c\u{1F6E3}\uFE0F [Dolphin Router]: Navigating to ${n}...`,"color: #3b82f6; font-weight: bold;"),f&&f.abort(),f=new AbortController;let c=f.signal,y=u();this.options.routerTransitions&&y&&(y.classList.add("dolphin-fade-out"),await new Promise(b=>setTimeout(b,150)));let h=await fetch(n,{signal:c});if(!h.ok)throw new Error(`HTTP ${h.status}`);let m=await h.text();f=null;let _=new DOMParser().parseFromString(m,"text/html");_.title&&(document.title=_.title);let g=_.querySelector(this.options.routerViewport||"main, #viewport, body"),p=u();g&&p?(p.innerHTML=g.innerHTML,Array.from(g.attributes).forEach(b=>{p.setAttribute(b.name,b.value)})):p&&(p.innerHTML=_.body.innerHTML),o&&window.history.pushState({dolphinSpa:!0,url:n},"",n),this.options.routerTransitions&&p&&(p.classList.remove("dolphin-fade-out"),p.classList.add("dolphin-fade-in"),setTimeout(()=>p.classList.remove("dolphin-fade-in"),300)),await this._resolveImports(p),this._scanStoreBinds(),this._scanAndFetchAPIBinds()}catch(c){if(c&&c.name==="AbortError")return;console.error("[Dolphin Router Error]: Failed to route page:",c),window.location.href=n}};if(this.addDomListener(document,"click",n=>{let o=n.target.closest("a");if(!o||!o.hasAttribute("data-spa")&&o.getAttribute("data-spa")!=="true")return;let c=o.getAttribute("href");!c||c.startsWith("#")||c.startsWith("javascript:")||c.startsWith("mailto:")||c.startsWith("tel:")||new URL(c,window.location.href).origin!==window.location.origin||(n.preventDefault(),l(c))}),this.addDomListener(window,"popstate",n=>{n.state&&n.state.dolphinSpa?l(n.state.url,!1):n.state===null&&l(window.location.pathname,!1)}),this.options.routerTransitions){let n=document.createElement("style");n.innerHTML=`
27
27
  .dolphin-fade-out {
28
28
  opacity: 0;
29
29
  transition: opacity 0.15s ease-in-out;
@@ -34,4 +34,4 @@ var DolphinModule=(()=>{var R=Object.defineProperty;var Y=Object.getOwnPropertyD
34
34
  main, #viewport, body {
35
35
  transition: opacity 0.15s ease-in-out;
36
36
  }
37
- `,document.head.appendChild(r)}}}var N=class{client;db;isOnline;memoryCache=new Map;memoryMutations=[];constructor(t){this.client=t,this.isOnline=typeof window<"u"&&typeof navigator<"u"?navigator.onLine:!0,this.initDB(),this.setupNetworkListeners()}initDB(){if(!(typeof indexedDB>"u"))try{let t=indexedDB.open("dolphin_offline",1);t.onupgradeneeded=e=>{let n=e.target.result;n.objectStoreNames.contains("cache")||n.createObjectStore("cache"),n.objectStoreNames.contains("mutations")||n.createObjectStore("mutations",{keyPath:"id",autoIncrement:!0})},t.onsuccess=e=>{this.db=e.target.result,this.isOnline&&this.syncMutations()}}catch(t){console.warn("[Dolphin Offline] Failed to initialize IndexedDB:",t)}}setupNetworkListeners(){typeof window>"u"||(this.client.addDomListener(window,"online",()=>{this.isOnline=!0,this.client._dispatch("network:status",{online:!0}),this.syncMutations()}),this.client.addDomListener(window,"offline",()=>{this.isOnline=!1,this.client._dispatch("network:status",{online:!1})}))}async getCache(t){return this.db?new Promise(e=>{try{let i=this.db.transaction("cache","readonly").objectStore("cache").get(t);i.onsuccess=()=>e(i.result?i.result.data:null),i.onerror=()=>e(null)}catch{e(null)}}):this.memoryCache.get(t)}async setCache(t,e){if(!this.db){this.memoryCache.set(t,e);return}return new Promise(n=>{try{let s=this.db.transaction("cache","readwrite");s.objectStore("cache").put({data:e,timestamp:Date.now()},t),s.oncomplete=()=>n(),s.onerror=()=>{console.warn("[Dolphin Offline] setCache write failed for key:",t),n()}}catch{n()}})}async queueMutation(t,e,n){let s={method:t,path:e,payload:n,timestamp:Date.now()};if(!this.db){this.memoryMutations.push(s);return}return new Promise(i=>{try{let a=this.db.transaction("mutations","readwrite");a.objectStore("mutations").add(s),a.oncomplete=()=>i(),a.onerror=()=>{console.warn("[Dolphin Offline] queueMutation write failed:",t,e),this.memoryMutations.push(s),i()}}catch{i()}})}async getMutations(){return this.db?new Promise(t=>{try{let s=this.db.transaction("mutations","readonly").objectStore("mutations").getAll();s.onsuccess=()=>t(s.result||[]),s.onerror=()=>t([])}catch{t([])}}):[...this.memoryMutations]}async removeMutation(t){if(!this.db){this.memoryMutations=this.memoryMutations.filter(e=>e.id!==t);return}return new Promise(e=>{try{let n=this.db.transaction("mutations","readwrite");n.objectStore("mutations").delete(t),n.oncomplete=()=>e(),n.onerror=()=>{console.warn("[Dolphin Offline] removeMutation failed for id:",t),e()}}catch{e()}})}async syncMutations(){let t=await this.getMutations();if(t.length!==0){console.log(`[Dolphin Offline] Syncing ${t.length} queued mutations...`);for(let e of t)try{await this.client.api.requestDirect(e.method,e.path,e.payload),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift()}catch(n){if(console.error(`[Dolphin Offline] Sync failed for mutation ${e.method} ${e.path}:`,n),n&&n.status&&n.status>=400&&n.status<500)console.warn("[Dolphin Offline] Discarding invalid mutation."),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift();else break}}}};function H(A){A._initOffline=function(){this.offline=new N(this)}}function it(A,t,e){let n=t.split(",");for(let s of n){let i=s.trim().split(":"),a=i[0],l=i[1];if(a==="required"){if(!A||A.trim()==="")return"This field is required"}else if(a==="email"){if(A&&A.trim()!==""&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(A))return"Please enter a valid email address"}else if(a==="min"){let _=parseInt(l,10);if(!A||A.length<_)return`Must be at least ${_} characters`}else if(a==="match"&&e&&A!==e[l])return`Must match ${l}`}return null}function F(A){A.validateField=it}function W(A){A.animateElement=function(t,e,n=300){if(typeof t.animate!="function"){t.classList.add(e),setTimeout(()=>t.classList.remove(e),n);return}e==="fade-in"?t.animate([{opacity:0,transform:"translateY(10px)"},{opacity:1,transform:"translateY(0)"}],{duration:n,easing:"ease-out"}):e==="fade-out"&&t.animate([{opacity:1,transform:"translateY(0)"},{opacity:0,transform:"translateY(10px)"}],{duration:n,easing:"ease-in"})},A.staggerListItems=function(t,e,n=50){if(typeof document>"u")return;t.querySelectorAll(e).forEach((i,a)=>{i.style.animationDelay=`${a*n}ms`,i.classList.add("staggered-item")})}}function j(A){A._initA11y=function(){typeof document>"u"||(this.addDomListener(document,"keydown",t=>{if(t.key!=="Tab")return;document.querySelectorAll("[data-rt-a11y-focus-trap]").forEach(n=>{if(n.style.display==="none"||n.hasAttribute("aria-hidden")&&n.getAttribute("aria-hidden")==="true")return;let i=Array.from(n.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'));if(i.length===0)return;let a=i[0],l=i[i.length-1];t.shiftKey?document.activeElement===a&&(l.focus(),t.preventDefault()):document.activeElement===l&&(a.focus(),t.preventDefault())})}),this.addDomListener(document,"keydown",t=>{if(!["ArrowUp","ArrowDown","Enter"].includes(t.key))return;document.querySelectorAll("[data-rt-keynav]").forEach(n=>{let s=Array.from(n.children);if(s.length===0)return;let i=s.findIndex(a=>a.classList.contains("active")||document.activeElement===a);t.key==="ArrowDown"?(i=(i+1)%s.length,s[i].focus(),s.forEach((a,l)=>{l===i?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="ArrowUp"?(i=(i-1+s.length)%s.length,s[i].focus(),s.forEach((a,l)=>{l===i?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="Enter"&&i!==-1&&(s[i].click(),t.preventDefault())})}))},A.autoAriaModal=function(t,e){e?(t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.setAttribute("aria-hidden","false"),t.focus()):t.setAttribute("aria-hidden","true")}}function B(A){A._initI18n=function(){if(this.i18n=this.i18n||{locale:"en",dicts:{}},typeof document>"u")return;if(document.querySelectorAll("[data-i18n-dict]").forEach(e=>{let n=e.getAttribute("data-i18n-dict");if(n)try{let s=JSON.parse(e.textContent||"{}");this.i18n.dicts[n]={...this.i18n.dicts[n]||{},...s}}catch(s){console.warn("[Dolphin i18n] Failed to parse dictionary for locale:",n,s)}}),!this.i18n.locale&&typeof navigator<"u"){let e=navigator.language.split("-")[0];this.i18n.dicts[e]&&(this.i18n.locale=e)}this.addDomListener(document,"click",e=>{let n=e.target.closest("[data-i18n-switch]");if(n){let s=n.getAttribute("data-i18n-switch");s&&this.setLocale(s)}}),this.translateDOM()},A.setLocale=function(t){this.i18n=this.i18n||{locale:"en",dicts:{}},this.i18n.locale=t,this.translateDOM(),this.publish("i18n/locale",t)},A.translateDOM=function(){if(typeof document>"u")return;this.i18n=this.i18n||{locale:"en",dicts:{}};let t=this.i18n.locale||"en",e=this.i18n.dicts[t]||{};document.querySelectorAll("[data-i18n-key]").forEach(s=>{let i=s.getAttribute("data-i18n-key");if(!i)return;let a=i.split(".").reduce((_,v)=>_?_[v]:null,e);a==null&&(a=i);let l=s.getAttribute("data-i18n-params");if(l)try{let _=JSON.parse(l),v=k=>k.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");for(let k in _){let h=v(k);a=a.replace(new RegExp(`\\{\\{${h}\\}\\}`,"g"),_[k])}}catch{}s.tagName==="INPUT"||s.tagName==="TEXTAREA"?s.placeholder=a:s.textContent=a})}}function V(A){A._initDragDrop=function(){typeof document>"u"||(this.addDomListener(document,"dragstart",t=>{let e=t.target.closest("[data-drag]");if(!e)return;let n=e.getAttribute("data-drag");n&&(t.dataTransfer.setData("text/plain",n),t.dataTransfer.effectAllowed="move",e.classList.add("dragging"))}),this.addDomListener(document,"dragend",t=>{let e=t.target.closest("[data-drag]");e&&e.classList.remove("dragging")}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-drop]");e&&(t.preventDefault(),e.classList.add("drag-over"))}),this.addDomListener(document,"dragleave",t=>{let e=t.target.closest("[data-drop]");e&&e.classList.remove("drag-over")}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-drop]");if(!e)return;t.preventDefault(),e.classList.remove("drag-over");let n=e.getAttribute("data-drop"),s=t.dataTransfer.getData("text/plain");if(n&&s)try{let i=JSON.parse(s);this.publish(n,i)}catch{this.publish(n,{value:s})}}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;t.preventDefault();let n=e.querySelector(".dragging");if(!n)return;let i=Array.from(e.querySelectorAll("[data-drag]:not(.dragging)")).find(a=>{let l=a.getBoundingClientRect();return t.clientY-l.top-l.height/2<0});i?e.insertBefore(n,i):e.appendChild(n)}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;let n=e.getAttribute("data-sortable");if(!n)return;let i=Array.from(e.querySelectorAll("[data-drag]")).map((a,l)=>{let _=a.getAttribute("data-drag");try{return{index:l,payload:JSON.parse(_||"{}")}}catch{return{index:l,payload:_}}});this.publish(n,i)}))}}function z(A){A._initCollab=function(){if(typeof document>"u")return;let t=new Map,e=new Map,n=5e3;this.addDomListener(document,"mousemove",s=>{document.querySelectorAll("[data-rt-cursor-share]").forEach(a=>{let l=a.getAttribute("data-rt-cursor-share");if(!l)return;let _=a.getBoundingClientRect(),v=(s.clientX-_.left)/_.width,k=(s.clientY-_.top)/_.height,h=Date.now();(!a._lastSent||h-a._lastSent>50)&&(a._lastSent=h,this.pubPush(`collab/${l}/cursor/${this.deviceId}`,{deviceId:this.deviceId,x:v,y:k}))})}),this.addDomListener(document,"input",s=>{let i=s.target.getAttribute("data-rt-typing");if(!i)return;let a=i,l=_=>{this.pubPush(`collab/${a}/typing/${this.deviceId}`,{deviceId:this.deviceId,typing:_})};s.target._isTyping||(s.target._isTyping=!0,l(!0)),s.target._typingTimer&&clearTimeout(s.target._typingTimer),s.target._typingTimer=setTimeout(()=>{s.target._isTyping=!1,s.target._typingTimer=null,l(!1)},2e3)}),this.addDomListener(document,"input",s=>{let i=s.target.getAttribute("data-rt-crdt");if(!i)return;let a=i,l=s.target.value,_=Date.now();this.publish(`collab/${a}/crdt`,{deviceId:this.deviceId,value:l,timestamp:_,cursorPos:s.target.selectionStart})}),this.subscribe("collab/+/cursor/+",(s,i)=>{let a=i.split("/"),l=a[1],_=a[3];if(_===this.deviceId)return;let v=document.querySelector(`[data-rt-cursor-share="${l}"]`);if(!v)return;let k=`${l}::${_}`,h=t.get(k);(!h||!document.contains(h))&&(h=document.createElement("div"),h.className=`rt-cursor rt-cursor-${_}`,h.style.position="absolute",h.style.width="10px",h.style.height="10px",h.style.borderRadius="50%",h.style.backgroundColor="#"+Math.floor(Math.random()*16777215).toString(16).padStart(6,"0"),h.style.pointerEvents="none",v.appendChild(h),t.set(k,h));let d=v.getBoundingClientRect();h.style.left=s.x*d.width+"px",h.style.top=s.y*d.height+"px",e.has(k)&&clearTimeout(e.get(k)),e.set(k,setTimeout(()=>{let c=t.get(k);c&&c.parentNode&&c.parentNode.removeChild(c),t.delete(k),e.delete(k)},n))}),this.subscribe("collab/+/crdt",(s,i)=>{if(s.deviceId===this.deviceId)return;let l=i.split("/")[1];document.querySelectorAll(`[data-rt-crdt="${l}"]`).forEach(v=>{if(!v._lastUpdate||s.timestamp>v._lastUpdate){v._lastUpdate=s.timestamp;let k=v.selectionStart;v.value=s.value,document.activeElement===v&&v.setSelectionRange(k,k)}})}),this._collabCleanup=()=>{e.forEach(s=>clearTimeout(s)),e.clear(),t.forEach(s=>{s&&s.parentNode&&s.parentNode.removeChild(s)}),t.clear()}}}function K(A){A.registerServiceWorker=async function(t="/sw.js"){if(typeof navigator>"u"||!("serviceWorker"in navigator))return console.warn("[Dolphin PWA] Service Workers are not supported in this browser."),null;try{let e=await navigator.serviceWorker.register(t);return console.log("[Dolphin PWA] Service Worker registered successfully with scope:",e.scope),e}catch(e){return console.error("[Dolphin PWA] Service Worker registration failed:",e),null}},A.subscribePushNotifications=async function(t){if(typeof window>"u"||!("serviceWorker"in navigator)||!("PushManager"in window))return console.warn("[Dolphin PWA] Push notifications are not supported in this browser."),null;try{let e=await navigator.serviceWorker.ready,n=await e.pushManager.getSubscription();if(!n){let s="=".repeat((4-t.length%4)%4),i=(t+s).replace(/\-/g,"+").replace(/_/g,"/"),a=window.atob(i),l=new Uint8Array(a.length);for(let _=0;_<a.length;++_)l[_]=a.charCodeAt(_);n=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:l})}return console.log("[Dolphin PWA] Subscribed to push notifications:",n),n}catch(e){return console.error("[Dolphin PWA] Push notification subscription failed:",e),null}}}var O=class{static render(t){if(typeof document>"u")throw new Error("DolphinTestUtils.render requires a DOM document environment to execute.");let e=document.createElement("div");return e.innerHTML=t,document.body.appendChild(e),{container:e,find:n=>e.querySelector(n),fireEvent:(n,s)=>{let i=document.createEvent("Event");i.initEvent(s,!0,!0),n.dispatchEvent(i)}}}static mockWebSocket(){let t=[],e={readyState:1,send:n=>{t.push(n)},close:jest.fn(),onopen:jest.fn(),onmessage:jest.fn(),onclose:jest.fn(),onerror:jest.fn(),sentMessages:t};return global.WebSocket=class{static OPEN=1;readyState=e.readyState;send=e.send;close=e.close;set onopen(n){e.onopen=n}get onopen(){return e.onopen}set onmessage(n){e.onmessage=n}get onmessage(){return e.onmessage}set onclose(n){e.onclose=n}get getonclose(){return e.onclose}constructor(){setTimeout(()=>e.onopen&&e.onopen(),0)}},e}static simulateClick(t){let e={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.click||[]).forEach(s=>s(e))}static simulateChange(t,e){t.value=e;let n={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.change||[]).forEach(i=>i(n))}};function X(A){A.testing=O}U(C.prototype);H(C.prototype);F(C.prototype);W(C.prototype);j(C.prototype);B(C.prototype);V(C.prototype);z(C.prototype);K(C.prototype);X(C.prototype);typeof window<"u"&&(window.DolphinClient=C,document.addEventListener("DOMContentLoaded",()=>{if(!window.dolphin){let A=document.querySelector('script[src*="dolphin-client"]'),t=A?A.getAttribute("data-debug")==="true":!1,e=new C(void 0,void 0,{debug:t});window.dolphin=e,t&&(console.log("%c\u{1F42C} [Dolphin Client] Auto-initialized local reactive engine!","color: #06b6d4; font-weight: bold; font-size: 14px;"),console.log('%c\u{1F449} Tip: You can access the client instance via "window.dolphin" in console.',"color: #94a3b8; font-style: italic;")),document.querySelector('[data-store-write="app.username"]')&&e.setStoreState("app","username","\u0928\u092E\u0938\u094D\u0924\u0947 \u0938\u093E\u0925\u0940!")}}));return st(rt);})();
37
+ `,document.head.appendChild(n)}}}var H=class{client;db;isOnline;memoryCache=new Map;memoryMutations=[];constructor(t){this.client=t,this.isOnline=typeof window<"u"&&typeof navigator<"u"?navigator.onLine:!0,this.initDB(),this.setupNetworkListeners()}initDB(){if(!(typeof indexedDB>"u"))try{let t=indexedDB.open("dolphin_offline",1);t.onupgradeneeded=e=>{let s=e.target.result;s.objectStoreNames.contains("cache")||s.createObjectStore("cache"),s.objectStoreNames.contains("mutations")||s.createObjectStore("mutations",{keyPath:"id",autoIncrement:!0})},t.onsuccess=e=>{this.db=e.target.result,this.isOnline&&this.syncMutations()}}catch(t){console.warn("[Dolphin Offline] Failed to initialize IndexedDB:",t)}}setupNetworkListeners(){typeof window>"u"||(this.client.addDomListener(window,"online",()=>{this.isOnline=!0,this.client._dispatch("network:status",{online:!0}),this.syncMutations()}),this.client.addDomListener(window,"offline",()=>{this.isOnline=!1,this.client._dispatch("network:status",{online:!1})}))}async getCache(t){return this.db?new Promise(e=>{try{let r=this.db.transaction("cache","readonly").objectStore("cache").get(t);r.onsuccess=()=>e(r.result?r.result.data:null),r.onerror=()=>e(null)}catch{e(null)}}):this.memoryCache.get(t)}async setCache(t,e){if(!this.db){this.memoryCache.set(t,e);return}return new Promise(s=>{try{let i=this.db.transaction("cache","readwrite");i.objectStore("cache").put({data:e,timestamp:Date.now()},t),i.oncomplete=()=>s(),i.onerror=()=>{console.warn("[Dolphin Offline] setCache write failed for key:",t),s()}}catch{s()}})}async queueMutation(t,e,s){let i={method:t,path:e,payload:s,timestamp:Date.now()};if(!this.db){this.memoryMutations.push(i);return}return new Promise(r=>{try{let a=this.db.transaction("mutations","readwrite");a.objectStore("mutations").add(i),a.oncomplete=()=>r(),a.onerror=()=>{console.warn("[Dolphin Offline] queueMutation write failed:",t,e),this.memoryMutations.push(i),r()}}catch{r()}})}async getMutations(){return this.db?new Promise(t=>{try{let i=this.db.transaction("mutations","readonly").objectStore("mutations").getAll();i.onsuccess=()=>t(i.result||[]),i.onerror=()=>t([])}catch{t([])}}):[...this.memoryMutations]}async removeMutation(t){if(!this.db){this.memoryMutations=this.memoryMutations.filter(e=>e.id!==t);return}return new Promise(e=>{try{let s=this.db.transaction("mutations","readwrite");s.objectStore("mutations").delete(t),s.oncomplete=()=>e(),s.onerror=()=>{console.warn("[Dolphin Offline] removeMutation failed for id:",t),e()}}catch{e()}})}async syncMutations(){let t=await this.getMutations();if(t.length!==0){console.log(`[Dolphin Offline] Syncing ${t.length} queued mutations...`);for(let e of t)try{await this.client.api.requestDirect(e.method,e.path,e.payload),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift()}catch(s){if(console.error(`[Dolphin Offline] Sync failed for mutation ${e.method} ${e.path}:`,s),s&&s.status&&s.status>=400&&s.status<500)console.warn("[Dolphin Offline] Discarding invalid mutation."),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift();else break}}}};function W(A){A._initOffline=function(){this.offline=new H(this)}}function it(A,t,e){let s=t.split(",");for(let i of s){let r=i.trim().split(":"),a=r[0],d=r[1];if(a==="required"){if(!A||A.trim()==="")return"This field is required"}else if(a==="email"){if(A&&A.trim()!==""&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(A))return"Please enter a valid email address"}else if(a==="min"){let E=parseInt(d,10);if(!A||A.length<E)return`Must be at least ${E} characters`}else if(a==="match"&&e&&A!==e[d])return`Must match ${d}`}return null}function j(A){A.validateField=it}function z(A){A.animateElement=function(t,e,s=300){if(typeof t.animate!="function"){t.classList.add(e),setTimeout(()=>t.classList.remove(e),s);return}e==="fade-in"?t.animate([{opacity:0,transform:"translateY(10px)"},{opacity:1,transform:"translateY(0)"}],{duration:s,easing:"ease-out"}):e==="fade-out"&&t.animate([{opacity:1,transform:"translateY(0)"},{opacity:0,transform:"translateY(10px)"}],{duration:s,easing:"ease-in"})},A.staggerListItems=function(t,e,s=50){if(typeof document>"u")return;t.querySelectorAll(e).forEach((r,a)=>{r.style.animationDelay=`${a*s}ms`,r.classList.add("staggered-item")})}}function V(A){A._initA11y=function(){typeof document>"u"||(this.addDomListener(document,"keydown",t=>{if(t.key!=="Tab")return;document.querySelectorAll("[data-rt-a11y-focus-trap]").forEach(s=>{if(s.style.display==="none"||s.hasAttribute("aria-hidden")&&s.getAttribute("aria-hidden")==="true")return;let r=Array.from(s.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'));if(r.length===0)return;let a=r[0],d=r[r.length-1];t.shiftKey?document.activeElement===a&&(d.focus(),t.preventDefault()):document.activeElement===d&&(a.focus(),t.preventDefault())})}),this.addDomListener(document,"keydown",t=>{if(!["ArrowUp","ArrowDown","Enter"].includes(t.key))return;document.querySelectorAll("[data-rt-keynav]").forEach(s=>{let i=Array.from(s.children);if(i.length===0)return;let r=i.findIndex(a=>a.classList.contains("active")||document.activeElement===a);t.key==="ArrowDown"?(r=(r+1)%i.length,i[r].focus(),i.forEach((a,d)=>{d===r?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="ArrowUp"?(r=(r-1+i.length)%i.length,i[r].focus(),i.forEach((a,d)=>{d===r?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="Enter"&&r!==-1&&(i[r].click(),t.preventDefault())})}))},A.autoAriaModal=function(t,e){e?(t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.setAttribute("aria-hidden","false"),t.focus()):t.setAttribute("aria-hidden","true")}}function K(A){A._initI18n=function(){if(this.i18n=this.i18n||{locale:"en",dicts:{}},typeof document>"u")return;if(document.querySelectorAll("[data-i18n-dict]").forEach(e=>{let s=e.getAttribute("data-i18n-dict");if(s)try{let i=JSON.parse(e.textContent||"{}");this.i18n.dicts[s]={...this.i18n.dicts[s]||{},...i}}catch(i){console.warn("[Dolphin i18n] Failed to parse dictionary for locale:",s,i)}}),!this.i18n.locale&&typeof navigator<"u"){let e=navigator.language.split("-")[0];this.i18n.dicts[e]&&(this.i18n.locale=e)}this.addDomListener(document,"click",e=>{let s=e.target.closest("[data-i18n-switch]");if(s){let i=s.getAttribute("data-i18n-switch");i&&this.setLocale(i)}}),this.translateDOM()},A.setLocale=function(t){this.i18n=this.i18n||{locale:"en",dicts:{}},this.i18n.locale=t,this.translateDOM(),this.publish("i18n/locale",t)},A.translateDOM=function(){if(typeof document>"u")return;this.i18n=this.i18n||{locale:"en",dicts:{}};let t=this.i18n.locale||"en",e=this.i18n.dicts[t]||{};document.querySelectorAll("[data-i18n-key]").forEach(i=>{let r=i.getAttribute("data-i18n-key");if(!r)return;let a=r.split(".").reduce((E,S)=>E?E[S]:null,e);a==null&&(a=r);let d=i.getAttribute("data-i18n-params");if(d)try{let E=JSON.parse(d),S=k=>k.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");for(let k in E){let f=S(k);a=a.replace(new RegExp(`\\{\\{${f}\\}\\}`,"g"),E[k])}}catch{}i.tagName==="INPUT"||i.tagName==="TEXTAREA"?i.placeholder=a:i.textContent=a})}}function X(A){A._initDragDrop=function(){typeof document>"u"||(this.addDomListener(document,"dragstart",t=>{let e=t.target.closest("[data-drag]");if(!e)return;let s=e.getAttribute("data-drag");s&&(t.dataTransfer.setData("text/plain",s),t.dataTransfer.effectAllowed="move",e.classList.add("dragging"))}),this.addDomListener(document,"dragend",t=>{let e=t.target.closest("[data-drag]");e&&e.classList.remove("dragging")}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-drop]");e&&(t.preventDefault(),e.classList.add("drag-over"))}),this.addDomListener(document,"dragleave",t=>{let e=t.target.closest("[data-drop]");e&&e.classList.remove("drag-over")}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-drop]");if(!e)return;t.preventDefault(),e.classList.remove("drag-over");let s=e.getAttribute("data-drop"),i=t.dataTransfer.getData("text/plain");if(s&&i)try{let r=JSON.parse(i);this.publish(s,r)}catch{this.publish(s,{value:i})}}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;t.preventDefault();let s=e.querySelector(".dragging");if(!s)return;let r=Array.from(e.querySelectorAll("[data-drag]:not(.dragging)")).find(a=>{let d=a.getBoundingClientRect();return t.clientY-d.top-d.height/2<0});r?e.insertBefore(s,r):e.appendChild(s)}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;let s=e.getAttribute("data-sortable");if(!s)return;let r=Array.from(e.querySelectorAll("[data-drag]")).map((a,d)=>{let E=a.getAttribute("data-drag");try{return{index:d,payload:JSON.parse(E||"{}")}}catch{return{index:d,payload:E}}});this.publish(s,r)}))}}function J(A){A._initCollab=function(){if(typeof document>"u")return;let t=new Map,e=new Map,s=5e3;this.addDomListener(document,"mousemove",i=>{document.querySelectorAll("[data-rt-cursor-share]").forEach(a=>{let d=a.getAttribute("data-rt-cursor-share");if(!d)return;let E=a.getBoundingClientRect(),S=(i.clientX-E.left)/E.width,k=(i.clientY-E.top)/E.height,f=Date.now();(!a._lastSent||f-a._lastSent>50)&&(a._lastSent=f,this.pubPush(`collab/${d}/cursor/${this.deviceId}`,{deviceId:this.deviceId,x:S,y:k}))})}),this.addDomListener(document,"input",i=>{let r=i.target.getAttribute("data-rt-typing");if(!r)return;let a=r,d=E=>{this.pubPush(`collab/${a}/typing/${this.deviceId}`,{deviceId:this.deviceId,typing:E})};i.target._isTyping||(i.target._isTyping=!0,d(!0)),i.target._typingTimer&&clearTimeout(i.target._typingTimer),i.target._typingTimer=setTimeout(()=>{i.target._isTyping=!1,i.target._typingTimer=null,d(!1)},2e3)}),this.addDomListener(document,"input",i=>{let r=i.target.getAttribute("data-rt-crdt");if(!r)return;let a=r,d=i.target.value,E=Date.now();this.publish(`collab/${a}/crdt`,{deviceId:this.deviceId,value:d,timestamp:E,cursorPos:i.target.selectionStart})}),this.subscribe("collab/+/cursor/+",(i,r)=>{let a=r.split("/"),d=a[1],E=a[3];if(E===this.deviceId)return;let S=document.querySelector(`[data-rt-cursor-share="${d}"]`);if(!S)return;let k=`${d}::${E}`,f=t.get(k);(!f||!document.contains(f))&&(f=document.createElement("div"),f.className=`rt-cursor rt-cursor-${E}`,f.style.position="absolute",f.style.width="10px",f.style.height="10px",f.style.borderRadius="50%",f.style.backgroundColor="#"+Math.floor(Math.random()*16777215).toString(16).padStart(6,"0"),f.style.pointerEvents="none",S.appendChild(f),t.set(k,f));let u=S.getBoundingClientRect();f.style.left=i.x*u.width+"px",f.style.top=i.y*u.height+"px",e.has(k)&&clearTimeout(e.get(k)),e.set(k,setTimeout(()=>{let l=t.get(k);l&&l.parentNode&&l.parentNode.removeChild(l),t.delete(k),e.delete(k)},s))}),this.subscribe("collab/+/crdt",(i,r)=>{if(i.deviceId===this.deviceId)return;let d=r.split("/")[1];document.querySelectorAll(`[data-rt-crdt="${d}"]`).forEach(S=>{if(!S._lastUpdate||i.timestamp>S._lastUpdate){S._lastUpdate=i.timestamp;let k=S.selectionStart;S.value=i.value,document.activeElement===S&&S.setSelectionRange(k,k)}})}),this._collabCleanup=()=>{e.forEach(i=>clearTimeout(i)),e.clear(),t.forEach(i=>{i&&i.parentNode&&i.parentNode.removeChild(i)}),t.clear()}}}function Q(A){A.registerServiceWorker=async function(t="/sw.js"){if(typeof navigator>"u"||!("serviceWorker"in navigator))return console.warn("[Dolphin PWA] Service Workers are not supported in this browser."),null;try{let e=await navigator.serviceWorker.register(t);return console.log("[Dolphin PWA] Service Worker registered successfully with scope:",e.scope),e}catch(e){return console.error("[Dolphin PWA] Service Worker registration failed:",e),null}},A.subscribePushNotifications=async function(t){if(typeof window>"u"||!("serviceWorker"in navigator)||!("PushManager"in window))return console.warn("[Dolphin PWA] Push notifications are not supported in this browser."),null;try{let e=await navigator.serviceWorker.ready,s=await e.pushManager.getSubscription();if(!s){let i="=".repeat((4-t.length%4)%4),r=(t+i).replace(/\-/g,"+").replace(/_/g,"/"),a=window.atob(r),d=new Uint8Array(a.length);for(let E=0;E<a.length;++E)d[E]=a.charCodeAt(E);s=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:d})}return console.log("[Dolphin PWA] Subscribed to push notifications:",s),s}catch(e){return console.error("[Dolphin PWA] Push notification subscription failed:",e),null}}}var F=class{static render(t){if(typeof document>"u")throw new Error("DolphinTestUtils.render requires a DOM document environment to execute.");let e=document.createElement("div");return e.innerHTML=t,document.body.appendChild(e),{container:e,find:s=>e.querySelector(s),fireEvent:(s,i)=>{let r=document.createEvent("Event");r.initEvent(i,!0,!0),s.dispatchEvent(r)}}}static mockWebSocket(){let t=[],e={readyState:1,send:s=>{t.push(s)},close:jest.fn(),onopen:jest.fn(),onmessage:jest.fn(),onclose:jest.fn(),onerror:jest.fn(),sentMessages:t};return global.WebSocket=class{static OPEN=1;readyState=e.readyState;send=e.send;close=e.close;set onopen(s){e.onopen=s}get onopen(){return e.onopen}set onmessage(s){e.onmessage=s}get onmessage(){return e.onmessage}set onclose(s){e.onclose=s}get getonclose(){return e.onclose}constructor(){setTimeout(()=>e.onopen&&e.onopen(),0)}},e}static simulateClick(t){let e={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.click||[]).forEach(i=>i(e))}static simulateChange(t,e){t.value=e;let s={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.change||[]).forEach(r=>r(s))}};function Y(A){A.testing=F}B(C.prototype);W(C.prototype);j(C.prototype);z(C.prototype);V(C.prototype);K(C.prototype);X(C.prototype);J(C.prototype);Q(C.prototype);Y(C.prototype);typeof window<"u"&&(window.DolphinClient=C,document.addEventListener("DOMContentLoaded",()=>{if(!window.dolphin){let A=document.querySelector('script[src*="dolphin-client"]'),t=A?A.getAttribute("data-debug")==="true":!1,e=new C(void 0,void 0,{debug:t});window.dolphin=e,t&&(console.log("%c\u{1F42C} [Dolphin Client] Auto-initialized local reactive engine!","color: #06b6d4; font-weight: bold; font-size: 14px;"),console.log('%c\u{1F449} Tip: You can access the client instance via "window.dolphin" in console.',"color: #94a3b8; font-style: italic;")),document.querySelector('[data-store-write="app.username"]')&&e.setStoreState("app","username","\u0928\u092E\u0938\u094D\u0924\u0947 \u0938\u093E\u0925\u0940!")}}));return st(rt);})();
package/dist/index.cjs CHANGED
@@ -41,7 +41,7 @@ var APIHandler = class {
41
41
  target.patch = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PATCH", pathOrBody, bodyOrOptions, options) : this.request("PATCH", joined, pathOrBody, bodyOrOptions);
42
42
  target.del = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("DELETE", pathOrOptions, null, options) : this.request("DELETE", joined, null, pathOrOptions);
43
43
  target.request = (method, subPath, body, options) => {
44
- const finalPath = subPath ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : joined;
44
+ const finalPath = subPath ? joined ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : subPath : joined;
45
45
  return this.request(method, finalPath, body, options);
46
46
  };
47
47
  target.requestDirect = (method, path, body, options) => {
@@ -225,8 +225,9 @@ var APIHandler = class {
225
225
  const _isRetry = options._isRetry === true;
226
226
  let finalMethod = method.toUpperCase();
227
227
  let finalBody = body;
228
+ const hasBody = !["GET", "HEAD"].includes(finalMethod);
228
229
  const headers = {
229
- "Content-Type": "application/json",
230
+ ...hasBody ? { "Content-Type": "application/json" } : {},
230
231
  ...options.headers || {}
231
232
  };
232
233
  if (["PUT", "PATCH", "DELETE"].includes(finalMethod)) {
@@ -1376,6 +1377,86 @@ function attachDOMBinding(clientProto) {
1376
1377
  console.error("%cFailed Expression:", "color: #3b82f6; font-style: italic;", expression);
1377
1378
  }
1378
1379
  };
1380
+ clientProto._showAlert = function(id, duration) {
1381
+ if (typeof document === "undefined") return;
1382
+ const el = document.getElementById(id) || document.querySelector(`[id="${id}"]`);
1383
+ if (!el) return;
1384
+ el.removeAttribute("hidden");
1385
+ el.style.display = "";
1386
+ if (duration && duration > 0) {
1387
+ setTimeout(() => {
1388
+ el.setAttribute("hidden", "");
1389
+ }, duration);
1390
+ }
1391
+ };
1392
+ clientProto._showToast = function(message, type = "success") {
1393
+ if (typeof document === "undefined") return;
1394
+ const colors = {
1395
+ success: { bg: "rgba(16,185,129,0.15)", border: "#10b981", icon: "\u2705" },
1396
+ error: { bg: "rgba(239,68,68,0.15)", border: "#ef4444", icon: "\u274C" },
1397
+ info: { bg: "rgba(59,130,246,0.15)", border: "#3b82f6", icon: "\u2139\uFE0F" }
1398
+ };
1399
+ const c = colors[type] || colors.success;
1400
+ const toast = document.createElement("div");
1401
+ toast.setAttribute("data-dolphin-toast", "");
1402
+ const existing = document.querySelectorAll("[data-dolphin-toast]");
1403
+ const offset = existing.length * 72;
1404
+ toast.style.cssText = [
1405
+ "position:fixed",
1406
+ `bottom:${24 + offset}px`,
1407
+ "right:24px",
1408
+ "z-index:2147483647",
1409
+ `background:${c.bg}`,
1410
+ `border:1px solid ${c.border}`,
1411
+ "color:#fff",
1412
+ "padding:14px 20px",
1413
+ "border-radius:14px",
1414
+ "font-size:14px",
1415
+ "font-weight:600",
1416
+ "font-family:system-ui,sans-serif",
1417
+ "box-shadow:0 8px 32px rgba(0,0,0,0.4)",
1418
+ "display:flex",
1419
+ "align-items:center",
1420
+ "gap:10px",
1421
+ "max-width:380px",
1422
+ "word-break:break-word",
1423
+ "transform:translateY(80px)",
1424
+ "opacity:0",
1425
+ "transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1),opacity 0.3s ease",
1426
+ "pointer-events:auto",
1427
+ "backdrop-filter:blur(12px)"
1428
+ ].join(";");
1429
+ toast.innerHTML = `<span style="font-size:18px">${c.icon}</span><span>${message}</span>`;
1430
+ document.body.appendChild(toast);
1431
+ const restack = () => {
1432
+ const remaining = document.querySelectorAll("[data-dolphin-toast]");
1433
+ remaining.forEach((el, i) => {
1434
+ el.style.bottom = `${24 + i * 72}px`;
1435
+ });
1436
+ };
1437
+ const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
1438
+ raf(() => raf(() => {
1439
+ toast.style.transform = "translateY(0)";
1440
+ toast.style.opacity = "1";
1441
+ }));
1442
+ const removeToast = () => {
1443
+ clearTimeout(toast._removeTimer);
1444
+ if (toast.parentNode) {
1445
+ toast.parentNode.removeChild(toast);
1446
+ restack();
1447
+ }
1448
+ };
1449
+ const hideTimer = setTimeout(() => {
1450
+ toast.style.transform = "translateY(80px)";
1451
+ toast.style.opacity = "0";
1452
+ toast._removeTimer = setTimeout(removeToast, 400);
1453
+ }, 3500);
1454
+ toast._hideTimer = hideTimer;
1455
+ toast.addEventListener("click", () => {
1456
+ clearTimeout(toast._hideTimer);
1457
+ removeToast();
1458
+ }, { once: true });
1459
+ };
1379
1460
  clientProto._initDOMBinding = function() {
1380
1461
  if (this._domInitialized) return;
1381
1462
  this._domInitialized = true;
@@ -1474,6 +1555,11 @@ function attachDOMBinding(clientProto) {
1474
1555
  resolvedTopic = resolvedTopic.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1475
1556
  }
1476
1557
  this.publish(resolvedTopic, data);
1558
+ const rtSuccessId = e.target.getAttribute("data-rt-api-success");
1559
+ if (rtSuccessId) {
1560
+ const rtSuccessEl = document.getElementById(rtSuccessId);
1561
+ if (rtSuccessEl) this._showToast(rtSuccessEl.textContent || rtSuccessEl.innerText || "", "success");
1562
+ }
1477
1563
  } else if (apiTarget) {
1478
1564
  let resolvedTarget = apiTarget;
1479
1565
  for (const k in parentCtx) {
@@ -1490,16 +1576,40 @@ function attachDOMBinding(clientProto) {
1490
1576
  const result = await this.api.request(method, path, data);
1491
1577
  const resultBind = e.target.getAttribute("data-api-result");
1492
1578
  if (resultBind) this._updateDOM(resultBind, result);
1579
+ const successToast = e.target.getAttribute("data-api-toast");
1580
+ if (successToast) this._showToast(successToast, "success");
1581
+ const successElId = e.target.getAttribute("data-rt-api-success");
1582
+ if (successElId) {
1583
+ const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
1584
+ this._showAlert(successElId, duration);
1585
+ const errElId = e.target.getAttribute("data-rt-api-error");
1586
+ if (errElId) {
1587
+ const errEl = document.getElementById(errElId);
1588
+ if (errEl) errEl.setAttribute("hidden", "");
1589
+ }
1590
+ }
1493
1591
  const redirect = e.target.getAttribute("data-api-redirect");
1494
1592
  if (redirect) window.location.href = redirect;
1495
1593
  if (e.target.hasAttribute("data-api-reload")) window.location.reload();
1496
1594
  } catch (err) {
1497
1595
  console.error("[Dolphin] API Submit Error:", err);
1596
+ const errorToast = e.target.getAttribute("data-api-error-toast");
1597
+ if (errorToast) this._showToast(errorToast, "error");
1598
+ const errorElId = e.target.getAttribute("data-rt-api-error");
1599
+ if (errorElId) {
1600
+ const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
1601
+ this._showAlert(errorElId, duration);
1602
+ const sucElId = e.target.getAttribute("data-rt-api-success");
1603
+ if (sucElId) {
1604
+ const sucEl = document.getElementById(sucElId);
1605
+ if (sucEl) sucEl.setAttribute("hidden", "");
1606
+ }
1607
+ }
1498
1608
  }
1499
1609
  }
1500
1610
  }
1501
1611
  });
1502
- const INTERACTION_EVENTS = ["click", "change", "submit", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1612
+ const INTERACTION_EVENTS = ["click", "change", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1503
1613
  INTERACTION_EVENTS.forEach((evtName) => {
1504
1614
  this.addDomListener(document, evtName, async (e) => {
1505
1615
  if (!e.target || !e.target.closest) return;
@@ -1530,7 +1640,12 @@ function attachDOMBinding(clientProto) {
1530
1640
  const apiTarget = apiBtn.getAttribute(`data-api-${evtName}`);
1531
1641
  const actionData = apiBtn.getAttribute("data-api-payload");
1532
1642
  const parentCtx = this.getClosestContext(apiBtn) || {};
1533
- const parts = apiTarget.trim().split(" ");
1643
+ let resolvedApiTarget = apiTarget;
1644
+ for (const k in parentCtx) {
1645
+ const escapedK = escapeRegExp(k);
1646
+ resolvedApiTarget = resolvedApiTarget.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1647
+ }
1648
+ const parts = resolvedApiTarget.trim().split(" ");
1534
1649
  const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
1535
1650
  const path = parts.length > 1 ? parts[1] : parts[0];
1536
1651
  let payload = null;
@@ -1550,11 +1665,35 @@ function attachDOMBinding(clientProto) {
1550
1665
  const result = await this.api.request(method, path, payload);
1551
1666
  const resultBind = apiBtn.getAttribute("data-api-result");
1552
1667
  if (resultBind) this._updateDOM(resultBind, result);
1668
+ const successToast = apiBtn.getAttribute("data-api-toast");
1669
+ if (successToast) this._showToast(successToast, "success");
1670
+ const successElId = apiBtn.getAttribute("data-rt-api-success");
1671
+ if (successElId) {
1672
+ const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
1673
+ this._showAlert(successElId, duration);
1674
+ const errElId = apiBtn.getAttribute("data-rt-api-error");
1675
+ if (errElId) {
1676
+ const errEl = document.getElementById(errElId);
1677
+ if (errEl) errEl.setAttribute("hidden", "");
1678
+ }
1679
+ }
1553
1680
  const redirect = apiBtn.getAttribute("data-api-redirect");
1554
1681
  if (redirect) window.location.href = redirect;
1555
1682
  if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
1556
1683
  } catch (err) {
1557
1684
  console.error(`[Dolphin] API ${evtName} Error:`, err);
1685
+ const errorToast = apiBtn.getAttribute("data-api-error-toast");
1686
+ if (errorToast) this._showToast(errorToast, "error");
1687
+ const errorElId = apiBtn.getAttribute("data-rt-api-error");
1688
+ if (errorElId) {
1689
+ const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
1690
+ this._showAlert(errorElId, duration);
1691
+ const sucElId = apiBtn.getAttribute("data-rt-api-success");
1692
+ if (sucElId) {
1693
+ const sucEl = document.getElementById(sucElId);
1694
+ if (sucEl) sucEl.setAttribute("hidden", "");
1695
+ }
1696
+ }
1558
1697
  }
1559
1698
  }
1560
1699
  const storeActionBtn = e.target.closest(`[data-store-${evtName}]`);
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ var APIHandler = class {
16
16
  target.patch = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PATCH", pathOrBody, bodyOrOptions, options) : this.request("PATCH", joined, pathOrBody, bodyOrOptions);
17
17
  target.del = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("DELETE", pathOrOptions, null, options) : this.request("DELETE", joined, null, pathOrOptions);
18
18
  target.request = (method, subPath, body, options) => {
19
- const finalPath = subPath ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : joined;
19
+ const finalPath = subPath ? joined ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : subPath : joined;
20
20
  return this.request(method, finalPath, body, options);
21
21
  };
22
22
  target.requestDirect = (method, path, body, options) => {
@@ -200,8 +200,9 @@ var APIHandler = class {
200
200
  const _isRetry = options._isRetry === true;
201
201
  let finalMethod = method.toUpperCase();
202
202
  let finalBody = body;
203
+ const hasBody = !["GET", "HEAD"].includes(finalMethod);
203
204
  const headers = {
204
- "Content-Type": "application/json",
205
+ ...hasBody ? { "Content-Type": "application/json" } : {},
205
206
  ...options.headers || {}
206
207
  };
207
208
  if (["PUT", "PATCH", "DELETE"].includes(finalMethod)) {
@@ -1351,6 +1352,86 @@ function attachDOMBinding(clientProto) {
1351
1352
  console.error("%cFailed Expression:", "color: #3b82f6; font-style: italic;", expression);
1352
1353
  }
1353
1354
  };
1355
+ clientProto._showAlert = function(id, duration) {
1356
+ if (typeof document === "undefined") return;
1357
+ const el = document.getElementById(id) || document.querySelector(`[id="${id}"]`);
1358
+ if (!el) return;
1359
+ el.removeAttribute("hidden");
1360
+ el.style.display = "";
1361
+ if (duration && duration > 0) {
1362
+ setTimeout(() => {
1363
+ el.setAttribute("hidden", "");
1364
+ }, duration);
1365
+ }
1366
+ };
1367
+ clientProto._showToast = function(message, type = "success") {
1368
+ if (typeof document === "undefined") return;
1369
+ const colors = {
1370
+ success: { bg: "rgba(16,185,129,0.15)", border: "#10b981", icon: "\u2705" },
1371
+ error: { bg: "rgba(239,68,68,0.15)", border: "#ef4444", icon: "\u274C" },
1372
+ info: { bg: "rgba(59,130,246,0.15)", border: "#3b82f6", icon: "\u2139\uFE0F" }
1373
+ };
1374
+ const c = colors[type] || colors.success;
1375
+ const toast = document.createElement("div");
1376
+ toast.setAttribute("data-dolphin-toast", "");
1377
+ const existing = document.querySelectorAll("[data-dolphin-toast]");
1378
+ const offset = existing.length * 72;
1379
+ toast.style.cssText = [
1380
+ "position:fixed",
1381
+ `bottom:${24 + offset}px`,
1382
+ "right:24px",
1383
+ "z-index:2147483647",
1384
+ `background:${c.bg}`,
1385
+ `border:1px solid ${c.border}`,
1386
+ "color:#fff",
1387
+ "padding:14px 20px",
1388
+ "border-radius:14px",
1389
+ "font-size:14px",
1390
+ "font-weight:600",
1391
+ "font-family:system-ui,sans-serif",
1392
+ "box-shadow:0 8px 32px rgba(0,0,0,0.4)",
1393
+ "display:flex",
1394
+ "align-items:center",
1395
+ "gap:10px",
1396
+ "max-width:380px",
1397
+ "word-break:break-word",
1398
+ "transform:translateY(80px)",
1399
+ "opacity:0",
1400
+ "transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1),opacity 0.3s ease",
1401
+ "pointer-events:auto",
1402
+ "backdrop-filter:blur(12px)"
1403
+ ].join(";");
1404
+ toast.innerHTML = `<span style="font-size:18px">${c.icon}</span><span>${message}</span>`;
1405
+ document.body.appendChild(toast);
1406
+ const restack = () => {
1407
+ const remaining = document.querySelectorAll("[data-dolphin-toast]");
1408
+ remaining.forEach((el, i) => {
1409
+ el.style.bottom = `${24 + i * 72}px`;
1410
+ });
1411
+ };
1412
+ const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
1413
+ raf(() => raf(() => {
1414
+ toast.style.transform = "translateY(0)";
1415
+ toast.style.opacity = "1";
1416
+ }));
1417
+ const removeToast = () => {
1418
+ clearTimeout(toast._removeTimer);
1419
+ if (toast.parentNode) {
1420
+ toast.parentNode.removeChild(toast);
1421
+ restack();
1422
+ }
1423
+ };
1424
+ const hideTimer = setTimeout(() => {
1425
+ toast.style.transform = "translateY(80px)";
1426
+ toast.style.opacity = "0";
1427
+ toast._removeTimer = setTimeout(removeToast, 400);
1428
+ }, 3500);
1429
+ toast._hideTimer = hideTimer;
1430
+ toast.addEventListener("click", () => {
1431
+ clearTimeout(toast._hideTimer);
1432
+ removeToast();
1433
+ }, { once: true });
1434
+ };
1354
1435
  clientProto._initDOMBinding = function() {
1355
1436
  if (this._domInitialized) return;
1356
1437
  this._domInitialized = true;
@@ -1449,6 +1530,11 @@ function attachDOMBinding(clientProto) {
1449
1530
  resolvedTopic = resolvedTopic.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1450
1531
  }
1451
1532
  this.publish(resolvedTopic, data);
1533
+ const rtSuccessId = e.target.getAttribute("data-rt-api-success");
1534
+ if (rtSuccessId) {
1535
+ const rtSuccessEl = document.getElementById(rtSuccessId);
1536
+ if (rtSuccessEl) this._showToast(rtSuccessEl.textContent || rtSuccessEl.innerText || "", "success");
1537
+ }
1452
1538
  } else if (apiTarget) {
1453
1539
  let resolvedTarget = apiTarget;
1454
1540
  for (const k in parentCtx) {
@@ -1465,16 +1551,40 @@ function attachDOMBinding(clientProto) {
1465
1551
  const result = await this.api.request(method, path, data);
1466
1552
  const resultBind = e.target.getAttribute("data-api-result");
1467
1553
  if (resultBind) this._updateDOM(resultBind, result);
1554
+ const successToast = e.target.getAttribute("data-api-toast");
1555
+ if (successToast) this._showToast(successToast, "success");
1556
+ const successElId = e.target.getAttribute("data-rt-api-success");
1557
+ if (successElId) {
1558
+ const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
1559
+ this._showAlert(successElId, duration);
1560
+ const errElId = e.target.getAttribute("data-rt-api-error");
1561
+ if (errElId) {
1562
+ const errEl = document.getElementById(errElId);
1563
+ if (errEl) errEl.setAttribute("hidden", "");
1564
+ }
1565
+ }
1468
1566
  const redirect = e.target.getAttribute("data-api-redirect");
1469
1567
  if (redirect) window.location.href = redirect;
1470
1568
  if (e.target.hasAttribute("data-api-reload")) window.location.reload();
1471
1569
  } catch (err) {
1472
1570
  console.error("[Dolphin] API Submit Error:", err);
1571
+ const errorToast = e.target.getAttribute("data-api-error-toast");
1572
+ if (errorToast) this._showToast(errorToast, "error");
1573
+ const errorElId = e.target.getAttribute("data-rt-api-error");
1574
+ if (errorElId) {
1575
+ const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
1576
+ this._showAlert(errorElId, duration);
1577
+ const sucElId = e.target.getAttribute("data-rt-api-success");
1578
+ if (sucElId) {
1579
+ const sucEl = document.getElementById(sucElId);
1580
+ if (sucEl) sucEl.setAttribute("hidden", "");
1581
+ }
1582
+ }
1473
1583
  }
1474
1584
  }
1475
1585
  }
1476
1586
  });
1477
- const INTERACTION_EVENTS = ["click", "change", "submit", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1587
+ const INTERACTION_EVENTS = ["click", "change", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1478
1588
  INTERACTION_EVENTS.forEach((evtName) => {
1479
1589
  this.addDomListener(document, evtName, async (e) => {
1480
1590
  if (!e.target || !e.target.closest) return;
@@ -1505,7 +1615,12 @@ function attachDOMBinding(clientProto) {
1505
1615
  const apiTarget = apiBtn.getAttribute(`data-api-${evtName}`);
1506
1616
  const actionData = apiBtn.getAttribute("data-api-payload");
1507
1617
  const parentCtx = this.getClosestContext(apiBtn) || {};
1508
- const parts = apiTarget.trim().split(" ");
1618
+ let resolvedApiTarget = apiTarget;
1619
+ for (const k in parentCtx) {
1620
+ const escapedK = escapeRegExp(k);
1621
+ resolvedApiTarget = resolvedApiTarget.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1622
+ }
1623
+ const parts = resolvedApiTarget.trim().split(" ");
1509
1624
  const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
1510
1625
  const path = parts.length > 1 ? parts[1] : parts[0];
1511
1626
  let payload = null;
@@ -1525,11 +1640,35 @@ function attachDOMBinding(clientProto) {
1525
1640
  const result = await this.api.request(method, path, payload);
1526
1641
  const resultBind = apiBtn.getAttribute("data-api-result");
1527
1642
  if (resultBind) this._updateDOM(resultBind, result);
1643
+ const successToast = apiBtn.getAttribute("data-api-toast");
1644
+ if (successToast) this._showToast(successToast, "success");
1645
+ const successElId = apiBtn.getAttribute("data-rt-api-success");
1646
+ if (successElId) {
1647
+ const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
1648
+ this._showAlert(successElId, duration);
1649
+ const errElId = apiBtn.getAttribute("data-rt-api-error");
1650
+ if (errElId) {
1651
+ const errEl = document.getElementById(errElId);
1652
+ if (errEl) errEl.setAttribute("hidden", "");
1653
+ }
1654
+ }
1528
1655
  const redirect = apiBtn.getAttribute("data-api-redirect");
1529
1656
  if (redirect) window.location.href = redirect;
1530
1657
  if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
1531
1658
  } catch (err) {
1532
1659
  console.error(`[Dolphin] API ${evtName} Error:`, err);
1660
+ const errorToast = apiBtn.getAttribute("data-api-error-toast");
1661
+ if (errorToast) this._showToast(errorToast, "error");
1662
+ const errorElId = apiBtn.getAttribute("data-rt-api-error");
1663
+ if (errorElId) {
1664
+ const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
1665
+ this._showAlert(errorElId, duration);
1666
+ const sucElId = apiBtn.getAttribute("data-rt-api-success");
1667
+ if (sucElId) {
1668
+ const sucEl = document.getElementById(sucElId);
1669
+ if (sucEl) sucEl.setAttribute("hidden", "");
1670
+ }
1671
+ }
1533
1672
  }
1534
1673
  }
1535
1674
  const storeActionBtn = e.target.closest(`[data-store-${evtName}]`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dolphin-client",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "HTML is back! Hookless, framework-agnostic real-time reactive DOM-binding client for Dolphin Server with WebSockets, WebRTC signaling, and offline REST API fallbacks.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",