@fish-under-sea/magpie-neartoken 0.4.1 → 0.5.0
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/index.mjs +26 -21
- package/package.json +1 -1
- package/page/app.js +132 -62
- package/page/market.css +44 -0
- package/page/style.css +5 -0
- package/page/version.js +1 -1
package/index.mjs
CHANGED
|
@@ -163,27 +163,32 @@ async function catalog(disabledGroups = new Set(), disabledModels = new Set()) {
|
|
|
163
163
|
}
|
|
164
164
|
const groups = all.filter((g) => !disabledGroups.has(String(g ?? '').trim()))
|
|
165
165
|
if (!groups.length) continue
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
166
|
+
// 一个模型只出一行:多分组不再展开成多行,否则「只启用一个模型」在
|
|
167
|
+
// 供应商列表里仍会冒出五六行同名条目。分组与倍率折进显示名里,
|
|
168
|
+
// 请求按该模型最便宜(倍率最低)的分组走。
|
|
169
|
+
const ranked = groups.map((g) => ({
|
|
170
|
+
name: String(g ?? '').trim(),
|
|
171
|
+
ref: String(g ?? '').trim() && refs[String(g ?? '').trim()] ? String(refs[String(g ?? '').trim()]) : '',
|
|
172
|
+
ratio: ratioOf(ratios, String(g ?? '').trim()),
|
|
173
|
+
})).sort((a, b) => (a.ratio || 1e9) - (b.ratio || 1e9))
|
|
174
|
+
const main = ranked[0] || { name: '', ref: '', ratio: 0 }
|
|
175
|
+
const label = main.name
|
|
176
|
+
? (main.ratio > 0 ? `${main.name} ${ratioText(main.ratio)}x` : main.name) + ' · ' + model
|
|
177
|
+
: model
|
|
178
|
+
out[model] = {
|
|
179
|
+
name: label,
|
|
180
|
+
api: { id: model, url: SITE + '/v1', npm: '@ai-sdk/openai-compatible' },
|
|
181
|
+
limit: {
|
|
182
|
+
context: Number(row?.context_window) > 0 ? Number(row.context_window) : 0,
|
|
183
|
+
output: Number(row?.max_tokens) > 0 ? Number(row.max_tokens) : 0,
|
|
184
|
+
},
|
|
185
|
+
...(main.ref ? { headers: { 'x-neartoken-group': main.ref } } : {}),
|
|
186
|
+
// 站点自己的分类,交给 magpie 的四个分区(图像/视频/语音/向量)
|
|
187
|
+
...(kind === 'chat' ? {} : { kind }),
|
|
188
|
+
capabilities: {
|
|
189
|
+
toolcall: true,
|
|
190
|
+
output: kindOutput(kind),
|
|
191
|
+
},
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
writeGroups([...groupsOut.values()].map((g) => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fish-under-sea/magpie-neartoken",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "NearToken for magpie: a provider whose sign-in is the site's own device authorization, models listed per billing group with their multiplier, balance and vouchers on magpie's usage card, plus a page for spending and top-ups.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
package/page/app.js
CHANGED
|
@@ -84,6 +84,8 @@
|
|
|
84
84
|
|
|
85
85
|
/** showError 把宿主的话放到页脚的一条提示上,不弹窗。 */
|
|
86
86
|
let lastError = ''
|
|
87
|
+
let notice = ''
|
|
88
|
+
function showNotice(text) { notice = text; lastError = ''; render() }
|
|
87
89
|
function showError(error) {
|
|
88
90
|
const said = String(error && error.message ? error.message : error)
|
|
89
91
|
// options 写入限流由 saveDisabled 自己合并重试,不用挂到页脚
|
|
@@ -197,13 +199,14 @@
|
|
|
197
199
|
if (tab === 'market') frag.append(marketCard())
|
|
198
200
|
else if (tab === 'logs') frag.append(logsCard())
|
|
199
201
|
else frag.append(el('div', { class: 'home-grid' },
|
|
200
|
-
accountCard(), boardCard(), topupCard(), providerCard()))
|
|
202
|
+
accountCard(), boardCard(), topupCard(), providerCard(), referralCard()))
|
|
201
203
|
} else {
|
|
202
204
|
setStatus('未连接')
|
|
203
205
|
frag.append(connectCard())
|
|
204
206
|
}
|
|
205
207
|
if (view.error) frag.append(el('p', { class: 'note bad', text: view.error }))
|
|
206
208
|
if (lastError) frag.append(el('p', { class: 'note bad', text: lastError }))
|
|
209
|
+
if (notice) frag.append(el('p', { class: 'note', text: notice }))
|
|
207
210
|
if (pendingAct) frag.append(el('p', { class: 'note', text: '正在处理:' + pendingAct }))
|
|
208
211
|
body.replaceChildren(frag)
|
|
209
212
|
if (view.pending) paintCountdown()
|
|
@@ -213,8 +216,9 @@
|
|
|
213
216
|
|
|
214
217
|
/** equalizeHome 量第一张卡(账户)的高度,锁到同组所有卡上。 */
|
|
215
218
|
function equalizeHome() {
|
|
216
|
-
const cards = document.querySelectorAll('.home-grid > .card')
|
|
219
|
+
const cards = [...document.querySelectorAll('.home-grid > .card:not(.home-wide)')]
|
|
217
220
|
if (cards.length < 2) return
|
|
221
|
+
cards.forEach((c) => { c.style.height = '' })
|
|
218
222
|
const h = Math.round(cards[0].getBoundingClientRect().height)
|
|
219
223
|
if (!h) return
|
|
220
224
|
cards.forEach((c) => { c.style.height = h + 'px' })
|
|
@@ -290,6 +294,23 @@
|
|
|
290
294
|
el('p', { class: 'note', id: 'copy-hint', text: '' }))
|
|
291
295
|
}
|
|
292
296
|
|
|
297
|
+
/** 推荐计划:站点「复制推荐链接」那一栏的长条卡,跨整行放最下面。 */
|
|
298
|
+
function referralCard() {
|
|
299
|
+
const aff = view.affCode || view.referralCode || ''
|
|
300
|
+
const url = aff ? 'https://neartoken.cc/sign-up?aff=' + encodeURIComponent(aff)
|
|
301
|
+
: 'https://neartoken.cc/console'
|
|
302
|
+
const invites = Number(view.invites) || 0
|
|
303
|
+
return el('section', { class: 'card home-wide' },
|
|
304
|
+
el('div', { class: 'rf' },
|
|
305
|
+
el('div', { class: 'rf-ico', text: '◈' }),
|
|
306
|
+
el('div', { class: 'rf-text' },
|
|
307
|
+
el('h2', { text: '推荐计划', style: 'margin:0' }),
|
|
308
|
+
el('p', { class: 'note', text: '用户通过您的推荐链接注册后,您即可获得奖励。可随时将累计奖励转入余额。', style: 'margin:2px 0 0' })),
|
|
309
|
+
el('div', { class: 'rf-invites' }, el('div', { class: 'k', text: '邀请' }), el('div', { class: 'v', text: String(invites) })),
|
|
310
|
+
el('div', { class: 'rf-link' }, el('span', { text: url })),
|
|
311
|
+
el('button', { class: 'rf-copy', title: '复制推荐链接', onclick: () => copy(url) }, '⧉')))
|
|
312
|
+
}
|
|
313
|
+
|
|
293
314
|
/** 附件:账户、余额、代金券。 */
|
|
294
315
|
function accountCard() {
|
|
295
316
|
const account = view.account || {}
|
|
@@ -360,47 +381,72 @@
|
|
|
360
381
|
return card(null, ...parts)
|
|
361
382
|
}
|
|
362
383
|
|
|
363
|
-
/**
|
|
384
|
+
/**
|
|
385
|
+
* 充值卡:站点「添加资金」页的缩小版。
|
|
386
|
+
* 金额大卡(带优惠角标)、自定义金额 + 待支付、支付方式图标卡、兑换码。
|
|
387
|
+
*/
|
|
364
388
|
function topupCard() {
|
|
365
389
|
const info = topupInfo
|
|
366
|
-
if (!info) return card(
|
|
367
|
-
if (info.unavailable) return card(
|
|
390
|
+
if (!info) return card(null, el('p', { class: 'note', text: '正在读取充值档位…' }))
|
|
391
|
+
if (info.unavailable) return card(null, el('p', { class: 'note', text: info.unavailable }))
|
|
368
392
|
const options = Array.isArray(info.options) && info.options.length ? info.options : []
|
|
369
|
-
const
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
393
|
+
const methods = Array.isArray(info.methods) ? info.methods : []
|
|
394
|
+
const cur = info.currency || '¥'
|
|
395
|
+
const amount = pick.amount || Number(pick.custom || 0)
|
|
396
|
+
const method = pick.method || (methods[0] && methods[0].type) || ''
|
|
397
|
+
const payable = (v) => v >= 500 ? Math.round(v * 0.96) : v >= 200 ? Math.round(v * 0.98) : v
|
|
398
|
+
const saved = (v) => v - payable(v)
|
|
399
|
+
const head = el('div', { class: 'tp-head' },
|
|
400
|
+
el('div', { class: 'tp-badge', text: '¥' }),
|
|
401
|
+
el('div', { class: 'grow' },
|
|
402
|
+
el('h2', { text: '添加资金', style: 'margin:0' }),
|
|
403
|
+
el('p', { class: 'note', text: '选择金额和支付方式', style: 'margin:2px 0 0' })),
|
|
404
|
+
el('a', { class: 'ghost link', href: 'https://neartoken.cc/wallet', target: '_blank', rel: 'noreferrer' }, '订单历史 ↗'),
|
|
405
|
+
el('button', { class: 'primary', disabled: !!pendingAct || !amount, onclick: () => pay(method) }, '去支付'))
|
|
406
|
+
const tiles = el('div', { class: 'tp-tiles' }, options.map((v) => {
|
|
407
|
+
const on = v === amount && !pick.custom
|
|
408
|
+
const discount = v >= 500 ? '优惠 4%' : v >= 200 ? '优惠 2%' : ''
|
|
409
|
+
return el('button', {
|
|
410
|
+
class: 'tp-tile' + (on ? ' on' : ''), 'aria-pressed': String(on),
|
|
411
|
+
onclick: () => { pick.amount = v; pick.custom = ''; render() },
|
|
412
|
+
},
|
|
413
|
+
el('b', { text: String(v) }),
|
|
414
|
+
el('span', { class: 'tp-pay', text: discount ? cur + payable(v) + ' · 节省 ' + saved(v) : cur + v }),
|
|
415
|
+
discount ? el('span', { class: 'tp-off', text: discount }) : null)
|
|
416
|
+
}))
|
|
417
|
+
const custom = el('div', { class: 'tp-custom' },
|
|
418
|
+
el('label', { class: 'tp-label', text: '自定义金额' }),
|
|
419
|
+
el('div', { class: 'tp-custom-row' },
|
|
420
|
+
el('label', { class: 'field tp-field' },
|
|
421
|
+
el('span', { class: 'cur', text: cur }),
|
|
422
|
+
el('input', {
|
|
423
|
+
type: 'text', inputmode: 'numeric', placeholder: '输入金额', value: pick.custom,
|
|
424
|
+
'aria-label': '自定义金额',
|
|
425
|
+
oninput: (e) => { pick.custom = e.target.value.replace(/[^\d]/g, ''); pick.amount = null; render() },
|
|
426
|
+
})),
|
|
427
|
+
el('div', { class: 'tp-due' }, el('span', { text: '待支付金额:' }), el('b', { text: cur + (amount || 0) }))))
|
|
428
|
+
const payMethods = el('div', null,
|
|
429
|
+
el('div', { class: 'tp-label', text: '付款方式' }),
|
|
430
|
+
el('div', { class: 'tp-methods' }, methods.map((m) => {
|
|
431
|
+
const on = m.type === method
|
|
432
|
+
return el('button', {
|
|
433
|
+
class: 'tp-method' + (on ? ' on' : ''), 'aria-pressed': String(on),
|
|
434
|
+
onclick: () => { pick.method = m.type; render() },
|
|
435
|
+
},
|
|
436
|
+
el('span', { class: 'tp-micon ' + (m.type === 'alipay' ? 'ali' : 'wx'), text: m.type === 'alipay' ? '支' : '微' }),
|
|
437
|
+
el('b', { text: m.name }))
|
|
438
|
+
})),
|
|
439
|
+
el('p', { class: 'note', text: '其他支付方式请联系站长', style: 'margin:6px 0 0' }))
|
|
440
|
+
const redeem = el('div', { class: 'tp-redeem' },
|
|
441
|
+
el('label', { class: 'tp-label', text: '🎁 有兑换码吗?' }),
|
|
442
|
+
el('div', { class: 'tp-custom-row' },
|
|
443
|
+
el('input', {
|
|
444
|
+
class: 'tp-input', type: 'text', placeholder: '输入您的兑换码',
|
|
445
|
+
oninput: (e) => { pick.redeem = e.target.value },
|
|
446
|
+
}),
|
|
447
|
+
el('button', { class: 'ghost', onclick: () => showNotice('兑换额度需要宿主提供兑换端点,可在站点钱包页兑换。') }, '兑换额度')))
|
|
448
|
+
const foot = el('p', { class: 'note', text: '付完 5 分钟内余额会自动刷新;订单与发票在站点的钱包页。' })
|
|
449
|
+
return card(null, head, tiles, custom, payMethods, redeem, foot)
|
|
404
450
|
}
|
|
405
451
|
|
|
406
452
|
/**
|
|
@@ -617,10 +663,29 @@
|
|
|
617
663
|
el('a', { class: 'link', href: 'https://neartoken.cc/pricing', target: '_blank', rel: 'noreferrer' }, '详情 ›')))
|
|
618
664
|
}
|
|
619
665
|
|
|
620
|
-
/**
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
666
|
+
/**
|
|
667
|
+
* 模型按系列归类:DeepSeek → GLM → Kimi → Qwen → GPT → Claude → Gemini
|
|
668
|
+
* → Grok → 生图 → 视频 → 语音 → 向量 → 其他。同系列挨在一起。
|
|
669
|
+
*/
|
|
670
|
+
const SERIES = [
|
|
671
|
+
['DeepSeek', /deepseek|ds-|kimseek/i],
|
|
672
|
+
['GLM', /glm|智谱/i],
|
|
673
|
+
['Kimi', /kimi|moonshot/i],
|
|
674
|
+
['Qwen', /qwen|通义/i],
|
|
675
|
+
['GPT', /gpt-|o[134]-|codex|sora/i],
|
|
676
|
+
['Claude', /claude/i],
|
|
677
|
+
['Gemini', /gemini/i],
|
|
678
|
+
['Grok', /grok/i],
|
|
679
|
+
['生图', /banana|seedream|flux|image|ideogram|z-image|qwen-image|dall/i],
|
|
680
|
+
['视频', /veo|sd2|omni|seedance|minimax-h3|video|kling|sora/i],
|
|
681
|
+
['语音', /tts|asr|suno|whisper|speech|lyrics|sound/i],
|
|
682
|
+
['向量', /embedding|embed/i],
|
|
683
|
+
]
|
|
684
|
+
function modelSeriesOf(name) {
|
|
685
|
+
const n = String(name || '').toLowerCase()
|
|
686
|
+
const i = SERIES.findIndex(([, re]) => re.test(n))
|
|
687
|
+
return i === -1 ? SERIES.length : i
|
|
688
|
+
}
|
|
624
689
|
|
|
625
690
|
/** filteredModels 把搜索、分组、类型、厂商筛选叠起来。 */
|
|
626
691
|
function filteredModels() {
|
|
@@ -628,12 +693,8 @@
|
|
|
628
693
|
if (!p) return []
|
|
629
694
|
let list = p.models.slice()
|
|
630
695
|
if (marketSort !== 'latest') {
|
|
631
|
-
//
|
|
632
|
-
list.sort((a, b) =>
|
|
633
|
-
const ia = SITE_FIRST.indexOf(a.name), ib = SITE_FIRST.indexOf(b.name)
|
|
634
|
-
if (ia !== -1 || ib !== -1) return (ia === -1 ? 999 : ia) - (ib === -1 ? 999 : ib)
|
|
635
|
-
return 0
|
|
636
|
-
})
|
|
696
|
+
// 默认按系列归类(ds → glm → kimi → …),同系列内按名字排
|
|
697
|
+
list.sort((a, b) => modelSeriesOf(a.name) - modelSeriesOf(b.name) || a.name.localeCompare(b.name))
|
|
637
698
|
}
|
|
638
699
|
if (marketGroup) list = list.filter((m) => (m.enable_groups || []).includes(marketGroup))
|
|
639
700
|
if (marketKind !== 'all') list = list.filter((m) => marketKindOf(m) === marketKind)
|
|
@@ -661,10 +722,13 @@
|
|
|
661
722
|
const p = pricing()
|
|
662
723
|
// 先按功能分栏,栏内按首标签(满血 → 官Key → 低价 → …)再按倍率排
|
|
663
724
|
const FUNC_ORDER = ['对话', '生图', '视频', '语音', '向量']
|
|
725
|
+
// 分组栏:低价分组整体排上面(是列表里的上下位置,不是标签内前后权重),
|
|
726
|
+
// 其余按倍率升序。标签内部仍按「功能 → 满血/官Key → 低价」重绘。
|
|
727
|
+
const isLow = (g) => /低价/i.test(g)
|
|
664
728
|
const rankOf = (g) => {
|
|
665
729
|
const t = groupTags(g)
|
|
666
|
-
|
|
667
|
-
|
|
730
|
+
const f = FUNC_ORDER.indexOf(t.func)
|
|
731
|
+
return (isLow(g) ? 0 : 1) * 10000 + (f === -1 ? 99 : f) * 100 + (Number(p.groups[g]) || 0)
|
|
668
732
|
}
|
|
669
733
|
const groups = Object.keys(p?.groups || {}).sort((a, b) => rankOf(a) - rankOf(b) || a.localeCompare(b, 'zh'))
|
|
670
734
|
const vendors = (p?.vendors || []).slice().sort((a, b) => String(a.name).localeCompare(String(b.name), 'zh'))
|
|
@@ -696,27 +760,33 @@
|
|
|
696
760
|
el('div', { class: 'market-seg' },
|
|
697
761
|
el('button', { 'aria-pressed': String(marketLayout === 'grid'), title: '网格', onclick: () => { marketLayout = 'grid'; refreshMarketList() } }, '▦'),
|
|
698
762
|
el('button', { 'aria-pressed': String(marketLayout === 'list'), title: '列表', onclick: () => { marketLayout = 'list'; refreshMarketList() } }, '☰')))
|
|
699
|
-
//
|
|
763
|
+
// 分组栏两级:低价分组整体排最上面,其余按功能分栏
|
|
764
|
+
const lowGroups = groups.filter(isLow)
|
|
765
|
+
const otherGroups = groups.filter((g) => !isLow(g))
|
|
700
766
|
const byFunc = new Map()
|
|
701
|
-
for (const g of
|
|
767
|
+
for (const g of otherGroups) {
|
|
702
768
|
const f = groupTags(g).func
|
|
703
769
|
if (!byFunc.has(f)) byFunc.set(f, [])
|
|
704
770
|
byFunc.get(f).push(g)
|
|
705
771
|
}
|
|
772
|
+
const chipOf = (g) => {
|
|
773
|
+
const ratio = Number(p.groups[g]) || 0
|
|
774
|
+
return el('button', {
|
|
775
|
+
class: 'market-chip' + (marketGroup === g ? ' on' : ''),
|
|
776
|
+
title: '按这个分组筛选',
|
|
777
|
+
onclick: () => { marketGroup = marketGroup === g ? '' : g; render() },
|
|
778
|
+
}, groupBadge(g), ratio > 0 ? el('span', { class: 'ratio', text: 'x' + ratioText(ratio) }) : null)
|
|
779
|
+
}
|
|
706
780
|
const groupChips = el('div', { class: 'market-chips' },
|
|
707
781
|
el('button', {
|
|
708
782
|
class: 'market-chip' + (!marketGroup ? ' on' : ''), onclick: () => { marketGroup = ''; render() },
|
|
709
783
|
}, '所有分组'),
|
|
784
|
+
lowGroups.length ? el('div', { class: 'chip-group' },
|
|
785
|
+
el('div', { class: 'chip-group-name low', text: '低价' }),
|
|
786
|
+
el('div', { class: 'market-chips' }, lowGroups.map(chipOf))) : null,
|
|
710
787
|
FUNC_ORDER.filter((f) => byFunc.has(f)).map((f) => el('div', { class: 'chip-group' },
|
|
711
788
|
el('div', { class: 'chip-group-name', text: f }),
|
|
712
|
-
el('div', { class: 'market-chips' }, byFunc.get(f).map(
|
|
713
|
-
const ratio = Number(p.groups[g]) || 0
|
|
714
|
-
return el('button', {
|
|
715
|
-
class: 'market-chip' + (marketGroup === g ? ' on' : ''),
|
|
716
|
-
title: '按这个分组筛选',
|
|
717
|
-
onclick: () => { marketGroup = marketGroup === g ? '' : g; render() },
|
|
718
|
-
}, groupBadge(g), ratio > 0 ? el('span', { class: 'ratio', text: 'x' + ratioText(ratio) }) : null)
|
|
719
|
-
})))))
|
|
789
|
+
el('div', { class: 'market-chips' }, byFunc.get(f).map(chipOf)))))
|
|
720
790
|
const kinds = el('div', { class: 'market-chips' },
|
|
721
791
|
MARKET_KINDS.map(([k, label]) => el('button', {
|
|
722
792
|
class: 'market-chip' + (marketKind === k ? ' on' : ''),
|
package/page/market.css
CHANGED
|
@@ -100,8 +100,52 @@
|
|
|
100
100
|
.gb-tag.lead { font-weight: 700; opacity: 1; box-shadow: 0 0 0 1px rgba(255,255,255,.32); }
|
|
101
101
|
.gb-tag.purple { background: #7b52b8; }
|
|
102
102
|
.gb-tag.teal { background: #2f8f8a; }
|
|
103
|
+
.chip-group-name.low { color: #4cc38a; font-weight: 700; font-size: 11px; }
|
|
103
104
|
.chip-group { width: 100%; margin: 2px 0 4px; }
|
|
104
105
|
.chip-group-name { font-size: 10px; color: var(--nt-faint); margin: 6px 0 3px; letter-spacing: .04em; }
|
|
106
|
+
|
|
107
|
+
/* 充值卡:站点「添加资金」缩小版 */
|
|
108
|
+
.tp-head { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--nt-line); margin-bottom: 8px; }
|
|
109
|
+
.tp-badge { width: 28px; height: 28px; border-radius: 9px; display: grid; place-items: center; background: rgba(76,195,138,.14); color: #4cc38a; font-size: 15px; font-weight: 700; }
|
|
110
|
+
.tp-head h2 { font-size: 14px; }
|
|
111
|
+
.tp-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
|
|
112
|
+
.tp-tile {
|
|
113
|
+
position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
|
|
114
|
+
min-height: 48px; padding: 6px 9px; text-align: left;
|
|
115
|
+
border: 1px solid var(--nt-line); border-radius: 9px; background: rgba(255,255,255,.02); color: var(--nt-ink);
|
|
116
|
+
}
|
|
117
|
+
.tp-tile b { font-size: 16px; font-variant-numeric: tabular-nums; }
|
|
118
|
+
.tp-tile .tp-pay { font-size: 9.5px; color: var(--nt-muted); }
|
|
119
|
+
.tp-tile .tp-off { position: absolute; right: 6px; top: 6px; font-size: 9px; color: #4cc38a; }
|
|
120
|
+
.tp-tile[aria-pressed="true"] { border-color: var(--nt-accent); background: var(--nt-accent-soft); box-shadow: inset 0 0 0 1px var(--nt-accent); }
|
|
121
|
+
.tp-label { display: block; font-size: 10px; color: var(--nt-muted); margin: 8px 0 4px; }
|
|
122
|
+
.tp-custom-row { display: flex; align-items: center; gap: 8px; }
|
|
123
|
+
.tp-field { height: 32px; }
|
|
124
|
+
.tp-field input { width: 100%; font-size: 12px; }
|
|
125
|
+
.tp-due { flex: 1; display: flex; align-items: center; justify-content: space-between; gap: 8px; height: 32px; padding: 0 9px; border: 1px solid var(--nt-line); border-radius: 9px; font-size: 10.5px; color: var(--nt-muted); }
|
|
126
|
+
.tp-due b { color: var(--nt-ink); font-family: var(--nt-code); font-size: 12px; }
|
|
127
|
+
.tp-methods { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
128
|
+
.tp-method { display: inline-flex; align-items: center; gap: 8px; height: 34px; padding: 0 12px; border: 1px solid var(--nt-line); border-radius: 9px; background: rgba(255,255,255,.02); color: var(--nt-ink); font-size: 11.5px; }
|
|
129
|
+
.tp-method[aria-pressed="true"] { border-color: var(--nt-accent); background: var(--nt-accent-soft); }
|
|
130
|
+
.tp-micon { width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; color: #fff; font-size: 10px; font-weight: 700; }
|
|
131
|
+
.tp-micon.ali { background: #1677ff; }
|
|
132
|
+
.tp-micon.wx { background: #07c160; }
|
|
133
|
+
.tp-redeem { border-top: 1px solid var(--nt-line); margin-top: 8px; padding-top: 2px; }
|
|
134
|
+
.tp-input { flex: 1; height: 32px; padding: 0 9px; border: 1px solid var(--nt-line); border-radius: 9px; background: rgba(0,0,0,.2); color: var(--nt-ink); font-size: 11.5px; }
|
|
135
|
+
.tp-redeem .ghost { height: 32px; }
|
|
136
|
+
|
|
137
|
+
/* 推荐计划长条卡 */
|
|
138
|
+
.rf { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
139
|
+
.rf-ico { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; background: rgba(155,124,224,.16); color: #9b7ce0; font-size: 16px; }
|
|
140
|
+
.rf-text { flex: 1 1 240px; }
|
|
141
|
+
.rf-text h2 { font-size: 13px; }
|
|
142
|
+
.rf-invites { text-align: center; min-width: 54px; }
|
|
143
|
+
.rf-invites .k { font-size: 10px; color: var(--nt-muted); }
|
|
144
|
+
.rf-invites .v { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
145
|
+
.rf-link { flex: 1 1 260px; min-width: 0; display: flex; align-items: center; height: 36px; padding: 0 12px; border: 1px solid var(--nt-line); border-radius: 10px; background: rgba(0,0,0,.2); overflow: hidden; }
|
|
146
|
+
.rf-link span { font-family: var(--nt-code); font-size: 11.5px; color: var(--nt-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
147
|
+
.rf-copy { width: 36px; height: 36px; border: 1px solid var(--nt-accent); border-radius: 10px; background: var(--nt-accent-soft); color: var(--nt-accent); font-size: 15px; }
|
|
148
|
+
@media (max-width: 620px) { .tp-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
105
149
|
.mc-image { border-color: rgba(139,79,158,.5); }
|
|
106
150
|
.mc-video { border-color: rgba(214,140,60,.5); }
|
|
107
151
|
.mc-audio { border-color: rgba(63,111,168,.5); }
|
package/page/style.css
CHANGED
|
@@ -46,6 +46,11 @@ html, body {
|
|
|
46
46
|
内容超出时卡片内部滚动,不把整页撑长。 */
|
|
47
47
|
.home-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; }
|
|
48
48
|
.home-grid > .card { margin: 0; overflow-y: auto; scrollbar-width: thin; }
|
|
49
|
+
.home-grid > .card.home-wide {
|
|
50
|
+
grid-column: 1 / -1;
|
|
51
|
+
overflow: visible; /* 长条卡不参与等高锁死 */
|
|
52
|
+
height: auto !important;
|
|
53
|
+
}
|
|
49
54
|
.home-grid > .card::-webkit-scrollbar { width: 6px; }
|
|
50
55
|
.home-grid > .card::-webkit-scrollbar-thumb { background: var(--nt-line); border-radius: 3px; }
|
|
51
56
|
@media (max-width: 720px) { .home-grid { grid-template-columns: 1fr; } .home-grid > .card { height: auto !important; } }
|
package/page/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// 由 scripts/stamp.mjs 生成,别手改:版本号在 package.json。
|
|
2
|
-
self.NEARTOKEN_VERSION = "0.
|
|
2
|
+
self.NEARTOKEN_VERSION = "0.5.0"
|