Compare commits
25 Commits
3237ef8271
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25eb3276f9 | ||
| 9cb1bf5c70 | |||
|
|
d42a4135f8 | ||
| 4bd1f3a2f0 | |||
| f9cabfb7c7 | |||
| 77fc031f4a | |||
| 971e0f0d68 | |||
| 39fd2c2428 | |||
| 6c398add3f | |||
| bd00cbafd0 | |||
| 62763a538f | |||
| a90b5e8c51 | |||
| 7c29d0479e | |||
| 7cdc1eb31b | |||
| ea3e683900 | |||
| fb11a1d365 | |||
| 47dcdabc35 | |||
| 89dacb708f | |||
| 34e05c6dc0 | |||
| a41e12500d | |||
| 20eb9388c3 | |||
| cc9bbd07cc | |||
| a3126d3e7e | |||
| f46cf75aec | |||
| 5a69e68e40 |
@@ -31,13 +31,14 @@
|
||||
└── .memory/
|
||||
```
|
||||
|
||||
## 已写入 Figma Drafts(slim 模式 43 个)
|
||||
## 已写入 Figma Drafts(最终 88 个文件 key,47/56 套)
|
||||
|
||||
- W1 测试 1 个(手动 open)+ 42 个 slim 批量
|
||||
- **W37 Daily UI**:30 天的 kit 只入了第 1 天("Day 03 - Videos Website Landing"),其他 29 天本地 `extracted/W37/` 保留
|
||||
- W5 Wiloa 全 4 变体(Hotel/Plant/Restaurant/Travel)
|
||||
- W56 Orabel 全 6 页(Home/About/Portfolio/Blog/Contact/Open Menu)
|
||||
- 其他 32 个 template 各 1 个 fig
|
||||
- 35 套 `.fig` 原生全上云
|
||||
- 12 套 `.sketch` 经 Figma 自动转换上云
|
||||
- **W37 Daily UI 30 天全部上云**(30 个 variants,modal 8 行折行 tabs)
|
||||
- W5 Wiloa 4 变体 / W12 Premise 2 变体 / W36 AppStarter 4 变体 / W56 Orabel 6 页 全部多变体 tab 切换
|
||||
- 9 套 XD/PSD 只本地保留(Figma Import 不收 .xd/.psd)
|
||||
- 有 8 个重复 fileKey(Playwright 崩溃 + Figma 自动重试导致),API 403 删不了,需手动 Drafts 页删
|
||||
|
||||
## 非 Figma 原生的 21 套(已搁置)
|
||||
|
||||
|
||||
@@ -34,6 +34,188 @@
|
||||
"message": "auto-save 2026-04-22 15:51 (~1)",
|
||||
"hash": "54a0ca8",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T15:57:23+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 15:57 (~2)",
|
||||
"hash": "be6d4f7",
|
||||
"files_changed": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T15:58:06+08:00",
|
||||
"type": "commit",
|
||||
"message": "feat: 12 套 Sketch 也上 Figma,47/47 模板全部投射就位",
|
||||
"hash": "3237ef8",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:02:53+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:02 (~1)",
|
||||
"hash": "5a69e68",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:08:22+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:08 (~1)",
|
||||
"hash": "f46cf75",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:13:52+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:13 (~1)",
|
||||
"hash": "a3126d3",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:19:21+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:19 (~1)",
|
||||
"hash": "cc9bbd0",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:24:51+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:24 (~1)",
|
||||
"hash": "20eb938",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:30:21+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:30 (~1)",
|
||||
"hash": "a41e125",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:35:50+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:35 (~1)",
|
||||
"hash": "34e05c6",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:41:21+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:41 (~1)",
|
||||
"hash": "89dacb7",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:46:51+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:46 (~6)",
|
||||
"hash": "47dcdab",
|
||||
"files_changed": 6
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:52:21+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:52 (~1)",
|
||||
"hash": "fb11a1d",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T16:57:50+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 16:57 (~1)",
|
||||
"hash": "ea3e683",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:03:20+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:03 (~1)",
|
||||
"hash": "7cdc1eb",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:08:49+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:08 (+1, ~1)",
|
||||
"hash": "7c29d04",
|
||||
"files_changed": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:14:19+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:14 (~1)",
|
||||
"hash": "a90b5e8",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:19:51+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:19 (~1)",
|
||||
"hash": "62763a5",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:25:21+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:25 (~1)",
|
||||
"hash": "bd00cba",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:31:01+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:30 (~1)",
|
||||
"hash": "6c398ad",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:36:31+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:36 (~1)",
|
||||
"hash": "39fd2c2",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:42:01+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:41 (~1)",
|
||||
"hash": "971e0f0",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:47:31+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:47 (~1)",
|
||||
"hash": "77fc031",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:53:02+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:52 (~1)",
|
||||
"hash": "f9cabfb",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T17:58:32+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 17:58 (~1)",
|
||||
"hash": "4bd1f3a",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T18:02:30+08:00",
|
||||
"type": "commit",
|
||||
"message": "feat: W37 DailyUI 30 天全上云 + 多变体 tab",
|
||||
"hash": "d42a413",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-22T18:04:02+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-22 18:03 (~1)",
|
||||
"hash": "9cb1bf5",
|
||||
"files_changed": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
23
.project.json
Normal file
23
.project.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"created" : "2026-04-22",
|
||||
"name" : "figma模板库",
|
||||
"ownership" : "personal",
|
||||
"pin_order" : 1776848911,
|
||||
"pinned" : true,
|
||||
"status" : "new",
|
||||
"urls" : [
|
||||
{
|
||||
"label" : "git",
|
||||
"type" : "repo",
|
||||
"url" : "https:\/\/git.kang-kang.com\/kangwan\/figma-templates-showcase"
|
||||
},
|
||||
{
|
||||
"label" : "figma-lib",
|
||||
"type" : "app",
|
||||
"url" : "https:\/\/figma-lib.kang-kang.com"
|
||||
}
|
||||
],
|
||||
"worklog" : {
|
||||
"path" : "\/Users\/kangwan\/Projects\/research\/20260422-figma模板库\/.memory\/worklog.json"
|
||||
}
|
||||
}
|
||||
116
figma-files.json
116
figma-files.json
@@ -258,5 +258,121 @@
|
||||
{
|
||||
"key": "IGfS4r69yANQazOxLSjnyQ",
|
||||
"name": "yoyolabs-Limitless-for-Web-file"
|
||||
},
|
||||
{
|
||||
"key": "IdA0ICXerQVBhYUfjXGL1H",
|
||||
"name": "02_DailyUI - Discover Mountain Tour"
|
||||
},
|
||||
{
|
||||
"key": "eVqhxYBPehYLPEGTCRipyW",
|
||||
"name": "02_DailyUI - Coffee Shop Subscription Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "r0qplmCsBONsRIIDavwYF3",
|
||||
"name": "02_DailyUI - Coffee Shop Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "0fNvQCQqC8KbFaquti76RM",
|
||||
"name": "04_DailyUI - Coffee Shop Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "8yrmXShwcW73BB3cx1RzUO",
|
||||
"name": "04_DailyUI - Coffee Shop Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "wppvrY7Zkept7rFW8BSEJv",
|
||||
"name": "04_DailyUI - Product Page Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "eLgBq75wWJQFA4cnyCv6I5",
|
||||
"name": "04_DailyUI - Preloved Electronic Ecommerce Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "hHHcJtfuhLAdrMLTAlF5Ba",
|
||||
"name": "04_DailyUI - Crypto Trading App Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "3Bs5YdV3g3jOgqAOVv7IOW",
|
||||
"name": "04_DailyUI - Creative Digital Agency Website"
|
||||
},
|
||||
{
|
||||
"key": "z669xSruU8CdphtgLhRjcO",
|
||||
"name": "04_DailyUI - Technology Blog News Website UI"
|
||||
},
|
||||
{
|
||||
"key": "0GK6JkzINs5VzztfvA73m2",
|
||||
"name": "04_DailyUI - Web Development Agency Website"
|
||||
},
|
||||
{
|
||||
"key": "DnblGVf6MKSfWi0wPj8KZc",
|
||||
"name": "04_DailyUI - Travel Tour Booking Website Landing"
|
||||
},
|
||||
{
|
||||
"key": "cWaRxvQvuam5z03BkeESwG",
|
||||
"name": "04_DailyUI - Nursery & Kids Furniture Product Sale"
|
||||
},
|
||||
{
|
||||
"key": "KaWKPHRdIJn5GiJabbwSzp",
|
||||
"name": "02_DailyUI - Dentist Service Appointment"
|
||||
},
|
||||
{
|
||||
"key": "9IEKPzpkUgZniVdnmC10Aw",
|
||||
"name": "03_DailyUI - Baby Fashion Landing"
|
||||
},
|
||||
{
|
||||
"key": "pB2abP5veEWUYLsf7HS9tF",
|
||||
"name": "03_DailyUI - Movie Streaming Landing"
|
||||
},
|
||||
{
|
||||
"key": "hvcfo3wknaJGqnjPBv9a0P",
|
||||
"name": "03_DailyUI - Female Boots Product Detail"
|
||||
},
|
||||
{
|
||||
"key": "sMyOUigPVBCqS1lt7YpIEV",
|
||||
"name": "03_DailyUI - Freelancer Hiring Website UI"
|
||||
},
|
||||
{
|
||||
"key": "oUvrAAeDBqoeFpRa2hh7Rc",
|
||||
"name": "03_DailyUI - Online Daily Catering Order"
|
||||
},
|
||||
{
|
||||
"key": "ZUA5LAN9mmwgckGYYov4Nx",
|
||||
"name": "03_DailyUI House Rental Booking"
|
||||
},
|
||||
{
|
||||
"key": "U8Ivq70rEJ0PfApY7UozDO",
|
||||
"name": "03_DailyUI_Bridal_Dress_Rent"
|
||||
},
|
||||
{
|
||||
"key": "oDTnlaSfecAEE7BwM1skE9",
|
||||
"name": "03_DailyUI_Bridal_Dress_Rent"
|
||||
},
|
||||
{
|
||||
"key": "4uZw8jE1RTmULcmfHUQaiq",
|
||||
"name": "03_DailyUI_Restaurant_Booking"
|
||||
},
|
||||
{
|
||||
"key": "0FrxkoqSlwBmCfUkyQcnFf",
|
||||
"name": "03_DailyUI_Series_Movie"
|
||||
},
|
||||
{
|
||||
"key": "5WPExo1H1neQp1JyIwqHeZ",
|
||||
"name": "04_DailyUI_Laptop_Product_Detail"
|
||||
},
|
||||
{
|
||||
"key": "wJgnKYmet4emAvRir9b9F7",
|
||||
"name": "02_DailyUI_Shoes_Shop"
|
||||
},
|
||||
{
|
||||
"key": "x0mb4vUi4NXV9ehkwiHVUt",
|
||||
"name": "04_MovieTime_DailyUI"
|
||||
},
|
||||
{
|
||||
"key": "nBqXtoL8mA0ei5R1v6UPqa",
|
||||
"name": "04_Gowezz_Bike_Product_Detail"
|
||||
},
|
||||
{
|
||||
"key": "0vZIEbrB7t4RUTVggvABZH",
|
||||
"name": "04_DailyUI_v4_UI_Workspace_Website"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,61 +35,62 @@ def main():
|
||||
sys.exit(1)
|
||||
figma_files = json.loads(figma_files_path.read_text()) # list of {key, name, ...}
|
||||
|
||||
# Match each template (fig preferred, else sketch) against cloud files
|
||||
# Match each template source file to ALL matching cloud files (supports multi-variant)
|
||||
matches = []
|
||||
used_keys = set()
|
||||
for t in manifest['templates']:
|
||||
source_files = t['fig'] if t['fig'] else t['sketch']
|
||||
if not source_files: continue
|
||||
kind = 'fig' if t['fig'] else 'sketch'
|
||||
targets = [Path(f).stem for f in source_files]
|
||||
if t.get('archive'):
|
||||
targets.append(Path(t['archive']).stem)
|
||||
targets.append(t['name'])
|
||||
best_overall = (0, None)
|
||||
for tgt in targets:
|
||||
score, cand = best_match(tgt, [f for f in figma_files if f['key'] not in used_keys])
|
||||
if score > best_overall[0]:
|
||||
best_overall = (score, cand)
|
||||
if score > 0.95:
|
||||
break
|
||||
score, cand = best_overall
|
||||
stem = Path(source_files[0]).stem
|
||||
if cand and score >= 0.6:
|
||||
used_keys.add(cand['key'])
|
||||
matches.append({
|
||||
'W': t['id'], 'name': t['name'], 'kind': kind, 'source_stem': stem,
|
||||
'matched': cand['name'], 'key': cand['key'], 'score': round(score, 3)
|
||||
})
|
||||
else:
|
||||
matches.append({
|
||||
'W': t['id'], 'name': t['name'], 'kind': kind, 'source_stem': stem,
|
||||
'matched': None, 'best_score': round(best_overall[0], 3) if cand else 0,
|
||||
'best_candidate': cand['name'] if cand else None
|
||||
})
|
||||
variants = []
|
||||
for src in source_files:
|
||||
stem = Path(src).stem
|
||||
# Match this specific source stem to best unused cloud file
|
||||
score, cand = best_match(stem, [f for f in figma_files if f['key'] not in used_keys])
|
||||
if cand and score >= 0.6:
|
||||
used_keys.add(cand['key'])
|
||||
variants.append({
|
||||
'source_stem': stem,
|
||||
'matched': cand['name'],
|
||||
'key': cand['key'],
|
||||
'score': round(score, 3)
|
||||
})
|
||||
matches.append({
|
||||
'W': t['id'], 'name': t['name'], 'kind': kind,
|
||||
'source_count': len(source_files),
|
||||
'variants': variants,
|
||||
})
|
||||
|
||||
# Update web/data.json
|
||||
data_path = ROOT/'web'/'data.json'
|
||||
data = json.loads(data_path.read_text())
|
||||
by_W = {m['W']: m for m in matches if m.get('key')}
|
||||
by_W = {m['W']: m for m in matches}
|
||||
for t in data['templates']:
|
||||
m = by_W.get(t['id'])
|
||||
if m:
|
||||
t['figma_key'] = m['key']
|
||||
t['figma_url'] = f"https://www.figma.com/file/{m['key']}"
|
||||
if m and m['variants']:
|
||||
first = m['variants'][0]
|
||||
t['figma_key'] = first['key']
|
||||
t['figma_url'] = f"https://www.figma.com/file/{first['key']}"
|
||||
t['figma_variants'] = [
|
||||
{'name': v['source_stem'], 'matched': v['matched'], 'key': v['key'],
|
||||
'url': f"https://www.figma.com/file/{v['key']}"}
|
||||
for v in m['variants']
|
||||
]
|
||||
else:
|
||||
t['figma_key'] = None
|
||||
t['figma_url'] = None
|
||||
t['figma_variants'] = []
|
||||
|
||||
# update banner with imported count
|
||||
# update banner with imported count + variant total
|
||||
imported = sum(1 for t in data['templates'] if t['figma_key'])
|
||||
total_variants = sum(len(t.get('figma_variants', [])) for t in data['templates'])
|
||||
fig_cnt = sum(1 for t in manifest['templates'] if t['fig'])
|
||||
sketch_only_cnt = sum(1 for t in manifest['templates'] if not t['fig'] and t['sketch'])
|
||||
data['imported_summary'] = (
|
||||
f"✅ <b>{imported}/56 套</b>已云端就位在 "
|
||||
f"✅ <b>{imported}/56 套</b>(共 {total_variants} 个文件)已云端就位在 "
|
||||
f"<a href='https://www.figma.com/files/team/1304178887825899477/drafts' target='_blank'>Figma Drafts</a>"
|
||||
f"({fig_cnt} 个 .fig 原生 + {sketch_only_cnt} 个 .sketch 经 Figma 转换)。"
|
||||
f"点卡片 → modal → iframe 实时投射。"
|
||||
f"({fig_cnt} .fig + {sketch_only_cnt} .sketch 转换)。"
|
||||
f"点卡片 → modal → 多变体 tab 切换 + iframe 实时投射。"
|
||||
)
|
||||
data_path.write_text(json.dumps(data, ensure_ascii=False, indent=2))
|
||||
|
||||
@@ -97,13 +98,14 @@ def main():
|
||||
report = ROOT/'figma-match-report.json'
|
||||
report.write_text(json.dumps(matches, ensure_ascii=False, indent=2))
|
||||
total_matchable = sum(1 for t in manifest['templates'] if t['fig'] or t['sketch'])
|
||||
print(f"Matched {imported}/{total_matchable} templates ({fig_cnt} fig + {sketch_only_cnt} sketch-only)")
|
||||
multi_variant = sum(1 for m in matches if len(m['variants']) > 1)
|
||||
print(f"Matched {imported}/{total_matchable} templates, {total_variants} total file keys, {multi_variant} with multi variants")
|
||||
print(f"Report: {report.relative_to(ROOT)}")
|
||||
unmatched = [m for m in matches if not m.get('key')]
|
||||
unmatched = [m for m in matches if not m['variants']]
|
||||
if unmatched:
|
||||
print(f"\n⚠️ {len(unmatched)} unmatched:")
|
||||
for m in unmatched:
|
||||
print(f" {m['W']:4s} {m['name'][:50]:50s} best='{m.get('best_candidate','')[:40]}' score={m.get('best_score',0)}")
|
||||
print(f" {m['W']:4s} {m['name'][:50]:50s}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
48
web/app.js
48
web/app.js
@@ -89,11 +89,28 @@ function openModal(id) {
|
||||
const t = DATA.templates.find(x => x.id === id);
|
||||
if (!t) return;
|
||||
const body = document.getElementById('modalBody');
|
||||
const figmaSection = t.figma_key
|
||||
? `<iframe class="figma-embed" src="https://embed.figma.com/design/${t.figma_key}/?embed-host=kang&footer=false" allowfullscreen></iframe>`
|
||||
const variants = t.figma_variants || (t.figma_key ? [{name: t.name, key: t.figma_key, url: t.figma_url}] : []);
|
||||
const hasMulti = variants.length > 1;
|
||||
|
||||
const tabsHtml = hasMulti
|
||||
? `<div class="variant-tabs" id="variantTabs">
|
||||
${variants.map((v, i) => `<button class="vtab ${i===0?'active':''}" data-idx="${i}">${escapeHtml(v.name || v.matched || `变体 ${i+1}`)}</button>`).join('')}
|
||||
</div>`
|
||||
: '';
|
||||
|
||||
const firstVariant = variants[0];
|
||||
const iframeHtml = firstVariant
|
||||
? `<iframe class="figma-embed" id="variantFrame" src="https://embed.figma.com/design/${firstVariant.key}/?embed-host=kang&footer=false" allowfullscreen></iframe>`
|
||||
: '';
|
||||
|
||||
const openBtn = firstVariant
|
||||
? `<a class="btn" id="variantOpenBtn" href="${firstVariant.url}" target="_blank">在 Figma 打开 →</a>`
|
||||
: (t.has_fig
|
||||
? `<a class="btn" href="https://www.figma.com/files/recent" target="_blank">在 Figma Drafts 搜 "${escapeAttr(t.name.split(/[\s\-–—]/)[0])}" →</a>`
|
||||
: '');
|
||||
|
||||
body.innerHTML = `
|
||||
<h2>${escapeHtml(t.name)}</h2>
|
||||
<h2>${escapeHtml(t.name)}${hasMulti ? ` <span class="vcount">${variants.length} 变体</span>` : ''}</h2>
|
||||
<div class="sub2">
|
||||
<code>${t.id}</code>
|
||||
<span>${t.archive_size_mb} MB</span>
|
||||
@@ -103,24 +120,35 @@ function openModal(id) {
|
||||
${t.has_psd ? `<span class="badge psd">PSD</span>` : ''}
|
||||
</div>
|
||||
<div class="actions">
|
||||
${t.figma_url
|
||||
? `<a class="btn" href="${t.figma_url}" target="_blank">在 Figma 打开 →</a>`
|
||||
: (t.has_fig
|
||||
? `<a class="btn" href="https://www.figma.com/files/recent" target="_blank">在 Figma Drafts 搜 "${escapeAttr(t.name.split(/[\s\-–—]/)[0])}" →</a>`
|
||||
: '')}
|
||||
${openBtn}
|
||||
<a class="btn ghost" href="${t.source_rel}" onclick="revealSource('${t.id}');return false;">在 Finder 中显示源包</a>
|
||||
<a class="btn ghost" href="javascript:void(0)" onclick="copyPath('${t.id}');">复制源文件路径</a>
|
||||
</div>
|
||||
${figmaSection}
|
||||
${tabsHtml}
|
||||
${iframeHtml}
|
||||
<div class="spec">
|
||||
<div class="k">源包</div><div class="v">source/${t.id}/${t.archive}</div>
|
||||
<div class="k">解压目录</div><div class="v">extracted/${t.id}/</div>
|
||||
${t.figma_key ? `<div class="k">Figma Key</div><div class="v">${t.figma_key}</div>` : ''}
|
||||
${firstVariant ? `<div class="k">Figma Key</div><div class="v" id="variantKey">${firstVariant.key}</div>` : ''}
|
||||
</div>
|
||||
<div class="gallery">
|
||||
${(t.gallery||[]).map(g => `<img src="${g}" loading="lazy">`).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (hasMulti) {
|
||||
document.getElementById('variantTabs').addEventListener('click', e => {
|
||||
const btn = e.target.closest('.vtab');
|
||||
if (!btn) return;
|
||||
const idx = +btn.dataset.idx;
|
||||
const v = variants[idx];
|
||||
document.querySelectorAll('.vtab').forEach(b => b.classList.toggle('active', b === btn));
|
||||
document.getElementById('variantFrame').src = `https://embed.figma.com/design/${v.key}/?embed-host=kang&footer=false`;
|
||||
document.getElementById('variantOpenBtn').href = v.url;
|
||||
document.getElementById('variantKey').textContent = v.key;
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('modal').hidden = false;
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
745
web/data.json
745
web/data.json
File diff suppressed because it is too large
Load Diff
@@ -79,6 +79,12 @@ main{padding:32px 0 80px}
|
||||
.spec .v{color:var(--text);font-family:ui-monospace,monospace;font-size:12px;word-break:break-all}
|
||||
.figma-embed{width:100%;aspect-ratio:16/10;border:0;border-radius:10px;background:var(--panel2);margin-bottom:16px}
|
||||
|
||||
.modal-body h2 .vcount{display:inline-block;margin-left:8px;padding:2px 10px;background:rgba(107,92,255,.2);color:#b5bbf8;font-size:12px;font-weight:500;border-radius:999px;vertical-align:middle}
|
||||
.variant-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--line)}
|
||||
.vtab{background:var(--panel2);border:1px solid var(--line);color:var(--muted);padding:6px 12px;border-radius:8px;cursor:pointer;font-size:12px;transition:all .15s;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.vtab:hover{color:var(--text);border-color:#3a3f55}
|
||||
.vtab.active{background:var(--brand);border-color:var(--brand);color:#fff}
|
||||
|
||||
/* FOOTER */
|
||||
footer{border-top:1px solid var(--line);padding:20px 0;color:var(--muted);font-size:12px}
|
||||
footer .wrap{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap}
|
||||
|
||||
Reference in New Issue
Block a user