@decentnetwork/lan 0.1.141 → 0.1.142

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.
@@ -52,6 +52,7 @@ const ICON_PATHS = {
52
52
  trash: '<path d="M3 6h18"/><path d="M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6"/><path d="M18.5 6l-1 13.5a2 2 0 0 1-2 1.5h-7a2 2 0 0 1-2-1.5L5.5 6"/><path d="M10 10.5v6M14 10.5v6"/>',
53
53
  arrowDown: '<path d="M12 4v15M6 13l6 6 6-6"/>',
54
54
  shrink: '<path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="m14 10 7-7"/><path d="m3 21 7-7"/>',
55
+ maximize: '<path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M21 8V5a2 2 0 0 0-2-2h-3"/><path d="M3 16v3a2 2 0 0 0 2 2h3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/>',
55
56
  speaker: '<path d="M11 5 6 9H3v6h3l5 4V5Z"/><path d="M16 9a3 3 0 0 1 0 6"/><path d="M19 7a6 6 0 0 1 0 10"/>',
56
57
  micFill: '<rect x="9" y="2.5" width="6" height="11.5" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0"/><path d="M12 17.5v3.5"/>'
57
58
  };
@@ -1030,9 +1031,9 @@ const inputStyle = {
1030
1031
  outline: "none",
1031
1032
  minWidth: 0
1032
1033
  };
1033
- function Msg({ m, peer, T }) {
1034
+ function Msg({ m, peer, T, onTheater }) {
1034
1035
  const mine = m.from === "me";
1035
- return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: mine ? "flex-end" : "flex-start", alignItems: "flex-end", gap: 8, margin: "3px 0" } }, !mine && /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 24, radius: 6, dot: false }), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: "64%", display: "flex", flexDirection: "column", alignItems: mine ? "flex-end" : "flex-start" } }, m.file ? (
1036
+ return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: mine ? "flex-end" : "flex-start", alignItems: "flex-end", gap: 8, margin: "3px 0" } }, !mine && /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 24, radius: 6, dot: false }), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: m.file && (m.file.media === "image" || m.file.media === "video") ? "min(680px, 88%)" : "64%", display: "flex", flexDirection: "column", alignItems: mine ? "flex-end" : "flex-start" } }, m.file ? (
1036
1037
  // Inline preview/player. Received media: as soon as it's saved.
1037
1038
  // Sent media: once it's fully delivered ('sent') — the daemon keeps a
1038
1039
  // local copy so it plays on the sender's side too.
@@ -1042,27 +1043,42 @@ function Msg({ m, peer, T }) {
1042
1043
  display: "flex",
1043
1044
  flexDirection: "column",
1044
1045
  gap: 6,
1045
- maxWidth: 300,
1046
+ maxWidth: m.file.media === "image" || m.file.media === "video" ? "100%" : 300,
1047
+ width: m.file.media === "image" || m.file.media === "video" ? "100%" : void 0,
1046
1048
  background: "var(--bub-them)",
1047
1049
  border: "1px solid var(--line)",
1048
1050
  borderRadius: 12,
1049
1051
  padding: 6
1050
- } }, m.file.media === "image" && /* @__PURE__ */ React.createElement("a", { href: dkFileUrl(m.file.name), target: "_blank", rel: "noreferrer", style: { display: "block", lineHeight: 0 } }, /* @__PURE__ */ React.createElement(
1052
+ } }, (m.file.media === "image" || m.file.media === "video") && /* @__PURE__ */ React.createElement("div", { style: { position: "relative", lineHeight: 0 } }, m.file.media === "image" ? /* @__PURE__ */ React.createElement(
1051
1053
  "img",
1052
1054
  {
1053
1055
  src: dkFileUrl(m.file.name),
1054
1056
  alt: m.file.name,
1055
- style: { display: "block", maxWidth: "100%", maxHeight: 280, borderRadius: 8, objectFit: "cover" }
1057
+ style: { display: "block", width: "100%", maxHeight: "72vh", borderRadius: 8, objectFit: "contain" }
1056
1058
  }
1057
- )), m.file.media === "video" && /* @__PURE__ */ React.createElement(
1059
+ ) : /* @__PURE__ */ React.createElement(
1058
1060
  "video",
1059
1061
  {
1060
1062
  src: dkFileUrl(m.file.name),
1061
1063
  controls: true,
1062
1064
  preload: "metadata",
1063
- style: { maxWidth: "100%", maxHeight: 320, borderRadius: 8, background: "#000" }
1065
+ style: { display: "block", width: "100%", maxHeight: "72vh", borderRadius: 8, objectFit: "contain", background: "#000" }
1064
1066
  }
1065
- ), m.file.media === "audio" && /* @__PURE__ */ React.createElement("audio", { src: dkFileUrl(m.file.name), controls: true, style: { width: "100%" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "0 2px" } }, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, m.file.name), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", flexShrink: 0 } }, m.file.size), /* @__PURE__ */ React.createElement("a", { href: dkFileDownloadUrl(m.file.name), download: m.file.name, title: "download", style: { display: "inline-flex", flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "download", size: 15, stroke: 2, color: "var(--accent)" }))))
1067
+ ), /* @__PURE__ */ React.createElement("button", { onClick: () => onTheater({ kind: m.file.media, url: dkFileUrl(m.file.name), name: m.file.name }), title: "theater / \u7F51\u9875\u5168\u5C4F", style: {
1068
+ position: "absolute",
1069
+ top: 8,
1070
+ right: 8,
1071
+ width: 30,
1072
+ height: 30,
1073
+ borderRadius: 8,
1074
+ border: "none",
1075
+ background: "rgba(0,0,0,0.5)",
1076
+ cursor: "pointer",
1077
+ display: "flex",
1078
+ alignItems: "center",
1079
+ justifyContent: "center",
1080
+ lineHeight: 0
1081
+ } }, /* @__PURE__ */ React.createElement(Icon, { name: "maximize", size: 17, stroke: 2, color: "#fff" }))), m.file.media === "audio" && /* @__PURE__ */ React.createElement("audio", { src: dkFileUrl(m.file.name), controls: true, style: { width: "100%" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "0 2px" } }, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, m.file.name), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)", flexShrink: 0 } }, m.file.size), /* @__PURE__ */ React.createElement("a", { href: dkFileDownloadUrl(m.file.name), download: m.file.name, title: "download", style: { display: "inline-flex", flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "download", size: 15, stroke: 2, color: "var(--accent)" }))))
1066
1082
  ) : (() => {
1067
1083
  const cardStyle = {
1068
1084
  display: "flex",
@@ -1129,9 +1145,21 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
1129
1145
  const [draft, setDraft] = React.useState("");
1130
1146
  const [dragOver, setDragOver] = React.useState(false);
1131
1147
  const [sending, setSending] = React.useState(null);
1148
+ const [theater, setTheater] = React.useState(null);
1132
1149
  React.useEffect(() => {
1133
1150
  setDraft("");
1134
1151
  }, [peer.id]);
1152
+ React.useEffect(() => {
1153
+ setTheater(null);
1154
+ }, [peer.id]);
1155
+ React.useEffect(() => {
1156
+ if (!theater) return;
1157
+ const onKey = (e) => {
1158
+ if (e.key === "Escape") setTheater(null);
1159
+ };
1160
+ window.addEventListener("keydown", onKey);
1161
+ return () => window.removeEventListener("keydown", onKey);
1162
+ }, [theater]);
1135
1163
  const sendDraft = () => {
1136
1164
  if (draft.trim()) {
1137
1165
  onSend(draft);
@@ -1172,7 +1200,7 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
1172
1200
  setMenu(false);
1173
1201
  onRemove(peer);
1174
1202
  } }))))),
1175
- /* @__PURE__ */ React.createElement("div", { ref: scrollRef, style: { flex: 1, overflow: "auto", padding: "18px 22px" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto" } }, thread.map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(Msg, { key: i, m, peer, T })))),
1203
+ /* @__PURE__ */ React.createElement("div", { ref: scrollRef, style: { flex: 1, overflow: "auto", padding: "18px 22px" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto" } }, thread.map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(Msg, { key: i, m, peer, T, onTheater: setTheater })))),
1176
1204
  /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, borderTop: "1px solid var(--line)", padding: "12px 16px", background: "var(--panel)" } }, sending && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto 8px", display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "paperclip", size: 13, stroke: 2, color: "var(--accent)" }), (lang === "zh" ? "\u53D1\u9001\u4E2D: " : "Sending: ") + sending.name), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement(
1177
1205
  "input",
1178
1206
  {
@@ -1198,7 +1226,53 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
1198
1226
  placeholder: `${T.message} ${peer.alias || shortKey(peer.userId, 6, 4)}\u2026`,
1199
1227
  style: { flex: 1, height: 38, borderRadius: 9, border: "1px solid var(--line)", background: "var(--panel-2)", color: "var(--text)", fontFamily: "var(--ui)", fontSize: 13.5, padding: "0 14px", outline: "none", minWidth: 0 }
1200
1228
  }
1201
- ), /* @__PURE__ */ React.createElement(Btn, { tone: "solid", icon: "arrowUp", title: T.send, onClick: sendDraft })))
1229
+ ), /* @__PURE__ */ React.createElement(Btn, { tone: "solid", icon: "arrowUp", title: T.send, onClick: sendDraft }))),
1230
+ theater && /* @__PURE__ */ React.createElement("div", { onClick: () => setTheater(null), style: {
1231
+ position: "absolute",
1232
+ inset: 0,
1233
+ zIndex: 60,
1234
+ background: "rgba(0,0,0,0.86)",
1235
+ display: "flex",
1236
+ flexDirection: "column"
1237
+ } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { flexShrink: 0, height: 46, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, padding: "0 14px" } }, /* @__PURE__ */ React.createElement("span", { style: { minWidth: 0, fontFamily: "var(--mono)", fontSize: 12, color: "rgba(255,255,255,0.72)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, theater.name), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, flexShrink: 0 } }, /* @__PURE__ */ React.createElement(
1238
+ "button",
1239
+ {
1240
+ title: lang === "zh" ? "\u5168\u5C4F" : "fullscreen",
1241
+ onClick: () => {
1242
+ const el = document.getElementById("dk-theater-media");
1243
+ if (el && el.requestFullscreen) el.requestFullscreen();
1244
+ },
1245
+ style: { width: 34, height: 34, borderRadius: 8, border: "none", background: "rgba(255,255,255,0.14)", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }
1246
+ },
1247
+ /* @__PURE__ */ React.createElement(Icon, { name: "maximize", size: 18, stroke: 2, color: "#fff" })
1248
+ ), /* @__PURE__ */ React.createElement(
1249
+ "button",
1250
+ {
1251
+ title: (lang === "zh" ? "\u5173\u95ED" : "close") + " \xB7 Esc",
1252
+ onClick: () => setTheater(null),
1253
+ style: { width: 34, height: 34, borderRadius: 8, border: "none", background: "rgba(255,255,255,0.14)", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }
1254
+ },
1255
+ /* @__PURE__ */ React.createElement(Icon, { name: "x", size: 20, stroke: 2.2, color: "#fff" })
1256
+ ))), /* @__PURE__ */ React.createElement("div", { onClick: () => setTheater(null), style: { flex: 1, minHeight: 0, display: "flex", alignItems: "center", justifyContent: "center", padding: "0 16px 16px" } }, theater.kind === "video" ? /* @__PURE__ */ React.createElement(
1257
+ "video",
1258
+ {
1259
+ id: "dk-theater-media",
1260
+ src: theater.url,
1261
+ controls: true,
1262
+ autoPlay: true,
1263
+ onClick: (e) => e.stopPropagation(),
1264
+ style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain", borderRadius: 6, background: "#000" }
1265
+ }
1266
+ ) : /* @__PURE__ */ React.createElement(
1267
+ "img",
1268
+ {
1269
+ id: "dk-theater-media",
1270
+ src: theater.url,
1271
+ alt: theater.name,
1272
+ onClick: (e) => e.stopPropagation(),
1273
+ style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain", borderRadius: 6 }
1274
+ }
1275
+ )))
1202
1276
  );
1203
1277
  }
1204
1278
  function MenuItem({ icon, label, onClick, danger }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.141",
3
+ "version": "0.1.142",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -79,7 +79,7 @@
79
79
  },
80
80
  "dependencies": {
81
81
  "@decentnetwork/dora": "^0.1.11",
82
- "@decentnetwork/peer": "^0.1.74",
82
+ "@decentnetwork/peer": "^0.1.75",
83
83
  "ink": "^5.2.1",
84
84
  "js-yaml": "^4.1.0",
85
85
  "node-forge": "^1.4.0",