most-box 0.3.5 → 0.3.7
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/README.md +23 -16
- package/out/about/index.html +2 -2
- package/out/admin/index.html +0 -0
- package/out/app/index.html +0 -0
- package/out/assets/AccountMenu-D_rcXDZr.js +1 -0
- package/out/assets/AppShell-CKP_dglR.js +1 -0
- package/out/assets/{AppTop-BE5kKBYH.js → AppTop-C03PsDr_.js} +1 -1
- package/out/assets/ChatRestoringIndicator-CLV78gNW.js +1 -0
- package/out/assets/{CopyButton-DnFqfCN9.js → CopyButton-ClwBN6Iy.js} +1 -1
- package/out/assets/{MarketingHeader-8gNzHawY.js → MarketingHeader-ChTIIzXG.js} +1 -1
- package/out/assets/{MarketingLayout-BPdjpEfc.js → MarketingLayout-hkPAtieL.js} +1 -1
- package/out/assets/{MilkdownEditor-C388llV6.js → MilkdownEditor-X9MUH_0q.js} +1 -1
- package/out/assets/{OpenSidebarButton-OP8zl7ad.js → OpenSidebarButton-CXBAjjCE.js} +1 -1
- package/out/assets/{chatJoinInvite-CeC6H6t8.js → chatJoinInvite-7tuD-STm.js} +1 -1
- package/out/assets/i18n-DKsg67mH.js +1 -0
- package/out/assets/{index-D1mozno-.js → index-DJzLHA0V.js} +3 -3
- package/out/assets/index-FEwag7ej.css +1 -0
- package/out/assets/index.lazy-B8ud1tpC.js +1 -0
- package/out/assets/{index.lazy-CX2l1_Gi.js → index.lazy-BEDWJZPP.js} +1 -1
- package/out/assets/{index.lazy-CsTaY5WS.js → index.lazy-BQ_Dyw5C.js} +1 -1
- package/out/assets/{index.lazy-D7E1pb1S.js → index.lazy-C0L9TDqK.js} +1 -1
- package/out/assets/{index.lazy-ClrSkRx5.js → index.lazy-C8luNCxY.js} +1 -1
- package/out/assets/{index.lazy-B1iJsXQ8.js → index.lazy-CT5TGyGl.js} +1 -1
- package/out/assets/{index.lazy-59kmyNMW.js → index.lazy-CTLlLPjy.js} +1 -1
- package/out/assets/{index.lazy-m9XAzMt1.js → index.lazy-Cgrv1NmQ.js} +1 -1
- package/out/assets/{index.lazy-Bz77Jlzu.js → index.lazy-CnpeKB4C.js} +1 -1
- package/out/assets/{index.lazy-DzaN9qwA.js → index.lazy-CwAN1t6l.js} +2 -2
- package/out/assets/{index.lazy-FvXAYdEO.js → index.lazy-Cy249kq1.js} +1 -1
- package/out/assets/index.lazy-DA98XQ0Q.js +1 -0
- package/out/assets/index.lazy-DYhpVW5B.js +3 -0
- package/out/assets/{index.lazy-CVFcovqX.js → index.lazy-b811JgFg.js} +1 -1
- package/out/assets/{index.lazy-CflWbocP.js → index.lazy-psYKmxdW.js} +1 -1
- package/out/assets/{saveLocalFile-2CNt4X0T.js → saveLocalFile-DTgdBgO9.js} +1 -1
- package/out/assets/useChannelMessages-BrqZa2SG.js +4 -0
- package/out/assets/{useGameRoom-asr2gyY6.js → useGameRoom-B3wvUICl.js} +1 -1
- package/out/chat/index.html +0 -0
- package/out/chat/join/demo/index.html +0 -0
- package/out/chat/join/index.html +0 -0
- package/out/download/index.html +2 -2
- package/out/game/gandengyan/index.html +0 -0
- package/out/game/index.html +0 -0
- package/out/game/zhajinhua/index.html +0 -0
- package/out/index.html +2 -2
- package/out/note/index.html +0 -0
- package/out/ping/index.html +2 -2
- package/out/profile/index.html +0 -0
- package/out/web3/index.html +0 -0
- package/package.json +1 -1
- package/server/src/index.js +212 -77
- package/out/assets/AccountMenu-D-wa5tux.js +0 -1
- package/out/assets/AppShell-BdRKZIZ9.js +0 -1
- package/out/assets/i18n-C6yaqRst.js +0 -1
- package/out/assets/index-C6IjeTBh.css +0 -1
- package/out/assets/index.lazy-C_J9afOY.js +0 -1
- package/out/assets/index.lazy-CazlK1-Y.js +0 -3
- package/out/assets/index.lazy-JwrS2vDI.js +0 -1
- package/out/assets/useChannelMessages-DDBqKBXU.js +0 -4
package/server/src/index.js
CHANGED
|
@@ -101,6 +101,26 @@ function isChannelHistoryEntry(entry) {
|
|
|
101
101
|
return entry?.type === 'message' || entry?.type === 'system'
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function getChannelHistoryDedupeKey(message) {
|
|
105
|
+
const type = String(message?.type || '')
|
|
106
|
+
const event = String(message?.event || '')
|
|
107
|
+
const author = normalizeOwnerAddress(message?.author)
|
|
108
|
+
const content = String(message?.content || '').trim()
|
|
109
|
+
|
|
110
|
+
if (type === 'system' && event === CHANNEL_MEMBER_JOINED_EVENT && author) {
|
|
111
|
+
return `${type}:${event}:${author}:${content}`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return [
|
|
115
|
+
message?._coreKey || '',
|
|
116
|
+
type,
|
|
117
|
+
event,
|
|
118
|
+
message?.author || '',
|
|
119
|
+
message?.timestamp || '',
|
|
120
|
+
content,
|
|
121
|
+
].join(':')
|
|
122
|
+
}
|
|
123
|
+
|
|
104
124
|
function createMemoryDuplexPair() {
|
|
105
125
|
let left
|
|
106
126
|
let right
|
|
@@ -293,6 +313,42 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
293
313
|
return this.#pruneStaleChannelPresence()
|
|
294
314
|
}
|
|
295
315
|
|
|
316
|
+
#isClosedSessionError(err) {
|
|
317
|
+
if (!err || typeof err !== 'object') return false
|
|
318
|
+
const code = String(err.code || '')
|
|
319
|
+
const message = String(err.message || '')
|
|
320
|
+
return (
|
|
321
|
+
code === 'SESSION_CLOSED' ||
|
|
322
|
+
message.includes('SESSION_CLOSED') ||
|
|
323
|
+
message.includes('closed session')
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async #reopenChannelLocalWriter(channel) {
|
|
328
|
+
const channelKey = channel.channelKey
|
|
329
|
+
const localKeyHex =
|
|
330
|
+
this.#channelLocalCoreKey.get(channelKey) || channel.localWriterCoreKey
|
|
331
|
+
const coresMap = this.#channelCores.get(channelKey)
|
|
332
|
+
const staleCore = localKeyHex && coresMap ? coresMap.get(localKeyHex) : null
|
|
333
|
+
|
|
334
|
+
if (staleCore) {
|
|
335
|
+
coresMap.delete(localKeyHex)
|
|
336
|
+
await staleCore.close().catch(() => {})
|
|
337
|
+
}
|
|
338
|
+
this.#channelLocalCoreKey.delete(channelKey)
|
|
339
|
+
|
|
340
|
+
await this.#openChannelRuntime(channel)
|
|
341
|
+
|
|
342
|
+
const reopenedKeyHex = this.#channelLocalCoreKey.get(channelKey)
|
|
343
|
+
const reopenedCore = reopenedKeyHex
|
|
344
|
+
? this.#channelCores.get(channelKey)?.get(reopenedKeyHex)
|
|
345
|
+
: null
|
|
346
|
+
if (!reopenedCore) {
|
|
347
|
+
throw new Error('频道未初始化或无可写 core')
|
|
348
|
+
}
|
|
349
|
+
return reopenedCore
|
|
350
|
+
}
|
|
351
|
+
|
|
296
352
|
/**
|
|
297
353
|
* 初始化引擎 — 必须在调用其他方法之前调用
|
|
298
354
|
*/
|
|
@@ -624,29 +680,38 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
624
680
|
const publishedBucket = this.#getPublishedBucket(ownerAddress, true)
|
|
625
681
|
// 检查相同内容是否已存在
|
|
626
682
|
const existingIndex = publishedBucket.findIndex(f => f.cid === cidString)
|
|
683
|
+
const repairingMissingContent = existingIndex !== -1
|
|
627
684
|
if (existingIndex !== -1) {
|
|
628
685
|
const existing = publishedBucket[existingIndex]
|
|
629
|
-
await this.#
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
this.#upsertHolding({
|
|
634
|
-
cid: cidString,
|
|
635
|
-
fileName: existing.fileName,
|
|
636
|
-
size: fileSize,
|
|
637
|
-
driveName: name,
|
|
638
|
-
source: 'published',
|
|
686
|
+
const existingContent = await this.#getLocalCidContent(cidString, {
|
|
687
|
+
ownerAddress,
|
|
688
|
+
public: true,
|
|
689
|
+
allowHoldingFallback: true,
|
|
639
690
|
})
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
691
|
+
if (existingContent) {
|
|
692
|
+
await this.#joinCidTopicInternal(cidString, {
|
|
693
|
+
server: true,
|
|
694
|
+
client: false,
|
|
695
|
+
})
|
|
696
|
+
this.#upsertHolding({
|
|
697
|
+
cid: cidString,
|
|
698
|
+
fileName: existing.fileName,
|
|
699
|
+
size: fileSize,
|
|
700
|
+
driveName: name,
|
|
701
|
+
source: 'published',
|
|
702
|
+
})
|
|
703
|
+
return {
|
|
704
|
+
cid: cidString,
|
|
705
|
+
link: buildMostLink(cidString, existing.fileName),
|
|
706
|
+
fileName: existing.fileName,
|
|
707
|
+
alreadyExists: true,
|
|
708
|
+
}
|
|
645
709
|
}
|
|
646
710
|
}
|
|
647
711
|
|
|
648
712
|
this.#assertDisplayNameAvailable(safeFileName, {
|
|
649
713
|
ownerAddress,
|
|
714
|
+
excludeCid: repairingMissingContent ? cidString : undefined,
|
|
650
715
|
})
|
|
651
716
|
|
|
652
717
|
// 获取或创建该 CID 对应的 drive
|
|
@@ -667,40 +732,17 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
667
732
|
|
|
668
733
|
// Hyperdrive 中用 CID 作为 key 存储(解耦目录结构)
|
|
669
734
|
const driveKey = '/' + cidString
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
let offset = 0
|
|
675
|
-
const waitForDrain = () =>
|
|
676
|
-
new Promise(resolve => ws.once('drain', resolve))
|
|
677
|
-
|
|
678
|
-
try {
|
|
679
|
-
while (offset < content.length) {
|
|
680
|
-
const chunk = content.slice(offset, offset + FILE_WRITE_CHUNK_SIZE)
|
|
681
|
-
const canContinue = ws.write(chunk)
|
|
682
|
-
offset += chunk.length
|
|
683
|
-
if (!canContinue && offset < content.length) {
|
|
684
|
-
await waitForDrain()
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
ws.end()
|
|
688
|
-
await new Promise((resolve, reject) => {
|
|
689
|
-
ws.on('finish', resolve)
|
|
690
|
-
ws.on('error', reject)
|
|
691
|
-
})
|
|
692
|
-
} catch (err) {
|
|
693
|
-
ws.destroy()
|
|
735
|
+
try {
|
|
736
|
+
await this.#writeDriveFile(drive, driveKey, content, cleanPath)
|
|
737
|
+
} catch (err) {
|
|
738
|
+
if (!this.#isClosedSessionError(err)) {
|
|
694
739
|
throw err
|
|
695
740
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
rs.pipe(ws)
|
|
700
|
-
ws.on('finish', resolve)
|
|
701
|
-
ws.on('error', reject)
|
|
702
|
-
rs.on('error', reject)
|
|
741
|
+
drive = await this.#reopenDrive(name, {
|
|
742
|
+
server: true,
|
|
743
|
+
client: false,
|
|
703
744
|
})
|
|
745
|
+
await this.#writeDriveFile(drive, driveKey, content, cleanPath)
|
|
704
746
|
}
|
|
705
747
|
|
|
706
748
|
// 存储 displayName(用户看到的文件夹路径),不存储 drivePath
|
|
@@ -715,7 +757,11 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
715
757
|
starred: false,
|
|
716
758
|
syncUpdatedAt: now,
|
|
717
759
|
}
|
|
718
|
-
|
|
760
|
+
if (repairingMissingContent) {
|
|
761
|
+
publishedBucket[existingIndex] = fileRecord
|
|
762
|
+
} else {
|
|
763
|
+
publishedBucket.push(fileRecord)
|
|
764
|
+
}
|
|
719
765
|
this.#savePublishedMetadata()
|
|
720
766
|
this.#upsertHolding({
|
|
721
767
|
cid: cidString,
|
|
@@ -2214,7 +2260,10 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
2214
2260
|
async #hasLocalDriveContent(drive, key) {
|
|
2215
2261
|
try {
|
|
2216
2262
|
return await drive.has(key)
|
|
2217
|
-
} catch {
|
|
2263
|
+
} catch (err) {
|
|
2264
|
+
if (this.#isClosedSessionError(err)) {
|
|
2265
|
+
throw err
|
|
2266
|
+
}
|
|
2218
2267
|
return false
|
|
2219
2268
|
}
|
|
2220
2269
|
}
|
|
@@ -2234,42 +2283,53 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
2234
2283
|
}
|
|
2235
2284
|
const holding = this.#holdings.find(item => item.cid === cid)
|
|
2236
2285
|
const { driveName } = this.#getCidInfo(cid)
|
|
2237
|
-
|
|
2286
|
+
let drive = await this.#getOrCreateDrive(
|
|
2238
2287
|
fileRecord?.driveName || holding?.driveName || driveName,
|
|
2239
2288
|
{ server: true, client: false }
|
|
2240
2289
|
)
|
|
2241
2290
|
const driveKey = '/' + cid
|
|
2242
2291
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2292
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
2293
|
+
try {
|
|
2294
|
+
const entry = await drive.entry(driveKey, { wait: false })
|
|
2295
|
+
if (!entry?.value?.blob) {
|
|
2296
|
+
return null
|
|
2297
|
+
}
|
|
2298
|
+
const hasContent = await this.#hasLocalDriveContent(drive, driveKey)
|
|
2299
|
+
if (!hasContent) {
|
|
2300
|
+
return null
|
|
2301
|
+
}
|
|
2252
2302
|
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
size,
|
|
2262
|
-
fileRecord: fileRecord || {
|
|
2263
|
-
cid,
|
|
2264
|
-
fileName: holding?.fileName || cid,
|
|
2265
|
-
driveName: holding?.driveName || driveName,
|
|
2303
|
+
const size =
|
|
2304
|
+
Number(entry.value.blob.byteLength) ||
|
|
2305
|
+
Number(fileRecord?.size) ||
|
|
2306
|
+
Number(holding?.size) ||
|
|
2307
|
+
0
|
|
2308
|
+
return {
|
|
2309
|
+
drive,
|
|
2310
|
+
entry,
|
|
2266
2311
|
size,
|
|
2267
|
-
|
|
2268
|
-
|
|
2312
|
+
fileRecord: fileRecord || {
|
|
2313
|
+
cid,
|
|
2314
|
+
fileName: holding?.fileName || cid,
|
|
2315
|
+
driveName: holding?.driveName || driveName,
|
|
2316
|
+
size,
|
|
2317
|
+
ownerAddress,
|
|
2318
|
+
},
|
|
2319
|
+
}
|
|
2320
|
+
} catch (err) {
|
|
2321
|
+
if (attempt === 0 && this.#isClosedSessionError(err)) {
|
|
2322
|
+
drive = await this.#reopenDrive(
|
|
2323
|
+
fileRecord?.driveName || holding?.driveName || driveName,
|
|
2324
|
+
{ server: true, client: false }
|
|
2325
|
+
)
|
|
2326
|
+
continue
|
|
2327
|
+
}
|
|
2328
|
+
return null
|
|
2269
2329
|
}
|
|
2270
|
-
} catch {
|
|
2271
|
-
return null
|
|
2272
2330
|
}
|
|
2331
|
+
|
|
2332
|
+
return null
|
|
2273
2333
|
}
|
|
2274
2334
|
|
|
2275
2335
|
// --- 频道管理 ---
|
|
@@ -2632,7 +2692,7 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
2632
2692
|
|
|
2633
2693
|
const seen = new Set()
|
|
2634
2694
|
const unique = allMessages.filter(m => {
|
|
2635
|
-
const key =
|
|
2695
|
+
const key = getChannelHistoryDedupeKey(m)
|
|
2636
2696
|
if (seen.has(key)) return false
|
|
2637
2697
|
seen.add(key)
|
|
2638
2698
|
return true
|
|
@@ -2721,7 +2781,15 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
2721
2781
|
if (event) message.event = event
|
|
2722
2782
|
}
|
|
2723
2783
|
|
|
2724
|
-
|
|
2784
|
+
try {
|
|
2785
|
+
await core.append(message)
|
|
2786
|
+
} catch (err) {
|
|
2787
|
+
if (!this.#isClosedSessionError(err)) {
|
|
2788
|
+
throw err
|
|
2789
|
+
}
|
|
2790
|
+
const reopenedCore = await this.#reopenChannelLocalWriter(channel)
|
|
2791
|
+
await reopenedCore.append(message)
|
|
2792
|
+
}
|
|
2725
2793
|
if (channel) {
|
|
2726
2794
|
channel.lastMessageAt = new Date(message.timestamp).toISOString()
|
|
2727
2795
|
this.#saveChannelsMetadata()
|
|
@@ -4085,6 +4153,19 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
4085
4153
|
if (!this.#holdings.some(current => current.cid === holding.cid)) {
|
|
4086
4154
|
return
|
|
4087
4155
|
}
|
|
4156
|
+
const localContent = await this.#getLocalCidContent(holding.cid, {
|
|
4157
|
+
public: true,
|
|
4158
|
+
allowHoldingFallback: true,
|
|
4159
|
+
})
|
|
4160
|
+
if (!localContent) {
|
|
4161
|
+
this.#setSeedState(holding.cid, {
|
|
4162
|
+
status: 'error',
|
|
4163
|
+
topic: holding.topic,
|
|
4164
|
+
driveName: holding.driveName,
|
|
4165
|
+
error: 'Local CID content missing',
|
|
4166
|
+
})
|
|
4167
|
+
return
|
|
4168
|
+
}
|
|
4088
4169
|
await this.#joinCidTopicInternal(holding.cid, {
|
|
4089
4170
|
server: true,
|
|
4090
4171
|
client: false,
|
|
@@ -4312,6 +4393,60 @@ export class MostBoxEngine extends EventEmitter {
|
|
|
4312
4393
|
return drive
|
|
4313
4394
|
}
|
|
4314
4395
|
|
|
4396
|
+
async #reopenDrive(name, options = { server: true, client: false }) {
|
|
4397
|
+
const staleDrive = this.#drives.get(name)
|
|
4398
|
+
if (staleDrive) {
|
|
4399
|
+
this.#drives.delete(name)
|
|
4400
|
+
await staleDrive.close().catch(() => {})
|
|
4401
|
+
}
|
|
4402
|
+
this.#drivePromises.delete(name)
|
|
4403
|
+
return this.#getOrCreateDrive(name, options)
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
async #writeDriveFile(drive, driveKey, content, cleanPath) {
|
|
4407
|
+
const ws = drive.createWriteStream(driveKey)
|
|
4408
|
+
|
|
4409
|
+
if (Buffer.isBuffer(content)) {
|
|
4410
|
+
let offset = 0
|
|
4411
|
+
const waitForDrain = () =>
|
|
4412
|
+
new Promise(resolve => ws.once('drain', resolve))
|
|
4413
|
+
|
|
4414
|
+
try {
|
|
4415
|
+
while (offset < content.length) {
|
|
4416
|
+
const chunk = content.slice(offset, offset + FILE_WRITE_CHUNK_SIZE)
|
|
4417
|
+
const canContinue = ws.write(chunk)
|
|
4418
|
+
offset += chunk.length
|
|
4419
|
+
if (!canContinue && offset < content.length) {
|
|
4420
|
+
await waitForDrain()
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
ws.end()
|
|
4424
|
+
await new Promise((resolve, reject) => {
|
|
4425
|
+
ws.on('finish', resolve)
|
|
4426
|
+
ws.on('error', reject)
|
|
4427
|
+
})
|
|
4428
|
+
} catch (err) {
|
|
4429
|
+
ws.destroy()
|
|
4430
|
+
throw err
|
|
4431
|
+
}
|
|
4432
|
+
return
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
const rs = fs.createReadStream(cleanPath)
|
|
4436
|
+
try {
|
|
4437
|
+
await new Promise((resolve, reject) => {
|
|
4438
|
+
rs.pipe(ws)
|
|
4439
|
+
ws.on('finish', resolve)
|
|
4440
|
+
ws.on('error', reject)
|
|
4441
|
+
rs.on('error', reject)
|
|
4442
|
+
})
|
|
4443
|
+
} catch (err) {
|
|
4444
|
+
ws.destroy()
|
|
4445
|
+
rs.destroy()
|
|
4446
|
+
throw err
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
|
|
4315
4450
|
#getOwnerKey(ownerAddress) {
|
|
4316
4451
|
return getOwnerBucketKey(ownerAddress)
|
|
4317
4452
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./jsx-runtime-Bt-cYkS5.js";import{B as t,E as n,W as r,t as i}from"./useAppStore-DNhyhD20.js";import{a,c as o,o as s}from"./i18n-C6yaqRst.js";import{S as c,g as l}from"./index-D1mozno-.js";var u=t(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),d=t(`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`}]]),f=t(`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`}]]),p=t(`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`}]]),m=t(`user`,[[`path`,{d:`M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2`,key:`975kel`}],[`circle`,{cx:`12`,cy:`7`,r:`4`,key:`17ys0d`}]]),h=e();function g(){let{t:e}=a(),t=i(e=>e.isDarkMode),n=i(e=>e.setIsDarkMode),r=e(`common.appearance.toggle`);return(0,h.jsx)(`button`,{type:`button`,className:`header-tool-btn`,title:r,"aria-label":r,"aria-pressed":t,onClick:()=>n(!t),children:t?(0,h.jsx)(p,{size:16}):(0,h.jsx)(f,{size:16})})}function _(){let{locale:e,setLocale:t,t:n}=a(),r=n(`common.locale.choose`);return(0,h.jsx)(c,{ariaLabel:r,className:`language-toggle`,items:s.map(n=>({key:n,label:o[n],icon:n===e?(0,h.jsx)(u,{size:16}):(0,h.jsx)(`span`,{className:`language-toggle-placeholder`,"aria-hidden":`true`}),onSelect:()=>{n!==e&&t(n)}})),renderTrigger:e=>(0,h.jsx)(`button`,{...e,className:`header-tool-btn`,title:r,"aria-label":r,children:(0,h.jsx)(d,{size:16})})})}function v(){let{t:e}=a(),t=n(e=>e.identity),i=l(t?.address,t?.avatar),o=e(`nav.profile`);return(0,h.jsx)(r,{to:`/profile/`,className:`account-profile-link`,title:o,"aria-label":o,children:t?(0,h.jsx)(`img`,{className:`account-profile-link-avatar`,src:i,alt:``,"aria-hidden":`true`}):(0,h.jsx)(m,{size:18})})}export{u as a,m as i,_ as n,g as r,v as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,t as n}from"./jsx-runtime-Bt-cYkS5.js";import{B as r}from"./useAppStore-DNhyhD20.js";import{a as i}from"./i18n-C6yaqRst.js";import{n as a,r as o,t as s}from"./AccountMenu-D-wa5tux.js";import"./index-D1mozno-.js";var c=r(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),l=t(e(),1);function u(e,t){return typeof t==`boolean`?t:typeof window<`u`&&`matchMedia`in window?window.matchMedia(e).matches:!1}function d(e,t,{getInitialValueInEffect:n}={getInitialValueInEffect:!0}){let[r,i]=(0,l.useState)(n?t:u(e));return(0,l.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 f(e=!1,t={}){let[n,r]=(0,l.useState)(e),i=(0,l.useCallback)(()=>{r(e=>e||(t.onOpen?.(),!0))},[t.onOpen]),a=(0,l.useCallback)(()=>{r(e=>e&&(t.onClose?.(),!1))},[t.onClose]);return[n,{open:i,close:a,toggle:(0,l.useCallback)(()=>{n?a():i()},[a,i,n]),set:r}]}var p=n(),m=(0,l.createContext)(null);function h(){let e=(0,l.useContext)(m);if(!e)throw Error(`useAppShell must be used within AppShell`);return e}function g({sidebar:e,className:t=``,headerTitle:n,headerRight:r,sidebarToggleReplacement:u,defaultHide:h=!1,hideAccountMenu:g=!1,children:_}){let[v,y]=f(!1),[b,x]=(0,l.useState)(h),S=(0,l.useRef)(h),C=d(`(max-width: 768px)`),{t:w}=i(),T=(0,l.useCallback)((e={})=>{y.close(),e.collapse&&x(!0)},[y]);(0,l.useEffect)(()=>{h!==S.current&&(h?T({collapse:!0}):x(!1),S.current=h)},[h,T]);let E=()=>{C?y.toggle():x(!b)},D=()=>{C?y.open():x(!1)},O=C?v:!b;return(0,p.jsx)(m.Provider,{value:{closeSidebar:T,openSidebar:D,isSidebarVisible:O},children:(0,p.jsxs)(`div`,{className:[`app-layout`,t].filter(Boolean).join(` `),children:[(0,p.jsx)(`div`,{className:`sidebar-overlay ${v?`visible`:``}`,onClick:()=>T()}),(0,p.jsx)(`div`,{className:`sidebar ${v?`open`:``} ${b?`collapsed`:``}`,children:e({closeSidebar:T,openSidebar:D})}),(0,p.jsxs)(`div`,{className:`main-content`,children:[(0,p.jsxs)(`header`,{className:`app-header`,children:[(0,p.jsxs)(`div`,{className:`header-left`,children:[u??(0,p.jsx)(`button`,{onClick:E,className:`btn btn-icon sidebar-toggle-btn`,"aria-label":w(C?`appShell.openMenu`:b?`appShell.expandSidebar`:`appShell.collapseSidebar`),children:(0,p.jsx)(c,{size:16})}),n]}),(0,p.jsxs)(`div`,{className:`header-right`,children:[r,(0,p.jsx)(o,{}),(0,p.jsx)(a,{}),!g&&(0,p.jsx)(s,{})]})]}),_]})]})})}export{h as n,f as r,g as t};
|