feat: 初始化 Figma 模板库 56 套展示站
- 56 套模板元数据(35 Figma 原生 + 21 非 Figma) - 静态展示站 (HTML/CSS/JS 无框架):格式筛选、lightbox、搜索 - 35 个 .fig 已真上传 Figma Drafts 云端 - iframe 实时投射(登录态可看私有 Drafts) - 部署:nginx:alpine + basic-auth (kang) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
41
scripts/import-to-figma.sh
Executable file
41
scripts/import-to-figma.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Batch open .fig files in Figma desktop app (→ Drafts)
|
||||
# Mode: "all" (72 files) or "slim" (W37 only 1, rest all = 48 files)
|
||||
set -euo pipefail
|
||||
MODE="${1:-slim}"
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
python3 - "$MODE" <<'PY' > /tmp/fig-list.txt
|
||||
import json, sys, os
|
||||
mode = sys.argv[1]
|
||||
m = json.load(open("manifest.json"))
|
||||
already_opened = {"W1"} # already done in earlier test
|
||||
files = []
|
||||
for t in m["templates"]:
|
||||
if not t["fig"]: continue
|
||||
figs = list(t["fig"])
|
||||
if mode == "slim" and t["id"] == "W37":
|
||||
figs = figs[:1]
|
||||
for f in figs:
|
||||
path = f'extracted/{t["id"]}/{f}'
|
||||
if t["id"] in already_opened and figs.index(f) == 0 and t["id"] == "W1":
|
||||
continue # skip W1 first fig (already opened)
|
||||
files.append(path)
|
||||
print('\n'.join(files))
|
||||
PY
|
||||
|
||||
count=$(wc -l < /tmp/fig-list.txt | tr -d ' ')
|
||||
echo "Mode=$MODE Files to open: $count"
|
||||
echo ""
|
||||
|
||||
i=0
|
||||
while IFS= read -r f; do
|
||||
i=$((i+1))
|
||||
printf "[%2d/%d] %s\n" "$i" "$count" "$f"
|
||||
open -a Figma "$ROOT/$f"
|
||||
sleep 1.5
|
||||
done < /tmp/fig-list.txt
|
||||
|
||||
echo ""
|
||||
echo "Done. All $count .fig files sent to Figma."
|
||||
105
scripts/match-and-update.py
Executable file
105
scripts/match-and-update.py
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Match Figma cloud Drafts files (from figma-files.json) back to W{N} entries in
|
||||
manifest.json by fuzzy name match, then update web/data.json with figma_key+url.
|
||||
"""
|
||||
import json, re, sys, difflib
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
def normalize(s):
|
||||
s = s.lower()
|
||||
s = re.sub(r'[^a-z0-9]+', ' ', s).strip()
|
||||
return s
|
||||
|
||||
def best_match(target, candidates):
|
||||
"""Return (score, candidate) best match from candidates list of dicts with .name"""
|
||||
nt = normalize(target)
|
||||
best = (0, None)
|
||||
for c in candidates:
|
||||
nc = normalize(c['name'])
|
||||
# exact / prefix / contains / sequence
|
||||
if nt == nc: s = 1.0
|
||||
elif nt in nc or nc in nt: s = 0.85
|
||||
else: s = difflib.SequenceMatcher(None, nt, nc).ratio()
|
||||
if s > best[0]:
|
||||
best = (s, c)
|
||||
return best
|
||||
|
||||
def main():
|
||||
manifest = json.loads((ROOT/'manifest.json').read_text())
|
||||
figma_files_path = ROOT/'figma-files.json'
|
||||
if not figma_files_path.exists():
|
||||
print(f"missing {figma_files_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
figma_files = json.loads(figma_files_path.read_text()) # list of {key, name, ...}
|
||||
|
||||
# for each W template that has fig, find matching figma file
|
||||
matches = []
|
||||
used_keys = set()
|
||||
for t in manifest['templates']:
|
||||
if not t['fig']: continue
|
||||
# use the .fig file's stem as match target (closer than archive name)
|
||||
targets = [Path(f).stem for f in t['fig']]
|
||||
# add archive stem as backup
|
||||
if t.get('archive'):
|
||||
targets.append(Path(t['archive']).stem)
|
||||
targets.append(t['name'])
|
||||
# try each target
|
||||
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
|
||||
if cand and score >= 0.6:
|
||||
used_keys.add(cand['key'])
|
||||
matches.append({
|
||||
'W': t['id'], 'name': t['name'], 'fig_stem': Path(t['fig'][0]).stem if t['fig'] else None,
|
||||
'matched': cand['name'], 'key': cand['key'], 'score': round(score, 3)
|
||||
})
|
||||
else:
|
||||
matches.append({
|
||||
'W': t['id'], 'name': t['name'], 'fig_stem': Path(t['fig'][0]).stem if t['fig'] else None,
|
||||
'matched': None, 'best_score': round(best_overall[0], 3) if cand else 0,
|
||||
'best_candidate': cand['name'] if cand else None
|
||||
})
|
||||
|
||||
# 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')}
|
||||
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']}"
|
||||
else:
|
||||
t['figma_key'] = None
|
||||
t['figma_url'] = None
|
||||
|
||||
# update banner with imported count
|
||||
imported = sum(1 for t in data['templates'] if t['figma_key'])
|
||||
data['imported_summary'] = (
|
||||
f"✅ <b>{imported} 个 Figma 原生文件</b>已云端就位在你的 "
|
||||
f"<a href='https://www.figma.com/files/team/1304178887825899477/drafts' target='_blank'>Figma Drafts</a>。"
|
||||
f"点卡片打开 modal 查看 iframe 实时投射 + 跳 Figma 编辑。"
|
||||
)
|
||||
data_path.write_text(json.dumps(data, ensure_ascii=False, indent=2))
|
||||
|
||||
# write match report
|
||||
report = ROOT/'figma-match-report.json'
|
||||
report.write_text(json.dumps(matches, ensure_ascii=False, indent=2))
|
||||
print(f"Matched {imported}/{sum(1 for t in manifest['templates'] if t['fig'])} fig templates")
|
||||
print(f"Report: {report.relative_to(ROOT)}")
|
||||
unmatched = [m for m in matches if not m.get('key')]
|
||||
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)}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user