@elefunc/send 0.1.8 → 0.1.10

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tui/app.ts +35 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elefunc/send",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Browser-compatible file transfer CLI and TUI powered by Bun, WebRTC, and Rezi.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/tui/app.ts CHANGED
@@ -115,7 +115,7 @@ const DEFAULT_WEB_URL = "https://send.rt.ht/"
115
115
  const DEFAULT_SAVE_DIR = resolve(process.cwd(), "downloads")
116
116
  const ABOUT_ELEFUNC_URL = "https://elefunc.com"
117
117
  const ABOUT_TITLE = "About Send"
118
- const ABOUT_INTRO = "Room-based peer-to-peer file transfers for the web and terminal"
118
+ const ABOUT_INTRO = "Peer-to-Peer Transfers Web & CLI"
119
119
  const ABOUT_SUMMARY = "Join the same room, see who is there, and offer files directly to selected peers."
120
120
  const ABOUT_RUNTIME = "Send uses lightweight signaling to discover peers and negotiate WebRTC. Files move over WebRTC data channels, using direct paths when possible and TURN relay when needed."
121
121
  const ABOUT_CLI_LABEL = "bunx @elefunc/send@latest"
@@ -133,6 +133,9 @@ const pluralRules = new Intl.PluralRules()
133
133
  export const visiblePanes = (showEvents: boolean): VisiblePane[] => showEvents ? ["peers", "transfers", "logs"] : ["peers", "transfers"]
134
134
 
135
135
  const noop = () => {}
136
+ export const isEditableFocusId = (focusedId: string | null) =>
137
+ focusedId === ROOM_INPUT_ID || focusedId === NAME_INPUT_ID || focusedId === PEER_SEARCH_INPUT_ID || focusedId === DRAFT_INPUT_ID
138
+ export const shouldSwallowQQuit = (focusedId: string | null) => !isEditableFocusId(focusedId)
136
139
 
137
140
  const hashBool = (value: boolean) => value ? "1" : "0"
138
141
  const safeShellArgPattern = /^[A-Za-z0-9._/:?=&,+@%-]+$/
@@ -201,7 +204,7 @@ const renderCliCommand = (state: ShareCliState, { includeSelf = false, includePr
201
204
  }
202
205
 
203
206
  export const webInviteUrl = (state: ShareUrlState, baseUrl = resolveWebUrlBase()) => {
204
- return renderWebUrl(state, baseUrl, false)
207
+ return renderWebUrl(state, baseUrl)
205
208
  }
206
209
 
207
210
  export const aboutWebUrl = (state: ShareUrlState, baseUrl = DEFAULT_WEB_URL) => renderWebUrl(state, baseUrl)
@@ -708,21 +711,28 @@ const renderHeader = (state: TuiState, actions: TuiActions) => denseSection({
708
711
 
709
712
  const renderAboutModal = (state: TuiState, actions: TuiActions) => {
710
713
  const cliCommand = aboutCliCommand(state)
714
+ const cliCopyText = `${ABOUT_CLI_LABEL} ${cliCommand}`
715
+ const cliCopyUrl = `https://copy.rt.ht/#${new URLSearchParams({ text: cliCopyText })}`
711
716
  const currentWebUrl = aboutWebUrl(state)
712
717
  const currentWebLabel = aboutWebLabel(state)
713
718
  return ui.modal({
714
719
  id: "about-modal",
715
720
  title: ABOUT_TITLE,
716
721
  content: ui.column({ gap: 1 }, [
717
- ui.text(ABOUT_INTRO, { id: "about-intro", variant: "heading" }),
718
- ui.text(ABOUT_SUMMARY, { id: "about-summary" }),
719
- ui.text(ABOUT_RUNTIME, { id: "about-runtime" }),
722
+ ui.text(ABOUT_INTRO, { id: "about-intro", variant: "heading", wrap: true }),
723
+ ui.text(ABOUT_SUMMARY, { id: "about-summary", wrap: true }),
724
+ ui.text(ABOUT_RUNTIME, { id: "about-runtime", wrap: true }),
720
725
  ui.column({ gap: 0 }, [
721
- ui.text(ABOUT_CLI_LABEL, { id: "about-cli-label", variant: "caption" }),
722
- ui.text(cliCommand, { id: "about-current-cli" }),
726
+ ui.text(ABOUT_CLI_LABEL, { id: "about-cli-label", variant: "caption", wrap: true }),
727
+ ui.link({
728
+ id: "about-current-cli",
729
+ label: cliCommand,
730
+ accessibleLabel: "Copy current CLI command",
731
+ url: cliCopyUrl,
732
+ }),
723
733
  ]),
724
734
  ui.column({ gap: 0 }, [
725
- ui.text(ABOUT_WEB_LINK_LABEL, { id: "about-web-link-label", variant: "caption" }),
735
+ ui.text(ABOUT_WEB_LINK_LABEL, { id: "about-web-link-label", variant: "caption", wrap: true }),
726
736
  ui.link({
727
737
  id: "about-current-web-link",
728
738
  label: currentWebLabel,
@@ -743,6 +753,8 @@ const renderAboutModal = (state: TuiState, actions: TuiActions) => {
743
753
  width: 72,
744
754
  maxWidth: 84,
745
755
  minWidth: 54,
756
+ frameStyle: { background: rgb(0, 0, 0) },
757
+ backdrop: { variant: "none" },
746
758
  initialFocus: "close-about",
747
759
  returnFocusTo: ABOUT_TRIGGER_ID,
748
760
  onClose: actions.closeAbout,
@@ -782,6 +794,14 @@ const renderSelfMetric = (label: string, value: string) => ui.box({ flex: 1, min
782
794
  ])
783
795
 
784
796
  const renderSelfProfileLine = (value: string) => ui.text(value || "—")
797
+ const ipLookupUrl = (value: string) => value ? `https://gi.rt.ht/:${encodeURIComponent(value)}` : null
798
+ const renderIpProfileLine = (value: string) => {
799
+ const ip = value || ""
800
+ const url = ipLookupUrl(ip)
801
+ return url
802
+ ? ui.link({ label: ip, url, accessibleLabel: `Open IP lookup for ${ip}` })
803
+ : ui.text("—")
804
+ }
785
805
  const formatPeerRtt = (value: number) => Number.isFinite(value) ? `${countFormat.format(Math.round(value))}ms` : "—"
786
806
  const renderPeerMetric = (label: string, value: string, asTag = false) => ui.box({ flex: 1, minWidth: 10, border: "single", borderStyle: METRIC_BORDER_STYLE }, [
787
807
  ui.column({ gap: 0 }, [
@@ -815,7 +835,7 @@ const renderSelfCard = (state: TuiState, actions: TuiActions) => denseSection({
815
835
  renderSelfProfileLine(geoSummary(state.snapshot.profile)),
816
836
  renderSelfProfileLine(netSummary(state.snapshot.profile)),
817
837
  renderSelfProfileLine(uaSummary(state.snapshot.profile)),
818
- renderSelfProfileLine(profileIp(state.snapshot.profile)),
838
+ renderIpProfileLine(profileIp(state.snapshot.profile)),
819
839
  ]),
820
840
  ])
821
841
 
@@ -860,7 +880,7 @@ const renderPeerRow = (peer: PeerSnapshot, turnShareEnabled: boolean, actions: T
860
880
  renderSelfProfileLine(geoSummary(peer.profile)),
861
881
  renderSelfProfileLine(netSummary(peer.profile)),
862
882
  renderSelfProfileLine(uaSummary(peer.profile)),
863
- renderSelfProfileLine(profileIp(peer.profile)),
883
+ renderIpProfileLine(profileIp(peer.profile)),
864
884
  ]),
865
885
  peer.lastError ? ui.callout(peer.lastError, { variant: "error" }) : null,
866
886
  ]),
@@ -1678,6 +1698,11 @@ export const startTui = async (initialConfig: SessionConfig, launchOptions: TuiL
1678
1698
  })
1679
1699
  app.keys({
1680
1700
  "ctrl+c": { description: "Quit", handler: requestStop },
1701
+ q: {
1702
+ description: "no-op",
1703
+ when: ctx => shouldSwallowQQuit(ctx.focusedId),
1704
+ handler: noop,
1705
+ },
1681
1706
  tab: {
1682
1707
  description: "Accept focused preview row",
1683
1708
  when: ctx => ctx.focusedId === DRAFT_INPUT_ID && !!selectedFilePreviewMatch(state) && filePreviewVisible(state),