create-dwy 0.14.7 → 0.16.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/CHANGELOG.md +17 -0
- package/bin/index.js +8 -2
- package/package.json +1 -1
- package/src/skills-install.js +154 -0
- package/src/sync-all.js +7 -0
- package/templates/ai-tools/CLAUDE.md +28 -1
- package/templates/ai-tools/hooks/Git/pre-git-commit-sensitive-check.sh +10 -2
- package/templates/ai-tools/rules/Android/dwy-android-pitfalls.md +52 -0
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-strict.md +221 -0
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +193 -0
- package/templates/ai-tools/rules/Python/dwy-python-backend.md +16 -63
- package/templates/ai-tools/rules/Python/dwy-python-base-lib.md +89 -0
- package/templates/ai-tools/rules/Python/dwy-python-pitfalls.md +49 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-base-lib.md +127 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-core.md +22 -86
- package/templates/ai-tools/rules/Vue/dwy-vue-pitfalls.md +66 -0
- package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +159 -0
- package/templates/ai-tools/rules/iOS/dwy-swift-core.md +35 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +59 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +47 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +24 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +70 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-commercial/SKILL.md +58 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +23 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-explore/SKILL.md +83 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +24 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-poc/SKILL.md +24 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +23 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +73 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +155 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +42 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +27 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +30 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +54 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +37 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +31 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +30 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +32 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +34 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +29 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +24 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +365 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/electron-release-workflow-template.yml +98 -0
- package/templates/ai-tools/skills//{350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel → 345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish}/references/release-workflow-template.yml +3 -3
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +31 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +31 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +23 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +37 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +23 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +27 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +25 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +32 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/monitor-notify.md +36 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/publish-config.md +65 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/version-sync.md +30 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/scripts/release_config.sh +115 -0
- package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/SKILL.md +31 -21
- package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/python-commercial-guide.md +1 -1
- package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/sensitive-patterns.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +91 -0
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/SKILL.md +70 -0
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/assets/template.html +353 -0
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-sdk-spec/scripts/init_config.sh +0 -97
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/SKILL.md +0 -113
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/templates/STATE.md +0 -56
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dev-qa/SKILL.md +0 -117
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-market-analysis/SKILL.md +0 -92
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/SKILL.md +0 -72
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/templates/prd-coarse.md +0 -61
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/SKILL.md +0 -76
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/templates/prd-detailed.md +0 -119
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prototype/SKILL.md +0 -104
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/SKILL.md +0 -50
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/application-release.md +0 -106
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/sdk-release.md +0 -93
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-spike/SKILL.md +0 -81
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/SKILL.md +0 -212
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/templates/impl-plan.md +0 -115
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tech-feasibility/SKILL.md +0 -105
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/SKILL.md +0 -64
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/templates/version-plan.md +0 -72
- package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +0 -356
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/files-whitelist-snippet.json" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/js-commercial.npmignore" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial-MANIFEST.in" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial.gitignore" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/js-commercial-guide.md" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/publish-blacklist.md" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_comments.py" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_js_publish.sh" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_python_publish.sh" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/detect_sdk.sh" +0 -0
- /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/SKILL.md" +0 -0
- /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/references/semver-spec.md" +0 -0
- /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/scripts/bump.py" +0 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<!-- ============================================================
|
|
7
|
+
白板原型模板 · 复制本文件后只改三处:
|
|
8
|
+
① 画布区(.stage 列表)—— 每个状态一个画布,含左手机框 + 右标注栏
|
|
9
|
+
② 脚本里的 EDGES —— 声明白板内状态跳转拓扑(画连线)
|
|
10
|
+
③ 左侧 .sidenav —— 把当前页设为 .on,其余指向对应白板 html
|
|
11
|
+
引擎(平移缩放/连线/选中/复位/拖拽防误触)勿改。
|
|
12
|
+
============================================================ -->
|
|
13
|
+
<title>页面名 · 白板(含全状态)</title>
|
|
14
|
+
<style>
|
|
15
|
+
/* 可改①:配色 — 按产品定位重填下方色值(变量名/结构勿改),默认深色蓝调一套 */
|
|
16
|
+
:root{
|
|
17
|
+
--bg:#0A0A0B; --device:#111113; --surface:#18181B; --surface2:#1F1F23; --surface3:#27272B;
|
|
18
|
+
--ink:#F4F4F5; --sub:#8E8E96; --faint:#5C5C63;
|
|
19
|
+
--gold:#3B82F6; --goldHi:#60A5FA; --gold2:#2563EB;
|
|
20
|
+
--red:#FB7185; --green:#34D399; --hairline:rgba(255,255,255,.08);
|
|
21
|
+
--pro:#F5B85C; /* 付费锁牌金色(按需) */
|
|
22
|
+
--spec-bg:#0d0d0f; --spec-line:#222;
|
|
23
|
+
}
|
|
24
|
+
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
|
|
25
|
+
html,body{height:100%}
|
|
26
|
+
body{margin:0;background:#000;font-family:-apple-system,"SF Pro Text","PingFang SC",sans-serif;
|
|
27
|
+
color:var(--ink);overflow:hidden;user-select:none} /* 禁选中,拖拽不选画框文字 */
|
|
28
|
+
input{user-select:text}
|
|
29
|
+
|
|
30
|
+
/* ===== 左侧悬浮页面导航(fixed,不随白板移动)===== */
|
|
31
|
+
.sidenav{position:fixed;left:14px;top:50%;transform:translateY(-50%);z-index:30;
|
|
32
|
+
display:flex;flex-direction:column;gap:4px;background:rgba(10,11,14,.92);
|
|
33
|
+
backdrop-filter:blur(12px);border:1px solid var(--spec-line);border-radius:18px;padding:9px}
|
|
34
|
+
.sidenav .ov{display:flex;align-items:center;gap:9px;text-decoration:none;color:var(--sub);
|
|
35
|
+
padding:7px 12px 7px 8px;font-size:11px;font-weight:600;margin-bottom:3px;
|
|
36
|
+
border-bottom:1px solid var(--hairline);padding-bottom:9px}
|
|
37
|
+
.sidenav .ov:hover{color:var(--ink)}
|
|
38
|
+
.navitem{display:flex;align-items:center;gap:9px;text-decoration:none;color:var(--sub);
|
|
39
|
+
padding:6px 12px 6px 7px;border-radius:12px;transition:background .15s,color .15s;white-space:nowrap}
|
|
40
|
+
.navitem .nic{width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;
|
|
41
|
+
font-size:15px;background:var(--surface2);flex-shrink:0}
|
|
42
|
+
.navitem .nlb{font-size:12.5px;font-weight:600}
|
|
43
|
+
.navitem .nlb .todo{font-size:9px;color:var(--faint);margin-left:5px;font-weight:500}
|
|
44
|
+
.navitem.ready{cursor:pointer}
|
|
45
|
+
.navitem.ready:hover{background:var(--surface2);color:var(--ink)}
|
|
46
|
+
.navitem.on{background:rgba(59,130,246,.16);color:#fff}
|
|
47
|
+
.navitem.on .nic{background:linear-gradient(135deg,var(--goldHi),var(--gold));color:#fff}
|
|
48
|
+
.navitem.todo{opacity:.42;cursor:not-allowed}
|
|
49
|
+
|
|
50
|
+
/* ===== 白板视口 ===== */
|
|
51
|
+
#viewport{position:fixed;inset:0;overflow:hidden;cursor:grab;
|
|
52
|
+
background:radial-gradient(circle at 1px 1px,rgba(255,255,255,.05) 1px,transparent 0) 0 0/26px 26px,#050506}
|
|
53
|
+
#viewport.grabbing{cursor:grabbing}
|
|
54
|
+
#world{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}
|
|
55
|
+
#wires{position:absolute;top:0;left:0;overflow:visible;pointer-events:none;z-index:1}
|
|
56
|
+
#wires path{fill:none;stroke:rgba(96,165,250,.5);stroke-width:3}
|
|
57
|
+
#wires path.hot{stroke:#7DB5FF;stroke-width:5;filter:drop-shadow(0 0 4px rgba(96,165,250,.7))}
|
|
58
|
+
#wires .wlabel{fill:#cfe0ff;font-size:13px;font-weight:600;font-family:-apple-system,"PingFang SC",sans-serif}
|
|
59
|
+
#wires .wlabel-bg{fill:#10141c;stroke:rgba(96,165,250,.35);stroke-width:1;rx:6}
|
|
60
|
+
|
|
61
|
+
.pagehead{position:fixed;top:14px;left:50%;transform:translateX(-50%);z-index:20;text-align:center;
|
|
62
|
+
background:rgba(10,11,14,.82);backdrop-filter:blur(12px);border:1px solid var(--spec-line);
|
|
63
|
+
border-radius:14px;padding:10px 22px}
|
|
64
|
+
.pagehead h1{font-size:16px;font-weight:800;margin:0;letter-spacing:.3px}
|
|
65
|
+
.pagehead p{color:var(--sub);font-size:11.5px;line-height:1.5;margin:3px 0 0}
|
|
66
|
+
.toolbar{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:20;display:flex;gap:6px;
|
|
67
|
+
background:rgba(10,11,14,.9);backdrop-filter:blur(12px);border:1px solid var(--spec-line);
|
|
68
|
+
border-radius:12px;padding:6px}
|
|
69
|
+
.toolbar button{border:none;background:var(--surface2);color:var(--ink);font-size:13px;font-weight:600;
|
|
70
|
+
padding:8px 13px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
71
|
+
.toolbar button:hover{background:var(--surface3)}
|
|
72
|
+
.toolbar .zlabel{display:flex;align-items:center;padding:0 8px;color:var(--sub);font-size:12px;min-width:48px;justify-content:center}
|
|
73
|
+
|
|
74
|
+
/* ===== 画布:白板上绝对定位(data-x/data-y)===== */
|
|
75
|
+
.stage{position:absolute;display:flex;gap:22px;align-items:flex-start;z-index:2}
|
|
76
|
+
.frame{display:flex;flex-direction:column;gap:10px;flex-shrink:0}
|
|
77
|
+
/* 编号 = 选中入口(只点它才选中+复位展示),画框本身不响应选中 */
|
|
78
|
+
.canvas-no{font-size:13px;font-weight:700;color:var(--goldHi);letter-spacing:.3px;cursor:pointer}
|
|
79
|
+
.canvas-no:hover{color:#fff}
|
|
80
|
+
.canvas-no .n{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
|
|
81
|
+
border-radius:7px;background:rgba(59,130,246,.16);margin-right:7px;font-size:12px}
|
|
82
|
+
|
|
83
|
+
/* 可改②:设备框尺寸 — 按目标端改 width/height(iOS 390×844 / 鸿蒙 vp / iPad / Web 各自比例),默认 320×680 */
|
|
84
|
+
.phone{width:320px;height:680px;background:var(--bg);border-radius:44px;
|
|
85
|
+
border:9px solid #1c1c1e;box-shadow:0 24px 60px rgba(0,0,0,.6);position:relative;overflow:hidden;
|
|
86
|
+
transition:box-shadow .25s,transform .25s}
|
|
87
|
+
/* 选中:双层光圈 + 微放大,明显区别于按钮自带样式 */
|
|
88
|
+
.phone.selected{box-shadow:0 0 0 3px #0A0A0B,0 0 0 6px var(--gold),0 0 30px rgba(59,130,246,.6),0 24px 60px rgba(0,0,0,.6);transform:scale(1.015)}
|
|
89
|
+
.canvas-no.selected{color:#fff}
|
|
90
|
+
.canvas-no.selected .n{background:var(--gold);color:#fff}
|
|
91
|
+
.canvas-no.selected::after{content:'当前';margin-left:8px;font-size:10px;font-weight:700;
|
|
92
|
+
color:#0A1A33;background:var(--goldHi);padding:2px 7px;border-radius:6px;letter-spacing:.5px}
|
|
93
|
+
.notch{position:absolute;top:0;left:50%;transform:translateX(-50%);width:112px;height:28px;
|
|
94
|
+
background:#1c1c1e;border-radius:0 0 17px 17px;z-index:50}
|
|
95
|
+
.screen{position:absolute;inset:0;padding:50px 18px 22px;overflow-y:auto;background:var(--device)}
|
|
96
|
+
.screen::-webkit-scrollbar{display:none}
|
|
97
|
+
|
|
98
|
+
/* ===== 右侧标注栏 ===== */
|
|
99
|
+
.spec{width:300px;background:var(--spec-bg);border:1px solid var(--spec-line);border-radius:16px;padding:18px 18px 20px;flex-shrink:0}
|
|
100
|
+
.spec h2{font-size:15px;font-weight:700;margin:0 0 4px;color:var(--ink)}
|
|
101
|
+
.spec .use{color:var(--sub);font-size:12.5px;line-height:1.55;margin:0 0 16px}
|
|
102
|
+
.spec .grp{margin-bottom:15px}
|
|
103
|
+
.spec .grp:last-child{margin-bottom:0}
|
|
104
|
+
.spec .gt{font-size:11px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--faint);margin:0 0 8px}
|
|
105
|
+
.spec .gt.ix{color:var(--goldHi)}
|
|
106
|
+
.spec .gt.bd{color:var(--red)}
|
|
107
|
+
.spec .gt.pay{color:var(--pro)}
|
|
108
|
+
.spec ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
|
|
109
|
+
.spec li{font-size:12px;line-height:1.5;color:#c9c9cf;display:flex;gap:7px;align-items:flex-start}
|
|
110
|
+
.spec li .dot{color:var(--faint);flex-shrink:0}
|
|
111
|
+
.spec li .dot.ix{color:var(--goldHi)}
|
|
112
|
+
.spec li .dot.bd{color:var(--red)}
|
|
113
|
+
.spec li .dot.pay{color:var(--pro)}
|
|
114
|
+
/* 白板内跳转链接(data-go):点了 select+复位展示 */
|
|
115
|
+
.jump{color:var(--goldHi);font-weight:600;cursor:pointer;text-decoration:none;border-bottom:1px dashed rgba(96,165,250,.4)}
|
|
116
|
+
.jump:hover{color:#fff;border-bottom-color:#fff}
|
|
117
|
+
/* 跨白板跳转链接(href,绿色↗):交给浏览器切文件 */
|
|
118
|
+
.xjump{color:var(--green);font-weight:600;text-decoration:none;border-bottom:1px dashed rgba(52,211,153,.45)}
|
|
119
|
+
.xjump:hover{color:#fff;border-bottom-color:#fff}
|
|
120
|
+
.xjump::after{content:'↗';font-size:10px;margin-left:2px}
|
|
121
|
+
.spec code{font-family:ui-monospace,"SF Mono",monospace;font-size:11px;color:var(--goldHi);background:rgba(59,130,246,.1);padding:1px 5px;border-radius:4px}
|
|
122
|
+
|
|
123
|
+
/* ===== 画框内常用 UI(按需取用,可自行加)===== */
|
|
124
|
+
.hdr{display:flex;align-items:center;gap:10px;margin-bottom:18px}
|
|
125
|
+
.hdr .back{width:32px;height:32px;border-radius:10px;background:var(--surface);display:flex;align-items:center;justify-content:center;color:var(--sub);font-size:17px;cursor:pointer}
|
|
126
|
+
.hdr h1{font-size:18px;font-weight:700;margin:0;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
127
|
+
.sub{color:var(--sub);font-size:13px;line-height:1.5}
|
|
128
|
+
.faint{color:var(--faint);font-size:12px}
|
|
129
|
+
.sectiontitle{font-size:12px;color:var(--sub);font-weight:600;letter-spacing:.5px;text-transform:uppercase;margin:0 0 10px 2px}
|
|
130
|
+
.field{width:100%;background:var(--surface);border:1.5px solid var(--hairline);border-radius:12px;padding:11px 14px;color:var(--ink);font-size:14px;outline:none;margin-bottom:12px}
|
|
131
|
+
.field::placeholder{color:var(--faint)}
|
|
132
|
+
.btn{width:100%;border:none;border-radius:14px;padding:14px;font-size:15px;font-weight:700;
|
|
133
|
+
background:linear-gradient(135deg,var(--goldHi),var(--gold),var(--gold2));color:#fff;cursor:pointer;
|
|
134
|
+
box-shadow:0 8px 20px rgba(59,130,246,.4);transition:transform .15s cubic-bezier(.34,1.2,.64,1);display:flex;align-items:center;justify-content:center;gap:7px}
|
|
135
|
+
.btn:active{transform:scale(.93)}
|
|
136
|
+
.btn.ghost{background:transparent;color:var(--gold);box-shadow:none;border:1.5px solid rgba(59,130,246,.4)}
|
|
137
|
+
.btn.danger{background:linear-gradient(135deg,#FB7185,#E11D48);box-shadow:0 8px 20px rgba(225,29,72,.35)}
|
|
138
|
+
.btn.mini{padding:11px;font-size:14px;border-radius:11px}
|
|
139
|
+
.card{background:var(--surface2);border:1px solid var(--hairline);border-radius:14px;padding:15px;margin-bottom:13px}
|
|
140
|
+
.pill{display:inline-flex;align-items:center;gap:4px;background:rgba(59,130,246,.15);color:var(--gold);font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:7px}
|
|
141
|
+
.pill.ok{background:rgba(52,211,153,.15);color:var(--green)}
|
|
142
|
+
.pill.warn{background:rgba(251,113,133,.14);color:var(--red)}
|
|
143
|
+
.pill.lock{background:linear-gradient(135deg,rgba(245,184,92,.22),rgba(224,153,47,.22));color:var(--pro);border:1px solid rgba(245,184,92,.35)} /* 付费锁牌 */
|
|
144
|
+
.note{display:flex;gap:7px;align-items:flex-start;margin-top:10px;color:var(--faint);font-size:11.5px;line-height:1.5}
|
|
145
|
+
.note .d{color:var(--gold)}
|
|
146
|
+
.spin{width:42px;height:42px;border:4px solid rgba(59,130,246,.2);border-top-color:var(--gold);border-radius:50%;animation:rot 1s linear infinite;margin:30px auto 18px}
|
|
147
|
+
@keyframes rot{to{transform:rotate(360deg)}}
|
|
148
|
+
.centerblk{text-align:center;padding-top:30px}
|
|
149
|
+
.centerblk h2{font-size:18px;margin:0 0 8px}
|
|
150
|
+
/* 列表行:单行截断示范(min-width:0 是省略号生效前提)*/
|
|
151
|
+
.listrow{display:flex;align-items:center;gap:12px;background:var(--surface2);border:1px solid var(--hairline);border-radius:14px;padding:12px;margin-bottom:10px;cursor:pointer}
|
|
152
|
+
.listrow .ic{width:42px;height:42px;border-radius:11px;background:var(--surface3);display:flex;align-items:center;justify-content:center;font-size:21px;flex-shrink:0}
|
|
153
|
+
.listrow .meta{flex:1;min-width:0}
|
|
154
|
+
.listrow .meta .n{font-size:15px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
155
|
+
.listrow .meta .s{font-size:12px;color:var(--sub);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
156
|
+
.listrow .arr{color:var(--faint);font-size:17px;flex-shrink:0}
|
|
157
|
+
/* 空态 */
|
|
158
|
+
.empty{text-align:center;padding:70px 20px 40px;color:var(--faint)}
|
|
159
|
+
.empty .ei{font-size:48px;margin-bottom:16px;opacity:.6}
|
|
160
|
+
.empty .et{font-size:16px;color:var(--sub);font-weight:600}
|
|
161
|
+
.empty .ed{font-size:13px;margin-top:8px;line-height:1.6}
|
|
162
|
+
/* 加载骨架 */
|
|
163
|
+
.skel{height:64px;border-radius:14px;margin-bottom:10px;background:linear-gradient(90deg,var(--surface2) 25%,var(--surface3) 50%,var(--surface2) 75%);background-size:200% 100%;animation:sh 1.3s infinite}
|
|
164
|
+
@keyframes sh{to{background-position:-200% 0}}
|
|
165
|
+
</style>
|
|
166
|
+
</head>
|
|
167
|
+
<body>
|
|
168
|
+
|
|
169
|
+
<div class="pagehead">
|
|
170
|
+
<h1>页面名 · 全状态白板</h1>
|
|
171
|
+
<p>一句话页面定位。<span style="color:var(--green)">绿色 ↗</span> = 跨白板跳转</p>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<!-- ③ 左侧导航:把当前页设 .on(去掉 href),其余指向对应白板 html;待做的用 .todo + span -->
|
|
175
|
+
<nav class="sidenav">
|
|
176
|
+
<a class="ov" href="index-spec.html"><span>◳</span>原型总览</a>
|
|
177
|
+
<a class="navitem on"><span class="nic">📄</span><span class="nlb">本页</span></a>
|
|
178
|
+
<a class="navitem ready" href="other-spec.html"><span class="nic">📄</span><span class="nlb">其他页</span></a>
|
|
179
|
+
<span class="navitem todo"><span class="nic">📄</span><span class="nlb">待做页<span class="todo">待做</span></span></span>
|
|
180
|
+
</nav>
|
|
181
|
+
|
|
182
|
+
<div id="viewport">
|
|
183
|
+
<div id="world">
|
|
184
|
+
<svg id="wires"></svg>
|
|
185
|
+
|
|
186
|
+
<!-- ① 画布区:每个状态一个 .stage。data-id 唯一;data-x/data-y 是白板坐标(列距~760 行距~820)。
|
|
187
|
+
编号 .canvas-no 的 id 必须等于 data-id(选中靠它)。左 .phone 放 UI,右 .spec 放标注。 -->
|
|
188
|
+
|
|
189
|
+
<!-- 占位画布 A -->
|
|
190
|
+
<div class="stage" data-id="stateA" data-x="40" data-y="60">
|
|
191
|
+
<div class="frame">
|
|
192
|
+
<div class="canvas-no" id="stateA"><span class="n">1</span>状态 A(默认)</div>
|
|
193
|
+
<div class="phone"><div class="notch"></div>
|
|
194
|
+
<div class="screen">
|
|
195
|
+
<div class="hdr"><h1>页面标题</h1></div>
|
|
196
|
+
<!-- 列表单行截断示范 -->
|
|
197
|
+
<div class="listrow" data-go="stateB">
|
|
198
|
+
<div class="ic">📄</div>
|
|
199
|
+
<div class="meta"><div class="n">某条目超长名字会被省略号截断不撑破布局</div><div class="s">副信息行也截断</div></div>
|
|
200
|
+
<span class="arr">›</span>
|
|
201
|
+
</div>
|
|
202
|
+
<button class="btn" data-go="stateB">主操作 → 状态 B</button>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="spec">
|
|
207
|
+
<h2>状态 A(默认)</h2>
|
|
208
|
+
<p class="use">一句话说明本状态的职责。</p>
|
|
209
|
+
<div class="grp"><p class="gt ix">交互跳转</p><ul>
|
|
210
|
+
<li><span class="dot ix">→</span><span>主操作 → <a class="jump" data-go="stateB">② 状态 B</a></span></li>
|
|
211
|
+
<li><span class="dot ix">→</span><span>跨白板示例 → <a class="xjump" href="other-spec.html">其他页</a></span></li>
|
|
212
|
+
</ul></div>
|
|
213
|
+
<div class="grp"><p class="gt bd">边界处理</p><ul>
|
|
214
|
+
<li><span class="dot bd">·</span><span>单行长文 → 省略号截断(<code>min-width:0</code>)</span></li>
|
|
215
|
+
<li><span class="dot bd">·</span><span>空态 / 加载 / 错误 / 禁用 按需补画布</span></li>
|
|
216
|
+
</ul></div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<!-- 占位画布 B -->
|
|
221
|
+
<div class="stage" data-id="stateB" data-x="820" data-y="60">
|
|
222
|
+
<div class="frame">
|
|
223
|
+
<div class="canvas-no" id="stateB"><span class="n">2</span>状态 B</div>
|
|
224
|
+
<div class="phone"><div class="notch"></div>
|
|
225
|
+
<div class="screen">
|
|
226
|
+
<div class="hdr"><div class="back" data-go="stateA">‹</div><h1>状态 B</h1></div>
|
|
227
|
+
<div class="centerblk"><h2>这里放状态 B 的 UI</h2><p class="sub">返回 ‹ 回状态 A</p></div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
<div class="spec">
|
|
232
|
+
<h2>状态 B</h2>
|
|
233
|
+
<p class="use">一句话说明。</p>
|
|
234
|
+
<div class="grp"><p class="gt ix">交互跳转</p><ul>
|
|
235
|
+
<li><span class="dot ix">→</span><span>返回 ‹ → <a class="jump" data-go="stateA">① 状态 A</a></span></li>
|
|
236
|
+
</ul></div>
|
|
237
|
+
<div class="grp"><p class="gt bd">边界处理</p><ul>
|
|
238
|
+
<li><span class="dot bd">·</span><span>按需填写</span></li>
|
|
239
|
+
</ul></div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
</div><!-- /#world -->
|
|
244
|
+
</div><!-- /#viewport -->
|
|
245
|
+
|
|
246
|
+
<div class="toolbar">
|
|
247
|
+
<button id="btnFit">适应全部</button>
|
|
248
|
+
<button id="btnReset">复位</button>
|
|
249
|
+
<button id="btnZoomOut">−</button>
|
|
250
|
+
<span class="zlabel" id="zlabel">100%</span>
|
|
251
|
+
<button id="btnZoomIn">+</button>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<script>
|
|
255
|
+
// ② EDGES:白板内状态跳转拓扑,决定画哪些连线。from/to = .stage 的 data-id;label = 触发条件。
|
|
256
|
+
const EDGES = [
|
|
257
|
+
{from:'stateA', to:'stateB', label:'主操作'},
|
|
258
|
+
{from:'stateB', to:'stateA', label:'返回'},
|
|
259
|
+
];
|
|
260
|
+
const START='stateA'; // 初始选中/复位的画布
|
|
261
|
+
|
|
262
|
+
/* ===== 以下为白板引擎,勿改 ===== */
|
|
263
|
+
const viewport=document.getElementById('viewport'), world=document.getElementById('world'), wires=document.getElementById('wires');
|
|
264
|
+
const stages=[...document.querySelectorAll('.stage')];
|
|
265
|
+
const byId=id=>document.querySelector(`.stage[data-id="${id}"]`);
|
|
266
|
+
stages.forEach(s=>{ s.style.left=s.dataset.x+'px'; s.style.top=s.dataset.y+'px'; });
|
|
267
|
+
|
|
268
|
+
let scale=1,tx=0,ty=0;
|
|
269
|
+
const NS='http://www.w3.org/2000/svg';
|
|
270
|
+
function apply(){ world.style.transform=`translate(${tx}px,${ty}px) scale(${scale})`; document.getElementById('zlabel').textContent=Math.round(scale*100)+'%'; }
|
|
271
|
+
|
|
272
|
+
function drawWires(){
|
|
273
|
+
wires.innerHTML='';
|
|
274
|
+
let maxX=0,maxY=0; stages.forEach(s=>{ maxX=Math.max(maxX,s.offsetLeft+s.offsetWidth); maxY=Math.max(maxY,s.offsetTop+s.offsetHeight); });
|
|
275
|
+
wires.setAttribute('width',maxX+200); wires.setAttribute('height',maxY+200);
|
|
276
|
+
const defs=document.createElementNS(NS,'defs');
|
|
277
|
+
defs.innerHTML=`<marker id="arrow" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="11" markerHeight="11" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11 L4,6 z" fill="rgba(96,165,250,.85)"/></marker><marker id="arrowHot" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="13" markerHeight="13" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11 L4,6 z" fill="#7DB5FF"/></marker>`;
|
|
278
|
+
wires.appendChild(defs);
|
|
279
|
+
function edgeAnchor(stage,tX,tY){ // 从画框最近的边进出,连线不被框体盖住
|
|
280
|
+
const p=stage.querySelector('.phone');
|
|
281
|
+
const x=stage.offsetLeft+p.offsetLeft,y=stage.offsetTop+p.offsetTop,w=p.offsetWidth,h=p.offsetHeight,cx=x+w/2,cy=y+h/2,dx=tX-cx,dy=tY-cy;
|
|
282
|
+
if(Math.abs(dx)>=Math.abs(dy)) return dx>=0?{x:x+w,y:cy,side:'r'}:{x:x,y:cy,side:'l'};
|
|
283
|
+
return dy>=0?{x:cx,y:y+h,side:'b'}:{x:cx,y:y,side:'t'};
|
|
284
|
+
}
|
|
285
|
+
EDGES.forEach(e=>{
|
|
286
|
+
const a=byId(e.from),b=byId(e.to); if(!a||!b)return;
|
|
287
|
+
const pa=a.querySelector('.phone'),pb=b.querySelector('.phone');
|
|
288
|
+
const acx=a.offsetLeft+pa.offsetLeft+pa.offsetWidth/2,acy=a.offsetTop+pa.offsetTop+pa.offsetHeight/2;
|
|
289
|
+
const bcx=b.offsetLeft+pb.offsetLeft+pb.offsetWidth/2,bcy=b.offsetTop+pb.offsetTop+pb.offsetHeight/2;
|
|
290
|
+
const A=edgeAnchor(a,bcx,bcy),B=edgeAnchor(b,acx,acy),pull=120;
|
|
291
|
+
const cp=pt=>({x:pt.x+(pt.side==='r'?pull:pt.side==='l'?-pull:0),y:pt.y+(pt.side==='b'?pull:pt.side==='t'?-pull:0)});
|
|
292
|
+
const c1=cp(A),c2=cp(B);
|
|
293
|
+
const path=document.createElementNS(NS,'path');
|
|
294
|
+
path.setAttribute('d',`M${A.x},${A.y} C${c1.x},${c1.y} ${c2.x},${c2.y} ${B.x},${B.y}`);
|
|
295
|
+
path.setAttribute('marker-end','url(#arrow)'); path.dataset.from=e.from; path.dataset.to=e.to; wires.appendChild(path);
|
|
296
|
+
const mx=(A.x+B.x)/2,my=(A.y+B.y)/2,tw=e.label.length*13+14;
|
|
297
|
+
const bg=document.createElementNS(NS,'rect'); bg.setAttribute('class','wlabel-bg'); bg.setAttribute('x',mx-tw/2); bg.setAttribute('y',my-13); bg.setAttribute('width',tw); bg.setAttribute('height',24); bg.setAttribute('rx',6); bg.dataset.from=e.from; bg.dataset.to=e.to; wires.appendChild(bg);
|
|
298
|
+
const t=document.createElementNS(NS,'text'); t.setAttribute('class','wlabel'); t.setAttribute('x',mx); t.setAttribute('y',my+4); t.setAttribute('text-anchor','middle'); t.textContent=e.label; t.dataset.from=e.from; t.dataset.to=e.to; wires.appendChild(t);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// 选中:高亮画框+标题+「当前」标记 + 相关连线高亮
|
|
303
|
+
function select(id){
|
|
304
|
+
document.querySelectorAll('.phone.selected,.canvas-no.selected').forEach(el=>el.classList.remove('selected'));
|
|
305
|
+
wires.querySelectorAll('path.hot').forEach(p=>{ p.classList.remove('hot'); p.setAttribute('marker-end','url(#arrow)'); });
|
|
306
|
+
const stage=byId(id); if(!stage)return;
|
|
307
|
+
stage.querySelector('.canvas-no')?.classList.add('selected'); stage.querySelector('.phone')?.classList.add('selected');
|
|
308
|
+
wires.querySelectorAll(`path[data-from="${id}"],path[data-to="${id}"]`).forEach(p=>{ p.classList.add('hot'); p.setAttribute('marker-end','url(#arrowHot)'); });
|
|
309
|
+
return stage;
|
|
310
|
+
}
|
|
311
|
+
// 复位展示:缩放到单画布刚好填满 + 居中(不管之前缩放多少都看清)
|
|
312
|
+
function focusOn(id){
|
|
313
|
+
const stage=byId(id); if(!stage)return;
|
|
314
|
+
const NAV=210,pad=70,w=stage.offsetWidth+pad*2,h=stage.offsetHeight+pad*2,availW=viewport.clientWidth-NAV;
|
|
315
|
+
scale=Math.min(availW/w,viewport.clientHeight/h,2.2);
|
|
316
|
+
const cx=stage.offsetLeft+stage.offsetWidth/2,cy=stage.offsetTop+stage.offsetHeight/2;
|
|
317
|
+
tx=NAV+availW/2-cx*scale; ty=viewport.clientHeight/2-cy*scale; apply();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let dragging=false,sx=0,sy=0,stx=0,sty=0,moved=false;
|
|
321
|
+
viewport.addEventListener('pointerdown',e=>{
|
|
322
|
+
// 可点元素不启动拖拽:否则真实鼠标微小抖动会把点击判成拖拽吞掉(点编号/按钮失效)
|
|
323
|
+
if(e.target.closest('input,a,[data-go],.canvas-no'))return;
|
|
324
|
+
dragging=true;moved=false;sx=e.clientX;sy=e.clientY;stx=tx;sty=ty;viewport.classList.add('grabbing');try{viewport.setPointerCapture(e.pointerId)}catch{}
|
|
325
|
+
});
|
|
326
|
+
viewport.addEventListener('pointermove',e=>{ if(!dragging)return; const ddx=e.clientX-sx,ddy=e.clientY-sy; if(Math.abs(ddx)+Math.abs(ddy)>4)moved=true; tx=stx+ddx;ty=sty+ddy;apply(); });
|
|
327
|
+
viewport.addEventListener('pointerup',()=>{ dragging=false;viewport.classList.remove('grabbing'); });
|
|
328
|
+
viewport.addEventListener('wheel',e=>{ e.preventDefault(); const r=viewport.getBoundingClientRect(),mx=e.clientX-r.left,my=e.clientY-r.top,wx=(mx-tx)/scale,wy=(my-ty)/scale,f=e.deltaY<0?1.12:1/1.12; scale=Math.min(2.2,Math.max(.18,scale*f)); tx=mx-wx*scale;ty=my-wy*scale;apply(); },{passive:false});
|
|
329
|
+
viewport.addEventListener('click',e=>{
|
|
330
|
+
if(moved)return;
|
|
331
|
+
if(e.target.closest('a[href]'))return; // 跨白板链接交给浏览器
|
|
332
|
+
const go=e.target.closest('[data-go]'); // 画框内按钮 / 标注 jump → 白板内跳转
|
|
333
|
+
if(go){ const id=go.getAttribute('data-go'); select(id); focusOn(id); return; }
|
|
334
|
+
const no=e.target.closest('.canvas-no'); // 只点编号才选中(画框不响应)
|
|
335
|
+
if(no){ select(no.id); focusOn(no.id); }
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
function fitAll(){
|
|
339
|
+
let minX=1e9,minY=1e9,maxX=0,maxY=0;
|
|
340
|
+
stages.forEach(s=>{ minX=Math.min(minX,s.offsetLeft);minY=Math.min(minY,s.offsetTop);maxX=Math.max(maxX,s.offsetLeft+s.offsetWidth);maxY=Math.max(maxY,s.offsetTop+s.offsetHeight); });
|
|
341
|
+
const NAV=210,pad=80,w=maxX-minX+pad*2,h=maxY-minY+pad*2,availW=viewport.clientWidth-NAV;
|
|
342
|
+
scale=Math.min(availW/w,viewport.clientHeight/h,1);
|
|
343
|
+
tx=NAV+availW/2-((minX+maxX)/2)*scale; ty=viewport.clientHeight/2-((minY+maxY)/2)*scale; apply();
|
|
344
|
+
}
|
|
345
|
+
document.getElementById('btnFit').onclick=fitAll;
|
|
346
|
+
document.getElementById('btnReset').onclick=()=>{ select(START);focusOn(START); };
|
|
347
|
+
document.getElementById('btnZoomIn').onclick=()=>{ scale=Math.min(2.2,scale*1.15);apply(); };
|
|
348
|
+
document.getElementById('btnZoomOut').onclick=()=>{ scale=Math.max(.18,scale/1.15);apply(); };
|
|
349
|
+
|
|
350
|
+
drawWires(); select(START); fitAll();
|
|
351
|
+
</script>
|
|
352
|
+
</body>
|
|
353
|
+
</html>
|
package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-sdk-spec/scripts/init_config.sh
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# dwy-sdk-spec 配置缓存读写
|
|
3
|
-
# 用法:
|
|
4
|
-
# init_config.sh get <project_path>
|
|
5
|
-
# init_config.sh set <project_path> <edition> <languages_json> <sdk_paths_json>
|
|
6
|
-
# init_config.sh reset <project_path>
|
|
7
|
-
# init_config.sh list
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
CONFIG_DIR="${HOME}/.config/dwy-sdk-spec"
|
|
12
|
-
CONFIG_FILE="${CONFIG_DIR}/projects.json"
|
|
13
|
-
|
|
14
|
-
ensure_config() {
|
|
15
|
-
mkdir -p "$CONFIG_DIR"
|
|
16
|
-
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
17
|
-
echo '{"version":"1","projects":{}}' > "$CONFIG_FILE"
|
|
18
|
-
fi
|
|
19
|
-
# 检测损坏
|
|
20
|
-
if ! jq empty "$CONFIG_FILE" 2>/dev/null; then
|
|
21
|
-
echo "ERROR: $CONFIG_FILE is corrupted. Back it up and delete to reset." >&2
|
|
22
|
-
exit 2
|
|
23
|
-
fi
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
cmd_get() {
|
|
27
|
-
local project_path="$1"
|
|
28
|
-
ensure_config
|
|
29
|
-
jq --arg p "$project_path" '.projects[$p] // null' "$CONFIG_FILE"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
cmd_set() {
|
|
33
|
-
local project_path="$1"
|
|
34
|
-
local edition="$2"
|
|
35
|
-
local languages_json="$3"
|
|
36
|
-
local sdk_paths_json="$4"
|
|
37
|
-
|
|
38
|
-
if [[ "$edition" != "commercial" && "$edition" != "opensource" ]]; then
|
|
39
|
-
echo "ERROR: edition must be 'commercial' or 'opensource', got: $edition" >&2
|
|
40
|
-
exit 2
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
ensure_config
|
|
44
|
-
local now
|
|
45
|
-
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
46
|
-
|
|
47
|
-
local tmp
|
|
48
|
-
tmp=$(mktemp)
|
|
49
|
-
jq \
|
|
50
|
-
--arg p "$project_path" \
|
|
51
|
-
--arg e "$edition" \
|
|
52
|
-
--argjson l "$languages_json" \
|
|
53
|
-
--argjson s "$sdk_paths_json" \
|
|
54
|
-
--arg t "$now" \
|
|
55
|
-
'.projects[$p] = {edition: $e, languages: $l, sdk_paths: $s, configured_at: $t}' \
|
|
56
|
-
"$CONFIG_FILE" > "$tmp"
|
|
57
|
-
mv "$tmp" "$CONFIG_FILE"
|
|
58
|
-
|
|
59
|
-
echo "OK: cached $project_path -> $edition"
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
cmd_reset() {
|
|
63
|
-
local project_path="$1"
|
|
64
|
-
ensure_config
|
|
65
|
-
local tmp
|
|
66
|
-
tmp=$(mktemp)
|
|
67
|
-
jq --arg p "$project_path" 'del(.projects[$p])' "$CONFIG_FILE" > "$tmp"
|
|
68
|
-
mv "$tmp" "$CONFIG_FILE"
|
|
69
|
-
echo "OK: removed $project_path from cache"
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
cmd_list() {
|
|
73
|
-
ensure_config
|
|
74
|
-
jq '.projects | to_entries | map({path: .key, edition: .value.edition, languages: .value.languages})' "$CONFIG_FILE"
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
main() {
|
|
78
|
-
if [[ $# -lt 1 ]]; then
|
|
79
|
-
echo "Usage: $0 {get|set|reset|list} [args...]" >&2
|
|
80
|
-
exit 2
|
|
81
|
-
fi
|
|
82
|
-
local action="$1"
|
|
83
|
-
shift
|
|
84
|
-
|
|
85
|
-
case "$action" in
|
|
86
|
-
get) cmd_get "$@" ;;
|
|
87
|
-
set) cmd_set "$@" ;;
|
|
88
|
-
reset) cmd_reset "$@" ;;
|
|
89
|
-
list) cmd_list ;;
|
|
90
|
-
*)
|
|
91
|
-
echo "ERROR: unknown action: $action" >&2
|
|
92
|
-
exit 2
|
|
93
|
-
;;
|
|
94
|
-
esac
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
main "$@"
|
package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/SKILL.md
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: dwy-0to1
|
|
3
|
-
description: 端到端「0→1 产品孵化」编排器。用户丢一个产品想法进来,按九步带门流水线**逐步编排调用**同系列单点 skill:dwy-market-analysis(需求/市场)→ dwy-tech-feasibility(技术可行性)→(可选)dwy-spike(真机实测)→ dwy-prd-coarse(粗 PRD)→ dwy-prototype(低保真+高保真原型)→ dwy-prd-detailed(细 PRD)→ dwy-version-plan(版本规划)→ dwy-tdd-dev(逐版本 TDD 开发),每步卡用户确认门、产物落盘 docs/0to1/、用 STATE.md 做唯一真相源支持断点续传。涉及以下场景**必须**使用此 skill(即使用户没点名 'dwy-0to1'):用户说『我想做个 X』『帮我把这个想法做成产品』『从 0 到 1 做个 XX』『帮我孵化这个想法』,或丢一个产品概念让我判断值不值得做、怎么落地;用户说『继续做 X』『接着上次的产品』『孵化进度到哪了』时读 STATE 续传/汇报。只想单独做其中一步(只画原型、只写 PRD、只做市场分析等),直接用对应的 dwy-* 单点 skill,不必走整条流水线。核心纪律——每步卡门等用户确认才进下一步,绝不跳步、绝不未确认就写代码,所有产物落盘 docs/0to1/ 以支持断点续传。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# dwy-0to1(编排器)
|
|
7
|
-
|
|
8
|
-
把一个产品想法,按九步带门的流水线推到逐版本 TDD 开发落地。本 skill 是**编排器**:自己不实现各步细节,而是按顺序**委托调用**同系列单点 skill,并独占 STATE 真相源、统一卡门、管跨步顺序。每步落盘 `docs/0to1/`,可中断、可断点续传——这次聊到粗 PRD,下次进来从粗 PRD 接着聊。
|
|
9
|
-
|
|
10
|
-
**核心纪律**:每步有明确产物、卡用户确认门、落盘。门没过不许进下一步,更不许提前写代码。
|
|
11
|
-
|
|
12
|
-
> 单点能力可独立使用:用户只想画个原型 / 写个 PRD / 做个市场分析,直接用对应的 `dwy-*` skill 即可,不必走整条流水线。本编排器只在「完整 0→1 / 继续上次孵化」时启用。
|
|
13
|
-
|
|
14
|
-
## 第一件事:读 STATE,决定从哪开始(断点续传)
|
|
15
|
-
|
|
16
|
-
**每次进入本 skill,先做这步,不要直接开干。**
|
|
17
|
-
|
|
18
|
-
1. `Glob docs/0to1/STATE.md`(相对当前项目根)。
|
|
19
|
-
2. **存在** → Read 它,看 `current_step` 和「进度」表、「下一步」:
|
|
20
|
-
- 用 `AskUserQuestion` 跟用户确认:「上次做到 Step N(名称),下一步是 X。从这接着 / 改某步 / 整个重开?」
|
|
21
|
-
- 按用户选择定位到对应步骤。
|
|
22
|
-
3. **不存在** → 这是新想法:
|
|
23
|
-
- 先 `Read templates/STATE.md`,在 `docs/0to1/STATE.md` 建初始 STATE(用 `date +%Y-%m-%d` 取今天填 created/updated,current_step=1)。
|
|
24
|
-
- `docs/0to1/` 目录不存在就先建。
|
|
25
|
-
- 从 Step 1 开始。
|
|
26
|
-
|
|
27
|
-
> STATE.md 是整条流水线的**唯一真相源**。任何一步的状态、决策、产物路径都以它为准。它丢了,可以靠各产物文件名(`01-market.md`…编号即步号)+ frontmatter 重建。
|
|
28
|
-
|
|
29
|
-
## 门规则(每步通用,强制)
|
|
30
|
-
|
|
31
|
-
每一步都按「**委托 → 过门 → 记账**」走,缺一不可:
|
|
32
|
-
|
|
33
|
-
1. **委托调用单点 skill**:用 Skill 工具调对应 `dwy-*`,在调用指令里带「**流水线模式**」+ 前序产物路径 + 落盘路径(见路由表)。被调 skill 负责本步的工作、完成前自检(先验证再声称)、并**自己跑领域门**(`AskUserQuestion` 把产物摆给用户问 通过/改/换方向)。
|
|
34
|
-
2. **过门**:被调 skill 的门**用户点头**了,这步才算过。用户要改 → 让 skill 迭代重做再摆,**没点头不进下一步、更不许写生产代码**。
|
|
35
|
-
3. **记账(编排器独占)**:门过了,**编排器立刻更新 `docs/0to1/STATE.md`**——这步状态改 `done`、gate 列填 `✅ <date>`、产物路径、按 `templates/STATE.md`「关键决策」清单填本步定下的决策(Step1 真需求结论/差异化/核心能力/硬约束、Step2 技术方案、Step3 是否实测/端、Step4 定位/定价、Step8 选定版本/全局架构骨架)、`current_step` 指向下一步、写一句「下一步」。被调 skill **不碰 STATE**,记账只由编排器做,这是一次原子 checkpoint。
|
|
36
|
-
|
|
37
|
-
红线:**绝不跳步、绝不跳门、绝不在用户确认前写生产代码。**
|
|
38
|
-
|
|
39
|
-
## 九步流水线(路由)
|
|
40
|
-
|
|
41
|
-
每步:**委托调用下表的 skill(流水线模式,给前序产物路径 + 落盘路径)→ 过门 → 编排器更新 STATE**。
|
|
42
|
-
|
|
43
|
-
| # | 步骤 | 委托调用 | 流水线 args(前序 → 落盘) | 产物 |
|
|
44
|
-
|---|------|---------|--------------------------|------|
|
|
45
|
-
| 1 | 需求 & 市场分析 | `dwy-market-analysis` | 无前序 → `docs/0to1/01-market.md` | 01-market.md |
|
|
46
|
-
| 2 | 技术可行性 | `dwy-tech-feasibility` | 读 `01-market.md` → `docs/0to1/02-tech-feasibility.md` | 02-tech-feasibility.md |
|
|
47
|
-
| 3 | 实测(可选) | `dwy-spike` | 读 `02-tech-feasibility.md` → 代码 `docs/0to1/spike/`、结论 `docs/0to1/03-spike-result.md` | 03-spike-result.md + spike/ |
|
|
48
|
-
| 4 | 粗 PRD | `dwy-prd-coarse` | 读 `01-market.md` + `02-tech-feasibility.md`(+ 存在时 `03-spike-result.md`) → `docs/0to1/04-prd-coarse.md` | 04-prd-coarse.md |
|
|
49
|
-
| 5 | 低保真原型 | `dwy-prototype`(`phase=lowfi`) | 读 `04-prd-coarse.md` → `docs/0to1/prototype-lowfi/` | prototype-lowfi/index.html |
|
|
50
|
-
| 6 | 高保真原型 | `dwy-prototype`(`phase=hifi`) | 读 `04-prd-coarse.md` + `prototype-lowfi/` → `docs/0to1/prototype-hifi/` | prototype-hifi/ |
|
|
51
|
-
| 7 | 细 PRD | `dwy-prd-detailed` | 读 `04-prd-coarse.md` + `prototype-hifi/` → `docs/0to1/05-prd-detailed.md` | 05-prd-detailed.md |
|
|
52
|
-
| 8 | 版本规划 | `dwy-version-plan` | 读 `05-prd-detailed.md` → `docs/0to1/06-version-plan.md` | 06-version-plan.md |
|
|
53
|
-
| 9 | 逐版本 TDD 开发 | `dwy-tdd-dev` | 读 `06-version-plan.md` + `05-prd-detailed.md` + STATE 选定版本 → 实施计划 `docs/0to1/plans/vN-<slug>.md`,代码进项目源码 | plans/ + 代码 |
|
|
54
|
-
|
|
55
|
-
**Step 3 分支(编排器据 STATE 决策路由)**:
|
|
56
|
-
- Step 2 过门时记的「是否实测」= **否** → Step 3 不调 `dwy-spike`,进度表标 `skipped(无需实测)`,`current_step` 直接到 4。
|
|
57
|
-
- = **是** → 调 `dwy-spike`;它会先问做哪一端,编排器把答案记进 STATE 的 `target_platform`(Step 5/6/9 都按这个端走)。实测不通过 → `current_step` 回 2 重选方案。
|
|
58
|
-
|
|
59
|
-
**Step 5/6 同一个 skill 两道门**:`dwy-prototype` 在低保真(`phase=lowfi`)、高保真(`phase=hifi`)各调一次、各卡一道门,目的不同(验流程 vs 做底稿),别合并。
|
|
60
|
-
|
|
61
|
-
**Step 9 循环**:对 STATE「选定版本」里的每个版本依次调 `dwy-tdd-dev` 走完,编排器更新「版本开发进度」表;全部版本完,Step 9 标 `done`,给交付报告。每版完成的 commit 由 `dwy-tdd-dev` 按 Git 提交规范执行(敏感扫描 + 格式 + 禁 AI 署名),不裸 commit。
|
|
62
|
-
|
|
63
|
-
## 产物目录(项目内 docs/0to1/)
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
docs/0to1/
|
|
67
|
-
STATE.md # 断点真相源(编排器独占读写)
|
|
68
|
-
01-market.md # Step1 需求/市场/竞品/差异化
|
|
69
|
-
02-tech-feasibility.md # Step2 方案对比 + 推荐
|
|
70
|
-
03-spike-result.md # Step3 实测结论(不是代码)
|
|
71
|
-
spike/ # Step3 丢弃式 POC(仅需实测时)
|
|
72
|
-
04-prd-coarse.md # Step4 粗 PRD
|
|
73
|
-
prototype-lowfi/index.html # Step5 低保真原型
|
|
74
|
-
prototype-hifi/ # Step6 高保真原型(按端套壳)
|
|
75
|
-
05-prd-detailed.md # Step7 细 PRD(页面级)
|
|
76
|
-
06-version-plan.md # Step8 版本规划
|
|
77
|
-
plans/
|
|
78
|
-
v1-<slug>.md # Step9 每版本一份实施计划
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
各产物文件 frontmatter 由对应 skill 写(`status` / `updated`);STATE 丢失时靠文件名编号 + frontmatter 重建。
|
|
82
|
-
|
|
83
|
-
## 关键边界 / 禁止
|
|
84
|
-
|
|
85
|
-
- **编排器只编排、不实现**:各步的「怎么做」在对应 `dwy-*` skill 里,本文件只管顺序、门、STATE。被调 skill 不碰 STATE,记账只由编排器做。
|
|
86
|
-
- **spike 是丢弃式**(Step 3):实测代码隔离在 `docs/0to1/spike/`,只为验证核心流程跑不跑得通。开发期(Step 9)**只继承结论,不继承 spike 代码**——TDD 铁律是没失败测试不写生产代码,spike 不算。
|
|
87
|
-
- **两个原型卡不同门**:低保真(Step 5)验流程方向、高保真(Step 6)做细 PRD 底稿,目的不同别合并。**例外**:单页小工具可合并成一轮;CLI / 后端 / 库类**无 GUI** 产品,原型步**优先降级**为「终端会话 mock / API 契约示例」——产出了降级产物就在 STATE 标 `done(降级:无GUI)`;**只有连 mock/契约都没意义时才直接跳过**(标 `skipped(无GUI)`)。详见 `dwy-prototype` 的降级表。
|
|
88
|
-
- **自包含 + 可增强**:本编排器只依赖同系列 `dwy-*` skill,不依赖 gstack / superpowers 插件。Step 9 的 `dwy-tdd-dev` 已内置 spec/code 双审 + 版本级 code-review,等价覆盖 `dwy-dev-qa` 核心;需要更全面的系统级 QA 时才额外委托 `dwy-dev-qa` 或 `/code-review`,避免重复审查。
|
|
89
|
-
- **流水线边界**:交付止于「可运行代码 + 交付报告」。**上线 / 部署 / 市场验证是流水线边界外的人工动作**——Step1「真需求结论」的真正验证靠上线后人工度量。要再迭代就重进本 skill:方向变回 Step 1、产品调整回 Step 4、只加版本回 Step 8。
|
|
90
|
-
- **不跳步、不跳门、不提前写代码**(见「门规则」)。
|
|
91
|
-
|
|
92
|
-
## 文件结构
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
.claude/skills/dwy-0to1/
|
|
96
|
-
├── SKILL.md ← 本文件(编排骨架 + STATE 协议 + 门规则 + 路由)
|
|
97
|
-
└── templates/
|
|
98
|
-
└── STATE.md ← 状态文件模板(编排器独占)
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
九步的执行细则与产物模板已拆到同级独立 skill,可单独触发,也被本编排器按路由表委托调用:
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
开发流程/
|
|
105
|
-
├── dwy-market-analysis/ ← Step1 需求/市场分析
|
|
106
|
-
├── dwy-tech-feasibility/ ← Step2 技术选型可行性
|
|
107
|
-
├── dwy-spike/ ← Step3 丢弃式实测
|
|
108
|
-
├── dwy-prd-coarse/ ← Step4 粗 PRD(含 templates/prd-coarse.md)
|
|
109
|
-
├── dwy-prototype/ ← Step5+6 低保真+高保真原型
|
|
110
|
-
├── dwy-prd-detailed/ ← Step7 细 PRD(含 templates/prd-detailed.md)
|
|
111
|
-
├── dwy-version-plan/ ← Step8 版本规划(含 templates/version-plan.md)
|
|
112
|
-
└── dwy-tdd-dev/ ← Step9 逐版本 TDD 开发(含 templates/impl-plan.md)
|
|
113
|
-
```
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
project: <产品名>
|
|
3
|
-
slug: <kebab-slug>
|
|
4
|
-
created: <YYYY-MM-DD>
|
|
5
|
-
updated: <YYYY-MM-DD>
|
|
6
|
-
current_step: 1
|
|
7
|
-
target_platform: none # Step3 选定后改: ios | android | desktop | web | none
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# <产品名> · 0→1 孵化状态
|
|
11
|
-
|
|
12
|
-
> 本文件是整条流水线的唯一真相源。每过一个门必须更新它。
|
|
13
|
-
|
|
14
|
-
## 进度
|
|
15
|
-
|
|
16
|
-
| 步 | 名称 | 状态 | 产物 | gate |
|
|
17
|
-
|---|------|------|------|------|
|
|
18
|
-
| 1 | 需求&市场分析 | pending | 01-market.md | |
|
|
19
|
-
| 2 | 技术可行性 | pending | 02-tech-feasibility.md | |
|
|
20
|
-
| 3 | 实测spike | pending | 03-spike-result.md | |
|
|
21
|
-
| 4 | 粗PRD | pending | 04-prd-coarse.md | |
|
|
22
|
-
| 5 | 低保真原型 | pending | prototype-lowfi/ | |
|
|
23
|
-
| 6 | 高保真原型 | pending | prototype-hifi/ | |
|
|
24
|
-
| 7 | 细PRD | pending | 05-prd-detailed.md | |
|
|
25
|
-
| 8 | 版本规划 | pending | 06-version-plan.md | |
|
|
26
|
-
| 9 | 开发 | pending | plans/ | |
|
|
27
|
-
|
|
28
|
-
<!-- 状态枚举:pending | in-progress | awaiting-approval | done | skipped(原因) -->
|
|
29
|
-
<!-- gate 列:门过了填 ✅ + 日期(如 ✅ 2026-06-02);待用户确认填 ⏳ -->
|
|
30
|
-
|
|
31
|
-
## 关键决策
|
|
32
|
-
|
|
33
|
-
<!-- 每步定下的、后续步骤要用的决策都记在这里 -->
|
|
34
|
-
- 真需求结论:<Step1,做 / 不做 / 调整方向>
|
|
35
|
-
- 差异化打法:<Step1>
|
|
36
|
-
- 核心能力/最大技术风险:<Step1,最该被 Step2 验证的技术核心>
|
|
37
|
-
- 硬约束:<Step1/2,合规/PII/实时性/规模水位 → 约束选型;无则写 无>
|
|
38
|
-
- 技术方案:<Step2 选定的库/方案>
|
|
39
|
-
- 是否实测:<Step3,是(端=xxx) / 否>
|
|
40
|
-
- 端:<Step3 选定:ios|android|desktop|web|none>
|
|
41
|
-
- 产品定位:<Step4>
|
|
42
|
-
- 定价策略:<Step4>
|
|
43
|
-
- 全局数据模型/架构骨架:<Step8,核心实体+关系+架构方向>
|
|
44
|
-
- 选定版本:<Step8,可多个,如 v1+v2>
|
|
45
|
-
|
|
46
|
-
## 版本开发进度(Step 9)
|
|
47
|
-
|
|
48
|
-
| 版本 | 实施计划 | 状态 | code-review |
|
|
49
|
-
|------|---------|------|-------------|
|
|
50
|
-
| v1 | plans/v1-<slug>.md | pending | — |
|
|
51
|
-
|
|
52
|
-
<!-- 版本状态:pending | in-progress | done。code-review:— / 进行中 / ✅全绿 -->
|
|
53
|
-
|
|
54
|
-
## 下一步
|
|
55
|
-
|
|
56
|
-
<一句话写清:从哪步、哪个子项接着干。例:继续 Step4,讨论粗 PRD 的定价策略部分。>
|