@yss123/cage-cli 0.0.0-stage → 0.1.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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -2
  3. package/bin/cage.js +6 -0
  4. package/bin/create-cage-app.js +6 -0
  5. package/dist/analyze.d.ts +18 -0
  6. package/dist/analyze.js +26 -0
  7. package/dist/analyze.js.map +1 -0
  8. package/dist/baseline.d.ts +5 -0
  9. package/dist/baseline.js +50 -0
  10. package/dist/baseline.js.map +1 -0
  11. package/dist/budget.d.ts +7 -0
  12. package/dist/budget.js +52 -0
  13. package/dist/budget.js.map +1 -0
  14. package/dist/config.d.ts +30 -0
  15. package/dist/config.js +47 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/create.d.ts +1 -0
  18. package/dist/create.js +47 -0
  19. package/dist/create.js.map +1 -0
  20. package/dist/database.d.ts +5 -0
  21. package/dist/database.js +70 -0
  22. package/dist/database.js.map +1 -0
  23. package/dist/derive.d.ts +14 -0
  24. package/dist/derive.js +163 -0
  25. package/dist/derive.js.map +1 -0
  26. package/dist/dev.d.ts +2 -0
  27. package/dist/dev.js +155 -0
  28. package/dist/dev.js.map +1 -0
  29. package/dist/env.d.ts +1 -0
  30. package/dist/env.js +15 -0
  31. package/dist/env.js.map +1 -0
  32. package/dist/findings.d.ts +11 -0
  33. package/dist/findings.js +13 -0
  34. package/dist/findings.js.map +1 -0
  35. package/dist/gen/contracts.d.ts +6 -0
  36. package/dist/gen/contracts.js +167 -0
  37. package/dist/gen/contracts.js.map +1 -0
  38. package/dist/gen/index.d.ts +19 -0
  39. package/dist/gen/index.js +137 -0
  40. package/dist/gen/index.js.map +1 -0
  41. package/dist/gen/zod-code.d.ts +7 -0
  42. package/dist/gen/zod-code.js +161 -0
  43. package/dist/gen/zod-code.js.map +1 -0
  44. package/dist/graph-snapshot.d.ts +13 -0
  45. package/dist/graph-snapshot.js +46 -0
  46. package/dist/graph-snapshot.js.map +1 -0
  47. package/dist/graph.d.ts +11 -0
  48. package/dist/graph.js +87 -0
  49. package/dist/graph.js.map +1 -0
  50. package/dist/impact.d.ts +28 -0
  51. package/dist/impact.js +223 -0
  52. package/dist/impact.js.map +1 -0
  53. package/dist/index.d.ts +16 -0
  54. package/dist/index.js +18 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/load.d.ts +6 -0
  57. package/dist/load.js +17 -0
  58. package/dist/load.js.map +1 -0
  59. package/dist/main.d.ts +1 -0
  60. package/dist/main.js +176 -0
  61. package/dist/main.js.map +1 -0
  62. package/dist/migrate.d.ts +7 -0
  63. package/dist/migrate.js +69 -0
  64. package/dist/migrate.js.map +1 -0
  65. package/dist/ontology.d.ts +41 -0
  66. package/dist/ontology.js +270 -0
  67. package/dist/ontology.js.map +1 -0
  68. package/dist/process.d.ts +6 -0
  69. package/dist/process.js +39 -0
  70. package/dist/process.js.map +1 -0
  71. package/dist/rules.d.ts +23 -0
  72. package/dist/rules.js +251 -0
  73. package/dist/rules.js.map +1 -0
  74. package/dist/static-check.d.ts +23 -0
  75. package/dist/static-check.js +225 -0
  76. package/dist/static-check.js.map +1 -0
  77. package/dist/sync.d.ts +5 -0
  78. package/dist/sync.js +46 -0
  79. package/dist/sync.js.map +1 -0
  80. package/dist/upgrade.d.ts +10 -0
  81. package/dist/upgrade.js +81 -0
  82. package/dist/upgrade.js.map +1 -0
  83. package/dist/verify.d.ts +7 -0
  84. package/dist/verify.js +88 -0
  85. package/dist/verify.js.map +1 -0
  86. package/dist/version.d.ts +4 -0
  87. package/dist/version.js +6 -0
  88. package/dist/version.js.map +1 -0
  89. package/graph-ui/app.js +1065 -0
  90. package/graph-ui/index.html +197 -0
  91. package/graph-ui/model.js +141 -0
  92. package/graph-ui/relations.js +125 -0
  93. package/graph-ui/structure.js +74 -0
  94. package/graph-ui/style.css +1698 -0
  95. package/managed/errors/CAGE-APPR-001.md +32 -0
  96. package/managed/errors/CAGE-APPR-002.md +32 -0
  97. package/managed/errors/CAGE-APPR-003.md +32 -0
  98. package/managed/errors/CAGE-APPR-004.md +32 -0
  99. package/managed/errors/CAGE-APPR-005.md +32 -0
  100. package/managed/errors/CAGE-APPR-006.md +32 -0
  101. package/managed/errors/CAGE-APPR-007.md +32 -0
  102. package/managed/errors/CAGE-APPR-010.md +32 -0
  103. package/managed/errors/CAGE-AUTH-001.md +32 -0
  104. package/managed/errors/CAGE-BUDGET-001.md +32 -0
  105. package/managed/errors/CAGE-BUDGET-002.md +32 -0
  106. package/managed/errors/CAGE-BUDGET-003.md +32 -0
  107. package/managed/errors/CAGE-CMD-002.md +32 -0
  108. package/managed/errors/CAGE-CMD-003.md +34 -0
  109. package/managed/errors/CAGE-CONN-001.md +32 -0
  110. package/managed/errors/CAGE-CONN-002.md +32 -0
  111. package/managed/errors/CAGE-CONN-003.md +32 -0
  112. package/managed/errors/CAGE-CONN-004.md +32 -0
  113. package/managed/errors/CAGE-CONN-005.md +32 -0
  114. package/managed/errors/CAGE-CONN-006.md +32 -0
  115. package/managed/errors/CAGE-CONN-007.md +32 -0
  116. package/managed/errors/CAGE-CONN-008.md +32 -0
  117. package/managed/errors/CAGE-CONN-009.md +32 -0
  118. package/managed/errors/CAGE-CONN-010.md +32 -0
  119. package/managed/errors/CAGE-CONN-011.md +32 -0
  120. package/managed/errors/CAGE-CONN-012.md +32 -0
  121. package/managed/errors/CAGE-CREATE-001.md +32 -0
  122. package/managed/errors/CAGE-DECL-001.md +34 -0
  123. package/managed/errors/CAGE-DECL-002.md +34 -0
  124. package/managed/errors/CAGE-DECL-003.md +34 -0
  125. package/managed/errors/CAGE-DECL-004.md +34 -0
  126. package/managed/errors/CAGE-DEP-001.md +34 -0
  127. package/managed/errors/CAGE-DEP-002.md +34 -0
  128. package/managed/errors/CAGE-DEP-003.md +34 -0
  129. package/managed/errors/CAGE-DEP-004.md +34 -0
  130. package/managed/errors/CAGE-DEP-005.md +34 -0
  131. package/managed/errors/CAGE-DLQ-001.md +32 -0
  132. package/managed/errors/CAGE-ENV-001.md +32 -0
  133. package/managed/errors/CAGE-EVENT-001.md +32 -0
  134. package/managed/errors/CAGE-EVENT-002.md +32 -0
  135. package/managed/errors/CAGE-FIELD-001.md +32 -0
  136. package/managed/errors/CAGE-FIELD-002.md +32 -0
  137. package/managed/errors/CAGE-FIELD-003.md +32 -0
  138. package/managed/errors/CAGE-FIELD-004.md +32 -0
  139. package/managed/errors/CAGE-GEN-001.md +33 -0
  140. package/managed/errors/CAGE-GEN-002.md +33 -0
  141. package/managed/errors/CAGE-GRANT-001.md +32 -0
  142. package/managed/errors/CAGE-GRANT-002.md +32 -0
  143. package/managed/errors/CAGE-GRANT-003.md +32 -0
  144. package/managed/errors/CAGE-IMPACT-001.md +32 -0
  145. package/managed/errors/CAGE-JOB-001.md +32 -0
  146. package/managed/errors/CAGE-JOB-002.md +32 -0
  147. package/managed/errors/CAGE-JOB-003.md +32 -0
  148. package/managed/errors/CAGE-LINT-001.md +32 -0
  149. package/managed/errors/CAGE-MIG-001.md +32 -0
  150. package/managed/errors/CAGE-MIG-002.md +32 -0
  151. package/managed/errors/CAGE-MIG-003.md +32 -0
  152. package/managed/errors/CAGE-MIG-004.md +32 -0
  153. package/managed/errors/CAGE-MIG-005.md +32 -0
  154. package/managed/errors/CAGE-NAME-001.md +32 -0
  155. package/managed/errors/CAGE-NAME-002.md +32 -0
  156. package/managed/errors/CAGE-NAME-003.md +32 -0
  157. package/managed/errors/CAGE-NAME-004.md +32 -0
  158. package/managed/errors/CAGE-NAME-005.md +32 -0
  159. package/managed/errors/CAGE-NAME-006.md +32 -0
  160. package/managed/errors/CAGE-ORG-001.md +32 -0
  161. package/managed/errors/CAGE-PAIR-002.md +33 -0
  162. package/managed/errors/CAGE-QUERY-001.md +32 -0
  163. package/managed/errors/CAGE-QUERY-002.md +32 -0
  164. package/managed/errors/CAGE-QUERY-003.md +32 -0
  165. package/managed/errors/CAGE-REACT-001.md +32 -0
  166. package/managed/errors/CAGE-REACT-002.md +32 -0
  167. package/managed/errors/CAGE-READS-001.md +32 -0
  168. package/managed/errors/CAGE-REG-001.md +32 -0
  169. package/managed/errors/CAGE-REG-002.md +32 -0
  170. package/managed/errors/CAGE-REG-003.md +32 -0
  171. package/managed/errors/CAGE-ROUTE-001.md +32 -0
  172. package/managed/errors/CAGE-SCOPE-001.md +32 -0
  173. package/managed/errors/CAGE-SCOPE-002.md +32 -0
  174. package/managed/errors/CAGE-SECRET-001.md +32 -0
  175. package/managed/errors/CAGE-SECRET-002.md +32 -0
  176. package/managed/errors/CAGE-SENS-001.md +32 -0
  177. package/managed/errors/CAGE-SQL-001.md +32 -0
  178. package/managed/errors/CAGE-SQL-002.md +32 -0
  179. package/managed/errors/CAGE-STATE-001.md +32 -0
  180. package/managed/errors/CAGE-STATE-002.md +32 -0
  181. package/managed/errors/CAGE-STATE-003.md +32 -0
  182. package/managed/errors/CAGE-STATE-004.md +32 -0
  183. package/managed/errors/CAGE-STATE-005.md +32 -0
  184. package/managed/errors/CAGE-STATE-010.md +32 -0
  185. package/managed/errors/CAGE-STATE-011.md +32 -0
  186. package/managed/errors/CAGE-SYNC-001.md +35 -0
  187. package/managed/errors/CAGE-TEST-001.md +33 -0
  188. package/managed/errors/CAGE-UNSAFE-001.md +32 -0
  189. package/managed/errors/CAGE-UPGRADE-001.md +32 -0
  190. package/managed/errors/README.md +99 -0
  191. package/package.json +60 -3
  192. package/templates/app/.dockerignore +10 -0
  193. package/templates/app/.env.example +17 -0
  194. package/templates/app/.github/workflows/cage.yml +44 -0
  195. package/templates/app/AGENTS.md +13 -0
  196. package/templates/app/Dockerfile +23 -0
  197. package/templates/app/README.md +58 -0
  198. package/templates/app/apps/api/package.json +16 -0
  199. package/templates/app/apps/api/src/main.ts +10 -0
  200. package/templates/app/apps/api/src/worker.ts +14 -0
  201. package/templates/app/apps/api/tsconfig.json +5 -0
  202. package/templates/app/apps/web/index.html +13 -0
  203. package/templates/app/apps/web/package.json +22 -0
  204. package/templates/app/apps/web/src/main.tsx +552 -0
  205. package/templates/app/apps/web/src/style.css +663 -0
  206. package/templates/app/apps/web/tsconfig.json +11 -0
  207. package/templates/app/apps/web/vite.config.ts +16 -0
  208. package/templates/app/cage.config.json +114 -0
  209. package/templates/app/compose.yaml +39 -0
  210. package/templates/app/db/migrations/0001_init.sql +112 -0
  211. package/templates/app/package.json +25 -0
  212. package/templates/app/packages/contracts/package.json +16 -0
  213. package/templates/app/packages/contracts/src/dept.ts +74 -0
  214. package/templates/app/packages/contracts/src/index.ts +81 -0
  215. package/templates/app/packages/contracts/src/inventory-lock.ts +52 -0
  216. package/templates/app/packages/contracts/src/inventory.ts +57 -0
  217. package/templates/app/packages/contracts/src/purchase-order.ts +72 -0
  218. package/templates/app/packages/contracts/src/queries.ts +17 -0
  219. package/templates/app/packages/contracts/src/role.ts +85 -0
  220. package/templates/app/packages/contracts/src/user.ts +89 -0
  221. package/templates/app/packages/contracts/tsconfig.json +5 -0
  222. package/templates/app/packages/domain/package.json +23 -0
  223. package/templates/app/packages/domain/src/approvals.ts +18 -0
  224. package/templates/app/packages/domain/src/erp.ts +36 -0
  225. package/templates/app/packages/domain/src/index.ts +9 -0
  226. package/templates/app/packages/domain/src/inventory.ts +43 -0
  227. package/templates/app/packages/domain/src/jobs.ts +13 -0
  228. package/templates/app/packages/domain/src/purchase-order.test.ts +137 -0
  229. package/templates/app/packages/domain/src/purchase-order.ts +122 -0
  230. package/templates/app/packages/domain/src/queries.ts +10 -0
  231. package/templates/app/packages/domain/src/reactions.ts +24 -0
  232. package/templates/app/packages/domain/tsconfig.json +5 -0
  233. package/templates/app/pnpm-workspace.yaml +10 -0
  234. package/templates/app/renovate.json +5 -0
  235. package/templates/app/tsconfig.base.json +8 -0
@@ -0,0 +1,1698 @@
1
+ /* 图谱工作台使用低饱和的纸面色与矿物绿。节点颜色承担类型区分,装饰保持克制,
2
+ 让长名称、连线和选中状态始终是画面的第一层信息。 */
3
+ :root {
4
+ font-family: 'Bahnschrift', 'Microsoft YaHei UI', 'PingFang SC', sans-serif;
5
+ color: #283d37;
6
+ background: #f6f7f3;
7
+ font-synthesis: none;
8
+ --green: #286b59;
9
+ --muted: #83918b;
10
+ --border: #e4e8e2;
11
+ --soft: #f6f8f4;
12
+ --sidebar: 216px;
13
+ }
14
+ * {
15
+ box-sizing: border-box;
16
+ }
17
+ body {
18
+ margin: 0;
19
+ }
20
+ button,
21
+ input,
22
+ a {
23
+ -webkit-tap-highlight-color: transparent;
24
+ }
25
+ button,
26
+ input {
27
+ font: inherit;
28
+ }
29
+ button,
30
+ a {
31
+ touch-action: manipulation;
32
+ }
33
+ button {
34
+ cursor: pointer;
35
+ }
36
+ button {
37
+ color: inherit;
38
+ }
39
+ button:disabled {
40
+ cursor: wait;
41
+ opacity: 0.65;
42
+ }
43
+ button:focus-visible,
44
+ a:focus-visible,
45
+ input:focus-visible {
46
+ outline: 2px solid #468b78;
47
+ outline-offset: 3px;
48
+ }
49
+ button {
50
+ border: 0;
51
+ }
52
+ a {
53
+ color: inherit;
54
+ text-decoration: none;
55
+ }
56
+ [hidden] {
57
+ display: none !important;
58
+ }
59
+ .app-shell {
60
+ min-height: 100vh;
61
+ display: grid;
62
+ grid-template-columns: var(--sidebar) minmax(0, 1fr);
63
+ grid-template-rows: 69px 1fr;
64
+ }
65
+ .topbar {
66
+ grid-column: 1/-1;
67
+ display: flex;
68
+ align-items: center;
69
+ border-bottom: 1px solid var(--border);
70
+ background: #fbfcf9;
71
+ padding: 0 29px 0 25px;
72
+ gap: 34px;
73
+ }
74
+ .brand {
75
+ width: 157px;
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 12px;
79
+ }
80
+ .brand-mark {
81
+ width: 30px;
82
+ height: 30px;
83
+ border-radius: 8px;
84
+ background: #28604f;
85
+ display: grid;
86
+ grid-template-columns: repeat(2, 6px);
87
+ grid-template-rows: repeat(2, 6px);
88
+ gap: 3px;
89
+ place-content: center;
90
+ transform: rotate(-3deg);
91
+ }
92
+ .brand-mark i {
93
+ background: #c5ded3;
94
+ border-radius: 1px;
95
+ }
96
+ .brand-mark i:last-child {
97
+ background: #88b798;
98
+ }
99
+ .brand strong {
100
+ font-size: 18px;
101
+ letter-spacing: 2.4px;
102
+ }
103
+ .brand strong span {
104
+ font-size: 10px;
105
+ letter-spacing: 2.8px;
106
+ font-weight: 400;
107
+ display: block;
108
+ color: #8a9790;
109
+ margin-top: 1px;
110
+ }
111
+ .breadcrumb {
112
+ display: flex;
113
+ gap: 14px;
114
+ align-items: center;
115
+ font-size: 12px;
116
+ color: #8a9690;
117
+ }
118
+ .breadcrumb b {
119
+ font-weight: 400;
120
+ color: #c4cbc5;
121
+ }
122
+ .breadcrumb span:last-child {
123
+ color: #485e54;
124
+ }
125
+ .local-badge {
126
+ margin-left: auto;
127
+ font-size: 11px;
128
+ display: flex;
129
+ align-items: center;
130
+ gap: 7px;
131
+ padding: 6px 9px;
132
+ border: 1px solid var(--border);
133
+ border-radius: 5px;
134
+ background: white;
135
+ }
136
+ .local-badge i,
137
+ .tiny-dot {
138
+ width: 6px;
139
+ height: 6px;
140
+ background: #74a58a;
141
+ border-radius: 50%;
142
+ display: inline-block;
143
+ }
144
+ .mobile-filters {
145
+ display: none;
146
+ }
147
+ .sidebar {
148
+ padding: 28px 17px 16px;
149
+ background: #f9faf6;
150
+ border-right: 1px solid var(--border);
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: 0;
154
+ min-height: calc(100vh - 69px);
155
+ }
156
+ .section-label {
157
+ font-size: 9px;
158
+ letter-spacing: 2px;
159
+ color: #8e9b94;
160
+ display: flex;
161
+ justify-content: space-between;
162
+ padding: 0 12px;
163
+ margin-bottom: 14px;
164
+ }
165
+ .section-label span {
166
+ letter-spacing: 0;
167
+ }
168
+ .nav-active {
169
+ padding: 11px 13px;
170
+ border-radius: 6px;
171
+ background: #e8efe7;
172
+ color: #316a54;
173
+ font-size: 12px;
174
+ display: flex;
175
+ align-items: center;
176
+ gap: 10px;
177
+ font-weight: 600;
178
+ }
179
+ .nav-symbol {
180
+ font-size: 21px;
181
+ line-height: 20px;
182
+ }
183
+ .nav-dot {
184
+ width: 5px;
185
+ height: 5px;
186
+ border-radius: 50%;
187
+ background: var(--green);
188
+ margin-left: auto;
189
+ }
190
+ .sidebar-heading {
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: space-between;
194
+ font-size: 10px;
195
+ letter-spacing: 0.7px;
196
+ color: #939e97;
197
+ padding: 26px 11px 10px;
198
+ }
199
+ .sidebar-heading span {
200
+ font-size: 10px;
201
+ }
202
+ .text-button {
203
+ background: transparent;
204
+ font-size: 10px;
205
+ color: #5c8270;
206
+ padding: 2px 0;
207
+ }
208
+ .text-button:hover {
209
+ color: #225d49;
210
+ text-decoration: underline;
211
+ }
212
+ .aggregate-list {
213
+ display: grid;
214
+ gap: 3px;
215
+ }
216
+ .aggregate-button {
217
+ display: flex;
218
+ align-items: center;
219
+ gap: 8px;
220
+ width: 100%;
221
+ text-align: left;
222
+ background: transparent;
223
+ font-size: 11px;
224
+ border-radius: 5px;
225
+ padding: 10px 11px;
226
+ color: #6b7c71;
227
+ }
228
+ .aggregate-button span {
229
+ overflow: hidden;
230
+ text-overflow: ellipsis;
231
+ white-space: nowrap;
232
+ }
233
+ .aggregate-button b {
234
+ margin-left: auto;
235
+ color: #a1aaa3;
236
+ font-size: 10px;
237
+ font-weight: 400;
238
+ }
239
+ .aggregate-button.active {
240
+ color: #2d6657;
241
+ background: #eef2eb;
242
+ }
243
+ .aggregate-button:hover {
244
+ background: #eef2eb;
245
+ }
246
+ .type-filters {
247
+ display: grid;
248
+ gap: 1px;
249
+ }
250
+ .type-filter {
251
+ display: flex;
252
+ align-items: center;
253
+ gap: 8px;
254
+ padding: 6px 10px;
255
+ cursor: pointer;
256
+ font-size: 11px;
257
+ color: #5f7267;
258
+ }
259
+ .type-filter .type-dot {
260
+ width: 7px;
261
+ height: 7px;
262
+ border-radius: 2px;
263
+ }
264
+ .type-filter small {
265
+ margin-left: auto;
266
+ color: #a1aaa2;
267
+ font-size: 10px;
268
+ }
269
+ .type-filter input,
270
+ .system-toggle input {
271
+ accent-color: #3b7761;
272
+ width: 12px;
273
+ height: 12px;
274
+ margin: 0;
275
+ }
276
+ .type-filter:hover {
277
+ background: #eef2eb;
278
+ border-radius: 4px;
279
+ }
280
+ .system-toggle {
281
+ display: flex;
282
+ gap: 8px;
283
+ align-items: center;
284
+ border-top: 1px solid var(--border);
285
+ margin: 18px 10px 0;
286
+ padding-top: 17px;
287
+ font-size: 10px;
288
+ color: #6f8075;
289
+ }
290
+ .sidebar-note {
291
+ margin-top: auto;
292
+ padding: 29px 9px 18px;
293
+ display: flex;
294
+ gap: 9px;
295
+ }
296
+ .note-icon {
297
+ color: #92aa95;
298
+ font-size: 18px;
299
+ }
300
+ .sidebar-note strong {
301
+ font-size: 11px;
302
+ color: #748a79;
303
+ font-weight: 500;
304
+ }
305
+ .sidebar-note p {
306
+ font-size: 10px;
307
+ line-height: 1.9;
308
+ color: #a1aba1;
309
+ margin: 5px 0 0;
310
+ }
311
+ .sidebar-footer {
312
+ border-top: 1px solid var(--border);
313
+ font-size: 10px;
314
+ color: #8a998e;
315
+ padding: 15px 8px 0;
316
+ display: flex;
317
+ justify-content: space-between;
318
+ }
319
+ .main {
320
+ padding: 32px 30px 18px;
321
+ min-width: 0;
322
+ }
323
+ .page-heading {
324
+ display: flex;
325
+ justify-content: space-between;
326
+ align-items: center;
327
+ margin-bottom: 25px;
328
+ gap: 20px;
329
+ }
330
+ .eyebrow {
331
+ font-size: 9px;
332
+ letter-spacing: 2.5px;
333
+ color: #869c88;
334
+ margin-bottom: 10px;
335
+ }
336
+ h1 {
337
+ font-weight: 500;
338
+ font-size: 28px;
339
+ letter-spacing: 1.5px;
340
+ margin: 0;
341
+ color: #263f32;
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 12px;
345
+ }
346
+ .version-tag {
347
+ font-size: 10px;
348
+ letter-spacing: 0;
349
+ font-family: Consolas, monospace;
350
+ font-weight: 400;
351
+ background: #e9eee6;
352
+ padding: 4px 6px;
353
+ border-radius: 4px;
354
+ color: #8b9e86;
355
+ }
356
+ .page-heading p {
357
+ font-size: 11px;
358
+ color: #89968b;
359
+ margin: 10px 0 0;
360
+ letter-spacing: 0.4px;
361
+ }
362
+ .heading-actions {
363
+ display: flex;
364
+ gap: 9px;
365
+ }
366
+ .button {
367
+ padding: 10px 14px;
368
+ border-radius: 6px;
369
+ font-size: 11px;
370
+ display: inline-flex;
371
+ align-items: center;
372
+ justify-content: center;
373
+ gap: 7px;
374
+ white-space: nowrap;
375
+ transition:
376
+ background 0.15s,
377
+ transform 0.15s;
378
+ }
379
+ .button:active {
380
+ transform: translateY(1px);
381
+ }
382
+ .button.primary {
383
+ background: #326e59;
384
+ color: #fff;
385
+ border: 1px solid #326e59;
386
+ box-shadow: 0 2px 3px #315b4912;
387
+ }
388
+ .button.primary:hover {
389
+ background: #245d49;
390
+ }
391
+ .button.secondary {
392
+ background: #fbfcf9;
393
+ border: 1px solid #dde4d9;
394
+ color: #697e6d;
395
+ }
396
+ .button.secondary:hover {
397
+ background: #eef3e9;
398
+ }
399
+ .button span {
400
+ font-size: 16px;
401
+ line-height: 12px;
402
+ }
403
+ .overview {
404
+ display: flex;
405
+ align-items: center;
406
+ min-height: 82px;
407
+ background: #fcfdf9;
408
+ border: 1px solid var(--border);
409
+ border-radius: 9px;
410
+ margin-bottom: 21px;
411
+ padding: 17px 25px;
412
+ gap: 26px;
413
+ }
414
+ .overview-item {
415
+ display: flex;
416
+ gap: 13px;
417
+ align-items: center;
418
+ padding-right: 26px;
419
+ border-right: 1px solid #e9ece5;
420
+ min-width: 150px;
421
+ }
422
+ .metric-icon {
423
+ width: 37px;
424
+ height: 37px;
425
+ display: grid;
426
+ place-items: center;
427
+ font-size: 22px;
428
+ border-radius: 9px;
429
+ background: #edf2e9;
430
+ color: #6b8b70;
431
+ }
432
+ .node-icon {
433
+ color: #6a859d;
434
+ background: #eef2f4;
435
+ }
436
+ .edge-icon {
437
+ color: #ac9669;
438
+ background: #f5f1e9;
439
+ }
440
+ .overview-item small {
441
+ display: block;
442
+ font-size: 10px;
443
+ color: #9aa395;
444
+ margin-bottom: 4px;
445
+ }
446
+ .overview-item strong {
447
+ font-family: 'Bahnschrift', Consolas, sans-serif;
448
+ font-size: 24px;
449
+ line-height: 1;
450
+ font-weight: 500;
451
+ color: #415845;
452
+ letter-spacing: -0.6px;
453
+ }
454
+ .overview-health {
455
+ margin-left: auto;
456
+ display: flex;
457
+ align-items: center;
458
+ gap: 10px;
459
+ }
460
+ .health-dot {
461
+ width: 7px;
462
+ height: 7px;
463
+ background: #74a17e;
464
+ border-radius: 50%;
465
+ box-shadow: 0 0 0 4px #edf4e9;
466
+ }
467
+ .health-dot.warning {
468
+ background: #c49242;
469
+ box-shadow: 0 0 0 4px #f7f0e2;
470
+ }
471
+ .overview-health strong {
472
+ display: block;
473
+ font-size: 11px;
474
+ font-weight: 400;
475
+ color: #657d61;
476
+ }
477
+ .overview-health small {
478
+ display: block;
479
+ font-size: 9px;
480
+ color: #a0aa9b;
481
+ margin-top: 6px;
482
+ }
483
+ .notice {
484
+ padding: 12px 15px;
485
+ margin-bottom: 16px;
486
+ background: #fff4e2;
487
+ border: 1px solid #e8d5b3;
488
+ color: #876c38;
489
+ border-radius: 6px;
490
+ font-size: 12px;
491
+ white-space: pre-wrap;
492
+ }
493
+ .explorer {
494
+ display: grid;
495
+ grid-template-columns: minmax(0, 1fr) 267px;
496
+ border: 1px solid #dfe5dc;
497
+ border-radius: 10px;
498
+ overflow: hidden;
499
+ background: #fff;
500
+ box-shadow: 0 7px 24px #354d3510;
501
+ height: calc(100vh - 325px);
502
+ min-height: 570px;
503
+ max-height: 1000px;
504
+ }
505
+ .graph-panel {
506
+ min-width: 0;
507
+ display: flex;
508
+ flex-direction: column;
509
+ }
510
+ .graph-toolbar {
511
+ display: flex;
512
+ align-items: center;
513
+ justify-content: space-between;
514
+ padding: 13px 17px;
515
+ border-bottom: 1px solid #edf0e9;
516
+ gap: 12px;
517
+ }
518
+ .tabs {
519
+ display: flex;
520
+ background: #f1f4ee;
521
+ border-radius: 5px;
522
+ padding: 3px;
523
+ flex-shrink: 0;
524
+ }
525
+ .tabs button {
526
+ background: transparent;
527
+ padding: 6px 10px;
528
+ font-size: 10px;
529
+ color: #8c9887;
530
+ border-radius: 4px;
531
+ }
532
+ .tabs button.active {
533
+ background: #fff;
534
+ color: #55704d;
535
+ box-shadow: 0 1px 4px #405a3412;
536
+ }
537
+ .search-wrap {
538
+ display: flex;
539
+ align-items: center;
540
+ border: 1px solid #e6ebe1;
541
+ border-radius: 5px;
542
+ background: #fff;
543
+ padding: 0 9px;
544
+ gap: 6px;
545
+ position: relative;
546
+ width: 230px;
547
+ }
548
+ .search-wrap > span {
549
+ font-size: 20px;
550
+ color: #9aab96;
551
+ }
552
+ .search-wrap input {
553
+ border: 0;
554
+ outline: none;
555
+ background: transparent;
556
+ width: 100%;
557
+ min-width: 0;
558
+ height: 30px;
559
+ font-size: 10px;
560
+ color: #3c5848;
561
+ }
562
+ .search-wrap input::placeholder {
563
+ color: #a1ad9b;
564
+ }
565
+ .search-wrap kbd {
566
+ border: 1px solid #e6eadf;
567
+ border-radius: 3px;
568
+ font-size: 9px;
569
+ padding: 1px 4px;
570
+ color: #a6afa0;
571
+ }
572
+ .search-results {
573
+ position: absolute;
574
+ z-index: 10;
575
+ top: 37px;
576
+ right: 0;
577
+ width: 320px;
578
+ max-height: 310px;
579
+ overflow: auto;
580
+ border: 1px solid var(--border);
581
+ border-radius: 7px;
582
+ background: #fff;
583
+ box-shadow: 0 8px 35px #314d3326;
584
+ padding: 5px;
585
+ }
586
+ .search-result {
587
+ display: flex;
588
+ gap: 8px;
589
+ align-items: center;
590
+ width: 100%;
591
+ text-align: left;
592
+ padding: 10px 8px;
593
+ background: white;
594
+ border-radius: 4px;
595
+ font-size: 11px;
596
+ }
597
+ .search-result:hover {
598
+ background: #f0f5eb;
599
+ }
600
+ .search-result small {
601
+ font-size: 9px;
602
+ min-width: 35px;
603
+ color: #8e9c84;
604
+ }
605
+ .search-result span {
606
+ overflow: hidden;
607
+ text-overflow: ellipsis;
608
+ }
609
+ .search-empty {
610
+ font-size: 11px;
611
+ padding: 14px;
612
+ color: #83917b;
613
+ }
614
+ .graph-subbar {
615
+ height: 39px;
616
+ display: flex;
617
+ align-items: center;
618
+ padding: 0 19px;
619
+ font-size: 10px;
620
+ color: #7e8e78;
621
+ gap: 10px;
622
+ background: #fefefc;
623
+ }
624
+ .graph-subbar > span:first-child {
625
+ display: flex;
626
+ gap: 7px;
627
+ align-items: center;
628
+ min-width: 0;
629
+ }
630
+ .graph-subbar strong {
631
+ font-weight: 500;
632
+ color: #63795b;
633
+ white-space: nowrap;
634
+ }
635
+ .graph-subbar #view-count {
636
+ color: #a8b19f;
637
+ font-size: 9px;
638
+ white-space: nowrap;
639
+ }
640
+ .drag-hint {
641
+ margin-left: auto;
642
+ font-size: 9px;
643
+ color: #a8b19f;
644
+ white-space: nowrap;
645
+ }
646
+ .canvas {
647
+ flex: 1;
648
+ min-height: 0;
649
+ position: relative;
650
+ overflow: hidden;
651
+ background-color: #fafcf8;
652
+ background-image: radial-gradient(#cdd8c6 0.7px, transparent 0.7px);
653
+ background-size: 20px 20px;
654
+ }
655
+ .canvas svg {
656
+ width: 100%;
657
+ height: 100%;
658
+ display: block;
659
+ cursor: grab;
660
+ touch-action: none;
661
+ user-select: none;
662
+ }
663
+ .canvas svg.dragging {
664
+ cursor: grabbing;
665
+ }
666
+ .canvas-signature {
667
+ position: absolute;
668
+ bottom: 19px;
669
+ left: 20px;
670
+ pointer-events: none;
671
+ color: #b4bfb0;
672
+ font-size: 8px;
673
+ letter-spacing: 1.5px;
674
+ }
675
+ .canvas-signature span {
676
+ padding: 0 4px;
677
+ color: #c4ccc0;
678
+ }
679
+ .zoom-controls {
680
+ position: absolute;
681
+ bottom: 14px;
682
+ right: 16px;
683
+ display: flex;
684
+ align-items: center;
685
+ gap: 1px;
686
+ background: #fff;
687
+ border: 1px solid #e2e8dc;
688
+ border-radius: 6px;
689
+ box-shadow: 0 3px 8px #42553308;
690
+ padding: 4px;
691
+ }
692
+ .zoom-controls button {
693
+ background: white;
694
+ width: 27px;
695
+ height: 26px;
696
+ color: #728668;
697
+ font-size: 18px;
698
+ border-radius: 3px;
699
+ }
700
+ .zoom-controls button:hover {
701
+ background: #f0f4e9;
702
+ }
703
+ .zoom-controls span {
704
+ font-family: Consolas, monospace;
705
+ font-size: 9px;
706
+ width: 37px;
707
+ text-align: center;
708
+ color: #87977b;
709
+ }
710
+ .zoom-controls i {
711
+ height: 15px;
712
+ border-left: 1px solid #e8ecdf;
713
+ margin: 0 4px;
714
+ }
715
+ .graph-footer {
716
+ height: 33px;
717
+ border-top: 1px solid #edf0e6;
718
+ font-size: 9px;
719
+ color: #9eaa93;
720
+ display: flex;
721
+ align-items: center;
722
+ justify-content: space-between;
723
+ padding: 0 17px;
724
+ background: #fdfefa;
725
+ gap: 10px;
726
+ }
727
+ .graph-footer > div {
728
+ display: flex;
729
+ align-items: center;
730
+ gap: 6px;
731
+ }
732
+ .legend-line {
733
+ display: inline-block;
734
+ width: 16px;
735
+ border-top: 1px solid #9bab91;
736
+ }
737
+ .legend-line.dashed {
738
+ border-top-style: dashed;
739
+ margin-left: 7px;
740
+ }
741
+ .edge {
742
+ fill: none;
743
+ stroke: #aebdb0;
744
+ stroke-width: 1.15;
745
+ opacity: 0.5;
746
+ transition: opacity 0.15s;
747
+ }
748
+ .edge-group.derived .edge {
749
+ stroke-dasharray: 4 4;
750
+ opacity: 0.37;
751
+ }
752
+ .edge-group.active .edge,
753
+ .edge-group:hover .edge,
754
+ .edge-group:focus-visible .edge {
755
+ stroke: #438574;
756
+ stroke-width: 1.6;
757
+ opacity: 0.95;
758
+ }
759
+ .edge-group.dim .edge {
760
+ opacity: 0.09;
761
+ }
762
+ .node {
763
+ cursor: pointer;
764
+ outline: none;
765
+ }
766
+ .node .node-box {
767
+ fill: #fff;
768
+ stroke: #dae3d7;
769
+ stroke-width: 1;
770
+ filter: drop-shadow(0 2px 3px #2e48220a);
771
+ }
772
+ .node:hover .node-box,
773
+ .node:focus-visible .node-box {
774
+ stroke: #76a489;
775
+ stroke-width: 1.7;
776
+ }
777
+ .node .selected-ring {
778
+ fill: #e5f0e9;
779
+ opacity: 0;
780
+ }
781
+ .node.selected .selected-ring {
782
+ opacity: 1;
783
+ }
784
+ .node.selected .node-box {
785
+ stroke: #3f8a70;
786
+ stroke-width: 1.6;
787
+ fill: #fcfffc;
788
+ }
789
+ .node.dim {
790
+ opacity: 0.26;
791
+ }
792
+ .node-title {
793
+ font-family: 'Bahnschrift', 'Microsoft YaHei UI', sans-serif;
794
+ font-size: 12px;
795
+ fill: #3e5447;
796
+ }
797
+ .node-subtitle {
798
+ font-family: Consolas, 'Microsoft YaHei UI', monospace;
799
+ font-size: 8.5px;
800
+ fill: #94a08e;
801
+ }
802
+ .node-code {
803
+ font-family: Consolas, monospace;
804
+ font-size: 8px;
805
+ font-weight: bold;
806
+ }
807
+ .node-indicator {
808
+ fill: #9aaa95;
809
+ }
810
+ .empty-state,
811
+ .loading-state {
812
+ position: absolute;
813
+ inset: 0;
814
+ display: flex;
815
+ align-items: center;
816
+ justify-content: center;
817
+ flex-direction: column;
818
+ background: #fafcf8f0;
819
+ gap: 10px;
820
+ text-align: center;
821
+ }
822
+ .empty-state > span {
823
+ font-size: 40px;
824
+ color: #abc0a5;
825
+ }
826
+ .empty-state strong,
827
+ .loading-state strong {
828
+ font-size: 14px;
829
+ font-weight: 500;
830
+ color: #6e8664;
831
+ }
832
+ .empty-state p {
833
+ font-size: 11px;
834
+ color: #97a58c;
835
+ margin: 0 0 8px;
836
+ }
837
+ .loader {
838
+ width: 25px;
839
+ height: 25px;
840
+ border: 2px solid #dce8d6;
841
+ border-top-color: #54845b;
842
+ border-radius: 50%;
843
+ animation: spin 1s linear infinite;
844
+ }
845
+ @keyframes spin {
846
+ to {
847
+ transform: rotate(360deg);
848
+ }
849
+ }
850
+ .inspector {
851
+ border-left: 1px solid #e5eadd;
852
+ background: #fff;
853
+ overflow: auto;
854
+ padding: 21px 20px;
855
+ }
856
+ .inspector-top {
857
+ display: flex;
858
+ align-items: center;
859
+ justify-content: space-between;
860
+ font-size: 9px;
861
+ color: #9ca992;
862
+ letter-spacing: 0.6px;
863
+ margin-bottom: 21px;
864
+ }
865
+ .inspector-icon {
866
+ width: 38px;
867
+ height: 38px;
868
+ border-radius: 9px;
869
+ display: grid;
870
+ place-items: center;
871
+ font-size: 12px;
872
+ font-family: Consolas, monospace;
873
+ font-weight: bold;
874
+ margin-bottom: 14px;
875
+ }
876
+ .inspector h2 {
877
+ font-size: 19px;
878
+ letter-spacing: -0.2px;
879
+ font-weight: 500;
880
+ overflow-wrap: anywhere;
881
+ margin: 0 0 8px;
882
+ color: #36523e;
883
+ line-height: 1.4;
884
+ }
885
+ .kind-pill {
886
+ display: inline-block;
887
+ font-size: 9px;
888
+ padding: 4px 6px;
889
+ border-radius: 4px;
890
+ margin-bottom: 12px;
891
+ }
892
+ .inspector-description {
893
+ font-size: 10px;
894
+ color: #94a08c;
895
+ line-height: 1.8;
896
+ margin: 0 0 16px;
897
+ }
898
+ .inspector-actions {
899
+ display: flex;
900
+ gap: 7px;
901
+ margin-bottom: 21px;
902
+ }
903
+ .inspector-actions button {
904
+ font-size: 10px;
905
+ padding: 8px 9px;
906
+ }
907
+ .detail-section {
908
+ border-top: 1px solid #edf0e7;
909
+ padding-top: 16px;
910
+ margin-top: 17px;
911
+ }
912
+ .detail-section h3 {
913
+ font-size: 10px;
914
+ font-weight: 500;
915
+ color: #839575;
916
+ margin: 0 0 12px;
917
+ display: flex;
918
+ align-items: center;
919
+ justify-content: space-between;
920
+ }
921
+ .detail-section h3 span {
922
+ font-size: 9px;
923
+ font-family: Consolas, monospace;
924
+ color: #a4b098;
925
+ }
926
+ .detail-row {
927
+ display: flex;
928
+ align-items: flex-start;
929
+ justify-content: space-between;
930
+ font-size: 10px;
931
+ gap: 10px;
932
+ line-height: 1.9;
933
+ padding: 3px 0;
934
+ }
935
+ .detail-row > span:first-child {
936
+ color: #a0ad94;
937
+ flex-shrink: 0;
938
+ }
939
+ .detail-row > span:last-child {
940
+ color: #657e55;
941
+ overflow-wrap: anywhere;
942
+ text-align: right;
943
+ max-width: 150px;
944
+ }
945
+ .source-location {
946
+ background: #f5f8f0;
947
+ border: 1px solid #e9eee0;
948
+ border-radius: 5px;
949
+ padding: 10px;
950
+ font:
951
+ 9px/1.7 Consolas,
952
+ 'Microsoft YaHei UI',
953
+ monospace;
954
+ overflow-wrap: anywhere;
955
+ color: #7b906c;
956
+ }
957
+ .source-note {
958
+ font-size: 10px;
959
+ color: #9baa8e;
960
+ line-height: 1.7;
961
+ }
962
+ .relation-button {
963
+ width: 100%;
964
+ background: transparent;
965
+ border-radius: 5px;
966
+ padding: 8px 6px;
967
+ margin: 0 -6px;
968
+ text-align: left;
969
+ display: flex;
970
+ gap: 8px;
971
+ align-items: center;
972
+ }
973
+ .relation-button:hover {
974
+ background: #f2f6ec;
975
+ }
976
+ .relation-dot {
977
+ width: 6px;
978
+ height: 6px;
979
+ border-radius: 50%;
980
+ flex-shrink: 0;
981
+ }
982
+ .relation-button div {
983
+ min-width: 0;
984
+ }
985
+ .relation-button strong {
986
+ display: block;
987
+ overflow: hidden;
988
+ text-overflow: ellipsis;
989
+ white-space: nowrap;
990
+ font-size: 10px;
991
+ font-weight: 400;
992
+ color: #697f59;
993
+ }
994
+ .relation-button small {
995
+ display: block;
996
+ font-size: 8px;
997
+ color: #a3af96;
998
+ margin-top: 4px;
999
+ }
1000
+ .relation-button > span:last-child {
1001
+ margin-left: auto;
1002
+ font-size: 12px;
1003
+ color: #b0bba5;
1004
+ }
1005
+ .detail-json {
1006
+ white-space: pre-wrap;
1007
+ overflow-wrap: anywhere;
1008
+ font:
1009
+ 9px/1.7 Consolas,
1010
+ monospace;
1011
+ background: #f7f9f3;
1012
+ padding: 10px;
1013
+ border-radius: 5px;
1014
+ color: #7b8b6d;
1015
+ }
1016
+ .inspector details summary {
1017
+ cursor: pointer;
1018
+ font-size: 10px;
1019
+ color: #92a183;
1020
+ padding: 13px 0;
1021
+ }
1022
+ .page-footer {
1023
+ display: flex;
1024
+ justify-content: space-between;
1025
+ font-size: 9px;
1026
+ color: #9faa96;
1027
+ padding: 13px 1px 0;
1028
+ gap: 10px;
1029
+ }
1030
+ .page-footer span:first-child {
1031
+ font-family: Consolas, monospace;
1032
+ }
1033
+ .icon-button {
1034
+ background: transparent;
1035
+ border-radius: 4px;
1036
+ width: 28px;
1037
+ height: 28px;
1038
+ font-size: 18px;
1039
+ color: #91a183;
1040
+ }
1041
+ dialog {
1042
+ border: 1px solid var(--border);
1043
+ border-radius: 10px;
1044
+ padding: 24px;
1045
+ max-width: 650px;
1046
+ width: calc(100% - 40px);
1047
+ color: #37543f;
1048
+ box-shadow: 0 15px 70px #23342130;
1049
+ }
1050
+ dialog::backdrop {
1051
+ background: #263c2d44;
1052
+ backdrop-filter: blur(3px);
1053
+ }
1054
+ .dialog-heading {
1055
+ display: flex;
1056
+ align-items: center;
1057
+ justify-content: space-between;
1058
+ }
1059
+ .dialog-heading h2 {
1060
+ font-size: 19px;
1061
+ font-weight: 500;
1062
+ }
1063
+ .finding {
1064
+ padding: 15px 0;
1065
+ border-top: 1px solid var(--border);
1066
+ font-size: 12px;
1067
+ line-height: 1.8;
1068
+ }
1069
+ .finding code {
1070
+ font-size: 11px;
1071
+ color: #a4783b;
1072
+ }
1073
+ .finding p {
1074
+ margin: 5px 0;
1075
+ }
1076
+ .finding small {
1077
+ font-size: 10px;
1078
+ color: #8d9f7e;
1079
+ }
1080
+ @media (min-width: 1600px) {
1081
+ .main {
1082
+ padding: 36px 39px 18px;
1083
+ }
1084
+ .explorer {
1085
+ grid-template-columns: minmax(0, 1fr) 294px;
1086
+ }
1087
+ .inspector {
1088
+ padding: 24px;
1089
+ }
1090
+ .sidebar {
1091
+ padding-top: 32px;
1092
+ }
1093
+ .overview {
1094
+ gap: 38px;
1095
+ }
1096
+ .overview-item {
1097
+ min-width: 182px;
1098
+ }
1099
+ .page-heading {
1100
+ margin-bottom: 29px;
1101
+ }
1102
+ }
1103
+ @media (max-width: 1200px) {
1104
+ :root {
1105
+ --sidebar: 186px;
1106
+ }
1107
+ .main {
1108
+ padding: 25px 19px 15px;
1109
+ }
1110
+ .topbar {
1111
+ gap: 17px;
1112
+ }
1113
+ .brand {
1114
+ width: 144px;
1115
+ }
1116
+ .overview {
1117
+ gap: 15px;
1118
+ padding: 15px;
1119
+ }
1120
+ .overview-item {
1121
+ min-width: 110px;
1122
+ padding-right: 15px;
1123
+ gap: 9px;
1124
+ }
1125
+ .overview-health small {
1126
+ font-size: 8px;
1127
+ }
1128
+ .explorer {
1129
+ grid-template-columns: minmax(0, 1fr) 230px;
1130
+ }
1131
+ .inspector {
1132
+ padding: 17px 15px;
1133
+ }
1134
+ .graph-toolbar {
1135
+ padding: 10px;
1136
+ gap: 8px;
1137
+ }
1138
+ .tabs button {
1139
+ padding: 6px 7px;
1140
+ }
1141
+ .search-wrap {
1142
+ width: 175px;
1143
+ }
1144
+ .drag-hint {
1145
+ display: none;
1146
+ }
1147
+ .canvas-signature {
1148
+ display: none;
1149
+ }
1150
+ }
1151
+ @media (max-width: 980px) {
1152
+ :root {
1153
+ --sidebar: 175px;
1154
+ }
1155
+ .overview-health {
1156
+ display: none;
1157
+ }
1158
+ .overview {
1159
+ justify-content: space-between;
1160
+ }
1161
+ .overview-item:last-of-type {
1162
+ border: 0;
1163
+ }
1164
+ .explorer {
1165
+ grid-template-columns: minmax(0, 1fr);
1166
+ height: auto;
1167
+ max-height: none;
1168
+ min-height: 0;
1169
+ }
1170
+ .graph-panel {
1171
+ height: 600px;
1172
+ }
1173
+ .inspector {
1174
+ border-left: 0;
1175
+ border-top: 1px solid var(--border);
1176
+ max-height: 380px;
1177
+ }
1178
+ .inspector-top {
1179
+ margin-bottom: 12px;
1180
+ }
1181
+ .inspector-icon {
1182
+ float: left;
1183
+ margin-right: 13px;
1184
+ }
1185
+ .inspector h2 {
1186
+ font-size: 18px;
1187
+ }
1188
+ .detail-section {
1189
+ margin-top: 12px;
1190
+ }
1191
+ .page-footer {
1192
+ font-size: 8px;
1193
+ }
1194
+ .heading-actions .secondary {
1195
+ display: none;
1196
+ }
1197
+ }
1198
+ @media (max-width: 640px) {
1199
+ .app-shell {
1200
+ display: block;
1201
+ }
1202
+ .topbar {
1203
+ height: 62px;
1204
+ padding: 0 16px;
1205
+ }
1206
+ .brand {
1207
+ width: auto;
1208
+ }
1209
+ .brand strong {
1210
+ font-size: 15px;
1211
+ }
1212
+ .brand strong span {
1213
+ font-size: 8px;
1214
+ }
1215
+ .breadcrumb {
1216
+ display: none;
1217
+ }
1218
+ .local-badge {
1219
+ font-size: 10px;
1220
+ }
1221
+ .mobile-filters {
1222
+ display: block;
1223
+ }
1224
+ .sidebar {
1225
+ display: none;
1226
+ position: absolute;
1227
+ left: 0;
1228
+ top: 62px;
1229
+ z-index: 15;
1230
+ width: 220px;
1231
+ min-height: 0;
1232
+ max-height: calc(100vh - 62px);
1233
+ overflow: auto;
1234
+ box-shadow: 10px 10px 30px #30473120;
1235
+ }
1236
+ .sidebar.open {
1237
+ display: flex;
1238
+ }
1239
+ .sidebar-note {
1240
+ display: none;
1241
+ }
1242
+ .main {
1243
+ padding: 25px 13px 15px;
1244
+ }
1245
+ .page-heading {
1246
+ gap: 10px;
1247
+ margin-bottom: 20px;
1248
+ }
1249
+ h1 {
1250
+ font-size: 25px;
1251
+ }
1252
+ .eyebrow {
1253
+ font-size: 8px;
1254
+ }
1255
+ .page-heading p {
1256
+ font-size: 10px;
1257
+ }
1258
+ .button {
1259
+ padding: 9px 11px;
1260
+ font-size: 10px;
1261
+ }
1262
+ .overview {
1263
+ gap: 8px;
1264
+ padding: 12px;
1265
+ margin-bottom: 15px;
1266
+ min-height: 70px;
1267
+ }
1268
+ .overview-item {
1269
+ min-width: 0;
1270
+ padding-right: 13px;
1271
+ gap: 7px;
1272
+ }
1273
+ .metric-icon {
1274
+ width: 27px;
1275
+ height: 29px;
1276
+ font-size: 17px;
1277
+ }
1278
+ .overview-item strong {
1279
+ font-size: 20px;
1280
+ }
1281
+ .overview-item small {
1282
+ font-size: 8px;
1283
+ }
1284
+ .graph-toolbar {
1285
+ flex-wrap: wrap;
1286
+ gap: 10px;
1287
+ }
1288
+ .search-wrap {
1289
+ width: 100%;
1290
+ flex: 1;
1291
+ min-width: 155px;
1292
+ }
1293
+ .search-wrap kbd {
1294
+ display: none;
1295
+ }
1296
+ .search-results {
1297
+ width: 280px;
1298
+ right: -5px;
1299
+ }
1300
+ .graph-panel {
1301
+ height: 510px;
1302
+ }
1303
+ .graph-footer {
1304
+ font-size: 8px;
1305
+ padding: 0 10px;
1306
+ }
1307
+ .graph-subbar {
1308
+ padding: 0 11px;
1309
+ }
1310
+ .page-footer {
1311
+ flex-direction: column;
1312
+ gap: 6px;
1313
+ }
1314
+ .inspector {
1315
+ max-height: 440px;
1316
+ }
1317
+ }
1318
+ @media (prefers-reduced-motion: reduce) {
1319
+ *,
1320
+ *::before,
1321
+ *::after {
1322
+ animation: none !important;
1323
+ transition: none !important;
1324
+ }
1325
+ }
1326
+ /* 缩放后的节点仍需保留可辨识的主次文字,弱化背景节点但不把它们隐去。 */
1327
+ .node-title {
1328
+ font-size: 14px;
1329
+ }
1330
+ .node-subtitle {
1331
+ font-size: 10px;
1332
+ }
1333
+ .node.dim {
1334
+ opacity: 0.48;
1335
+ }
1336
+ .edge-group.dim .edge {
1337
+ opacity: 0.15;
1338
+ }
1339
+ /* 中文主标题与英文技术标识分层,长名称换行,关系列表保留足够阅读对比度。 */
1340
+ .node-code {
1341
+ font-family: 'Microsoft YaHei UI', 'PingFang SC', sans-serif;
1342
+ font-size: 11px;
1343
+ font-weight: 500;
1344
+ }
1345
+ .node-identifier {
1346
+ font:
1347
+ 10px/1.7 Consolas,
1348
+ monospace;
1349
+ color: #849581;
1350
+ overflow-wrap: anywhere;
1351
+ margin: 0 0 12px;
1352
+ }
1353
+ .search-names {
1354
+ display: flex;
1355
+ flex-direction: column;
1356
+ gap: 5px;
1357
+ min-width: 0;
1358
+ }
1359
+ .search-names strong {
1360
+ font-size: 12px;
1361
+ font-weight: 500;
1362
+ color: #47634d;
1363
+ }
1364
+ .search-names > span {
1365
+ font:
1366
+ 9px Consolas,
1367
+ monospace;
1368
+ color: #93a08e;
1369
+ overflow: hidden;
1370
+ text-overflow: ellipsis;
1371
+ }
1372
+ .relation-code {
1373
+ font-family: Consolas, monospace;
1374
+ }
1375
+ .inspector h2 {
1376
+ letter-spacing: 0.5px;
1377
+ }
1378
+ .detail-row > span:last-child {
1379
+ color: #526a49;
1380
+ }
1381
+
1382
+ /* 标签是连线的一部分:浅底隔开交叉线,选中时用深绿明确当前解释对象。
1383
+ * 概览只标注相关边,聚焦小图显示全部,避免百级关系的文字堆叠。 */
1384
+ .relation-guide {
1385
+ display: flex;
1386
+ align-items: center;
1387
+ gap: 8px;
1388
+ padding: 9px 17px;
1389
+ color: #617861;
1390
+ background: #f2f6ef;
1391
+ border-bottom: 1px solid #e4ebdf;
1392
+ font-size: 11px;
1393
+ line-height: 1.6;
1394
+ }
1395
+ .relation-guide > span:first-child {
1396
+ font-size: 17px;
1397
+ color: #3c7c65;
1398
+ }
1399
+ .edge-group {
1400
+ cursor: pointer;
1401
+ outline: none;
1402
+ }
1403
+ .edge-hit {
1404
+ fill: none;
1405
+ stroke: transparent;
1406
+ stroke-width: 16;
1407
+ pointer-events: stroke;
1408
+ }
1409
+ .edge-label {
1410
+ visibility: hidden;
1411
+ }
1412
+ .edge-label rect {
1413
+ fill: #f9fcf7;
1414
+ stroke: #d2dfce;
1415
+ stroke-width: 1;
1416
+ }
1417
+ .edge-label text {
1418
+ fill: #536c52;
1419
+ font-size: 14px;
1420
+ font-family: 'Microsoft YaHei UI', sans-serif;
1421
+ pointer-events: none;
1422
+ }
1423
+ .edge-group.show-label .edge-label,
1424
+ .edge-group:hover .edge-label,
1425
+ .edge-group:focus-visible .edge-label {
1426
+ visibility: visible;
1427
+ }
1428
+ .edge-group.dim .edge-label {
1429
+ opacity: 0.7;
1430
+ }
1431
+ .edge-group:hover .edge-label,
1432
+ .edge-group:focus-visible .edge-label {
1433
+ opacity: 1;
1434
+ }
1435
+ .edge-group:hover .edge-label rect,
1436
+ .edge-group:focus-visible .edge-label rect {
1437
+ stroke: #438574;
1438
+ stroke-width: 2;
1439
+ }
1440
+ .edge-group.chosen .edge {
1441
+ stroke-width: 2.5;
1442
+ }
1443
+ .edge-group.chosen .edge-label rect {
1444
+ fill: #357c6c;
1445
+ stroke: #357c6c;
1446
+ }
1447
+ .edge-group.chosen .edge-label text {
1448
+ fill: #fff;
1449
+ }
1450
+ .edge-group.derived .edge-label rect {
1451
+ stroke-dasharray: 3 2;
1452
+ }
1453
+ .relation-category {
1454
+ display: inline-block;
1455
+ font-size: 11px;
1456
+ color: #64816d;
1457
+ margin-bottom: 9px;
1458
+ }
1459
+ .relation-route {
1460
+ padding: 12px;
1461
+ border: 1px solid #dfe9dc;
1462
+ border-radius: 9px;
1463
+ background: #f6f9f3;
1464
+ margin: 17px 0 22px;
1465
+ }
1466
+ .relation-endpoint {
1467
+ display: flex;
1468
+ flex-direction: column;
1469
+ gap: 5px;
1470
+ width: 100%;
1471
+ text-align: left;
1472
+ background: #fff;
1473
+ border: 1px solid #e3ebdf;
1474
+ border-radius: 6px;
1475
+ padding: 10px;
1476
+ }
1477
+ .relation-endpoint:hover,
1478
+ .relation-endpoint:focus-visible {
1479
+ border-color: #55957a;
1480
+ }
1481
+ .relation-endpoint small {
1482
+ font-size: 10px;
1483
+ color: #789078;
1484
+ }
1485
+ .relation-endpoint strong {
1486
+ font-size: 13px;
1487
+ font-weight: 500;
1488
+ color: #365d47;
1489
+ }
1490
+ .relation-endpoint code {
1491
+ font:
1492
+ 10px/1.5 Consolas,
1493
+ monospace;
1494
+ color: #879782;
1495
+ overflow-wrap: anywhere;
1496
+ }
1497
+ .relation-arrow {
1498
+ display: block;
1499
+ margin: 8px 0;
1500
+ text-align: center;
1501
+ color: #5f846c;
1502
+ font-size: 11px;
1503
+ }
1504
+ .relation-explanation {
1505
+ margin: 18px 0;
1506
+ }
1507
+ .relation-explanation h3 {
1508
+ font-size: 12px;
1509
+ font-weight: 600;
1510
+ color: #365d47;
1511
+ margin: 0 0 7px;
1512
+ }
1513
+ .relation-explanation p {
1514
+ font-size: 12px;
1515
+ line-height: 1.85;
1516
+ color: #64735f;
1517
+ margin: 0;
1518
+ overflow-wrap: anywhere;
1519
+ }
1520
+ .relation-explanation .source-location {
1521
+ margin-top: 7px;
1522
+ font-size: 10px;
1523
+ }
1524
+ .relation-explanation .source-note {
1525
+ font-size: 10px;
1526
+ margin-top: 10px;
1527
+ }
1528
+
1529
+ /* 数据结构采用表卡片:较深的表头定位对象,字段行保持阅读对比度;
1530
+ * 颜色和行距呼应业务数据字典,避免几十个细碎节点造成的视觉噪声。 */
1531
+ .structure-note {
1532
+ padding: 14px 16px;
1533
+ margin: 0 12px 16px;
1534
+ border: 1px solid #e1e9d9;
1535
+ border-radius: 7px;
1536
+ background: #f3f7ec;
1537
+ color: #74856a;
1538
+ }
1539
+ .structure-note strong {
1540
+ font-size: 11px;
1541
+ font-weight: 500;
1542
+ color: #566c4b;
1543
+ }
1544
+ .structure-note p {
1545
+ font-size: 11px;
1546
+ line-height: 1.8;
1547
+ margin: 8px 0;
1548
+ }
1549
+ .structure-note small {
1550
+ font-size: 10px;
1551
+ color: #8a987e;
1552
+ }
1553
+ .table-card .table-shadow {
1554
+ fill: #e6eedf;
1555
+ opacity: 0;
1556
+ }
1557
+ .table-card.selected .table-shadow {
1558
+ opacity: 1;
1559
+ }
1560
+ .table-body {
1561
+ fill: #fafcf6;
1562
+ stroke: #d7e2cc;
1563
+ stroke-width: 1;
1564
+ filter: drop-shadow(0 5px 8px #4a613610);
1565
+ }
1566
+ .table-card.selected .table-body {
1567
+ stroke: #7e9f68;
1568
+ stroke-width: 1.5;
1569
+ }
1570
+ .table-header {
1571
+ outline: none;
1572
+ cursor: grab;
1573
+ }
1574
+ .table-header path {
1575
+ fill: #7b9b63;
1576
+ }
1577
+ .table-header:hover path,
1578
+ .table-header:focus-visible path {
1579
+ fill: #698a52;
1580
+ }
1581
+ .table-icon {
1582
+ fill: #eaf2e3;
1583
+ font-size: 21px;
1584
+ }
1585
+ .table-name {
1586
+ fill: #fff;
1587
+ font-size: 17px;
1588
+ font-weight: 600;
1589
+ font-family: 'Microsoft YaHei UI', sans-serif;
1590
+ }
1591
+ .table-id {
1592
+ fill: #e0eace;
1593
+ font:
1594
+ 11px Consolas,
1595
+ monospace;
1596
+ }
1597
+ .table-field {
1598
+ cursor: pointer;
1599
+ outline: none;
1600
+ }
1601
+ .table-field rect {
1602
+ fill: #fafcf6;
1603
+ }
1604
+ .table-field rect.alternate {
1605
+ fill: #edf4e1;
1606
+ }
1607
+ .table-field:hover rect,
1608
+ .table-field:focus-visible rect {
1609
+ fill: #e1edcf;
1610
+ }
1611
+ .table-field.selected rect {
1612
+ fill: #dae9c4;
1613
+ stroke: #8daa71;
1614
+ stroke-width: 1;
1615
+ }
1616
+ .table-field text {
1617
+ pointer-events: none;
1618
+ }
1619
+ .field-code {
1620
+ fill: #627e4e;
1621
+ font:
1622
+ 12px Consolas,
1623
+ monospace;
1624
+ }
1625
+ .field-name {
1626
+ fill: #6f875a;
1627
+ font-size: 12px;
1628
+ font-family: 'Microsoft YaHei UI', sans-serif;
1629
+ }
1630
+ .field-type {
1631
+ fill: #8fa47d;
1632
+ font:
1633
+ 10px 'Microsoft YaHei UI',
1634
+ sans-serif;
1635
+ }
1636
+ .field-ref {
1637
+ fill: #8ca371;
1638
+ font-size: 15px;
1639
+ }
1640
+ .field-dot {
1641
+ fill: #bed0a9;
1642
+ font-size: 18px;
1643
+ }
1644
+ .field-required {
1645
+ fill: #b3995d;
1646
+ font-size: 12px;
1647
+ }
1648
+ .table-footer {
1649
+ fill: #8da37a;
1650
+ font:
1651
+ 10px 'Microsoft YaHei UI',
1652
+ sans-serif;
1653
+ }
1654
+ .table-sql {
1655
+ fill: #8da37a;
1656
+ font:
1657
+ 10px Consolas,
1658
+ monospace;
1659
+ }
1660
+ .table-divider {
1661
+ stroke: #dfe8d5;
1662
+ stroke-width: 1;
1663
+ }
1664
+ .aggregate-explainer {
1665
+ padding: 12px;
1666
+ margin: 0 0 16px;
1667
+ background: #f2f6ed;
1668
+ border-left: 2px solid #98b37f;
1669
+ border-radius: 0 5px 5px 0;
1670
+ }
1671
+ .aggregate-explainer strong {
1672
+ font-size: 11px;
1673
+ font-weight: 500;
1674
+ color: #5b7649;
1675
+ }
1676
+ .aggregate-explainer p {
1677
+ font-size: 11px;
1678
+ color: #7e8f70;
1679
+ line-height: 1.85;
1680
+ margin: 7px 0 0;
1681
+ }
1682
+ @media (max-width: 1100px) {
1683
+ .graph-toolbar {
1684
+ flex-wrap: wrap;
1685
+ gap: 10px;
1686
+ }
1687
+ .graph-toolbar .search-wrap {
1688
+ flex: 1;
1689
+ min-width: 160px;
1690
+ }
1691
+ }
1692
+ /* 窄屏详情位于画布下方,随页面自然滚动,避免解释被困在一个短小的嵌套滚动区。 */
1693
+ @media (max-width: 980px) {
1694
+ .inspector:has(.relation-route) {
1695
+ max-height: none;
1696
+ overflow: visible;
1697
+ }
1698
+ }