@decentnetwork/beagle 0.1.1 → 0.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.
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.0";
1
+ window.__DK_UI_VERSION="0.1.1";
2
2
  const ICON_PATHS = {
3
3
  // ---- tab bar (the four must feel like one set) ----
4
4
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
@@ -1402,7 +1402,9 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
1402
1402
  init[c.id] = c.default || "";
1403
1403
  return init;
1404
1404
  });
1405
+ const [files, setFiles] = React.useState({});
1405
1406
  const [busy, setBusy] = React.useState(false);
1407
+ const [step, setStep] = React.useState("");
1406
1408
  const [err, setErr] = React.useState("");
1407
1409
  if (submitted) {
1408
1410
  return /* @__PURE__ */ React.createElement("div", { style: {
@@ -1415,13 +1417,14 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
1415
1417
  background: "var(--bub-them)",
1416
1418
  opacity: 0.85,
1417
1419
  minWidth: 240
1418
- } }, items.filter((c) => c.type === "select" || c.type === "text").map((c) => {
1420
+ } }, items.filter((c) => window.ChatComponents.INPUT_TYPES.has(c.type)).map((c) => {
1419
1421
  const raw = submitted.values && submitted.values[c.id] || "";
1420
- const opt = (c.options || []).find((o) => o.value === raw);
1421
- return /* @__PURE__ */ React.createElement("div", { key: c.id, style: { display: "flex", gap: 8, fontFamily: "var(--mono)", fontSize: 12.5 } }, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, c.label || c.id), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--text)", fontWeight: 600 } }, opt ? opt.label : raw));
1422
+ const shown = raw && typeof raw === "object" ? raw.name : ((c.options || []).find((o) => o.value === raw) || {}).label || raw;
1423
+ return /* @__PURE__ */ React.createElement("div", { key: c.id, style: { display: "flex", gap: 8, fontFamily: "var(--mono)", fontSize: 12.5 } }, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, c.label || c.id), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--text)", fontWeight: 600 } }, shown));
1422
1424
  }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6, marginTop: 2, color: "var(--faint)", fontSize: 11.5, fontFamily: "var(--mono)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 3, color: "var(--ok, #46d17f)" }), /* @__PURE__ */ React.createElement("span", null, "submitted")));
1423
1425
  }
1424
- const missing = items.filter((c) => c.required && (c.type === "select" || c.type === "text") && !vals[c.id]);
1426
+ const attachments = items.filter((c) => c.type === "attachment");
1427
+ const missing = items.filter((c) => c.required && window.ChatComponents.INPUT_TYPES.has(c.type) && (c.type === "attachment" ? !files[c.id] : !vals[c.id]));
1425
1428
  const send = async (componentId) => {
1426
1429
  if (missing.length) {
1427
1430
  setErr("Fill in: " + missing.map((c) => c.label || c.id).join(", "));
@@ -1431,7 +1434,29 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
1431
1434
  setErr("");
1432
1435
  try {
1433
1436
  const CC = window.ChatComponents;
1434
- const text = CC.encodeInteraction(CC.buildInteraction(form.custom_id, componentId, vals));
1437
+ const answers = { ...vals };
1438
+ for (const c of attachments) {
1439
+ const f = files[c.id];
1440
+ if (!f)
1441
+ continue;
1442
+ setStep("Sending " + f.name + "\u2026");
1443
+ const up = await DK.sendFile(peer.userId || peer.id, f);
1444
+ if (!up || !up.ok) {
1445
+ setErr(up && up.error || "file send failed");
1446
+ setBusy(false);
1447
+ setStep("");
1448
+ return;
1449
+ }
1450
+ if (!up.fileId) {
1451
+ setErr("This peer received the file inline, which carries no file id \u2014 send it as a normal message instead.");
1452
+ setBusy(false);
1453
+ setStep("");
1454
+ return;
1455
+ }
1456
+ answers[c.id] = { fileId: up.fileId, name: up.name || f.name, size: up.size != null ? up.size : f.size };
1457
+ }
1458
+ setStep("");
1459
+ const text = CC.encodeInteraction(CC.buildInteraction(form.custom_id, componentId, answers));
1435
1460
  const r = await fetch("/api/chat-send", {
1436
1461
  method: "POST",
1437
1462
  headers: { "content-type": "application/json" },
@@ -1501,6 +1526,20 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
1501
1526
  }
1502
1527
  ));
1503
1528
  }
1529
+ if (c.type === "attachment") {
1530
+ const picked = files[c.id];
1531
+ const tooBig = picked && c.max_bytes && picked.size > c.max_bytes;
1532
+ return /* @__PURE__ */ React.createElement("label", { key: c.id, style: { display: "flex", flexDirection: "column", gap: 4 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, c.label || c.id, c.required ? " *" : ""), /* @__PURE__ */ React.createElement(
1533
+ "input",
1534
+ {
1535
+ type: "file",
1536
+ disabled: busy,
1537
+ accept: (c.accept || []).join(",") || void 0,
1538
+ onChange: (e) => setFiles((f) => ({ ...f, [c.id]: e.target.files && e.target.files[0] })),
1539
+ style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--text)" }
1540
+ }
1541
+ ), picked && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: tooBig ? "#f59e0b" : "var(--faint)" } }, dkFileSize(picked.size), tooBig ? " \u2014 larger than this form asked for" : ""));
1542
+ }
1504
1543
  if (c.type === "submit") {
1505
1544
  return /* @__PURE__ */ React.createElement(
1506
1545
  "button",
@@ -1522,7 +1561,7 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
1522
1561
  opacity: busy ? 0.6 : 1
1523
1562
  }
1524
1563
  },
1525
- busy ? "\u2026" : c.label || "Submit"
1564
+ busy ? step || "\u2026" : c.label || "Submit"
1526
1565
  );
1527
1566
  }
1528
1567
  return null;
@@ -21,7 +21,9 @@ var ChatComponents = (() => {
21
21
  var dist_exports = {};
22
22
  __export(dist_exports, {
23
23
  FORM_FENCE: () => FORM_FENCE,
24
+ INPUT_TYPES: () => INPUT_TYPES,
24
25
  INTERACTION_FENCE: () => INTERACTION_FENCE,
26
+ MAX_ACCEPT: () => MAX_ACCEPT,
25
27
  MAX_COMPONENTS: () => MAX_COMPONENTS,
26
28
  MAX_CUSTOM_ID_LEN: () => MAX_CUSTOM_ID_LEN,
27
29
  MAX_ID_LEN: () => MAX_ID_LEN,
@@ -48,7 +50,18 @@ var ChatComponents = (() => {
48
50
  var MAX_VALUE_LEN = 512;
49
51
  var MAX_CUSTOM_ID_LEN = 128;
50
52
  var MAX_PAYLOAD_CHARS = 64 * 1024;
51
- var RENDERABLE = /* @__PURE__ */ new Set(["select", "submit", "text"]);
53
+ var RENDERABLE = /* @__PURE__ */ new Set([
54
+ "select",
55
+ "submit",
56
+ "text",
57
+ "attachment"
58
+ ]);
59
+ var MAX_ACCEPT = 12;
60
+ var INPUT_TYPES = /* @__PURE__ */ new Set([
61
+ "select",
62
+ "text",
63
+ "attachment"
64
+ ]);
52
65
  var str = (v, max) => {
53
66
  if (typeof v !== "string")
54
67
  return void 0;
@@ -118,11 +131,21 @@ var ChatComponents = (() => {
118
131
  if (def)
119
132
  component.default = def;
120
133
  }
134
+ if (type === "attachment") {
135
+ if (Array.isArray(c.accept)) {
136
+ const accept = c.accept.slice(0, MAX_ACCEPT).map((a) => str(a, MAX_LABEL_LEN)).filter((a) => a !== void 0);
137
+ if (accept.length)
138
+ component.accept = accept;
139
+ }
140
+ if (typeof c.max_bytes === "number" && Number.isFinite(c.max_bytes) && c.max_bytes > 0) {
141
+ component.max_bytes = Math.floor(c.max_bytes);
142
+ }
143
+ }
121
144
  out.push(component);
122
145
  }
123
146
  if (!out.some((c) => c.type === "submit"))
124
147
  return null;
125
- if (!out.some((c) => c.type === "select" || c.type === "text"))
148
+ if (!out.some((c) => INPUT_TYPES.has(c.type)))
126
149
  return null;
127
150
  return { custom_id: customId, components: out };
128
151
  }
@@ -164,7 +187,15 @@ var ChatComponents = (() => {
164
187
  const key = str(k, MAX_ID_LEN);
165
188
  if (!key)
166
189
  continue;
167
- clean[key] = typeof v === "string" ? v.slice(0, MAX_VALUE_LEN) : "";
190
+ if (typeof v === "string") {
191
+ clean[key] = v.slice(0, MAX_VALUE_LEN);
192
+ continue;
193
+ }
194
+ const fileId = str(v == null ? void 0 : v.fileId, 128);
195
+ const name = str(v == null ? void 0 : v.name, MAX_LABEL_LEN);
196
+ if (fileId && name && typeof (v == null ? void 0 : v.size) === "number" && Number.isFinite(v.size)) {
197
+ clean[key] = { fileId, name, size: v.size };
198
+ }
168
199
  }
169
200
  return { custom_id: customId.slice(0, MAX_CUSTOM_ID_LEN), component: component.slice(0, MAX_ID_LEN), values: clean };
170
201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Beagle — P2P chat, file transfer and calls for regular users, on the Decent Network. No admin privilege required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "start": "node dist/cli.js"
26
26
  },
27
27
  "dependencies": {
28
- "@decentnetwork/chat-components": "^0.1.2",
28
+ "@decentnetwork/chat-components": "^0.1.3",
29
29
  "@decentnetwork/lan": "^0.1.256",
30
30
  "@decentnetwork/peer": "^0.1.123",
31
31
  "@decentnetwork/peer-webrtc": "^0.2.10",