most-box 0.2.7 → 0.2.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.
Files changed (99) hide show
  1. package/README.md +9 -1
  2. package/electron/main.js +30 -420
  3. package/electron/preload.js +1 -16
  4. package/electron/updateChecker.js +6 -3
  5. package/electron/updateChecker.test.js +13 -97
  6. package/out/admin/index.html +0 -0
  7. package/out/app/index.html +0 -0
  8. package/out/assets/{FilePreviewOverlay-CW8lKJCw.js → FilePreviewOverlay-BYZIAMJ-.js} +1 -1
  9. package/out/assets/LanguageToggle-CSQtRZ3A.js +1 -0
  10. package/out/assets/MarketingHeader-ceWdRCpe.js +1 -0
  11. package/out/assets/MarketingLayout-Bun1d2wL.js +1 -0
  12. package/out/assets/{MarketingThemeToggle-Cl7PhFgv.js → MarketingThemeToggle-CIKP8x9W.js} +1 -1
  13. package/out/assets/{MilkdownEditor-B5SKAyjg.js → MilkdownEditor-CT6SHYKG.js} +1 -1
  14. package/out/assets/{OpenSidebarButton-Bdx86oG8.js → OpenSidebarButton-CLSCB7CG.js} +1 -1
  15. package/out/assets/{SidebarAccount-c8jirEMF.js → SidebarAccount-CNh0kvtB.js} +1 -1
  16. package/out/assets/SidebarHomeLink-DXv0c9Ob.js +1 -0
  17. package/out/assets/arrow-right-BU9c_SL4.js +1 -0
  18. package/out/assets/{circle-alert-Dkgyw_GE.js → circle-alert-iuHzciph.js} +1 -1
  19. package/out/assets/{copy-KIwotKoP.js → copy-DEzFUzeX.js} +1 -1
  20. package/out/assets/{download-DPA-3LpR.js → download-jEPn2njL.js} +1 -1
  21. package/out/assets/{downloadValidation-CH8BeY7_.js → downloadValidation-Bzl7cci9.js} +1 -1
  22. package/out/assets/{external-link-BmR2rZ80.js → external-link-BFaoZeK0.js} +1 -1
  23. package/out/assets/{file-text-BJQfDW9r.js → file-text-DPA1riJ_.js} +1 -1
  24. package/out/assets/{folder-Gb80aQnO.js → folder-Cfgc70_F.js} +1 -1
  25. package/out/assets/{hard-drive-P2dGA7FX.js → hard-drive-Dar_AZEq.js} +1 -1
  26. package/out/assets/i18n-NjvOSuV6.js +1 -0
  27. package/out/assets/index-BHZhApbo.css +1 -0
  28. package/out/assets/index-BI8oNSVC.js +31 -0
  29. package/out/assets/index.lazy-7omTRykl.js +2 -0
  30. package/out/assets/index.lazy-BOMHingE.js +3 -0
  31. package/out/assets/index.lazy-BSY5_gby.js +1 -0
  32. package/out/assets/{index.lazy-CIqPn0cy.js → index.lazy-C4ipXKZ7.js} +1 -1
  33. package/out/assets/index.lazy-CPZbEnJZ.js +1 -0
  34. package/out/assets/{index.lazy-Bh4jDrUl.js → index.lazy-Ch4Y17pJ.js} +1 -1
  35. package/out/assets/index.lazy-CvBIIYbm.js +1 -0
  36. package/out/assets/{index.lazy-B7u3Ztoe.js → index.lazy-Cz-OZwYL.js} +1 -1
  37. package/out/assets/index.lazy-D4zNn3D6.js +2 -0
  38. package/out/assets/index.lazy-DRY3xoQp.js +1 -0
  39. package/out/assets/{index.lazy-DAKkwnV2.js → index.lazy-_mEd0Q_u.js} +1 -1
  40. package/out/assets/index.lazy-bAp_Hgck.js +1 -0
  41. package/out/assets/index.lazy-ur0joIFl.js +1 -0
  42. package/out/assets/{key-round-DvpIlAJY.js → key-round-DNcpSuMw.js} +1 -1
  43. package/out/assets/{loader-8eVY8_SD.js → loader-BXQtdens.js} +1 -1
  44. package/out/assets/{lock-Cvtl60UJ.js → lock-CLIE8cct.js} +1 -1
  45. package/out/assets/{message-square-JK7ABH3O.js → message-square-Dp09WY1I.js} +1 -1
  46. package/out/assets/{mp-CMvgV1-c.js → mp-B6kdA94c.js} +1 -1
  47. package/out/assets/{music-YRSH1Jq6.js → music-B19Tg557.js} +1 -1
  48. package/out/assets/{notebook-pen-BjGx0coX.js → notebook-pen-C8TlK5sB.js} +1 -1
  49. package/out/assets/play-B-BjsTZz.js +1 -0
  50. package/out/assets/plus-4hygj-1Z.js +1 -0
  51. package/out/assets/{save-BcI68ZgS.js → save-BjrbPwhJ.js} +1 -1
  52. package/out/assets/search-DSkiWdvX.js +1 -0
  53. package/out/assets/{send-_ups0_3f.js → send-BL4d5iOt.js} +1 -1
  54. package/out/assets/{shield-check-Dzw5rdPe.js → shield-check-DzDuWO8r.js} +1 -1
  55. package/out/assets/{smartphone-DSQ5L_sr.js → smartphone-tRRFFO0a.js} +1 -1
  56. package/out/assets/{trash-2-BIrBn6Ce.js → trash-2-V12tJ8lI.js} +1 -1
  57. package/out/assets/triangle-alert-DGWc8ZHu.js +1 -0
  58. package/out/assets/{ui-u9yJHkb-.js → ui-BpDGD3IR.js} +2 -2
  59. package/out/assets/{upload-1jmNeGjH.js → upload-CoCptKya.js} +1 -1
  60. package/out/assets/{useChannelMessages-DNxfCew-.js → useChannelMessages-BeKF0gwo.js} +1 -1
  61. package/out/assets/{useGameRoom-Du07VEgb.js → useGameRoom-cs9YpYt7.js} +1 -1
  62. package/out/assets/{userProfile-CL1Ev2GK.js → userProfile-qcc60tHK.js} +1 -1
  63. package/out/assets/{wallet-BvM_X5nW.js → wallet-T7ONZ3Kg.js} +1 -1
  64. package/out/assets/{wifi-66t1dsgC.js → wifi-Bgo8eHY0.js} +1 -1
  65. package/out/chat/index.html +0 -0
  66. package/out/chat/join/index.html +0 -0
  67. package/out/download/index.html +2 -2
  68. package/out/game/gandengyan/index.html +0 -0
  69. package/out/game/index.html +0 -0
  70. package/out/game/zhajinhua/index.html +0 -0
  71. package/out/index.html +2 -2
  72. package/out/note/index.html +0 -0
  73. package/out/ping/index.html +2 -2
  74. package/out/profile/index.html +0 -0
  75. package/out/web3/index.html +0 -0
  76. package/package.json +1 -8
  77. package/server/src/core/releaseManifest.js +1 -18
  78. package/server/src/index.js +34 -286
  79. package/out/assets/LanguageToggle-Cfh33R2h.js +0 -1
  80. package/out/assets/MarketingHeader-BKBngzf5.js +0 -1
  81. package/out/assets/MarketingLayout-C3xrXmfU.js +0 -1
  82. package/out/assets/SidebarHomeLink-LzYXrJRr.js +0 -1
  83. package/out/assets/arrow-right-BC_GWahx.js +0 -1
  84. package/out/assets/i18n-Cqyi6NZb.js +0 -1
  85. package/out/assets/index-CSBg7dZN.css +0 -1
  86. package/out/assets/index-CiHRnvMj.js +0 -31
  87. package/out/assets/index.lazy-BdOfbfjo.js +0 -2
  88. package/out/assets/index.lazy-Bo6McUf1.js +0 -1
  89. package/out/assets/index.lazy-BxUje7fk.js +0 -1
  90. package/out/assets/index.lazy-CfuoMz6P.js +0 -1
  91. package/out/assets/index.lazy-DRaxm9OS.js +0 -1
  92. package/out/assets/index.lazy-DkrwcEBu.js +0 -2
  93. package/out/assets/index.lazy-K6IB-5kb.js +0 -3
  94. package/out/assets/index.lazy-Uu9DCtrI.js +0 -1
  95. package/out/assets/index.lazy-mXESMo49.js +0 -1
  96. package/out/assets/play-Ds19moWN.js +0 -1
  97. package/out/assets/plus-CvGkRhJV.js +0 -1
  98. package/out/assets/search-BIa4RSPA.js +0 -1
  99. package/out/assets/triangle-alert-CgWIEl4u.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "most-box",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "MostBox - P2P file sharing application",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -164,13 +164,6 @@
164
164
  "x64",
165
165
  "arm64"
166
166
  ]
167
- },
168
- {
169
- "target": "zip",
170
- "arch": [
171
- "x64",
172
- "arm64"
173
- ]
174
167
  }
175
168
  ],
176
169
  "icon": "public/logo-512.png",
@@ -1,6 +1,6 @@
1
1
  export const RELEASE_PLATFORMS = ['windows', 'macos', 'linux']
2
2
  export const RELEASE_ARCHES = ['x64', 'arm64']
3
- export const RELEASE_ASSET_KINDS = ['installer', 'updater']
3
+ export const RELEASE_ASSET_KINDS = ['installer']
4
4
 
5
5
  export const RELEASE_TARGETS = Object.freeze([
6
6
  { platform: 'windows', arch: 'x64' },
@@ -89,20 +89,3 @@ export function resolveReleaseAssetDownload(asset, preferredSource = 'r2') {
89
89
  : '',
90
90
  }
91
91
  }
92
-
93
- export function findCompatibleUpdateAsset(manifest, platform, arch) {
94
- if (!isReleaseManifest(manifest)) return null
95
-
96
- const compatibleAssets = manifest.assets.filter(
97
- asset =>
98
- asset.platform === platform &&
99
- asset.arch === arch &&
100
- (asset.kind === 'updater' || asset.kind === 'installer')
101
- )
102
-
103
- return (
104
- compatibleAssets.find(asset => asset.kind === 'updater') ||
105
- compatibleAssets.find(asset => asset.kind === 'installer') ||
106
- null
107
- )
108
- }
@@ -1940,290 +1940,6 @@ export class MostBoxEngine extends EventEmitter {
1940
1940
  return result
1941
1941
  }
1942
1942
 
1943
- /**
1944
- * 按 CID 将内容下载到指定路径,不写入用户文件列表。
1945
- * 用于桌面客户端更新等内部缓存场景。
1946
- * @param {object} input - 下载参数
1947
- * @param {string} input.cid - 文件 CID
1948
- * @param {string} input.targetPath - 目标文件路径
1949
- * @param {string} [input.fileName] - 持有记录展示名
1950
- * @param {string} [input.taskId] - 下载任务 ID
1951
- * @param {number} [input.timeout] - 等待 P2P 内容的超时时间
1952
- * @param {number} [input.streamReadTimeout] - 下载流无进度超时时间
1953
- * @param {boolean} [input.overwrite=true] - 是否覆盖已存在目标文件
1954
- * @param {string} [input.source='update'] - holding 来源
1955
- */
1956
- async downloadCidToPath(input = {}) {
1957
- this.#ensureInitialized()
1958
-
1959
- const cid = input.cid
1960
- const targetPath = input.targetPath
1961
- if (!cid) throw new ValidationError('cid is required')
1962
- if (!targetPath) throw new ValidationError('targetPath is required')
1963
-
1964
- this.#getCidInfo(cid)
1965
- const taskId =
1966
- input.taskId || `cid_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
1967
- const taskState = { aborted: false, readStream: null, writeStream: null }
1968
- const timeout = input.timeout || this.#options.downloadTimeout
1969
- const streamReadTimeout =
1970
- input.streamReadTimeout ?? STREAM_READ_TIMEOUT
1971
- const fileName = sanitizeFilename(input.fileName || path.basename(targetPath))
1972
- const overwrite = input.overwrite !== false
1973
- const resolvedTargetPath = path.resolve(targetPath)
1974
- const targetDir = path.dirname(resolvedTargetPath)
1975
- const tempPath = `${resolvedTargetPath}.part`
1976
-
1977
- this.#activeDownloads.set(taskId, taskState)
1978
-
1979
- try {
1980
- const writableCheck = await checkDirectoryWritable(targetDir)
1981
- if (!writableCheck.writable) {
1982
- throw new PermissionError(writableCheck.error)
1983
- }
1984
- if (!overwrite && fs.existsSync(resolvedTargetPath)) {
1985
- throw new ConflictError(`File already exists: ${resolvedTargetPath}`)
1986
- }
1987
- fs.rmSync(tempPath, { force: true })
1988
-
1989
- const { driveName } = this.#getCidInfo(cid)
1990
- let drive = this.#drives.get(driveName)
1991
- if (!drive) {
1992
- drive = await this.#getOrCreateDrive(driveName, {
1993
- server: true,
1994
- client: true,
1995
- })
1996
- this.emit('download:status', { taskId, status: 'connecting' })
1997
- }
1998
-
1999
- await this.#joinCidTopicInternal(cid, {
2000
- server: false,
2001
- client: true,
2002
- })
2003
-
2004
- if (taskState.aborted) throw new Error('Download cancelled')
2005
- this.emit('download:status', { taskId, status: 'finding-peers' })
2006
-
2007
- const driveKey = '/' + cid
2008
- const entry = await this.#waitForDriveEntry(
2009
- drive,
2010
- driveKey,
2011
- timeout,
2012
- taskId,
2013
- taskState
2014
- )
2015
-
2016
- if (!entry) {
2017
- throw new PeerNotFoundError(
2018
- '当前没有发现可下载的在线种子,请稍后重试或确认发布者在线'
2019
- )
2020
- }
2021
-
2022
- let totalBytes = 0
2023
- try {
2024
- const stat = await drive.entry(entry.key)
2025
- totalBytes = Number(stat?.value?.blob?.byteLength) || 0
2026
- } catch {}
2027
-
2028
- this.emit('download:status', {
2029
- taskId,
2030
- status: 'downloading',
2031
- file: fileName,
2032
- size: totalBytes ? formatFileSize(totalBytes) : null,
2033
- })
2034
-
2035
- const rs = drive.createReadStream(entry.key)
2036
- const ws = fs.createWriteStream(tempPath)
2037
- taskState.readStream = rs
2038
- taskState.writeStream = ws
2039
-
2040
- let loadedBytes = 0
2041
- let lastProgressUpdate = 0
2042
-
2043
- await new Promise((resolve, reject) => {
2044
- let settled = false
2045
- let readTimer = null
2046
-
2047
- const clearReadTimer = () => {
2048
- if (readTimer) {
2049
- clearTimeout(readTimer)
2050
- readTimer = null
2051
- }
2052
- }
2053
-
2054
- const fail = err => {
2055
- if (settled) return
2056
- settled = true
2057
- clearReadTimer()
2058
- rs.destroy(err)
2059
- ws.destroy()
2060
- fs.unlink(tempPath, () => {})
2061
- reject(err)
2062
- }
2063
-
2064
- const complete = () => {
2065
- if (settled) return
2066
- settled = true
2067
- clearReadTimer()
2068
- resolve()
2069
- }
2070
-
2071
- const resetReadTimer = () => {
2072
- clearReadTimer()
2073
- if (streamReadTimeout > 0) {
2074
- readTimer = setTimeout(() => {
2075
- fail(
2076
- new Error(
2077
- `Download stalled: no data received for ${streamReadTimeout / 1000}s`
2078
- )
2079
- )
2080
- }, streamReadTimeout)
2081
- }
2082
- }
2083
-
2084
- resetReadTimer()
2085
-
2086
- rs.on('data', chunk => {
2087
- if (taskState.aborted) {
2088
- fail(new Error('Download cancelled'))
2089
- return
2090
- }
2091
- resetReadTimer()
2092
- loadedBytes += chunk.length
2093
- const now = Date.now()
2094
- if (
2095
- totalBytes > 0 &&
2096
- now - lastProgressUpdate > PROGRESS_THROTTLE
2097
- ) {
2098
- lastProgressUpdate = now
2099
- this.emit('download:progress', {
2100
- taskId,
2101
- loaded: loadedBytes,
2102
- total: totalBytes,
2103
- percent: Math.round((loadedBytes / totalBytes) * 100),
2104
- })
2105
- }
2106
- })
2107
-
2108
- rs.pipe(ws)
2109
- ws.on('finish', complete)
2110
- ws.on('error', fail)
2111
- rs.on('error', fail)
2112
- rs.on('close', () => {
2113
- if (taskState.aborted) fail(new Error('Download cancelled'))
2114
- })
2115
- ws.on('close', () => {
2116
- if (taskState.aborted) fail(new Error('Download cancelled'))
2117
- })
2118
- })
2119
-
2120
- if (taskState.aborted) throw new Error('Download cancelled')
2121
-
2122
- this.emit('download:status', { taskId, status: 'verifying' })
2123
- const { cid: downloadedCid, size } = await calculateCid(tempPath)
2124
- if (downloadedCid.toString() !== cid) {
2125
- fs.unlinkSync(tempPath)
2126
- throw new IntegrityError(
2127
- `File content CID mismatch. Expected ${cid}, got ${downloadedCid.toString()}.`
2128
- )
2129
- }
2130
-
2131
- if (fs.existsSync(resolvedTargetPath)) {
2132
- fs.rmSync(resolvedTargetPath, { force: true })
2133
- }
2134
- fs.renameSync(tempPath, resolvedTargetPath)
2135
-
2136
- await this.seedCidFileFromPath(cid, resolvedTargetPath, {
2137
- fileName,
2138
- source: input.source || 'update',
2139
- })
2140
-
2141
- const result = {
2142
- taskId,
2143
- cid,
2144
- fileName,
2145
- savedPath: resolvedTargetPath,
2146
- size,
2147
- }
2148
- this.emit('download:success', result)
2149
- return result
2150
- } finally {
2151
- this.#activeDownloads.delete(taskId)
2152
- fs.rmSync(tempPath, { force: true })
2153
- }
2154
- }
2155
-
2156
- /**
2157
- * 将本地文件按既有 CID 写入 Hyperdrive 并记录 holding,不写入用户文件列表。
2158
- * @param {string} cid - 文件 CID
2159
- * @param {string} filePath - 本地文件路径
2160
- * @param {object} [options] - 选项
2161
- * @param {string} [options.fileName] - 持有记录展示名
2162
- * @param {string} [options.source='update'] - holding 来源
2163
- */
2164
- async seedCidFileFromPath(cid, filePath, options = {}) {
2165
- this.#ensureInitialized()
2166
- if (!cid) throw new ValidationError('cid is required')
2167
- if (!filePath) throw new ValidationError('filePath is required')
2168
-
2169
- this.#getCidInfo(cid)
2170
- const sizeValidation = await validateFileSize(
2171
- filePath,
2172
- this.#options.maxFileSize
2173
- )
2174
- if (!sizeValidation.valid) {
2175
- throw new FileSizeError(sizeValidation.error, sizeValidation.size)
2176
- }
2177
-
2178
- const { cid: actualCid } = await calculateCid(filePath)
2179
- const actualCidString = actualCid.toString()
2180
- if (actualCidString !== cid) {
2181
- throw new IntegrityError(
2182
- `File content CID mismatch. Expected ${cid}, got ${actualCidString}.`
2183
- )
2184
- }
2185
-
2186
- const existingHolding = this.#holdings.find(holding => holding.cid === cid)
2187
- if (!existingHolding) {
2188
- this.#checkCapacity(sizeValidation.size || 0)
2189
- }
2190
-
2191
- const { driveName } = this.#getCidInfo(cid)
2192
- const drive = await this.#getOrCreateDrive(driveName, {
2193
- server: true,
2194
- client: false,
2195
- })
2196
- const driveKey = '/' + cid
2197
- const localContent = await this.#getLocalCidContent(cid, {
2198
- public: true,
2199
- allowHoldingFallback: true,
2200
- })
2201
-
2202
- if (!localContent) {
2203
- const readStream = fs.createReadStream(filePath)
2204
- const writeStream = drive.createWriteStream(driveKey)
2205
- await new Promise((resolve, reject) => {
2206
- readStream.pipe(writeStream)
2207
- writeStream.on('finish', resolve)
2208
- writeStream.on('error', reject)
2209
- readStream.on('error', reject)
2210
- })
2211
- }
2212
-
2213
- await this.#joinCidTopicInternal(cid, {
2214
- server: true,
2215
- client: false,
2216
- })
2217
-
2218
- return this.#upsertHolding({
2219
- cid,
2220
- fileName: sanitizeFilename(options.fileName || path.basename(filePath)),
2221
- size: sizeValidation.size || 0,
2222
- driveName,
2223
- source: options.source || 'update',
2224
- })
2225
- }
2226
-
2227
1943
  /**
2228
1944
  * 读取已发布文件的内容(用于预览)
2229
1945
  * Hyperdrive 中用 CID 作为 key 存储
@@ -2872,7 +2588,7 @@ export class MostBoxEngine extends EventEmitter {
2872
2588
  /**
2873
2589
  * 获取频道内在线用户
2874
2590
  * @param {string} channelKeyInput - 内部频道 key,或本地唯一短频道 ID
2875
- * @returns {Array<{ peerId: string, authorName: string, lastSeen: number }>}
2591
+ * @returns {Array<{ peerId: string, authorName: string, lastSeen: number, memberAddresses: string[] }>}
2876
2592
  */
2877
2593
  getChannelPeers(channelKeyInput, options = {}) {
2878
2594
  this.#ensureInitialized()
@@ -2887,6 +2603,9 @@ export class MostBoxEngine extends EventEmitter {
2887
2603
  return [...peers.values()].map(p => ({
2888
2604
  peerId: p.peerId,
2889
2605
  authorName: p.authorName,
2606
+ memberAddresses: uniqueStrings(p.memberAddresses).map(address =>
2607
+ normalizeOwnerAddress(address)
2608
+ ).filter(Boolean),
2890
2609
  lastSeen: p.lastSeen,
2891
2610
  }))
2892
2611
  }
@@ -3385,6 +3104,13 @@ export class MostBoxEngine extends EventEmitter {
3385
3104
  )
3386
3105
  }
3387
3106
 
3107
+ #getChannelMemberAddresses(channel) {
3108
+ const members = Array.isArray(channel?.members) ? channel.members : []
3109
+ return uniqueStrings(
3110
+ members.map(member => normalizeOwnerAddress(member?.address))
3111
+ )
3112
+ }
3113
+
3388
3114
  async #getNextChannelMessageTimestamp(channelKey) {
3389
3115
  const coresMap = this.#channelCores.get(channelKey)
3390
3116
  let maxTimestamp = 0
@@ -4713,6 +4439,7 @@ export class MostBoxEngine extends EventEmitter {
4713
4439
  type: channel.type,
4714
4440
  createdAt: channel.createdAt,
4715
4441
  lastMessageAt: channel.lastMessageAt || '',
4442
+ memberAddresses: this.#getChannelMemberAddresses(channel),
4716
4443
  writerCoreKeys: uniqueStrings([
4717
4444
  ...(channel.writerCoreKeys || []),
4718
4445
  this.#channelLocalCoreKey.get(channel.channelKey),
@@ -4749,6 +4476,7 @@ export class MostBoxEngine extends EventEmitter {
4749
4476
  async #processChannelHelloMessage(msg) {
4750
4477
  if (msg.type !== 'channel-hello') return null
4751
4478
 
4479
+ const onlineChannels = []
4752
4480
  const remoteChannels = Array.isArray(msg.channels)
4753
4481
  ? msg.channels
4754
4482
  .filter(channel => channel && typeof channel === 'object')
@@ -4764,6 +4492,13 @@ export class MostBoxEngine extends EventEmitter {
4764
4492
  typeof channel.lastMessageAt === 'string'
4765
4493
  ? channel.lastMessageAt
4766
4494
  : '',
4495
+ memberAddresses: uniqueStrings(
4496
+ Array.isArray(channel.memberAddresses)
4497
+ ? channel.memberAddresses.map(address =>
4498
+ normalizeOwnerAddress(address)
4499
+ )
4500
+ : []
4501
+ ),
4767
4502
  writerCoreKeys: uniqueStrings(channel.writerCoreKeys),
4768
4503
  }
4769
4504
  })
@@ -4787,9 +4522,15 @@ export class MostBoxEngine extends EventEmitter {
4787
4522
  peers.set(msg.peerId, {
4788
4523
  peerId: msg.peerId,
4789
4524
  authorName: msg.authorName,
4525
+ memberAddresses: remoteChannel.memberAddresses,
4790
4526
  lastSeen: Date.now(),
4791
4527
  })
4792
4528
  }
4529
+ onlineChannels.push({
4530
+ channelKey: localChannel.channelKey,
4531
+ channelId: localChannel.channelId,
4532
+ memberAddresses: remoteChannel.memberAddresses,
4533
+ })
4793
4534
 
4794
4535
  for (const writerCoreKey of remoteChannel.writerCoreKeys) {
4795
4536
  if (
@@ -4807,6 +4548,7 @@ export class MostBoxEngine extends EventEmitter {
4807
4548
  this.emit('channel:peer:online', {
4808
4549
  peerId: msg.peerId,
4809
4550
  authorName: msg.authorName,
4551
+ channels: onlineChannels,
4810
4552
  })
4811
4553
 
4812
4554
  return msg.peerId
@@ -4845,13 +4587,19 @@ export class MostBoxEngine extends EventEmitter {
4845
4587
  closed = true
4846
4588
  this.#channelStreams.delete(stream)
4847
4589
  if (connectedPeerId) {
4848
- for (const [, peers] of this.#channelPeers) {
4590
+ for (const [channelKey, peers] of this.#channelPeers) {
4849
4591
  if (peers.has(connectedPeerId)) {
4850
4592
  const peer = peers.get(connectedPeerId)
4851
4593
  peers.delete(connectedPeerId)
4594
+ const channel = this.#channels.find(
4595
+ item => item.channelKey === channelKey
4596
+ )
4852
4597
  this.emit('channel:peer:offline', {
4853
4598
  peerId: connectedPeerId,
4854
4599
  authorName: peer?.authorName,
4600
+ channelKey,
4601
+ channelId: channel?.channelId || '',
4602
+ memberAddresses: peer?.memberAddresses || [],
4855
4603
  })
4856
4604
  }
4857
4605
  }
@@ -1 +0,0 @@
1
- import{t as e}from"./jsx-runtime-Bt-cYkS5.js";import{s as t,t as n,w as r}from"./ui-u9yJHkb-.js";import{a as i,c as a,o}from"./i18n-Cqyi6NZb.js";var s=r(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),c=r(`languages`,[[`path`,{d:`m5 8 6 6`,key:`1wu5hv`}],[`path`,{d:`m4 14 6-6 2-3`,key:`1k1g8d`}],[`path`,{d:`M2 5h12`,key:`or177f`}],[`path`,{d:`M7 2h1`,key:`1t2jsx`}],[`path`,{d:`m22 22-5-10-5 10`,key:`don7ne`}],[`path`,{d:`M14 18h6`,key:`1m8k6r`}]]),l=r(`moon`,[[`path`,{d:`M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401`,key:`kfwtm`}]]),u=r(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]),d=r(`sun`,[[`circle`,{cx:`12`,cy:`12`,r:`4`,key:`4exip2`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`m4.93 4.93 1.41 1.41`,key:`149t6j`}],[`path`,{d:`m17.66 17.66 1.41 1.41`,key:`ptbguv`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`m6.34 17.66-1.41 1.41`,key:`1m8zz5`}],[`path`,{d:`m19.07 4.93-1.41 1.41`,key:`1shlcs`}]]),f=e();function p(){let{t:e}=i(),{state:n,installAndRestart:r}=t(),a=n.status===`checking`,o=n.status===`downloading`,s=n.status===`downloaded`,c=n.status===`installing`,l=a||o||c;if(!a&&!o&&!s&&!c)return null;let d=e(`desktopUpdate.install`);a?d=e(`desktopUpdate.checking`):o?d=n.progress>0?e(`desktopUpdate.downloadingProgress`,{progress:n.progress}):e(`desktopUpdate.downloading`):c&&(d=e(`desktopUpdate.installing`));let p=a||o?e(`desktopUpdate.downloadingTitle`):e(`desktopUpdate.readyTitle`,{version:n.version||n.filename});return(0,f.jsxs)(`button`,{type:`button`,className:`btn ${s||c?`btn-primary`:`btn-secondary`} desktop-update-button`,disabled:!s||c,title:p,"aria-live":l?`polite`:void 0,onClick:()=>{s&&r()},children:[(0,f.jsx)(u,{size:16,className:l?`spin`:void 0}),d]})}function m({className:e=``}){let{locale:t,localeName:r,setLocale:l,t:u}=i(),d=u(`common.locale.choose`),p=`${d} (${u(`common.locale.current`)}: ${r})`;return(0,f.jsx)(n,{ariaLabel:d,placement:`bottom-end`,items:o.map(e=>({key:e,label:a[e],icon:e===t?(0,f.jsx)(s,{size:16}):(0,f.jsx)(`span`,{"aria-hidden":`true`}),onSelect:()=>{e!==t&&l(e)}})),renderTrigger:t=>(0,f.jsx)(`button`,{...t,className:[`language-toggle`,e].filter(Boolean).join(` `),"aria-label":d,title:p,children:(0,f.jsx)(c,{size:16})})})}export{l as a,u as i,p as n,s as o,d as r,m as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./jsx-runtime-Bt-cYkS5.js";import{n as t,t as n}from"./LanguageToggle-Cfh33R2h.js";import{I as r,j as i}from"./index-CiHRnvMj.js";import{t as a}from"./MarketingThemeToggle-Cl7PhFgv.js";var o=e();function s(){return(0,o.jsx)(`nav`,{className:`mkt-nav`,children:(0,o.jsxs)(`div`,{className:`mkt-nav-inner`,children:[(0,o.jsxs)(`button`,{type:`button`,className:`mkt-nav-logo`,onClick:i(),children:[(0,o.jsx)(r,{size:18}),(0,o.jsx)(`span`,{children:`MOST PEOPLE`})]}),(0,o.jsxs)(`div`,{className:`mkt-nav-cta`,children:[(0,o.jsx)(a,{}),(0,o.jsx)(n,{className:`mkt-theme-toggle`}),(0,o.jsx)(t,{})]})]})})}export{s as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./jsx-runtime-Bt-cYkS5.js";import{O as t,c as n,o as r}from"./ui-u9yJHkb-.js";import{a as i}from"./i18n-Cqyi6NZb.js";import{n as a,t as o}from"./LanguageToggle-Cfh33R2h.js";import{t as s}from"./download-DPA-3LpR.js";import{i as c}from"./index-CiHRnvMj.js";import{t as l}from"./MarketingThemeToggle-Cl7PhFgv.js";import{t as u}from"./LogoIcon-B2fFe0l1.js";var d={name:`most-box`,version:`0.2.7`,description:`MostBox - P2P file sharing application`,type:`module`,main:`electron/main.js`,bin:{"most-box":`server/cli.js`},files:[`out`,`public`,`electron`,`server/src`,`server/index.js`,`server/cli.js`],scripts:{start:`vite`,server:`node --watch server/index.js`,dev:`vite`,build:`vite build && node scripts/prepare-start-static.mjs`,preview:`vite preview`,serve:`npm run build && node server/index.js`,test:`node --test server/tests/**/*.test.js`,"test:unit":`node --test server/tests/unit/*.test.js`,"test:desktop":`node --test electron/*.test.js`,"test:frontend":`node --test src/tests/*.test.js`,"test:protocol":`node --test --test-name-pattern "extracts CID and filename|matches golden CID samples|publishes a file from Buffer|lists published files|pulls through local seed nodes" server/tests/unit/cid.test.js server/tests/integration/cid.test.js server/tests/integration/engine.test.js`,android:`npm --prefix mobile/android start`,"android:build":`npm --prefix mobile/android run apk`,"android:test":`npm --prefix mobile/android test`,typecheck:`tsc -p tsconfig.typecheck.json`,"typecheck:strict-router":`tsc -p tsconfig.strict-router.json`,"check:static-output":`node scripts/check-static-output.mjs`,format:`npx prettier --write .`,lint:`npx eslint .`,"r2:cors":`node scripts/configure-r2-cors.mjs`,"electron:dev":`concurrently "npm run build" "wait-on out/index.html && electron ."`,"electron:rebuild":`electron-rebuild -f -w sodium-native`,"electron:build":`npm run build && npm run electron:rebuild && electron-builder`,"electron:build:win":`npm run build && npm run electron:rebuild && electron-builder --win`,"electron:build:win:x64":`npm run electron:build:win -- --x64`,"electron:build:win:arm64":`npm run electron:build:win -- --arm64`,"electron:build:mac":`npm run build && npm run electron:rebuild && electron-builder --mac`,"electron:build:linux":`npm run build && npm run electron:rebuild && electron-builder --linux`},keywords:[`p2p`,`file-sharing`,`hyperswarm`,`hyperdrive`],license:`MIT`,dependencies:{"@dicebear/collection":`^9.4.2`,"@dicebear/core":`^9.4.2`,"@hono/node-server":`^2.0.4`,"@tanstack/react-router":`^1.170.15`,"@tanstack/react-start":`^1.168.25`,b4a:`^1.8.1`,busboy:`^1.6.0`,corestore:`^7.10.0`,dayjs:`^1.11.21`,ethers:`^6.16.0`,eventemitter3:`^5.0.4`,hono:`^4.12.23`,hypercore:`^11.33.1`,hyperdrive:`^13.3.2`,hyperswarm:`^4.17.0`,"ipfs-unixfs-importer":`^17.0.1`,multiformats:`^14.0.0`,tweetnacl:`^1.0.3`,ws:`^8.21.0`},devDependencies:{"@electron/rebuild":`^4.0.4`,"@eslint/js":`^10.0.1`,"@iconify/react":`^6.0.2`,"@mantine/hooks":`^9.3.0`,"@milkdown/crepe":`^7.21.2`,"@milkdown/utils":`^7.21.2`,"@types/react":`^19.2.16`,"@vitejs/plugin-react":`^6.0.2`,concurrently:`^10.0.3`,electron:`^42.3.3`,"electron-builder":`^26.8.1`,esbuild:`^0.28.0`,eslint:`^10.4.1`,globals:`^17.6.0`,ky:`^2.0.2`,"lucide-react":`^1.17.0`,prettier:`^3.8.3`,"qrcode.react":`^4.2.0`,react:`^19.2.7`,"react-dom":`^19.2.7`,typescript:`6.0.3`,"typescript-eslint":`^8.60.1`,vite:`^8.0.16`,"wait-on":`^9.0.10`,zustand:`^5.0.14`},overrides:{"brace-expansion":`5.0.6`,postcss:`8.5.15`,ws:`$ws`},build:{appId:`com.mostbox.app`,productName:`MostBox`,publish:[{provider:`github`,releaseType:`release`}],directories:{output:`dist`},afterPack:`electron/afterPack.cjs`,files:[`out/**/*`,`electron/**/*`,`server/src/**/*`,`server/index.js`,`server/cli.js`,`package.json`,`!**/*.map`,`!electron/**/*.test.js`,`!server/tests/**/*`],asarUnpack:[`**/*.node`,`**/sodium-native/**`],protocols:[{name:`MostBox Link`,schemes:[`most`]}],win:{target:[`nsis`],artifactName:"${productName}-${version}-win-${arch}-setup.${ext}",icon:`public/logo.ico`},nsis:{oneClick:!1,allowToChangeInstallationDirectory:!0,createDesktopShortcut:!0,createStartMenuShortcut:!0,shortcutName:`MostBox`},mac:{artifactName:"${productName}-${version}-mac-${arch}.${ext}",target:[{target:`dmg`,arch:[`x64`,`arm64`]},{target:`zip`,arch:[`x64`,`arm64`]}],icon:`public/logo-512.png`,category:`public.app-category.utilities`},dmg:{contents:[{x:130,y:220},{x:410,y:220,type:`link`,path:`/Applications`}]},linux:{artifactName:"${productName}-${version}-linux-${arch}.${ext}",target:[{target:`AppImage`,arch:[`x64`,`arm64`]}],icon:`public/logo-512.png`,category:`Utility`}}},f=e(),p=[{to:`/`,labelKey:`footer.about`},{to:`/ping/`,labelKey:`footer.network`},{href:`https://github.com/most-people/most`,labelKey:null,label:`GitHub`,external:!0}],m=d.version;function h(){let{t:e}=i();return(0,f.jsx)(`footer`,{className:`mkt-footer`,children:(0,f.jsx)(`div`,{className:`mkt-container`,children:(0,f.jsxs)(`div`,{className:`mkt-footer-inner`,children:[(0,f.jsx)(`div`,{className:`mkt-footer-links`,children:p.map(n=>`external`in n?(0,f.jsx)(`a`,{href:n.href,target:`_blank`,rel:`noopener noreferrer`,children:n.label},n.href):(0,f.jsx)(t,{to:n.to,children:e(n.labelKey)},n.to))}),(0,f.jsxs)(`span`,{className:`mkt-footer-copy`,children:[`© `,new Date().getFullYear(),` MOST PEOPLE · MIT License`]}),(0,f.jsxs)(`span`,{className:`mkt-footer-build`,translate:`no`,children:[`v`,m]})]})})})}function g({children:e,header:t}){return(0,f.jsxs)(`div`,{className:`mkt-layout`,children:[t??(0,f.jsx)(_,{}),(0,f.jsx)(`main`,{className:`mkt-layout-main`,children:e}),(0,f.jsx)(h,{})]})}function _(){let e=n(e=>e.identity),d=n(e=>e.openLoginModal),{t:p}=i(),m=r(),h=e?.displayName||e?.username||p(`nav.openWeb`);return(0,f.jsx)(`nav`,{className:`mkt-nav`,children:(0,f.jsxs)(`div`,{className:`mkt-nav-inner`,children:[(0,f.jsxs)(t,{to:`/`,className:`mkt-nav-logo`,children:[(0,f.jsx)(u,{}),`MOST PEOPLE`]}),(0,f.jsxs)(`div`,{className:`mkt-nav-cta`,children:[(0,f.jsx)(l,{}),(0,f.jsx)(o,{className:`mkt-theme-toggle`}),(0,f.jsx)(a,{}),!m&&(0,f.jsxs)(t,{to:`/download/`,className:`btn btn-primary mkt-nav-preview`,children:[(0,f.jsx)(s,{size:16}),p(`nav.downloadClient`)]}),e?(0,f.jsx)(t,{to:`/profile/`,className:`mkt-nav-avatar-trigger`,"aria-label":p(`nav.profile`),title:h,children:(0,f.jsx)(`img`,{className:`mkt-nav-avatar`,src:c(e.address,e.avatar),alt:``,"aria-hidden":`true`})}):(0,f.jsx)(`button`,{type:`button`,className:`btn btn-secondary`,onClick:d,children:p(`nav.getStarted`)})]})]})})}export{g as t};
@@ -1 +0,0 @@
1
- import{n as e,o as t,t as n}from"./jsx-runtime-Bt-cYkS5.js";import{O as r,w as i}from"./ui-u9yJHkb-.js";import{a}from"./i18n-Cqyi6NZb.js";import{n as o,t as s}from"./LanguageToggle-Cfh33R2h.js";import{I as c}from"./index-CiHRnvMj.js";var l=i(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),u=t(e(),1);function d(e,t){return typeof t==`boolean`?t:typeof window<`u`&&`matchMedia`in window?window.matchMedia(e).matches:!1}function f(e,t,{getInitialValueInEffect:n}={getInitialValueInEffect:!0}){let[r,i]=(0,u.useState)(n?t:d(e));return(0,u.useEffect)(()=>{try{if(`matchMedia`in window){let t=window.matchMedia(e);i(t.matches);let n=e=>i(e.matches);return t.addEventListener(`change`,n),()=>{t.removeEventListener(`change`,n)}}}catch{return}},[e]),r||!1}function p(e=!1,t={}){let[n,r]=(0,u.useState)(e),i=(0,u.useCallback)(()=>{r(e=>e||(t.onOpen?.(),!0))},[t.onOpen]),a=(0,u.useCallback)(()=>{r(e=>e&&(t.onClose?.(),!1))},[t.onClose]);return[n,{open:i,close:a,toggle:(0,u.useCallback)(()=>{n?a():i()},[a,i,n]),set:r}]}var m=n(),h=(0,u.createContext)(null);function g(){let e=(0,u.useContext)(h);if(!e)throw Error(`useAppShell must be used within AppShell`);return e}function _({sidebar:e,className:t=``,headerTitle:n,headerRight:r,sidebarToggleReplacement:i,defaultHide:c=!1,children:d}){let[g,_]=p(!1),[v,y]=(0,u.useState)(c),b=(0,u.useRef)(c),x=f(`(max-width: 768px)`),{t:S}=a(),C=(0,u.useCallback)((e={})=>{_.close(),e.collapse&&y(!0)},[_]);(0,u.useEffect)(()=>{c!==b.current&&(c?C({collapse:!0}):y(!1),b.current=c)},[c,C]);let w=()=>{x?_.toggle():y(!v)},T=()=>{x?_.open():y(!1)},E=x?g:!v;return(0,m.jsx)(h.Provider,{value:{closeSidebar:C,openSidebar:T,isSidebarVisible:E},children:(0,m.jsxs)(`div`,{className:[`app-layout`,t].filter(Boolean).join(` `),children:[(0,m.jsx)(`div`,{className:`sidebar-overlay ${g?`visible`:``}`,onClick:()=>C()}),(0,m.jsx)(`div`,{className:`sidebar ${g?`open`:``} ${v?`collapsed`:``}`,children:e({closeSidebar:C,openSidebar:T})}),(0,m.jsxs)(`div`,{className:`main-content`,children:[(0,m.jsxs)(`header`,{className:`app-header`,children:[(0,m.jsxs)(`div`,{className:`header-left`,children:[i??(0,m.jsx)(`button`,{onClick:w,className:`btn btn-icon sidebar-toggle-btn`,"aria-label":S(x?`appShell.openMenu`:v?`appShell.expandSidebar`:`appShell.collapseSidebar`),children:(0,m.jsx)(l,{size:16})}),n]}),(0,m.jsxs)(`div`,{className:`header-right`,children:[(0,m.jsx)(o,{}),(0,m.jsx)(s,{className:`btn btn-icon app-language-toggle`}),r]})]}),d]})]})})}function v({onNavigate:e}){let{t}=a(),n=t(`common.brand`);return(0,m.jsxs)(r,{to:`/`,className:`sidebar-header sidebar-header-link`,onClick:e,"aria-label":n,title:n,children:[(0,m.jsx)(c,{size:18}),(0,m.jsx)(`h1`,{children:n})]})}export{p as i,_ as n,g as r,v as t};
@@ -1 +0,0 @@
1
- import{w as e}from"./ui-u9yJHkb-.js";var t=e(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]);export{t};