dolphin-client 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -997,6 +997,82 @@ function attachDOMBinding(clientProto) {
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 @@ function attachDOMBinding(clientProto) {
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 @@ function attachDOMBinding(clientProto) {
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 @@ function attachDOMBinding(clientProto) {
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 @@ function attachDOMBinding(clientProto) {
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;
@@ -1337,20 +1433,49 @@ function attachDOMBinding(clientProto) {
1337
1433
  if (key) return ctx[key];
1338
1434
  return ctx;
1339
1435
  }
1340
- current = current.parentElement;
1436
+ current = current.parentElement || current.parentNode;
1341
1437
  }
1342
1438
  return null;
1343
1439
  };
1344
1440
  clientProto._executeStoreAction = function(expression, element) {
1345
1441
  this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1442
+ const parentCtx = element && typeof this.getClosestContext === "function" ? this.getClosestContext(element) : null;
1346
1443
  const context = new Proxy({}, {
1347
1444
  has: (target, prop) => {
1348
1445
  return true;
1349
1446
  },
1350
1447
  get: (target, prop) => {
1351
1448
  if (typeof prop === "string") {
1449
+ if (prop === "log") {
1450
+ return (arg) => {
1451
+ if (arg === void 0) {
1452
+ const allStores = {};
1453
+ this.uiStores.forEach((val, key) => {
1454
+ allStores[key] = { ...val };
1455
+ });
1456
+ console.log(`%c\u{1F4CA} [Dolphin All UI Stores]:`, "color: #06b6d4; font-weight: bold;", allStores);
1457
+ } else if (arg && typeof arg === "object" && arg.__isStoreProxy__) {
1458
+ const storeName = arg.__storeName__;
1459
+ const store = this.uiStores.get(storeName);
1460
+ console.log(`%c\u{1F4CA} [Dolphin Store: ${storeName}]:`, "color: #06b6d4; font-weight: bold;", store ? { ...store } : {});
1461
+ } else {
1462
+ console.log(`%c\u{1F4CA} [Dolphin Log]:`, "color: #06b6d4; font-weight: bold;", arg);
1463
+ }
1464
+ };
1465
+ }
1466
+ if (parentCtx && parentCtx[prop] !== void 0) {
1467
+ return parentCtx[prop];
1468
+ }
1469
+ if (typeof globalThis !== "undefined" && prop in globalThis) {
1470
+ return globalThis[prop];
1471
+ }
1472
+ if (typeof window !== "undefined" && prop in window) {
1473
+ return window[prop];
1474
+ }
1352
1475
  return new Proxy({}, {
1353
1476
  get: (subTarget, subProp) => {
1477
+ if (subProp === "__storeName__") return prop;
1478
+ if (subProp === "__isStoreProxy__") return true;
1354
1479
  if (typeof subProp === "string") {
1355
1480
  return this.getStoreState(prop, subProp);
1356
1481
  }
@@ -1377,86 +1502,6 @@ function attachDOMBinding(clientProto) {
1377
1502
  console.error("%cFailed Expression:", "color: #3b82f6; font-style: italic;", expression);
1378
1503
  }
1379
1504
  };
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
1505
  clientProto._initDOMBinding = function() {
1461
1506
  if (this._domInitialized) return;
1462
1507
  this._domInitialized = true;
@@ -1472,7 +1517,7 @@ function attachDOMBinding(clientProto) {
1472
1517
  const storeName = parts[0];
1473
1518
  const key = parts[1];
1474
1519
  const val = e.target.type === "checkbox" ? e.target.checked : e.target.value;
1475
- this.setStoreState(storeName, key, val);
1520
+ this.setStoreState(storeName, key, val, e.target);
1476
1521
  }
1477
1522
  }
1478
1523
  const rules = e.target.getAttribute("data-rt-validate");
@@ -1555,11 +1600,6 @@ function attachDOMBinding(clientProto) {
1555
1600
  resolvedTopic = resolvedTopic.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
1556
1601
  }
1557
1602
  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
1603
  } else if (apiTarget) {
1564
1604
  let resolvedTarget = apiTarget;
1565
1605
  for (const k in parentCtx) {
@@ -1576,40 +1616,16 @@ function attachDOMBinding(clientProto) {
1576
1616
  const result = await this.api.request(method, path, data);
1577
1617
  const resultBind = e.target.getAttribute("data-api-result");
1578
1618
  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
1619
  const redirect = e.target.getAttribute("data-api-redirect");
1592
1620
  if (redirect) window.location.href = redirect;
1593
1621
  if (e.target.hasAttribute("data-api-reload")) window.location.reload();
1594
1622
  } catch (err) {
1595
1623
  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
1624
  }
1609
1625
  }
1610
1626
  }
1611
1627
  });
1612
- const INTERACTION_EVENTS = ["click", "change", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1628
+ const INTERACTION_EVENTS = ["click", "change", "submit", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
1613
1629
  INTERACTION_EVENTS.forEach((evtName) => {
1614
1630
  this.addDomListener(document, evtName, async (e) => {
1615
1631
  if (!e.target || !e.target.closest) return;
@@ -1640,12 +1656,7 @@ function attachDOMBinding(clientProto) {
1640
1656
  const apiTarget = apiBtn.getAttribute(`data-api-${evtName}`);
1641
1657
  const actionData = apiBtn.getAttribute("data-api-payload");
1642
1658
  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(" ");
1659
+ const parts = apiTarget.trim().split(" ");
1649
1660
  const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
1650
1661
  const path = parts.length > 1 ? parts[1] : parts[0];
1651
1662
  let payload = null;
@@ -1665,35 +1676,11 @@ function attachDOMBinding(clientProto) {
1665
1676
  const result = await this.api.request(method, path, payload);
1666
1677
  const resultBind = apiBtn.getAttribute("data-api-result");
1667
1678
  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
1679
  const redirect = apiBtn.getAttribute("data-api-redirect");
1681
1680
  if (redirect) window.location.href = redirect;
1682
1681
  if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
1683
1682
  } catch (err) {
1684
1683
  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
1684
  }
1698
1685
  }
1699
1686
  const storeActionBtn = e.target.closest(`[data-store-${evtName}]`);
@@ -1890,24 +1877,33 @@ function attachDOMBinding(clientProto) {
1890
1877
  const processNode = (node) => {
1891
1878
  if (node.hasAttribute("data-rt-text")) {
1892
1879
  const key = node.getAttribute("data-rt-text");
1893
- if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) node.textContent = processedPayload[key];
1880
+ if (key) {
1881
+ const val = evaluateExpression(key, processedPayload);
1882
+ if (val !== void 0 && val !== null) node.textContent = val;
1883
+ }
1894
1884
  }
1895
1885
  if (node.hasAttribute("data-rt-html")) {
1896
1886
  const key = node.getAttribute("data-rt-html");
1897
- if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
1898
- node.innerHTML = sanitizeHTML(processedPayload[key]);
1887
+ if (key) {
1888
+ const val = evaluateExpression(key, processedPayload);
1889
+ if (val !== void 0 && val !== null) {
1890
+ node.innerHTML = sanitizeHTML(val);
1891
+ }
1899
1892
  }
1900
1893
  }
1901
1894
  if (node.hasAttribute("data-rt-attr")) {
1902
1895
  const attrStr = node.getAttribute("data-rt-attr");
1903
1896
  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]);
1897
+ splitByUnquotedChar(attrStr, ",").forEach((b) => {
1898
+ const pair = splitFirstUnquotedColon(b);
1899
+ if (pair) {
1900
+ const attrName = pair[0].trim();
1901
+ const key = pair[1].trim();
1902
+ if (attrName && key) {
1903
+ const val = evaluateExpression(key, processedPayload);
1904
+ if (val !== void 0 && val !== null) {
1905
+ node.setAttribute(attrName, val);
1906
+ }
1911
1907
  }
1912
1908
  }
1913
1909
  });
@@ -1916,13 +1912,13 @@ function attachDOMBinding(clientProto) {
1916
1912
  if (node.hasAttribute("data-rt-class")) {
1917
1913
  const classStr = node.getAttribute("data-rt-class");
1918
1914
  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();
1915
+ splitByUnquotedChar(classStr, ",").forEach((b) => {
1916
+ const pair = splitFirstUnquotedColon(b);
1917
+ if (pair) {
1918
+ const className = pair[0].trim();
1919
+ const key = pair[1].trim();
1924
1920
  const classNames = className.split(/\s+/).filter(Boolean);
1925
- if (processedPayload[key]) {
1921
+ if (evaluateExpression(key, processedPayload)) {
1926
1922
  classNames.forEach((c) => node.classList.add(c));
1927
1923
  } else {
1928
1924
  classNames.forEach((c) => node.classList.remove(c));
@@ -1934,7 +1930,7 @@ function attachDOMBinding(clientProto) {
1934
1930
  if (node.hasAttribute("data-rt-if")) {
1935
1931
  const key = node.getAttribute("data-rt-if");
1936
1932
  if (key) {
1937
- if (processedPayload[key]) {
1933
+ if (evaluateExpression(key, processedPayload)) {
1938
1934
  node.style.display = "";
1939
1935
  } else {
1940
1936
  node.style.display = "none";
@@ -1944,7 +1940,7 @@ function attachDOMBinding(clientProto) {
1944
1940
  if (node.hasAttribute("data-rt-hide")) {
1945
1941
  const key = node.getAttribute("data-rt-hide");
1946
1942
  if (key) {
1947
- if (processedPayload[key]) {
1943
+ if (evaluateExpression(key, processedPayload)) {
1948
1944
  node.style.display = "none";
1949
1945
  } else {
1950
1946
  node.style.display = "";
@@ -1994,21 +1990,35 @@ function attachDOMBinding(clientProto) {
1994
1990
  return;
1995
1991
  }
1996
1992
  resolvingSet.add(src);
1997
- let promise = componentPromiseCache.get(src);
1993
+ const hashIndex = src.indexOf("#");
1994
+ const url = hashIndex !== -1 ? src.substring(0, hashIndex) : src;
1995
+ const selector = hashIndex !== -1 ? src.substring(hashIndex) : null;
1996
+ let promise = componentPromiseCache.get(url);
1998
1997
  if (!promise) {
1999
- promise = fetch(src).then((res) => {
1998
+ promise = fetch(url).then((res) => {
2000
1999
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
2001
2000
  return res.text();
2002
2001
  });
2003
- promise.catch(() => componentPromiseCache.delete(src));
2004
- componentPromiseCache.set(src, promise);
2002
+ promise.catch(() => componentPromiseCache.delete(url));
2003
+ componentPromiseCache.set(url, promise);
2005
2004
  }
2006
2005
  let content = "";
2007
2006
  try {
2008
2007
  content = await promise;
2008
+ if (selector && typeof DOMParser !== "undefined") {
2009
+ const parser = new DOMParser();
2010
+ const doc = parser.parseFromString(content, "text/html");
2011
+ const targetEl = doc.querySelector(selector);
2012
+ if (targetEl) {
2013
+ content = targetEl.outerHTML;
2014
+ } else {
2015
+ console.warn(`[Dolphin Component Warning]: Selector "${selector}" not found in imported file "${url}".`);
2016
+ content = `<span style="color:orange;font-weight:bold;">Selector ${selector} not found in ${url}</span>`;
2017
+ }
2018
+ }
2009
2019
  } 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>`;
2020
+ console.error(`[Dolphin Component Error]: Failed to fetch component "${url}":`, err);
2021
+ content = `<span style="color:red;font-weight:bold;">Failed to import ${url}</span>`;
2012
2022
  }
2013
2023
  el.innerHTML = sanitizeHTML(content);
2014
2024
  el.removeAttribute("data-import");