dolphin-client 1.1.1 → 1.1.3

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.
@@ -997,6 +997,82 @@ var DolphinModule = (() => {
997
997
  function escapeRegExp(str) {
998
998
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
999
999
  }
1000
+ function evaluateExpression(expr, ctx) {
1001
+ if (!ctx || typeof ctx !== "object") return void 0;
1002
+ try {
1003
+ const safeCtx = new Proxy(ctx, {
1004
+ has(target, prop) {
1005
+ return true;
1006
+ },
1007
+ get(target, prop) {
1008
+ if (typeof prop === "string") {
1009
+ if (prop in target) return target[prop];
1010
+ if (typeof globalThis !== "undefined" && prop in globalThis) return globalThis[prop];
1011
+ if (typeof window !== "undefined" && prop in window) return window[prop];
1012
+ }
1013
+ return void 0;
1014
+ }
1015
+ });
1016
+ const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
1017
+ return fn(safeCtx);
1018
+ } catch {
1019
+ return ctx[expr];
1020
+ }
1021
+ }
1022
+ function splitByUnquotedChar(str, char) {
1023
+ const parts = [];
1024
+ let current = "";
1025
+ let inSingleQuote = false;
1026
+ let inDoubleQuote = false;
1027
+ let inBacktick = false;
1028
+ let depth = 0;
1029
+ for (let i = 0; i < str.length; i++) {
1030
+ const c = str[i];
1031
+ if (c === "'" && !inDoubleQuote && !inBacktick) {
1032
+ inSingleQuote = !inSingleQuote;
1033
+ } else if (c === '"' && !inSingleQuote && !inBacktick) {
1034
+ inDoubleQuote = !inDoubleQuote;
1035
+ } else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
1036
+ inBacktick = !inBacktick;
1037
+ } else if (c === "(" || c === "[" || c === "{") {
1038
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
1039
+ } else if (c === ")" || c === "]" || c === "}") {
1040
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
1041
+ }
1042
+ if (c === char && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
1043
+ parts.push(current);
1044
+ current = "";
1045
+ } else {
1046
+ current += c;
1047
+ }
1048
+ }
1049
+ parts.push(current);
1050
+ return parts;
1051
+ }
1052
+ function splitFirstUnquotedColon(str) {
1053
+ let inSingleQuote = false;
1054
+ let inDoubleQuote = false;
1055
+ let inBacktick = false;
1056
+ let depth = 0;
1057
+ for (let i = 0; i < str.length; i++) {
1058
+ const c = str[i];
1059
+ if (c === "'" && !inDoubleQuote && !inBacktick) {
1060
+ inSingleQuote = !inSingleQuote;
1061
+ } else if (c === '"' && !inSingleQuote && !inBacktick) {
1062
+ inDoubleQuote = !inDoubleQuote;
1063
+ } else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
1064
+ inBacktick = !inBacktick;
1065
+ } else if (c === "(" || c === "[" || c === "{") {
1066
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
1067
+ } else if (c === ")" || c === "]" || c === "}") {
1068
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
1069
+ }
1070
+ if (c === ":" && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
1071
+ return [str.slice(0, i), str.slice(i + 1)];
1072
+ }
1073
+ }
1074
+ return null;
1075
+ }
1000
1076
  function resolveTemplate(el) {
1001
1077
  const template = el.getAttribute("data-rt-template");
1002
1078
  if (!template) return null;
@@ -1013,9 +1089,26 @@ var DolphinModule = (() => {
1013
1089
  if (!templateStr.includes("{#if") && !templateStr.includes("{#each")) {
1014
1090
  let result = templateStr;
1015
1091
  for (let key in context) {
1016
- const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1017
- result = result.replace(new RegExp(`\\{\\{${escapedKey}\\}\\}`, "g"), context[key] !== void 0 && context[key] !== null ? context[key] : "");
1018
- }
1092
+ const escapedKey = key.replace(/[.*+?^$${}()|[\]\\]/g, "\\$&");
1093
+ result = result.replace(new RegExp("\\{\\{" + escapedKey + "\\}}", "g"), context[key] !== void 0 && context[key] !== null ? context[key] : "");
1094
+ }
1095
+ result = result.replace(/\{\{([\s\S]*?)\}\}/g, (match, expr) => {
1096
+ const trimmed = expr.trim();
1097
+ if (!trimmed) return "";
1098
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*(?:\??\.[a-zA-Z_$][a-zA-Z0-9_$]*)+$/.test(trimmed)) {
1099
+ const parts = trimmed.split(/\??\./);
1100
+ let val = context;
1101
+ for (const part of parts) {
1102
+ if (val === void 0 || val === null) {
1103
+ val = void 0;
1104
+ break;
1105
+ }
1106
+ val = val[part];
1107
+ }
1108
+ return val !== void 0 && val !== null ? val : "";
1109
+ }
1110
+ return match;
1111
+ });
1019
1112
  return result;
1020
1113
  }
1021
1114
  try {
@@ -1135,7 +1228,9 @@ var DolphinModule = (() => {
1135
1228
  if (typeof document === "undefined") return html;
1136
1229
  try {
1137
1230
  const parser = new DOMParser();
1138
- const doc = parser.parseFromString(html, "text/html");
1231
+ const hasBodyOrHtml = /<\s*(?:body|html)\b/i.test(html);
1232
+ const parseString = hasBodyOrHtml ? html : `<body>${html}</body>`;
1233
+ const doc = parser.parseFromString(parseString, "text/html");
1139
1234
  const body = doc.body;
1140
1235
  const sanitizeNode = (el) => {
1141
1236
  const tag = el.tagName.toLowerCase();
@@ -1249,7 +1344,7 @@ var DolphinModule = (() => {
1249
1344
  });
1250
1345
  }
1251
1346
  }
1252
- clientProto.setStoreState = function(storeName, key, val) {
1347
+ clientProto.setStoreState = function(storeName, key, val, originEl) {
1253
1348
  this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1254
1349
  if (!this.uiStores.has(storeName)) {
1255
1350
  this.uiStores.set(storeName, {});
@@ -1262,6 +1357,7 @@ var DolphinModule = (() => {
1262
1357
  if (typeof document !== "undefined") {
1263
1358
  const readElements = document.querySelectorAll(`[data-store-read="${storeName}.${key}"]`);
1264
1359
  readElements.forEach((el) => {
1360
+ if (el === originEl) return;
1265
1361
  if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
1266
1362
  if (el.type === "checkbox") {
1267
1363
  el.checked = !!val;
@@ -1285,6 +1381,73 @@ var DolphinModule = (() => {
1285
1381
  };
1286
1382
  clientProto._scanStoreBinds = function() {
1287
1383
  if (typeof document === "undefined") return;
1384
+ const storeElements = document.querySelectorAll("dolphin-store");
1385
+ storeElements.forEach((el) => {
1386
+ if (typeof el.getAttribute !== "function") return;
1387
+ const storeName = el.getAttribute("name") || el.getAttribute("data-store");
1388
+ if (!storeName) return;
1389
+ const hasChildren = el.children && el.children.length > 0;
1390
+ if (hasChildren) {
1391
+ if (typeof el.setAttribute === "function") {
1392
+ el.setAttribute("data-rt-bind", `store/${storeName}`);
1393
+ el.setAttribute("data-rt-type", "context");
1394
+ }
1395
+ } else {
1396
+ if (el.style) {
1397
+ el.style.display = "none";
1398
+ }
1399
+ }
1400
+ if (!hasChildren) {
1401
+ const content = el.textContent ? el.textContent.trim() : "";
1402
+ if (content && content.startsWith("{")) {
1403
+ try {
1404
+ const parsed = JSON.parse(content);
1405
+ if (parsed && typeof parsed === "object") {
1406
+ Object.keys(parsed).forEach((key) => {
1407
+ this.setStoreState(storeName, key, parsed[key]);
1408
+ });
1409
+ }
1410
+ } catch (err) {
1411
+ console.error(`[Dolphin Store Init Error] Failed to parse JSON inside <dolphin-store name="${storeName}">:`, err);
1412
+ }
1413
+ }
1414
+ }
1415
+ const templateSelector = el.getAttribute("template");
1416
+ if (el.attributes) {
1417
+ const excludeAttrs = ["name", "data-store", "style", "data-rt-bind", "data-rt-type", "template"];
1418
+ Array.from(el.attributes).forEach((attr) => {
1419
+ if (!excludeAttrs.includes(attr.name)) {
1420
+ let val = attr.value;
1421
+ if (val === "true") val = true;
1422
+ else if (val === "false") val = false;
1423
+ else if (val === "null") val = null;
1424
+ else if (!isNaN(Number(val)) && val.trim() !== "") val = Number(val);
1425
+ this.setStoreState(storeName, attr.name, val);
1426
+ }
1427
+ });
1428
+ }
1429
+ if (templateSelector && !hasChildren && el.parentNode && typeof document !== "undefined") {
1430
+ const markerId = `_ds_${storeName}_${templateSelector.replace(/[^a-z0-9]/gi, "_")}`;
1431
+ let wrapper = document.querySelector(`[data-ds-wired="${markerId}"]`);
1432
+ if (!wrapper) {
1433
+ wrapper = document.createElement("div");
1434
+ wrapper.setAttribute("data-rt-bind", `store/${storeName}`);
1435
+ wrapper.setAttribute("data-rt-template", templateSelector);
1436
+ wrapper.setAttribute("data-ds-wired", markerId);
1437
+ el.parentNode.insertBefore(wrapper, el.nextSibling);
1438
+ }
1439
+ if (typeof this._updateDOM === "function") {
1440
+ this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1441
+ const currentStore = this.uiStores.get(storeName) || {};
1442
+ this._updateDOM(`store/${storeName}`, currentStore);
1443
+ }
1444
+ }
1445
+ if (hasChildren && typeof this._updateDOM === "function") {
1446
+ this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1447
+ const currentStore = this.uiStores.get(storeName) || {};
1448
+ this._updateDOM(`store/${storeName}`, currentStore);
1449
+ }
1450
+ });
1288
1451
  const writeEls = document.querySelectorAll("[data-store-write]");
1289
1452
  writeEls.forEach((el) => {
1290
1453
  const writeBind = el.getAttribute("data-store-write");
@@ -1337,20 +1500,49 @@ var DolphinModule = (() => {
1337
1500
  if (key) return ctx[key];
1338
1501
  return ctx;
1339
1502
  }
1340
- current = current.parentElement;
1503
+ current = current.parentElement || current.parentNode;
1341
1504
  }
1342
1505
  return null;
1343
1506
  };
1344
1507
  clientProto._executeStoreAction = function(expression, element) {
1345
1508
  this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1509
+ const parentCtx = element && typeof this.getClosestContext === "function" ? this.getClosestContext(element) : null;
1346
1510
  const context = new Proxy({}, {
1347
1511
  has: (target, prop) => {
1348
1512
  return true;
1349
1513
  },
1350
1514
  get: (target, prop) => {
1351
1515
  if (typeof prop === "string") {
1516
+ if (prop === "log") {
1517
+ return (arg) => {
1518
+ if (arg === void 0) {
1519
+ const allStores = {};
1520
+ this.uiStores.forEach((val, key) => {
1521
+ allStores[key] = { ...val };
1522
+ });
1523
+ console.log(`%c\u{1F4CA} [Dolphin All UI Stores]:`, "color: #06b6d4; font-weight: bold;", allStores);
1524
+ } else if (arg && typeof arg === "object" && arg.__isStoreProxy__) {
1525
+ const storeName = arg.__storeName__;
1526
+ const store = this.uiStores.get(storeName);
1527
+ console.log(`%c\u{1F4CA} [Dolphin Store: ${storeName}]:`, "color: #06b6d4; font-weight: bold;", store ? { ...store } : {});
1528
+ } else {
1529
+ console.log(`%c\u{1F4CA} [Dolphin Log]:`, "color: #06b6d4; font-weight: bold;", arg);
1530
+ }
1531
+ };
1532
+ }
1533
+ if (parentCtx && parentCtx[prop] !== void 0) {
1534
+ return parentCtx[prop];
1535
+ }
1536
+ if (typeof globalThis !== "undefined" && prop in globalThis) {
1537
+ return globalThis[prop];
1538
+ }
1539
+ if (typeof window !== "undefined" && prop in window) {
1540
+ return window[prop];
1541
+ }
1352
1542
  return new Proxy({}, {
1353
1543
  get: (subTarget, subProp) => {
1544
+ if (subProp === "__storeName__") return prop;
1545
+ if (subProp === "__isStoreProxy__") return true;
1354
1546
  if (typeof subProp === "string") {
1355
1547
  return this.getStoreState(prop, subProp);
1356
1548
  }
@@ -1377,86 +1569,6 @@ var DolphinModule = (() => {
1377
1569
  console.error("%cFailed Expression:", "color: #3b82f6; font-style: italic;", expression);
1378
1570
  }
1379
1571
  };
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
- };
1460
1572
  clientProto._initDOMBinding = function() {
1461
1573
  if (this._domInitialized) return;
1462
1574
  this._domInitialized = true;
@@ -1472,7 +1584,7 @@ var DolphinModule = (() => {
1472
1584
  const storeName = parts[0];
1473
1585
  const key = parts[1];
1474
1586
  const val = e.target.type === "checkbox" ? e.target.checked : e.target.value;
1475
- this.setStoreState(storeName, key, val);
1587
+ this.setStoreState(storeName, key, val, e.target);
1476
1588
  }
1477
1589
  }
1478
1590
  const rules = e.target.getAttribute("data-rt-validate");
@@ -1555,11 +1667,6 @@ var DolphinModule = (() => {
1555
1667
  resolvedTopic = resolvedTopic.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1556
1668
  }
1557
1669
  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
- }
1563
1670
  } else if (apiTarget) {
1564
1671
  let resolvedTarget = apiTarget;
1565
1672
  for (const k in parentCtx) {
@@ -1576,40 +1683,16 @@ var DolphinModule = (() => {
1576
1683
  const result = await this.api.request(method, path, data);
1577
1684
  const resultBind = e.target.getAttribute("data-api-result");
1578
1685
  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
- }
1591
1686
  const redirect = e.target.getAttribute("data-api-redirect");
1592
1687
  if (redirect) window.location.href = redirect;
1593
1688
  if (e.target.hasAttribute("data-api-reload")) window.location.reload();
1594
1689
  } catch (err) {
1595
1690
  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
- }
1608
1691
  }
1609
1692
  }
1610
1693
  }
1611
1694
  });
1612
- const INTERACTION_EVENTS = ["click", "change", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1695
+ const INTERACTION_EVENTS = ["click", "change", "submit", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1613
1696
  INTERACTION_EVENTS.forEach((evtName) => {
1614
1697
  this.addDomListener(document, evtName, async (e) => {
1615
1698
  if (!e.target || !e.target.closest) return;
@@ -1640,12 +1723,7 @@ var DolphinModule = (() => {
1640
1723
  const apiTarget = apiBtn.getAttribute(`data-api-${evtName}`);
1641
1724
  const actionData = apiBtn.getAttribute("data-api-payload");
1642
1725
  const parentCtx = this.getClosestContext(apiBtn) || {};
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(" ");
1726
+ const parts = apiTarget.trim().split(" ");
1649
1727
  const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
1650
1728
  const path = parts.length > 1 ? parts[1] : parts[0];
1651
1729
  let payload = null;
@@ -1665,35 +1743,11 @@ var DolphinModule = (() => {
1665
1743
  const result = await this.api.request(method, path, payload);
1666
1744
  const resultBind = apiBtn.getAttribute("data-api-result");
1667
1745
  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
- }
1680
1746
  const redirect = apiBtn.getAttribute("data-api-redirect");
1681
1747
  if (redirect) window.location.href = redirect;
1682
1748
  if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
1683
1749
  } catch (err) {
1684
1750
  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
- }
1697
1751
  }
1698
1752
  }
1699
1753
  const storeActionBtn = e.target.closest(`[data-store-${evtName}]`);
@@ -1890,24 +1944,33 @@ var DolphinModule = (() => {
1890
1944
  const processNode = (node) => {
1891
1945
  if (node.hasAttribute("data-rt-text")) {
1892
1946
  const key = node.getAttribute("data-rt-text");
1893
- if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) node.textContent = processedPayload[key];
1947
+ if (key) {
1948
+ const val = evaluateExpression(key, processedPayload);
1949
+ if (val !== void 0 && val !== null) node.textContent = val;
1950
+ }
1894
1951
  }
1895
1952
  if (node.hasAttribute("data-rt-html")) {
1896
1953
  const key = node.getAttribute("data-rt-html");
1897
- if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
1898
- node.innerHTML = sanitizeHTML(processedPayload[key]);
1954
+ if (key) {
1955
+ const val = evaluateExpression(key, processedPayload);
1956
+ if (val !== void 0 && val !== null) {
1957
+ node.innerHTML = sanitizeHTML(val);
1958
+ }
1899
1959
  }
1900
1960
  }
1901
1961
  if (node.hasAttribute("data-rt-attr")) {
1902
1962
  const attrStr = node.getAttribute("data-rt-attr");
1903
1963
  if (attrStr) {
1904
- attrStr.split(",").forEach((b) => {
1905
- const parts = b.split(":");
1906
- if (parts.length === 2) {
1907
- const attrName = parts[0].trim();
1908
- const key = parts[1].trim();
1909
- if (attrName && key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
1910
- node.setAttribute(attrName, processedPayload[key]);
1964
+ splitByUnquotedChar(attrStr, ",").forEach((b) => {
1965
+ const pair = splitFirstUnquotedColon(b);
1966
+ if (pair) {
1967
+ const attrName = pair[0].trim();
1968
+ const key = pair[1].trim();
1969
+ if (attrName && key) {
1970
+ const val = evaluateExpression(key, processedPayload);
1971
+ if (val !== void 0 && val !== null) {
1972
+ node.setAttribute(attrName, val);
1973
+ }
1911
1974
  }
1912
1975
  }
1913
1976
  });
@@ -1916,13 +1979,13 @@ var DolphinModule = (() => {
1916
1979
  if (node.hasAttribute("data-rt-class")) {
1917
1980
  const classStr = node.getAttribute("data-rt-class");
1918
1981
  if (classStr) {
1919
- classStr.split(",").forEach((b) => {
1920
- const parts = b.split(":");
1921
- if (parts.length === 2) {
1922
- const className = parts[0].trim();
1923
- const key = parts[1].trim();
1982
+ splitByUnquotedChar(classStr, ",").forEach((b) => {
1983
+ const pair = splitFirstUnquotedColon(b);
1984
+ if (pair) {
1985
+ const className = pair[0].trim();
1986
+ const key = pair[1].trim();
1924
1987
  const classNames = className.split(/\s+/).filter(Boolean);
1925
- if (processedPayload[key]) {
1988
+ if (evaluateExpression(key, processedPayload)) {
1926
1989
  classNames.forEach((c) => node.classList.add(c));
1927
1990
  } else {
1928
1991
  classNames.forEach((c) => node.classList.remove(c));
@@ -1934,7 +1997,7 @@ var DolphinModule = (() => {
1934
1997
  if (node.hasAttribute("data-rt-if")) {
1935
1998
  const key = node.getAttribute("data-rt-if");
1936
1999
  if (key) {
1937
- if (processedPayload[key]) {
2000
+ if (evaluateExpression(key, processedPayload)) {
1938
2001
  node.style.display = "";
1939
2002
  } else {
1940
2003
  node.style.display = "none";
@@ -1944,7 +2007,7 @@ var DolphinModule = (() => {
1944
2007
  if (node.hasAttribute("data-rt-hide")) {
1945
2008
  const key = node.getAttribute("data-rt-hide");
1946
2009
  if (key) {
1947
- if (processedPayload[key]) {
2010
+ if (evaluateExpression(key, processedPayload)) {
1948
2011
  node.style.display = "none";
1949
2012
  } else {
1950
2013
  node.style.display = "";
@@ -1994,21 +2057,35 @@ var DolphinModule = (() => {
1994
2057
  return;
1995
2058
  }
1996
2059
  resolvingSet.add(src);
1997
- let promise = componentPromiseCache.get(src);
2060
+ const hashIndex = src.indexOf("#");
2061
+ const url = hashIndex !== -1 ? src.substring(0, hashIndex) : src;
2062
+ const selector = hashIndex !== -1 ? src.substring(hashIndex) : null;
2063
+ let promise = componentPromiseCache.get(url);
1998
2064
  if (!promise) {
1999
- promise = fetch(src).then((res) => {
2065
+ promise = fetch(url).then((res) => {
2000
2066
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
2001
2067
  return res.text();
2002
2068
  });
2003
- promise.catch(() => componentPromiseCache.delete(src));
2004
- componentPromiseCache.set(src, promise);
2069
+ promise.catch(() => componentPromiseCache.delete(url));
2070
+ componentPromiseCache.set(url, promise);
2005
2071
  }
2006
2072
  let content = "";
2007
2073
  try {
2008
2074
  content = await promise;
2075
+ if (selector && typeof DOMParser !== "undefined") {
2076
+ const parser = new DOMParser();
2077
+ const doc = parser.parseFromString(content, "text/html");
2078
+ const targetEl = doc.querySelector(selector);
2079
+ if (targetEl) {
2080
+ content = targetEl.outerHTML;
2081
+ } else {
2082
+ console.warn(`[Dolphin Component Warning]: Selector "${selector}" not found in imported file "${url}".`);
2083
+ content = `<span style="color:orange;font-weight:bold;">Selector ${selector} not found in ${url}</span>`;
2084
+ }
2085
+ }
2009
2086
  } catch (err) {
2010
- console.error(`[Dolphin Component Error]: Failed to fetch component "${src}":`, err);
2011
- content = `<span style="color:red;font-weight:bold;">Failed to import ${src}</span>`;
2087
+ console.error(`[Dolphin Component Error]: Failed to fetch component "${url}":`, err);
2088
+ content = `<span style="color:red;font-weight:bold;">Failed to import ${url}</span>`;
2012
2089
  }
2013
2090
  el.innerHTML = sanitizeHTML(content);
2014
2091
  el.removeAttribute("data-import");
@@ -2776,6 +2853,32 @@ var DolphinModule = (() => {
2776
2853
  }
2777
2854
 
2778
2855
  // src/testing.ts
2856
+ function createMockFn() {
2857
+ if (typeof jest !== "undefined" && typeof jest.fn === "function") {
2858
+ return jest.fn();
2859
+ }
2860
+ const fn = (...args) => {
2861
+ fn.mock.calls.push(args);
2862
+ if (fn._implementation) {
2863
+ return fn._implementation(...args);
2864
+ }
2865
+ return fn._returnValue;
2866
+ };
2867
+ fn.mock = {
2868
+ calls: []
2869
+ };
2870
+ fn._returnValue = void 0;
2871
+ fn._implementation = null;
2872
+ fn.mockReturnValue = (val) => {
2873
+ fn._returnValue = val;
2874
+ return fn;
2875
+ };
2876
+ fn.mockImplementation = (impl) => {
2877
+ fn._implementation = impl;
2878
+ return fn;
2879
+ };
2880
+ return fn;
2881
+ }
2779
2882
  var DolphinTestUtils = class {
2780
2883
  static render(html) {
2781
2884
  if (typeof document === "undefined") {
@@ -2802,11 +2905,11 @@ var DolphinModule = (() => {
2802
2905
  send: (data) => {
2803
2906
  sentMessages.push(data);
2804
2907
  },
2805
- close: jest.fn(),
2806
- onopen: jest.fn(),
2807
- onmessage: jest.fn(),
2808
- onclose: jest.fn(),
2809
- onerror: jest.fn(),
2908
+ close: createMockFn(),
2909
+ onopen: createMockFn(),
2910
+ onmessage: createMockFn(),
2911
+ onclose: createMockFn(),
2912
+ onerror: createMockFn(),
2810
2913
  sentMessages
2811
2914
  };
2812
2915
  global.WebSocket = class {
@@ -2841,8 +2944,8 @@ var DolphinModule = (() => {
2841
2944
  static simulateClick(el) {
2842
2945
  const clickEvt = {
2843
2946
  target: el,
2844
- preventDefault: jest.fn(),
2845
- stopPropagation: jest.fn()
2947
+ preventDefault: createMockFn(),
2948
+ stopPropagation: createMockFn()
2846
2949
  };
2847
2950
  const clickListeners = global.document._listeners?.["click"] || [];
2848
2951
  clickListeners.forEach((listener) => listener(clickEvt));
@@ -2851,8 +2954,8 @@ var DolphinModule = (() => {
2851
2954
  el.value = value;
2852
2955
  const changeEvt = {
2853
2956
  target: el,
2854
- preventDefault: jest.fn(),
2855
- stopPropagation: jest.fn()
2957
+ preventDefault: createMockFn(),
2958
+ stopPropagation: createMockFn()
2856
2959
  };
2857
2960
  const changeListeners = global.document._listeners?.["change"] || [];
2858
2961
  changeListeners.forEach((listener) => listener(changeEvt));