dolphin-client 1.0.6 → 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.
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.6",
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",
@@ -13,11 +13,17 @@
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
+ "bin": {
17
+ "dolphin-client": "./bin/cli.cjs"
18
+ },
16
19
  "files": [
17
20
  "dist",
18
21
  "README.md",
19
22
  "LICENSE",
20
- "fulltutorial.md"
23
+ "fulltutorial.md",
24
+ "scripts",
25
+ ".vscode",
26
+ "bin"
21
27
  ],
22
28
  "scripts": {
23
29
  "build": "npm run build:iife && npm run build:min && npm run build:esm && npm run build:cjs && tsc",
@@ -25,7 +31,8 @@
25
31
  "build:min": "esbuild ./src/index.ts --bundle --minify --outfile=dist/dolphin-client.min.js --format=iife --global-name=DolphinModule",
26
32
  "build:esm": "esbuild ./src/index.ts --bundle --outfile=dist/index.js --format=esm",
27
33
  "build:cjs": "esbuild ./src/index.ts --bundle --outfile=dist/index.cjs --format=cjs",
28
- "test": "jest"
34
+ "test": "jest",
35
+ "postinstall": "node scripts/postinstall.js"
29
36
  },
30
37
  "jest": {
31
38
  "preset": "ts-jest",
@@ -0,0 +1,57 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ // This script runs automatically after npm install dolphin-client.
5
+ // It sets up the VS Code Custom HTML Data configuration in the host project's .vscode folder
6
+ // so autocomplete for Dolphin Client attributes works instantly out-of-the-box!
7
+
8
+ try {
9
+ const currentPath = __dirname;
10
+ // Check if we are inside a node_modules folder (production installation)
11
+ if (currentPath.includes('node_modules')) {
12
+ // Resolve host project root (e.g. node_modules/dolphin-client/scripts -> hostRoot)
13
+ const hostRoot = path.resolve(__dirname, '../../..');
14
+ const vscodeDir = path.join(hostRoot, '.vscode');
15
+
16
+ // 1. Ensure the host's .vscode directory exists
17
+ if (!fs.existsSync(vscodeDir)) {
18
+ fs.mkdirSync(vscodeDir, { recursive: true });
19
+ console.log('[Dolphin Client] Created .vscode directory in project root.');
20
+ }
21
+
22
+ // 2. Copy the dolphin-tags.json file to the host's .vscode folder
23
+ const sourceTags = path.resolve(__dirname, '../.vscode/dolphin-tags.json');
24
+ const destTags = path.join(vscodeDir, 'dolphin-tags.json');
25
+ if (fs.existsSync(sourceTags)) {
26
+ fs.copyFileSync(sourceTags, destTags);
27
+ console.log('[Dolphin Client] Copied HTML custom data configuration to .vscode/dolphin-tags.json');
28
+ }
29
+
30
+ // 3. Update the host's settings.json to include the custom data file path
31
+ const settingsPath = path.join(vscodeDir, 'settings.json');
32
+ let settings = {};
33
+ if (fs.existsSync(settingsPath)) {
34
+ try {
35
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
36
+ } catch (e) {
37
+ settings = {};
38
+ }
39
+ }
40
+
41
+ if (!settings['html.customData']) {
42
+ settings['html.customData'] = [];
43
+ }
44
+
45
+ const relativePath = '.vscode/dolphin-tags.json';
46
+ if (!settings['html.customData'].includes(relativePath)) {
47
+ settings['html.customData'].push(relativePath);
48
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
49
+ console.log('[Dolphin Client] Registered dolphin-tags.json in .vscode/settings.json');
50
+ }
51
+
52
+ console.log('\x1b[36m%s\x1b[0m', '🐬 [Dolphin Client] VS Code Autocomplete successfully configured!');
53
+ console.log('\x1b[33m%s\x1b[0m', '👉 Note: Please run "Developer: Reload Window" in VS Code to activate suggestions.');
54
+ }
55
+ } catch (err) {
56
+ console.warn('[Dolphin Client] Failed to auto-configure VS Code autocomplete:', err.message);
57
+ }