@elefunc/send 0.1.8 → 0.1.9
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/package.json +1 -1
- package/src/tui/app.ts +35 -10
package/package.json
CHANGED
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 = "
|
|
118
|
+
const ABOUT_INTRO = "Peer-to-Peer Transfers for the Web and 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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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),
|