@shortlink-org/portolan 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 (826) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +387 -0
  3. package/catalog/enum_test.go +46 -0
  4. package/catalog/model.go +932 -0
  5. package/catalog/roundtrip_test.go +185 -0
  6. package/catalog/via_test.go +38 -0
  7. package/cli/portolan.mjs +368 -0
  8. package/go.mod +14 -0
  9. package/go.sum +14 -0
  10. package/index.html +13 -0
  11. package/internal/commands/cargo.go +154 -0
  12. package/internal/commands/commands.go +143 -0
  13. package/internal/commands/commands_test.go +323 -0
  14. package/internal/commands/gradle.go +77 -0
  15. package/internal/commands/justfile.go +82 -0
  16. package/internal/commands/makefile.go +106 -0
  17. package/internal/commands/maven.go +93 -0
  18. package/internal/commands/packagejson.go +159 -0
  19. package/internal/commands/pyproject.go +169 -0
  20. package/internal/commands/taskfile.go +101 -0
  21. package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
  22. package/internal/commands/testdata/estate/Makefile +33 -0
  23. package/internal/commands/testdata/estate/Taskfile.yml +21 -0
  24. package/internal/commands/testdata/estate/build.gradle.kts +21 -0
  25. package/internal/commands/testdata/estate/justfile +20 -0
  26. package/internal/commands/testdata/estate/package.json +13 -0
  27. package/internal/commands/testdata/estate/pom.xml +29 -0
  28. package/internal/commands/testdata/estate/pyproject.toml +27 -0
  29. package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
  30. package/internal/commands/testdata/golden/commands.json +305 -0
  31. package/internal/gohttp/analyze.go +2500 -0
  32. package/internal/gohttp/endpoints.go +1067 -0
  33. package/internal/gohttp/roots.go +320 -0
  34. package/internal/gohttp/typed.go +143 -0
  35. package/internal/goscan/constants.go +85 -0
  36. package/internal/goscan/goscan_test.go +227 -0
  37. package/internal/goscan/names.go +52 -0
  38. package/internal/goscan/parse_test.go +11 -0
  39. package/internal/goscan/source.go +36 -0
  40. package/internal/goscan/tree.go +155 -0
  41. package/internal/goscan/types.go +99 -0
  42. package/internal/wsdl/ids.go +127 -0
  43. package/internal/wsdl/ids_test.go +21 -0
  44. package/internal/wsdl/model.go +62 -0
  45. package/internal/wsdl/parse.go +920 -0
  46. package/internal/wsdl/parse_test.go +133 -0
  47. package/package.json +117 -0
  48. package/plugin/describe.go +107 -0
  49. package/plugin/describe_test.go +114 -0
  50. package/plugin/protocol.go +117 -0
  51. package/plugin/schematest/schematest.go +126 -0
  52. package/plugins/README.md +1160 -0
  53. package/plugins/extract-adr/describe.go +19 -0
  54. package/plugins/extract-adr/describe_test.go +11 -0
  55. package/plugins/extract-adr/extract.go +153 -0
  56. package/plugins/extract-adr/extract_test.go +350 -0
  57. package/plugins/extract-adr/history.go +99 -0
  58. package/plugins/extract-adr/main.go +65 -0
  59. package/plugins/extract-adr/options.schema.json +26 -0
  60. package/plugins/extract-adr/parse.go +642 -0
  61. package/plugins/extract-adr/parse_test.go +405 -0
  62. package/plugins/extract-asyncapi/describe.go +19 -0
  63. package/plugins/extract-asyncapi/describe_test.go +11 -0
  64. package/plugins/extract-asyncapi/extract.go +315 -0
  65. package/plugins/extract-asyncapi/extract_test.go +197 -0
  66. package/plugins/extract-asyncapi/main.go +48 -0
  67. package/plugins/extract-asyncapi/options.schema.json +26 -0
  68. package/plugins/extract-asyncapi/spec.go +151 -0
  69. package/plugins/extract-celery/README.md +169 -0
  70. package/plugins/extract-celery/extract.py +346 -0
  71. package/plugins/extract-celery/extract_test.py +192 -0
  72. package/plugins/extract-celery/main.py +66 -0
  73. package/plugins/extract-celery/options.py +31 -0
  74. package/plugins/extract-celery/options.schema.json +31 -0
  75. package/plugins/extract-celery/producers.py +156 -0
  76. package/plugins/extract-celery/routes_test.py +106 -0
  77. package/plugins/extract-commands/describe.go +19 -0
  78. package/plugins/extract-commands/describe_test.go +11 -0
  79. package/plugins/extract-commands/extract.go +79 -0
  80. package/plugins/extract-commands/extract_test.go +92 -0
  81. package/plugins/extract-commands/main.go +55 -0
  82. package/plugins/extract-commands/options.schema.json +20 -0
  83. package/plugins/extract-csr/avro.go +258 -0
  84. package/plugins/extract-csr/describe.go +19 -0
  85. package/plugins/extract-csr/describe_test.go +11 -0
  86. package/plugins/extract-csr/extract.go +338 -0
  87. package/plugins/extract-csr/extract_test.go +374 -0
  88. package/plugins/extract-csr/jsonschema.go +343 -0
  89. package/plugins/extract-csr/lock.go +25 -0
  90. package/plugins/extract-csr/main.go +84 -0
  91. package/plugins/extract-csr/options.schema.json +43 -0
  92. package/plugins/extract-csr/subject.go +102 -0
  93. package/plugins/extract-django/README.md +386 -0
  94. package/plugins/extract-django/apps.py +100 -0
  95. package/plugins/extract-django/auth.py +302 -0
  96. package/plugins/extract-django/auth_test.py +188 -0
  97. package/plugins/extract-django/clients.py +121 -0
  98. package/plugins/extract-django/contracts.py +458 -0
  99. package/plugins/extract-django/database.py +119 -0
  100. package/plugins/extract-django/domain.py +318 -0
  101. package/plugins/extract-django/events.py +86 -0
  102. package/plugins/extract-django/extract.py +601 -0
  103. package/plugins/extract-django/extract_test.py +397 -0
  104. package/plugins/extract-django/flows.py +1278 -0
  105. package/plugins/extract-django/flows_test.py +338 -0
  106. package/plugins/extract-django/ids.py +42 -0
  107. package/plugins/extract-django/ids_test.py +30 -0
  108. package/plugins/extract-django/lifecycle.py +222 -0
  109. package/plugins/extract-django/lifecycle_test.py +76 -0
  110. package/plugins/extract-django/main.py +66 -0
  111. package/plugins/extract-django/openapi.py +229 -0
  112. package/plugins/extract-django/openapi_test.py +60 -0
  113. package/plugins/extract-django/operations.py +96 -0
  114. package/plugins/extract-django/options.py +61 -0
  115. package/plugins/extract-django/options.schema.json +126 -0
  116. package/plugins/extract-django/routing.py +214 -0
  117. package/plugins/extract-django/routing_test.py +198 -0
  118. package/plugins/extract-django/serializers.py +558 -0
  119. package/plugins/extract-django/store.py +434 -0
  120. package/plugins/extract-django/transport.py +242 -0
  121. package/plugins/extract-flows/describe.go +19 -0
  122. package/plugins/extract-flows/describe_test.go +11 -0
  123. package/plugins/extract-flows/extract.go +91 -0
  124. package/plugins/extract-flows/main.go +45 -0
  125. package/plugins/extract-flows/options.schema.json +16 -0
  126. package/plugins/extract-flows/parse.go +593 -0
  127. package/plugins/extract-flows/parse_test.go +204 -0
  128. package/plugins/extract-glossary/describe.go +19 -0
  129. package/plugins/extract-glossary/describe_test.go +11 -0
  130. package/plugins/extract-glossary/extract.go +115 -0
  131. package/plugins/extract-glossary/extract_test.go +220 -0
  132. package/plugins/extract-glossary/main.go +59 -0
  133. package/plugins/extract-glossary/options.schema.json +21 -0
  134. package/plugins/extract-glossary/parse.go +214 -0
  135. package/plugins/extract-glossary/parse_test.go +203 -0
  136. package/plugins/extract-go/aggregate.go +214 -0
  137. package/plugins/extract-go/client.go +409 -0
  138. package/plugins/extract-go/client_test.go +266 -0
  139. package/plugins/extract-go/describe.go +19 -0
  140. package/plugins/extract-go/describe_test.go +11 -0
  141. package/plugins/extract-go/enum.go +195 -0
  142. package/plugins/extract-go/enum_test.go +82 -0
  143. package/plugins/extract-go/event.go +99 -0
  144. package/plugins/extract-go/extract.go +191 -0
  145. package/plugins/extract-go/extract_test.go +261 -0
  146. package/plugins/extract-go/flow.go +1441 -0
  147. package/plugins/extract-go/flow_test.go +609 -0
  148. package/plugins/extract-go/httpclient.go +174 -0
  149. package/plugins/extract-go/httpclient_test.go +296 -0
  150. package/plugins/extract-go/ids.go +92 -0
  151. package/plugins/extract-go/layout.go +225 -0
  152. package/plugins/extract-go/layout_test.go +125 -0
  153. package/plugins/extract-go/lifecycle.go +324 -0
  154. package/plugins/extract-go/lifecycle_test.go +108 -0
  155. package/plugins/extract-go/main.go +79 -0
  156. package/plugins/extract-go/operation.go +157 -0
  157. package/plugins/extract-go/options.schema.json +66 -0
  158. package/plugins/extract-go/source.go +316 -0
  159. package/plugins/extract-go/transport.go +321 -0
  160. package/plugins/extract-go/transport_test.go +145 -0
  161. package/plugins/extract-go/wiring.go +434 -0
  162. package/plugins/extract-go-nats/describe.go +19 -0
  163. package/plugins/extract-go-nats/describe_test.go +11 -0
  164. package/plugins/extract-go-nats/extract.go +177 -0
  165. package/plugins/extract-go-nats/extract_test.go +389 -0
  166. package/plugins/extract-go-nats/index.go +379 -0
  167. package/plugins/extract-go-nats/main.go +42 -0
  168. package/plugins/extract-go-nats/options.schema.json +21 -0
  169. package/plugins/extract-go-nats/resolve.go +161 -0
  170. package/plugins/extract-go-nats/sites.go +224 -0
  171. package/plugins/extract-graphql/describe.go +19 -0
  172. package/plugins/extract-graphql/describe_test.go +11 -0
  173. package/plugins/extract-graphql/extract.go +433 -0
  174. package/plugins/extract-graphql/extract_test.go +256 -0
  175. package/plugins/extract-graphql/ids.go +49 -0
  176. package/plugins/extract-graphql/lex.go +237 -0
  177. package/plugins/extract-graphql/main.go +51 -0
  178. package/plugins/extract-graphql/options.schema.json +31 -0
  179. package/plugins/extract-graphql/parse.go +621 -0
  180. package/plugins/extract-graphql/parse_test.go +122 -0
  181. package/plugins/extract-http-clients/describe.go +19 -0
  182. package/plugins/extract-http-clients/describe_test.go +11 -0
  183. package/plugins/extract-http-clients/extract.go +705 -0
  184. package/plugins/extract-http-clients/extract_test.go +1263 -0
  185. package/plugins/extract-http-clients/main.go +42 -0
  186. package/plugins/extract-http-clients/options.schema.json +33 -0
  187. package/plugins/extract-java/README.md +190 -0
  188. package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
  189. package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
  190. package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
  191. package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
  192. package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
  193. package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
  194. package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
  195. package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
  196. package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
  197. package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
  198. package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
  199. package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
  200. package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
  201. package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
  202. package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
  203. package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
  204. package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
  205. package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
  206. package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
  207. package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
  208. package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
  209. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
  210. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
  211. package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
  212. package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
  213. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
  214. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
  215. package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
  216. package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
  217. package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
  218. package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
  219. package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
  220. package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
  221. package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
  222. package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
  223. package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
  224. package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
  225. package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
  226. package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
  227. package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
  228. package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
  229. package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
  230. package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
  231. package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
  232. package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
  233. package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
  234. package/plugins/extract-java/options.schema.json +70 -0
  235. package/plugins/extract-openapi/describe.go +19 -0
  236. package/plugins/extract-openapi/describe_test.go +11 -0
  237. package/plugins/extract-openapi/discover.go +243 -0
  238. package/plugins/extract-openapi/extract.go +526 -0
  239. package/plugins/extract-openapi/extract_test.go +545 -0
  240. package/plugins/extract-openapi/main.go +75 -0
  241. package/plugins/extract-openapi/options.schema.json +62 -0
  242. package/plugins/extract-openapi/spec.go +350 -0
  243. package/plugins/extract-project/README.md +22 -0
  244. package/plugins/extract-project/describe.go +19 -0
  245. package/plugins/extract-project/describe_test.go +11 -0
  246. package/plugins/extract-project/extract.go +221 -0
  247. package/plugins/extract-project/extract_test.go +109 -0
  248. package/plugins/extract-project/main.go +41 -0
  249. package/plugins/extract-project/options.schema.json +48 -0
  250. package/plugins/extract-proto/ast.go +125 -0
  251. package/plugins/extract-proto/consumes.go +77 -0
  252. package/plugins/extract-proto/describe.go +19 -0
  253. package/plugins/extract-proto/extract.go +293 -0
  254. package/plugins/extract-proto/extract_test.go +459 -0
  255. package/plugins/extract-proto/ids.go +89 -0
  256. package/plugins/extract-proto/ids_test.go +57 -0
  257. package/plugins/extract-proto/lex.go +285 -0
  258. package/plugins/extract-proto/main.go +100 -0
  259. package/plugins/extract-proto/module.go +120 -0
  260. package/plugins/extract-proto/options.schema.json +51 -0
  261. package/plugins/extract-proto/parse.go +720 -0
  262. package/plugins/extract-proto/parse_test.go +307 -0
  263. package/plugins/extract-proto/provides.go +236 -0
  264. package/plugins/extract-proto/resolve.go +222 -0
  265. package/plugins/extract-proto/resolve_test.go +119 -0
  266. package/plugins/extract-python-kafka/README.md +36 -0
  267. package/plugins/extract-python-kafka/extract.py +211 -0
  268. package/plugins/extract-python-kafka/extract_test.py +68 -0
  269. package/plugins/extract-python-kafka/main.py +59 -0
  270. package/plugins/extract-python-kafka/options.py +29 -0
  271. package/plugins/extract-python-kafka/options.schema.json +31 -0
  272. package/plugins/extract-redis/README.md +11 -0
  273. package/plugins/extract-redis/describe.go +19 -0
  274. package/plugins/extract-redis/describe_test.go +11 -0
  275. package/plugins/extract-redis/extract.go +183 -0
  276. package/plugins/extract-redis/extract_test.go +168 -0
  277. package/plugins/extract-redis/keyspaces.go +469 -0
  278. package/plugins/extract-redis/main.go +44 -0
  279. package/plugins/extract-redis/options.schema.json +30 -0
  280. package/plugins/extract-river/describe.go +19 -0
  281. package/plugins/extract-river/describe_test.go +11 -0
  282. package/plugins/extract-river/extract.go +507 -0
  283. package/plugins/extract-river/extract_test.go +132 -0
  284. package/plugins/extract-river/main.go +42 -0
  285. package/plugins/extract-river/options.schema.json +21 -0
  286. package/plugins/extract-rust/Cargo.lock +133 -0
  287. package/plugins/extract-rust/Cargo.toml +23 -0
  288. package/plugins/extract-rust/README.md +204 -0
  289. package/plugins/extract-rust/options.schema.json +65 -0
  290. package/plugins/extract-rust/rustfmt.toml +1 -0
  291. package/plugins/extract-sql/ddl.go +893 -0
  292. package/plugins/extract-sql/ddl_test.go +401 -0
  293. package/plugins/extract-sql/describe.go +19 -0
  294. package/plugins/extract-sql/describe_test.go +11 -0
  295. package/plugins/extract-sql/layout.go +221 -0
  296. package/plugins/extract-sql/layout_test.go +55 -0
  297. package/plugins/extract-sql/lineage.go +107 -0
  298. package/plugins/extract-sql/main.go +141 -0
  299. package/plugins/extract-sql/maps.go +564 -0
  300. package/plugins/extract-sql/maps_java.go +117 -0
  301. package/plugins/extract-sql/maps_rust.go +333 -0
  302. package/plugins/extract-sql/maps_rust_test.go +70 -0
  303. package/plugins/extract-sql/maps_test.go +204 -0
  304. package/plugins/extract-sql/maps_ts.go +398 -0
  305. package/plugins/extract-sql/maps_ts_test.go +136 -0
  306. package/plugins/extract-sql/options.schema.json +53 -0
  307. package/plugins/extract-sql/projection.go +70 -0
  308. package/plugins/extract-sql/projection_test.go +50 -0
  309. package/plugins/extract-sql/store.go +420 -0
  310. package/plugins/extract-sql/store_test.go +233 -0
  311. package/plugins/extract-sql/view.go +295 -0
  312. package/plugins/extract-ts/README.md +306 -0
  313. package/plugins/extract-ts/ast.test.ts +178 -0
  314. package/plugins/extract-ts/ast.ts +648 -0
  315. package/plugins/extract-ts/clients.ts +185 -0
  316. package/plugins/extract-ts/domain.ts +164 -0
  317. package/plugins/extract-ts/extract.test.ts +208 -0
  318. package/plugins/extract-ts/extract.ts +200 -0
  319. package/plugins/extract-ts/flows.ts +783 -0
  320. package/plugins/extract-ts/graphql.test.ts +73 -0
  321. package/plugins/extract-ts/graphql.ts +181 -0
  322. package/plugins/extract-ts/ids.test.ts +21 -0
  323. package/plugins/extract-ts/ids.ts +52 -0
  324. package/plugins/extract-ts/lifecycle.ts +138 -0
  325. package/plugins/extract-ts/main.ts +48 -0
  326. package/plugins/extract-ts/openapi.test.ts +64 -0
  327. package/plugins/extract-ts/openapi.ts +99 -0
  328. package/plugins/extract-ts/operations.ts +93 -0
  329. package/plugins/extract-ts/options.schema.json +96 -0
  330. package/plugins/extract-ts/source.test.ts +174 -0
  331. package/plugins/extract-ts/source.ts +465 -0
  332. package/plugins/extract-ts/transport.ts +147 -0
  333. package/plugins/extract-ts/tsconfig.json +13 -0
  334. package/plugins/extract-ts/wiring.ts +190 -0
  335. package/plugins/extract-watermill/describe.go +19 -0
  336. package/plugins/extract-watermill/describe_test.go +11 -0
  337. package/plugins/extract-watermill/extract.go +1228 -0
  338. package/plugins/extract-watermill/extract_test.go +234 -0
  339. package/plugins/extract-watermill/main.go +41 -0
  340. package/plugins/extract-watermill/options.schema.json +21 -0
  341. package/plugins/extract-wsdl/describe.go +19 -0
  342. package/plugins/extract-wsdl/describe_test.go +11 -0
  343. package/plugins/extract-wsdl/extract.go +148 -0
  344. package/plugins/extract-wsdl/extract_test.go +45 -0
  345. package/plugins/extract-wsdl/main.go +53 -0
  346. package/plugins/extract-wsdl/options.schema.json +59 -0
  347. package/plugins/fetch-bsr/auth.go +114 -0
  348. package/plugins/fetch-bsr/bsr.go +240 -0
  349. package/plugins/fetch-bsr/cache.go +66 -0
  350. package/plugins/fetch-bsr/describe.go +19 -0
  351. package/plugins/fetch-bsr/fetch.go +192 -0
  352. package/plugins/fetch-bsr/fetch_test.go +465 -0
  353. package/plugins/fetch-bsr/lock.go +71 -0
  354. package/plugins/fetch-bsr/main.go +117 -0
  355. package/plugins/fetch-bsr/options.schema.json +42 -0
  356. package/plugins/fetch-csr/auth.go +95 -0
  357. package/plugins/fetch-csr/cache.go +69 -0
  358. package/plugins/fetch-csr/describe.go +19 -0
  359. package/plugins/fetch-csr/describe_test.go +11 -0
  360. package/plugins/fetch-csr/fetch.go +254 -0
  361. package/plugins/fetch-csr/fetch_test.go +494 -0
  362. package/plugins/fetch-csr/lock.go +95 -0
  363. package/plugins/fetch-csr/main.go +122 -0
  364. package/plugins/fetch-csr/options.schema.json +42 -0
  365. package/plugins/fetch-csr/registry.go +209 -0
  366. package/plugins/fetch-git/cache.go +65 -0
  367. package/plugins/fetch-git/describe.go +19 -0
  368. package/plugins/fetch-git/describe_test.go +11 -0
  369. package/plugins/fetch-git/fetch.go +181 -0
  370. package/plugins/fetch-git/fetch_test.go +332 -0
  371. package/plugins/fetch-git/git.go +169 -0
  372. package/plugins/fetch-git/lock.go +72 -0
  373. package/plugins/fetch-git/main.go +127 -0
  374. package/plugins/fetch-git/offline.go +39 -0
  375. package/plugins/fetch-git/options.schema.json +42 -0
  376. package/plugins/fetch-git/pin.go +90 -0
  377. package/plugins/fetch-git/pin_test.go +74 -0
  378. package/plugins/gen-backstage/describe.go +17 -0
  379. package/plugins/gen-backstage/main.go +22 -0
  380. package/plugins/gen-backstage/options.schema.json +30 -0
  381. package/plugins/gen-backstage/plugin.go +473 -0
  382. package/plugins/gen-backstage/plugin_test.go +145 -0
  383. package/plugins/gen-backstage.wasm +0 -0
  384. package/plugins/gen-markdown/adr.go +162 -0
  385. package/plugins/gen-markdown/aggregate.go +362 -0
  386. package/plugins/gen-markdown/canonical.go +93 -0
  387. package/plugins/gen-markdown/context.go +90 -0
  388. package/plugins/gen-markdown/coverage_test.go +89 -0
  389. package/plugins/gen-markdown/describe.go +19 -0
  390. package/plugins/gen-markdown/describe_test.go +11 -0
  391. package/plugins/gen-markdown/external.go +71 -0
  392. package/plugins/gen-markdown/flow.go +277 -0
  393. package/plugins/gen-markdown/glossary.go +76 -0
  394. package/plugins/gen-markdown/glossary_test.go +148 -0
  395. package/plugins/gen-markdown/llms.go +302 -0
  396. package/plugins/gen-markdown/main.go +22 -0
  397. package/plugins/gen-markdown/markdown.go +254 -0
  398. package/plugins/gen-markdown/markdown_test.go +100 -0
  399. package/plugins/gen-markdown/module.go +107 -0
  400. package/plugins/gen-markdown/options.schema.json +16 -0
  401. package/plugins/gen-markdown/plugin.go +39 -0
  402. package/plugins/gen-markdown/quality_test.go +176 -0
  403. package/plugins/gen-markdown/redis_test.go +38 -0
  404. package/plugins/gen-markdown/render.go +344 -0
  405. package/plugins/gen-markdown/render_test.go +186 -0
  406. package/plugins/gen-markdown/service.go +456 -0
  407. package/plugins/gen-markdown/source.go +177 -0
  408. package/plugins/gen-markdown/store.go +201 -0
  409. package/plugins/gen-markdown.wasm +0 -0
  410. package/plugins/gen-mermaid/describe.go +17 -0
  411. package/plugins/gen-mermaid/main.go +22 -0
  412. package/plugins/gen-mermaid/options.schema.json +10 -0
  413. package/plugins/gen-mermaid/plugin.go +104 -0
  414. package/plugins/gen-mermaid/plugin_test.go +33 -0
  415. package/plugins/gen-mermaid.wasm +0 -0
  416. package/plugins/openapi/ids.go +261 -0
  417. package/plugins/openapi/ids_test.go +98 -0
  418. package/plugins/pyplugin/catalog.py +209 -0
  419. package/plugins/pyplugin/celery_conf.py +436 -0
  420. package/plugins/pyplugin/celery_tasks.py +76 -0
  421. package/plugins/pyplugin/kafka.py +570 -0
  422. package/plugins/pyplugin/names.py +38 -0
  423. package/plugins/pyplugin/protocol.py +79 -0
  424. package/plugins/pyplugin/source.py +285 -0
  425. package/plugins/verify-codeowners/describe.go +19 -0
  426. package/plugins/verify-codeowners/describe_test.go +11 -0
  427. package/plugins/verify-codeowners/main.go +75 -0
  428. package/plugins/verify-codeowners/match.go +85 -0
  429. package/plugins/verify-codeowners/match_test.go +47 -0
  430. package/plugins/verify-codeowners/options.schema.json +15 -0
  431. package/plugins/verify-codeowners/owners.go +164 -0
  432. package/plugins/verify-codeowners/owners_test.go +225 -0
  433. package/plugins/verify-codeowners/parse.go +90 -0
  434. package/plugins/verify-codeowners/parse_test.go +62 -0
  435. package/plugins/verify-otel/describe.go +19 -0
  436. package/plugins/verify-otel/describe_test.go +11 -0
  437. package/plugins/verify-otel/main.go +53 -0
  438. package/plugins/verify-otel/match.go +336 -0
  439. package/plugins/verify-otel/options.schema.json +30 -0
  440. package/plugins/verify-otel/otlp.go +200 -0
  441. package/plugins/verify-otel/verify.go +734 -0
  442. package/plugins/verify-otel/verify_test.go +460 -0
  443. package/portolan.json +1073 -0
  444. package/public/favicon.svg +12 -0
  445. package/schema/portolan.schema.json +2558 -0
  446. package/scripts/README.md +53 -0
  447. package/scripts/build-report.mjs +60 -0
  448. package/scripts/build-report.test.mjs +44 -0
  449. package/scripts/builtin-plugins.mjs +49 -0
  450. package/scripts/catalog-sources.mjs +67 -0
  451. package/scripts/diff.mjs +315 -0
  452. package/scripts/diff.test.mjs +151 -0
  453. package/scripts/forge-comment.mjs +144 -0
  454. package/scripts/forge-comment.test.mjs +151 -0
  455. package/scripts/forge-release.mjs +138 -0
  456. package/scripts/forge-release.test.mjs +106 -0
  457. package/scripts/forge.mjs +61 -0
  458. package/scripts/gen-likec4.mjs +854 -0
  459. package/scripts/gen-likec4.test.mjs +258 -0
  460. package/scripts/gen.mjs +603 -0
  461. package/scripts/generated-docs.test.mjs +66 -0
  462. package/scripts/local-api.mjs +1192 -0
  463. package/scripts/local-api.test.mjs +387 -0
  464. package/scripts/manifest.mjs +214 -0
  465. package/scripts/manifest.test.mjs +148 -0
  466. package/scripts/output-path.mjs +79 -0
  467. package/scripts/output-path.test.mjs +45 -0
  468. package/scripts/package-smoke.mjs +37 -0
  469. package/scripts/plugin-host.mjs +419 -0
  470. package/scripts/plugin-host.test.mjs +109 -0
  471. package/scripts/plugin-wasm-worker.mjs +34 -0
  472. package/scripts/run-builtin.mjs +81 -0
  473. package/scripts/schema.mjs +357 -0
  474. package/scripts/site-docs.mjs +295 -0
  475. package/scripts/site-docs.test.mjs +181 -0
  476. package/scripts/vendor-lock.mjs +58 -0
  477. package/scripts/vendor-lock.test.mjs +69 -0
  478. package/src/README.md +38 -0
  479. package/src/app/App.tsx +448 -0
  480. package/src/app/BranchPicker.test.ts +46 -0
  481. package/src/app/BranchPicker.tsx +153 -0
  482. package/src/app/Breadcrumbs.test.ts +38 -0
  483. package/src/app/Breadcrumbs.tsx +223 -0
  484. package/src/app/BuildStamp.tsx +146 -0
  485. package/src/app/CommandPalette.tsx +292 -0
  486. package/src/app/HashScroll.tsx +45 -0
  487. package/src/app/Sidebar.tsx +1742 -0
  488. package/src/app/TopBar.tsx +393 -0
  489. package/src/app/density.tsx +64 -0
  490. package/src/app/forge-access.test.ts +27 -0
  491. package/src/app/forge-access.tsx +80 -0
  492. package/src/app/panels.tsx +141 -0
  493. package/src/app/pins.tsx +242 -0
  494. package/src/app/query-client.ts +24 -0
  495. package/src/app/responsive.ts +55 -0
  496. package/src/app/shortcuts.test.ts +39 -0
  497. package/src/app/shortcuts.tsx +282 -0
  498. package/src/app/theme.tsx +51 -0
  499. package/src/app/title.test.ts +14 -0
  500. package/src/app/title.ts +30 -0
  501. package/src/app/toast.tsx +59 -0
  502. package/src/app/tree-keys.test.ts +80 -0
  503. package/src/app/tree-keys.ts +87 -0
  504. package/src/app/ui-store.ts +52 -0
  505. package/src/catalog-error.test.ts +105 -0
  506. package/src/catalog-profile.test.ts +59 -0
  507. package/src/catalog-profile.ts +101 -0
  508. package/src/catalog-stores.test.ts +431 -0
  509. package/src/catalog.test.ts +1215 -0
  510. package/src/catalog.ts +3125 -0
  511. package/src/chat/Cards.tsx +408 -0
  512. package/src/chat/ChatPanel.tsx +79 -0
  513. package/src/chat/ChatSettings.tsx +219 -0
  514. package/src/chat/Composer.tsx +99 -0
  515. package/src/chat/Conversation.tsx +281 -0
  516. package/src/chat/Header.tsx +46 -0
  517. package/src/chat/Message.tsx +49 -0
  518. package/src/chat/Notice.tsx +195 -0
  519. package/src/chat/Starter.tsx +67 -0
  520. package/src/chat/Steps.tsx +45 -0
  521. package/src/chat/Waiting.tsx +87 -0
  522. package/src/chat/flags.test.ts +59 -0
  523. package/src/chat/flags.ts +155 -0
  524. package/src/chat/linkify.test.ts +30 -0
  525. package/src/chat/linkify.ts +45 -0
  526. package/src/chat/prefs.ts +56 -0
  527. package/src/chat/prompt.test.ts +43 -0
  528. package/src/chat/prompt.ts +157 -0
  529. package/src/chat/store.ts +35 -0
  530. package/src/chat/tools.ts +58 -0
  531. package/src/chat/transport.ts +70 -0
  532. package/src/components/AdrRow.tsx +29 -0
  533. package/src/components/AnchorLink.tsx +67 -0
  534. package/src/components/ApiReference.tsx +134 -0
  535. package/src/components/AsyncApiReference.tsx +120 -0
  536. package/src/components/CatalogStamp.tsx +155 -0
  537. package/src/components/ChannelRows.tsx +132 -0
  538. package/src/components/CommandRows.tsx +139 -0
  539. package/src/components/DiagramSkeleton.tsx +71 -0
  540. package/src/components/EditorLink.tsx +85 -0
  541. package/src/components/FieldTree.tsx +305 -0
  542. package/src/components/FlowTrigger.tsx +34 -0
  543. package/src/components/Ident.tsx +89 -0
  544. package/src/components/Integrations.tsx +243 -0
  545. package/src/components/LifecycleDiagram.tsx +110 -0
  546. package/src/components/MachineDocs.tsx +42 -0
  547. package/src/components/Markdown.tsx +173 -0
  548. package/src/components/MarkdownTable.tsx +206 -0
  549. package/src/components/Mermaid.tsx +211 -0
  550. package/src/components/MethodRows.tsx +309 -0
  551. package/src/components/Overlay.tsx +125 -0
  552. package/src/components/PageHeader.tsx +189 -0
  553. package/src/components/RowActions.tsx +97 -0
  554. package/src/components/SchemaDocument.tsx +89 -0
  555. package/src/components/Select.tsx +126 -0
  556. package/src/components/ShapeRows.tsx +118 -0
  557. package/src/components/SourceDoc.tsx +82 -0
  558. package/src/components/SourcePreview.tsx +441 -0
  559. package/src/components/TabRow.tsx +182 -0
  560. package/src/components/TechIcon.tsx +34 -0
  561. package/src/components/Toc.tsx +132 -0
  562. package/src/components/WhatLinksHere.tsx +273 -0
  563. package/src/components/WsdlReference.tsx +84 -0
  564. package/src/components/ddd-icons.tsx +107 -0
  565. package/src/components/kind.tsx +157 -0
  566. package/src/components/logo.tsx +61 -0
  567. package/src/components/primitives.tsx +264 -0
  568. package/src/data.test.ts +18 -0
  569. package/src/data.ts +202 -0
  570. package/src/enrich.test.ts +1150 -0
  571. package/src/enrich.ts +672 -0
  572. package/src/er/ErCanvas.tsx +533 -0
  573. package/src/er/RedisSchema.tsx +74 -0
  574. package/src/er/RelationCard.tsx +267 -0
  575. package/src/er/StoreHeader.tsx +155 -0
  576. package/src/er/TableNode.tsx +84 -0
  577. package/src/er/ViewNode.tsx +83 -0
  578. package/src/er/layout.test.ts +115 -0
  579. package/src/er/layout.ts +77 -0
  580. package/src/er/lineage.test.ts +140 -0
  581. package/src/er/lineage.ts +107 -0
  582. package/src/er/markers.tsx +79 -0
  583. package/src/er/spec.test.ts +344 -0
  584. package/src/er/spec.ts +426 -0
  585. package/src/flow/ChainList.tsx +162 -0
  586. package/src/flow/FlowTable.tsx +123 -0
  587. package/src/flow/FlowToolbar.tsx +372 -0
  588. package/src/flow/StepDetail.tsx +380 -0
  589. package/src/flow/StepRail.tsx +428 -0
  590. package/src/flow/answers.test.ts +50 -0
  591. package/src/flow/answers.ts +58 -0
  592. package/src/flow/chain.test.ts +383 -0
  593. package/src/flow/chain.ts +353 -0
  594. package/src/flow/chapters.test.ts +185 -0
  595. package/src/flow/chapters.ts +225 -0
  596. package/src/flow/continues.test.ts +102 -0
  597. package/src/flow/continues.ts +65 -0
  598. package/src/flow/cross-context.test.ts +97 -0
  599. package/src/flow/cross-context.ts +35 -0
  600. package/src/flow/mermaid.test.ts +22 -0
  601. package/src/flow/mermaid.ts +141 -0
  602. package/src/flow/outline.test.ts +212 -0
  603. package/src/flow/outline.ts +170 -0
  604. package/src/flow/paths.test.ts +129 -0
  605. package/src/flow/paths.ts +147 -0
  606. package/src/flow/prefs.test.ts +80 -0
  607. package/src/flow/prefs.ts +104 -0
  608. package/src/graph/DependencyGraph.tsx +283 -0
  609. package/src/graph/DependencyNodes.tsx +162 -0
  610. package/src/graph/EdgeLabel.tsx +61 -0
  611. package/src/graph/FocusedEventGraph.tsx +211 -0
  612. package/src/graph/GraphToolbar.tsx +187 -0
  613. package/src/graph/RoutedEdge.tsx +80 -0
  614. package/src/graph/dependency-layout.test.ts +360 -0
  615. package/src/graph/dependency-layout.ts +444 -0
  616. package/src/graph/elk.test.ts +70 -0
  617. package/src/graph/elk.ts +287 -0
  618. package/src/graph/nodes.tsx +97 -0
  619. package/src/graph/theme.ts +81 -0
  620. package/src/graph/useElkFlow.ts +125 -0
  621. package/src/index.css +1547 -0
  622. package/src/language/InLanguage.tsx +40 -0
  623. package/src/language/TermCard.tsx +132 -0
  624. package/src/language/cards.test.ts +114 -0
  625. package/src/language/cards.ts +111 -0
  626. package/src/lib/admonition.test.ts +49 -0
  627. package/src/lib/admonition.ts +48 -0
  628. package/src/lib/adr.test.ts +131 -0
  629. package/src/lib/adr.ts +68 -0
  630. package/src/lib/api.test.ts +122 -0
  631. package/src/lib/api.ts +135 -0
  632. package/src/lib/backlinks.test.ts +330 -0
  633. package/src/lib/backlinks.ts +567 -0
  634. package/src/lib/branch-compare.ts +16 -0
  635. package/src/lib/build-info.test.ts +76 -0
  636. package/src/lib/build-info.ts +80 -0
  637. package/src/lib/catalog-diff.test.ts +275 -0
  638. package/src/lib/catalog-diff.ts +655 -0
  639. package/src/lib/clipboard.ts +43 -0
  640. package/src/lib/comparison-memory.test.ts +40 -0
  641. package/src/lib/comparison-memory.ts +39 -0
  642. package/src/lib/context-color.ts +27 -0
  643. package/src/lib/context-map.test.ts +148 -0
  644. package/src/lib/context-map.ts +399 -0
  645. package/src/lib/data-model.test.ts +245 -0
  646. package/src/lib/data-model.ts +399 -0
  647. package/src/lib/data-problems.test.ts +178 -0
  648. package/src/lib/data-problems.ts +314 -0
  649. package/src/lib/derive.test.ts +251 -0
  650. package/src/lib/derive.ts +515 -0
  651. package/src/lib/editor-link.test.ts +125 -0
  652. package/src/lib/editor-link.ts +96 -0
  653. package/src/lib/editor-prefs.ts +27 -0
  654. package/src/lib/event-graph.test.ts +237 -0
  655. package/src/lib/event-graph.ts +327 -0
  656. package/src/lib/export-canvas.ts +37 -0
  657. package/src/lib/export-file.test.ts +30 -0
  658. package/src/lib/export-file.ts +20 -0
  659. package/src/lib/flow-tree.test.ts +177 -0
  660. package/src/lib/flow-tree.ts +160 -0
  661. package/src/lib/forge-refs.test.ts +50 -0
  662. package/src/lib/forge-refs.ts +60 -0
  663. package/src/lib/format.test.ts +35 -0
  664. package/src/lib/format.ts +64 -0
  665. package/src/lib/github-catalog.test.ts +301 -0
  666. package/src/lib/github-catalog.ts +610 -0
  667. package/src/lib/integrations.test.ts +119 -0
  668. package/src/lib/integrations.ts +133 -0
  669. package/src/lib/kinds.test.ts +91 -0
  670. package/src/lib/kinds.ts +204 -0
  671. package/src/lib/lifecycle.test.ts +93 -0
  672. package/src/lib/lifecycle.ts +165 -0
  673. package/src/lib/local-api.ts +194 -0
  674. package/src/lib/motion-features.ts +11 -0
  675. package/src/lib/motion.test.ts +120 -0
  676. package/src/lib/motion.tsx +269 -0
  677. package/src/lib/palette-recent.test.ts +80 -0
  678. package/src/lib/palette-recent.ts +90 -0
  679. package/src/lib/palette.test.ts +365 -0
  680. package/src/lib/palette.ts +593 -0
  681. package/src/lib/pins.test.ts +139 -0
  682. package/src/lib/pins.ts +130 -0
  683. package/src/lib/problems.test.ts +112 -0
  684. package/src/lib/project-preview.ts +5 -0
  685. package/src/lib/proto-problems.test.ts +121 -0
  686. package/src/lib/proto-problems.ts +65 -0
  687. package/src/lib/queries.test.ts +113 -0
  688. package/src/lib/queries.ts +131 -0
  689. package/src/lib/registry.test.ts +219 -0
  690. package/src/lib/registry.ts +208 -0
  691. package/src/lib/scenarios.ts +547 -0
  692. package/src/lib/setup-info.test.ts +199 -0
  693. package/src/lib/setup-info.ts +415 -0
  694. package/src/lib/shape.test.ts +289 -0
  695. package/src/lib/shape.ts +454 -0
  696. package/src/lib/sidebar-prefs.test.ts +32 -0
  697. package/src/lib/sidebar-prefs.ts +75 -0
  698. package/src/lib/source-code.test.ts +159 -0
  699. package/src/lib/source-code.ts +286 -0
  700. package/src/lib/source-doc.test.ts +131 -0
  701. package/src/lib/source-doc.ts +103 -0
  702. package/src/lib/source-highlight.test.ts +29 -0
  703. package/src/lib/source-highlight.ts +24 -0
  704. package/src/lib/source-link.test.ts +153 -0
  705. package/src/lib/source-link.ts +201 -0
  706. package/src/lib/spec-files.test.ts +24 -0
  707. package/src/lib/spec-files.ts +23 -0
  708. package/src/lib/table-directive.test.ts +139 -0
  709. package/src/lib/table-directive.ts +106 -0
  710. package/src/lib/tech.test.ts +120 -0
  711. package/src/lib/tech.ts +421 -0
  712. package/src/lib/terms.test.ts +67 -0
  713. package/src/lib/terms.ts +179 -0
  714. package/src/lib/wire-problems.test.ts +263 -0
  715. package/src/lib/wire-problems.ts +281 -0
  716. package/src/likec4/C4View.tsx +55 -0
  717. package/src/likec4/CanvasBridge.tsx +187 -0
  718. package/src/likec4/FlowView.tsx +235 -0
  719. package/src/likec4/InteractiveView.tsx +144 -0
  720. package/src/likec4/canvas-viewport.test.ts +149 -0
  721. package/src/likec4/canvas-viewport.ts +135 -0
  722. package/src/likec4/flow-edges.test.ts +51 -0
  723. package/src/likec4/flow-edges.ts +89 -0
  724. package/src/likec4/frame-theme.test.ts +76 -0
  725. package/src/likec4/frame-theme.ts +101 -0
  726. package/src/likec4/highlight-css.test.ts +49 -0
  727. package/src/likec4/highlight-css.ts +52 -0
  728. package/src/likec4/ids.test.ts +151 -0
  729. package/src/likec4/ids.ts +115 -0
  730. package/src/likec4/levels.tsx +70 -0
  731. package/src/likec4/mapping.test.ts +66 -0
  732. package/src/likec4/mapping.ts +54 -0
  733. package/src/likec4/reserved.json +141 -0
  734. package/src/likec4/view-index.ts +78 -0
  735. package/src/main.tsx +13 -0
  736. package/src/map/ContextMapGraph.tsx +274 -0
  737. package/src/map/ContextNode.tsx +70 -0
  738. package/src/map/FloatingEdge.tsx +86 -0
  739. package/src/merge.test.ts +1038 -0
  740. package/src/merge.ts +1004 -0
  741. package/src/pages/AdrDetail.tsx +242 -0
  742. package/src/pages/AdrIndex.tsx +114 -0
  743. package/src/pages/AggregatePage.tsx +727 -0
  744. package/src/pages/BlockPage.tsx +478 -0
  745. package/src/pages/CatalogFailure.tsx +111 -0
  746. package/src/pages/Changes.tsx +313 -0
  747. package/src/pages/ContextMap.tsx +534 -0
  748. package/src/pages/ContextPage.tsx +346 -0
  749. package/src/pages/EnumPage.tsx +282 -0
  750. package/src/pages/EventPage.tsx +686 -0
  751. package/src/pages/ExternalPage.tsx +204 -0
  752. package/src/pages/FlowDetail.tsx +663 -0
  753. package/src/pages/FlowIndex.tsx +303 -0
  754. package/src/pages/GraphPage.tsx +217 -0
  755. package/src/pages/Language.tsx +257 -0
  756. package/src/pages/ModulePage.tsx +366 -0
  757. package/src/pages/NotFound.tsx +54 -0
  758. package/src/pages/Overview.tsx +344 -0
  759. package/src/pages/Problems.tsx +360 -0
  760. package/src/pages/RegistryIndex.tsx +118 -0
  761. package/src/pages/ServicePage.tsx +746 -0
  762. package/src/pages/Settings.tsx +975 -0
  763. package/src/pages/StorePage.tsx +114 -0
  764. package/src/peek/PeekCard.test.ts +35 -0
  765. package/src/peek/PeekCard.tsx +118 -0
  766. package/src/peek/PeekLayer.tsx +147 -0
  767. package/src/peek/model.test.ts +156 -0
  768. package/src/peek/model.ts +253 -0
  769. package/src/routes.test.ts +222 -0
  770. package/src/routes.ts +449 -0
  771. package/src/selection/DetailPanel.tsx +1407 -0
  772. package/src/selection/hash.test.ts +73 -0
  773. package/src/selection/hash.ts +58 -0
  774. package/src/selection/model.test.ts +199 -0
  775. package/src/selection/model.ts +473 -0
  776. package/src/selection/pages.test.ts +115 -0
  777. package/src/selection/pages.ts +268 -0
  778. package/src/selection/store.test.ts +62 -0
  779. package/src/selection/store.ts +46 -0
  780. package/src/selection/sync.tsx +99 -0
  781. package/src/table/ColumnsMenu.tsx +72 -0
  782. package/src/table/DataTable.tsx +780 -0
  783. package/src/table/Facets.tsx +54 -0
  784. package/src/table/GroupMenu.tsx +69 -0
  785. package/src/table/Toolbar.tsx +190 -0
  786. package/src/table/cells.tsx +117 -0
  787. package/src/table/compare.test.ts +117 -0
  788. package/src/table/compare.ts +196 -0
  789. package/src/table/export.test.ts +88 -0
  790. package/src/table/export.ts +66 -0
  791. package/src/table/facet-groups.test.ts +74 -0
  792. package/src/table/facet-groups.ts +56 -0
  793. package/src/table/grouping.test.ts +84 -0
  794. package/src/table/grouping.ts +82 -0
  795. package/src/table/infer.test.ts +57 -0
  796. package/src/table/infer.ts +50 -0
  797. package/src/table/persist.test.ts +76 -0
  798. package/src/table/persist.ts +112 -0
  799. package/src/table/sort-url.test.ts +75 -0
  800. package/src/table/sort-url.ts +52 -0
  801. package/src/table/types.ts +80 -0
  802. package/src/table/useTableState.ts +220 -0
  803. package/src/test-catalog.ts +7 -0
  804. package/src/testing/estate/data__catalog.json +2847 -0
  805. package/src/testing/estate/data__flows.json +1724 -0
  806. package/src/testing/estate/examples__auth__portolan__api.json +212 -0
  807. package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
  808. package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
  809. package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
  810. package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
  811. package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
  812. package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
  813. package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
  814. package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
  815. package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
  816. package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
  817. package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
  818. package/src/testing/estate.ts +49 -0
  819. package/src/trail/Trail.tsx +81 -0
  820. package/src/trail/model.test.ts +157 -0
  821. package/src/trail/model.ts +189 -0
  822. package/src/trail/record.tsx +32 -0
  823. package/src/trail/store.test.ts +66 -0
  824. package/src/trail/store.ts +102 -0
  825. package/tsconfig.json +35 -0
  826. package/vite.config.ts +171 -0
package/src/index.css ADDED
@@ -0,0 +1,1547 @@
1
+ @import "tailwindcss";
2
+
3
+ /* Theme is driven by a class on <html> so the toggle is instant and SSR-free. */
4
+ @custom-variant dark (&:where(.dark, .dark *));
5
+
6
+ /* ---------------------------------------------------------------------------
7
+ Tokens. Defined once, here. Nothing else in the app hard-codes a colour,
8
+ a radius, a duration or a step of the spacing rhythm.
9
+
10
+ The rhythm is 4px. Every literal below is a multiple of it: page gutter 24,
11
+ card padding 20, section gap 32, grid gap 20, card gap 16.
12
+ --------------------------------------------------------------------------- */
13
+ :root,
14
+ /* The sheet a foreign document is drawn on takes the light palette whatever
15
+ the site's theme is - see the rule further down that says why. Listed here
16
+ rather than repeating the values: one palette, declared once. */
17
+ .spec-sheet {
18
+ --bg: #ffffff;
19
+ --surface: #f7f8f9;
20
+ --surface-2: #eef0f2;
21
+ --border: #d9dde1;
22
+ --border-strong: #b9c0c7;
23
+ --fg: #14181c;
24
+ --fg-muted: #61707d;
25
+ /* A step below muted, and the last grey that is still text. Section counts
26
+ live here: a count annotates the header it sits on, and at --fg-muted it
27
+ competes with the header for the same glance. */
28
+ --fg-faint: #96a1ac;
29
+ --accent: #1f6feb;
30
+ /* The two stops of the one sanctioned accent gradient. Derived from --accent
31
+ rather than named, so a theme swap carries the gradient with it. */
32
+ --accent-lo: #1a5ec9;
33
+ --accent-hi: #3d86f5;
34
+
35
+ /* Source preview syntax. These are semantic roles rather than one imported
36
+ editor theme, so code follows Portolan's light and dark palettes. */
37
+ --syntax-comment: #6a737d;
38
+ --syntax-keyword: #7a3fa8;
39
+ --syntax-string: #0f7a6c;
40
+ --syntax-number: #a8552b;
41
+ --syntax-title: #2563a8;
42
+ --syntax-attribute: #8c4a6b;
43
+
44
+ --status-verified: #157f4a;
45
+ --status-declared: #8a6d1f;
46
+ --status-unresolved: #c02a29;
47
+
48
+ /* Domain events are the loudest leaf in the tree. Amber, but a step darker
49
+ than amber-600 so 12px mono still clears contrast on white. */
50
+ --kind-event: #b45309;
51
+
52
+ /* GitHub paints `[!IMPORTANT]` purple. Purple already exists in the context
53
+ ramp, where it means "this context"; the alert gets a name of its own so
54
+ the two can never drift into each other. The other four alerts reuse the
55
+ status ramp, which already carries their meaning. */
56
+ --alert-important: #7a3fa8;
57
+
58
+ --ctx-0: #2563a8;
59
+ --ctx-1: #7a3fa8;
60
+ --ctx-2: #0f7a6c;
61
+ --ctx-3: #a8552b;
62
+ --ctx-4: #4a5b8c;
63
+ --ctx-5: #8c4a6b;
64
+
65
+ /* Scrollbar thumbs are their own pair: they must read on --surface, not on
66
+ --bg, or the thin ones vanish inside a raised pane. */
67
+ --scroll-thumb: #cbd2d8;
68
+ --scroll-thumb-hover: #aeb7bf;
69
+
70
+ /* One shadow scale. xs sits under cards at rest, md under overlays only. */
71
+ --shadow-xs: 0 1px 2px rgb(16 24 32 / 0.06), 0 0 0 1px rgb(16 24 32 / 0.03);
72
+ --shadow-md:
73
+ 0 8px 24px -6px rgb(16 24 32 / 0.18), 0 2px 6px -2px rgb(16 24 32 / 0.08);
74
+
75
+ /* A card on a diagram canvas. Three passes, the way Stripe draws a card: a
76
+ hairline ring that stands in for a border, a soft ambient pass, and a
77
+ crisp one-pixel contact line. The border is gone because a canvas of
78
+ forty outlined boxes is a wireframe; the ring reads as an edge without
79
+ being one. Lifted on hover, and the ring is what selection paints. */
80
+ --shadow-card:
81
+ 0 0 0 1px rgb(60 66 87 / 0.08), 0 2px 5px rgb(60 66 87 / 0.08),
82
+ 0 1px 1px rgb(0 0 0 / 0.06);
83
+ --shadow-card-hover:
84
+ 0 0 0 1px rgb(60 66 87 / 0.1), 0 3px 9px rgb(60 66 87 / 0.12),
85
+ 0 2px 2px rgb(0 0 0 / 0.06);
86
+ --ring:
87
+ 0 0 0 1px var(--accent),
88
+ 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
89
+ --radius-node: 8px;
90
+
91
+ /* The canvas is a shade cooler than the page and the cards on it are the
92
+ page's white, so a card is lighter than what it sits on - that contrast,
93
+ not a border, is what makes it a card. The dots are the faintest grey
94
+ that still registers as a grid. */
95
+ --flow-pane: #f6f9fc;
96
+ --flow-card: #ffffff;
97
+ --flow-dot: #d9dfe6;
98
+
99
+ --font-sans:
100
+ ui-sans-serif, -apple-system, "Segoe UI", Roboto, Helvetica, Arial,
101
+ sans-serif;
102
+ --font-mono:
103
+ ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
104
+
105
+ /* Space. Named so a page cannot invent a seventh value for a gutter. */
106
+ --gutter: 24px;
107
+ --pad-card: 20px;
108
+ --gap-section: 32px;
109
+ --gap-grid: 20px;
110
+ --gap-card: 16px;
111
+ --w-prose: 900px;
112
+ --w-table: 1200px;
113
+
114
+ /* Density. Two settings, four tokens, set on <html data-density>. The
115
+ comfortable row is 40px because that is a row you read; the compact one is
116
+ 30px because that is a row you scan. Nothing else changes: no font swap,
117
+ no border loss, no second layout. The header sits a step below its rows:
118
+ it is a label for the column, not another row of it. */
119
+ --row-h: 40px;
120
+ --row-h-head: 36px;
121
+ --row-h-tree: 32px;
122
+ --text-meta: 13px;
123
+
124
+ /* Motion. Three durations, two easings, nothing else. */
125
+ --dur-micro: 150ms;
126
+ --dur-panel: 250ms;
127
+ --dur-page: 250ms;
128
+ --dur-narrative: 400ms;
129
+ --ease-out: cubic-bezier(0.2, 0, 0, 1);
130
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
131
+ }
132
+
133
+ [data-density="compact"] {
134
+ --row-h: 30px;
135
+ --row-h-head: 28px;
136
+ --row-h-tree: 26px;
137
+ --text-meta: 12px;
138
+ }
139
+
140
+ .dark {
141
+ --bg: #0c0e11;
142
+ --surface: #14181d;
143
+ --surface-2: #1b2027;
144
+ --border: #262c34;
145
+ --border-strong: #3a424c;
146
+ --fg: #e3e8ee;
147
+ --fg-muted: #8b97a4;
148
+ --fg-faint: #626e7b;
149
+ --accent: #5b9dff;
150
+ --accent-lo: #3d7fe0;
151
+ --accent-hi: #7ab2ff;
152
+
153
+ --syntax-comment: #8b97a4;
154
+ --syntax-keyword: #c6a0f6;
155
+ --syntax-string: #72d5c4;
156
+ --syntax-number: #efa66a;
157
+ --syntax-title: #82b4f4;
158
+ --syntax-attribute: #e19abb;
159
+
160
+ --status-verified: #3fbf7f;
161
+ --status-declared: #d6a844;
162
+ --status-unresolved: #ff6b63;
163
+
164
+ --kind-event: #e8a33d;
165
+
166
+ --alert-important: #b98cf0;
167
+
168
+ --ctx-0: #6aa5f0;
169
+ --ctx-1: #b98cf0;
170
+ --ctx-2: #46bfae;
171
+ --ctx-3: #e0925f;
172
+ --ctx-4: #8e9fd8;
173
+ --ctx-5: #d98bab;
174
+
175
+ --scroll-thumb: #333b45;
176
+ --scroll-thumb-hover: #47515e;
177
+
178
+ /* Dark surfaces get no lift from a soft grey shadow, so the ambient pass is
179
+ near-black and the hairline that reads as elevation is carried by the ring. */
180
+ --shadow-xs: 0 1px 2px rgb(0 0 0 / 0.4), 0 0 0 1px rgb(255 255 255 / 0.04);
181
+ --shadow-md:
182
+ 0 12px 32px -8px rgb(0 0 0 / 0.7), 0 2px 8px -2px rgb(0 0 0 / 0.5);
183
+
184
+ /* Same three passes, but the ring goes light and does the lifting: a dark
185
+ card on a dark canvas has no shadow to cast, only an edge to catch. */
186
+ --shadow-card:
187
+ 0 0 0 1px rgb(255 255 255 / 0.07), 0 2px 5px rgb(0 0 0 / 0.4),
188
+ 0 1px 1px rgb(0 0 0 / 0.5);
189
+ --shadow-card-hover:
190
+ 0 0 0 1px rgb(255 255 255 / 0.12), 0 3px 9px rgb(0 0 0 / 0.5),
191
+ 0 2px 2px rgb(0 0 0 / 0.5);
192
+
193
+ /* Inverted: the canvas is the page's black and the cards are a step up. */
194
+ --flow-pane: #0c0e11;
195
+ --flow-card: #161b21;
196
+ --flow-dot: #262c34;
197
+ }
198
+
199
+ @theme inline {
200
+ /* Roles, not raw names: `bg-canvas`, `text-muted`, `border-line`. The raw
201
+ custom properties above stay the single source of truth, so a theme swap
202
+ needs no rebuild and SVG/canvas code can still read them directly. */
203
+ --color-canvas: var(--bg);
204
+ --color-surface: var(--surface);
205
+ --color-raised: var(--surface-2);
206
+ --color-line: var(--border);
207
+ --color-line-strong: var(--border-strong);
208
+ --color-ink: var(--fg);
209
+ --color-muted: var(--fg-muted);
210
+ --color-faint: var(--fg-faint);
211
+ --color-accent: var(--accent);
212
+ --color-verified: var(--status-verified);
213
+ --color-declared: var(--status-declared);
214
+ --color-unresolved: var(--status-unresolved);
215
+ --color-event: var(--kind-event);
216
+
217
+ --font-sans: var(--font-sans);
218
+ --font-mono: var(--font-mono);
219
+
220
+ /* 12 / 13 / 14 / 16 / 20 / 28. Prose leads at 1.5, headings at 1.2. */
221
+ --text-xs: 12px;
222
+ --text-xs--line-height: 1.5;
223
+ --text-sm: 13px;
224
+ --text-sm--line-height: 1.5;
225
+ --text-base: 14px;
226
+ --text-base--line-height: 1.5;
227
+ --text-md: 16px;
228
+ --text-md--line-height: 1.5;
229
+ --text-lg: 20px;
230
+ --text-lg--line-height: 1.2;
231
+ --text-lg--letter-spacing: -0.01em;
232
+ --text-xl: 28px;
233
+ --text-xl--line-height: 1.2;
234
+ --text-xl--letter-spacing: -0.01em;
235
+
236
+ /* 6 controls, 10 cards, 14 modals and the palette. */
237
+ --radius-control: 6px;
238
+ --radius-card: 10px;
239
+ --radius-modal: 14px;
240
+ --radius-node: var(--radius-node);
241
+
242
+ --shadow-xs: var(--shadow-xs);
243
+ --shadow-md: var(--shadow-md);
244
+ --shadow-card: var(--shadow-card);
245
+
246
+ --spacing-gutter: var(--gutter);
247
+ --spacing-card: var(--pad-card);
248
+ --spacing-section: var(--gap-section);
249
+ --spacing-grid: var(--gap-grid);
250
+
251
+ --container-prose: var(--w-prose);
252
+ --container-table: var(--w-table);
253
+ }
254
+
255
+ /* ---------------------------------------------------------------------------
256
+ Utilities
257
+ --------------------------------------------------------------------------- */
258
+
259
+ /* Identifiers are always monospace; prose never is. Counts, versions and
260
+ coverage all pass through here, so they are tabular by construction. */
261
+ @utility mono {
262
+ font-family: var(--font-mono);
263
+ font-size: var(--text-xs);
264
+ letter-spacing: -0.01em;
265
+ font-variant-numeric: tabular-nums;
266
+ }
267
+
268
+ /* For the digits that are *not* monospace - card stats, tab counts. */
269
+ @utility tnum {
270
+ font-variant-numeric: tabular-nums;
271
+ }
272
+
273
+ /* Paints from a --ctx custom property set on the element, so one class covers
274
+ every context colour without generating a utility per context. */
275
+ @utility ctx {
276
+ color: var(--ctx);
277
+ border-color: var(--ctx);
278
+ }
279
+
280
+ /* Sanctioned gradient 1 - the page hero strip. A 120px wash of the entity's
281
+ own context colour behind the header; the content below it stays on solid
282
+ background, so nothing is ever read off a gradient. */
283
+ @utility hero {
284
+ position: relative;
285
+ isolation: isolate;
286
+ }
287
+ @utility hero-wash {
288
+ position: absolute;
289
+ inset: 0 0 auto 0;
290
+ height: 120px;
291
+ z-index: -1;
292
+ pointer-events: none;
293
+ background: linear-gradient(
294
+ to bottom,
295
+ color-mix(in srgb, var(--ctx, var(--accent)) 6%, transparent),
296
+ transparent
297
+ );
298
+ }
299
+
300
+ /* Sanctioned gradient 3 - a soft accent glow behind an empty-state
301
+ illustration or the palette input. 4%, centred, never on a border. */
302
+ @utility glow {
303
+ background-image: radial-gradient(
304
+ 60% 120% at 50% 0%,
305
+ color-mix(in srgb, var(--accent) 4%, transparent),
306
+ transparent 70%
307
+ );
308
+ }
309
+
310
+ /* Custom scrollbars on inner panes only: the shell itself never scrolls. */
311
+ @utility pane {
312
+ scrollbar-width: thin;
313
+ scrollbar-color: var(--scroll-thumb) transparent;
314
+ }
315
+
316
+ /* Names that can overflow truncate, and carry the full string in `title`. */
317
+ @utility trunc {
318
+ overflow: hidden;
319
+ text-overflow: ellipsis;
320
+ white-space: nowrap;
321
+ min-width: 0;
322
+ }
323
+
324
+ /* The same, cut from the other end. A dotted id reads right to left: the head
325
+ is the context, which every rail already says in colour, and the tail is the
326
+ only part that tells two participants apart. `shop.oms` and `shop.pricing`
327
+ truncated from the right are both `sho…`; from the left they are `…oms` and
328
+ `…pricing`, which is the whole question the line was asked.
329
+ The box is rtl so the ellipsis lands at the start; the text inside must be
330
+ wrapped in <bdi> so it is isolated and still reads left to right. */
331
+ @utility trunc-tail {
332
+ overflow: hidden;
333
+ text-overflow: ellipsis;
334
+ white-space: nowrap;
335
+ min-width: 0;
336
+ direction: rtl;
337
+ }
338
+
339
+ @layer components {
340
+ .source-code .hljs-comment,
341
+ .source-code .hljs-quote {
342
+ color: var(--syntax-comment);
343
+ font-style: italic;
344
+ }
345
+ .source-code .hljs-keyword,
346
+ .source-code .hljs-selector-tag,
347
+ .source-code .hljs-meta,
348
+ .source-code .hljs-doctag {
349
+ color: var(--syntax-keyword);
350
+ }
351
+ .source-code .hljs-string,
352
+ .source-code .hljs-regexp,
353
+ .source-code .hljs-template-tag,
354
+ .source-code .hljs-template-variable,
355
+ .source-code .hljs-addition {
356
+ color: var(--syntax-string);
357
+ }
358
+ .source-code .hljs-number,
359
+ .source-code .hljs-literal,
360
+ .source-code .hljs-symbol,
361
+ .source-code .hljs-bullet,
362
+ .source-code .hljs-deletion {
363
+ color: var(--syntax-number);
364
+ }
365
+ .source-code .hljs-title,
366
+ .source-code .hljs-section,
367
+ .source-code .hljs-built_in,
368
+ .source-code .hljs-type {
369
+ color: var(--syntax-title);
370
+ }
371
+ .source-code .hljs-attr,
372
+ .source-code .hljs-attribute,
373
+ .source-code .hljs-variable,
374
+ .source-code .hljs-params,
375
+ .source-code .hljs-property {
376
+ color: var(--syntax-attribute);
377
+ }
378
+ .source-code .hljs-emphasis {
379
+ font-style: italic;
380
+ }
381
+ .source-code .hljs-strong {
382
+ font-weight: 600;
383
+ }
384
+
385
+ /* Floating UI publishes the whole available viewport as an inline maximum.
386
+ A branch list may contain a hundred refs; cap it to a readable picker and
387
+ let the list scroll instead of turning the picker into a page-sized veil. */
388
+ .branch-options {
389
+ max-height: min(28rem, calc(100vh - 64px)) !important;
390
+ }
391
+
392
+ /* The bordered monospace token used for status, contexts and classes. */
393
+ .chip {
394
+ @apply mono inline-flex items-center gap-1 rounded-control border px-1.5 py-px leading-4;
395
+ }
396
+ .chip-lg {
397
+ @apply mono inline-flex items-center gap-1.5 rounded-control border px-2 py-0.5 leading-4;
398
+ }
399
+ /* Square marker that inherits the chip's colour. */
400
+ .dot {
401
+ @apply inline-block size-1.5 shrink-0 rounded-[1px] bg-current;
402
+ }
403
+
404
+ .status-verified {
405
+ color: var(--status-verified);
406
+ border-color: var(--status-verified);
407
+ }
408
+ .status-declared {
409
+ color: var(--status-declared);
410
+ border-color: var(--status-declared);
411
+ }
412
+ .status-unresolved {
413
+ color: var(--status-unresolved);
414
+ border-color: var(--status-unresolved);
415
+ }
416
+
417
+ /* Section label above a block of content. */
418
+ .label {
419
+ @apply mono uppercase text-muted;
420
+ }
421
+
422
+ /* The heading of a page section, and whatever the section puts opposite it.
423
+
424
+ They were both `.label`: the same 12px muted grey, so "Consumers" and
425
+ "published by auth.auth" carried the same weight and the eye had no
426
+ reason to read one before the other. The title keeps the mono eyebrow -
427
+ it is this app's signature - but takes a step up into ink, and the aside
428
+ takes a step down into faint, which is what the token was defined for.
429
+
430
+ The aside is sans, because most of them are sentences rather than counts:
431
+ "identity matters - these are tracked over time" is prose, and prose set
432
+ in 12px monospace reads as a log line. Digits are tabular anyway, so a
433
+ count loses nothing; an identifier in there still asks for `.mono`. */
434
+ .section-title {
435
+ @apply mono uppercase text-ink;
436
+ font-size: var(--text-sm);
437
+ font-weight: 500;
438
+ }
439
+ .section-aside {
440
+ @apply text-faint;
441
+ font-size: var(--text-meta);
442
+ font-variant-numeric: tabular-nums;
443
+ }
444
+
445
+ /* A bordered, hoverable block that is also a link. Padding is the card
446
+ token; the shadow is the only elevation a card ever gets - a card does not
447
+ rise on hover, because md is the overlay tier and a grid of twelve cards
448
+ that all want to be overlays is a grid nobody can read. What changes on
449
+ hover is the border, from the hairline to the strong one: the card firms
450
+ up rather than lifts. */
451
+ .card {
452
+ @apply relative block rounded-card border border-line p-card shadow-xs;
453
+ transition:
454
+ background-color var(--dur-micro) var(--ease-out),
455
+ border-color var(--dur-micro) var(--ease-out);
456
+ }
457
+ .card:hover {
458
+ @apply bg-surface;
459
+ border-color: var(--border-strong);
460
+ }
461
+
462
+ /* A card that HOLDS destinations rather than being one. It must not light
463
+ up under the pointer: a surface that reacts to hover and answers no click
464
+ is the fastest way to teach a reader that hover means nothing here. */
465
+ .card-static:hover {
466
+ background: transparent;
467
+ border-color: var(--border);
468
+ }
469
+
470
+ /* The context stripe, as a class rather than three inline style longhands
471
+ on every card that wants one. It stays its context's colour while the
472
+ other three edges firm up, so hovering never costs the card its tag. */
473
+ .card-tagged {
474
+ border-left-width: 3px;
475
+ border-left-color: var(--ctx, var(--accent));
476
+ }
477
+ .card-tagged:hover {
478
+ border-left-color: var(--ctx, var(--accent));
479
+ }
480
+
481
+ /*
482
+ * A card whose title is a link, made clickable everywhere.
483
+ *
484
+ * A 300px card with one 90px link in the corner of it is a target the reader
485
+ * has to aim at, and every card in this app is a card about ONE thing. The
486
+ * title's ::after covers the whole card, so a click anywhere follows it;
487
+ * every other control inside is lifted above that sheet so the counts, the
488
+ * chips and the row actions keep their own clicks. Links cannot nest, which
489
+ * is why this is a pseudo-element and not a wrapper.
490
+ */
491
+ .card-link::after {
492
+ content: "";
493
+ position: absolute;
494
+ inset: 0;
495
+ border-radius: inherit;
496
+ }
497
+ .card :is(a, button):not(.card-link) {
498
+ position: relative;
499
+ z-index: 1;
500
+ }
501
+ .card:hover .card-link {
502
+ text-decoration: underline;
503
+ }
504
+ /* The ring belongs to the card, not to the eight characters of its name:
505
+ j and k move focus to the title, and what the reader needs to see is
506
+ which card they are standing on. */
507
+ .card:has(.card-link:focus-visible) {
508
+ outline: 2px solid color-mix(in srgb, var(--accent) 40%, transparent);
509
+ outline-offset: 2px;
510
+ }
511
+ .card .card-link:focus-visible {
512
+ outline: none;
513
+ }
514
+ .row {
515
+ @apply flex items-center gap-3 rounded-control border border-line px-3 py-2;
516
+ min-height: var(--row-h);
517
+ transition: background-color var(--dur-micro) var(--ease-out);
518
+ }
519
+ .row:hover {
520
+ @apply bg-surface;
521
+ }
522
+
523
+ /* A list whose rows line up. The list owns the columns and each row is a
524
+ subgrid of them, so the same field starts at the same x on every line and
525
+ the eye reads down a column instead of hunting along each row. Rows keep
526
+ their own border and hover, which is why this is a subgrid rather than one
527
+ flat grid with the row chrome painted over it.
528
+
529
+ The list says what the columns are - `rows grid-cols-[auto_1fr_auto]` -
530
+ and every row emits one cell per column, empty ones included: a skipped
531
+ cell shifts everything after it one column to the left. */
532
+ .rows {
533
+ @apply grid gap-1;
534
+ }
535
+ .rows > * {
536
+ @apply col-span-full grid grid-cols-subgrid;
537
+ }
538
+ /* A chip is a badge, not a cell. It keeps the width of what it says rather
539
+ than stretching to the column the alignment handed it. */
540
+ .rows > * > .chip {
541
+ @apply justify-self-start;
542
+ }
543
+
544
+ /* Toolbar and filter buttons. Border colour is the only thing that changes
545
+ on hover or press, so nothing reflows underneath. */
546
+ .tbtn {
547
+ @apply mono flex items-center gap-1.5 rounded-control border border-line px-2 py-1 text-muted;
548
+ transition:
549
+ background-color var(--dur-micro) var(--ease-out),
550
+ border-color var(--dur-micro) var(--ease-out),
551
+ color var(--dur-micro) var(--ease-out);
552
+ }
553
+ .tbtn:hover {
554
+ @apply bg-surface;
555
+ }
556
+ .tbtn-on {
557
+ border-color: var(--accent);
558
+ color: var(--accent);
559
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
560
+ }
561
+
562
+ /* Sanctioned gradient 2 - the accent surface. Reserved for a primary button;
563
+ white on --accent-lo clears 4.5:1 in both themes. */
564
+ .btn-accent {
565
+ @apply mono flex items-center gap-1.5 rounded-control px-3 py-1.5;
566
+ color: #fff;
567
+ background-image: linear-gradient(
568
+ to bottom,
569
+ var(--accent-hi),
570
+ var(--accent-lo)
571
+ );
572
+ transition: filter var(--dur-micro) var(--ease-out);
573
+ }
574
+ .btn-accent:hover {
575
+ filter: brightness(1.06);
576
+ }
577
+ .btn-accent:disabled,
578
+ .tbtn:disabled {
579
+ cursor: not-allowed;
580
+ opacity: 0.55;
581
+ }
582
+
583
+ /* A group of related controls reads as one object: a single border around
584
+ the set, hairlines only between its members. */
585
+ .seg {
586
+ @apply flex items-center overflow-hidden rounded-control border border-line;
587
+ }
588
+ .seg > * + * {
589
+ border-left: 1px solid var(--border);
590
+ }
591
+ .seg > button,
592
+ .seg > a {
593
+ @apply mono px-2 py-1 text-muted;
594
+ transition:
595
+ background-color var(--dur-micro) var(--ease-out),
596
+ color var(--dur-micro) var(--ease-out);
597
+ }
598
+ .seg > button:hover,
599
+ .seg > a:hover {
600
+ @apply bg-surface;
601
+ }
602
+ .seg > .is-on {
603
+ color: var(--accent);
604
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
605
+ }
606
+
607
+ /* Rows of switches under one border. The box belongs to the stack, the
608
+ hairlines to what is inside it: the rows are the same object seen twice,
609
+ not two objects stacked. */
610
+ .seg-stack {
611
+ @apply overflow-hidden rounded-control border border-line;
612
+ }
613
+ .seg-stack > .seg {
614
+ @apply rounded-none border-0;
615
+ }
616
+ .seg-stack > .seg + .seg {
617
+ border-top: 1px solid var(--border);
618
+ }
619
+ /* A segment floating over a canvas. It is a card, so it wears the card's
620
+ shadow in place of its border; the hairlines between members stay. */
621
+ .seg-float {
622
+ @apply shadow-card;
623
+ background: var(--flow-card);
624
+ border-color: transparent;
625
+ }
626
+
627
+ /* ---------------------------------------------------------------------
628
+ A node on a React Flow canvas. The class carries what every kind of node
629
+ shares - the card surface, the radius, the shadow and its lift - so a
630
+ component is left with only what makes it a service, an event or a
631
+ table. A ghost is not a card: it is a name someone wrote down, and it is
632
+ drawn as an outline of one.
633
+ --------------------------------------------------------------------- */
634
+ .flow-card {
635
+ background: var(--flow-card);
636
+ border-radius: var(--radius-node);
637
+ box-shadow: var(--shadow-card);
638
+ transition: box-shadow var(--dur-micro) var(--ease-out);
639
+ }
640
+ .flow-card:hover {
641
+ box-shadow: var(--shadow-card-hover);
642
+ }
643
+ .flow-card-ghost {
644
+ background: transparent;
645
+ border-radius: var(--radius-node);
646
+ border: 1px dashed currentColor;
647
+ }
648
+ /* The small tinted square an icon sits in, which is where a card keeps
649
+ the colour of the context that owns it. */
650
+ .flow-tile {
651
+ @apply flex shrink-0 items-center justify-center rounded-control;
652
+ width: 22px;
653
+ height: 22px;
654
+ background: color-mix(in srgb, currentColor 12%, transparent);
655
+ }
656
+
657
+ /* Non-navigable header for a group of leaves inside an aggregate. */
658
+ .group-row {
659
+ @apply mono flex w-full items-center gap-1.5 py-[3px] pr-2 text-left text-muted hover:bg-surface;
660
+ }
661
+
662
+ /* ---------------------------------------------------------------------
663
+ The table of contents dock. 24px of the page, whatever the section names
664
+ are - the panel inside is absolute and anchored to the dock's right edge,
665
+ so opening it costs the layout nothing and closing it costs no reflow.
666
+ --------------------------------------------------------------------- */
667
+ .toc-dock {
668
+ width: 24px;
669
+ /* Pinned just under the page's name row, which is pinned too. */
670
+ top: var(--page-header-h, 0px);
671
+ }
672
+ /* In flow, so the dock has a height to be hovered at all - what overlays is
673
+ the WIDTH. The panel grows to 216px while its left margin goes equally
674
+ negative, so its right edge never moves and the extra 192px are taken out
675
+ of the page rather than added to it. */
676
+ .toc-panel {
677
+ position: relative;
678
+ z-index: 20;
679
+ width: 24px;
680
+ overflow: hidden;
681
+ padding-bottom: 4px;
682
+ border: 1px solid transparent;
683
+ border-radius: var(--radius-card);
684
+ transition:
685
+ width var(--dur-micro) var(--ease-out),
686
+ margin-left var(--dur-micro) var(--ease-out),
687
+ background-color var(--dur-micro) var(--ease-out),
688
+ border-color var(--dur-micro) var(--ease-out),
689
+ box-shadow var(--dur-micro) var(--ease-out);
690
+ }
691
+ .toc-dock:hover .toc-panel,
692
+ .toc-panel:focus-within {
693
+ width: 216px;
694
+ margin-left: -192px;
695
+ border-color: var(--border-strong);
696
+ background: var(--bg);
697
+ box-shadow: var(--shadow-md);
698
+ }
699
+ /* The tick is the marker and the hairline at once: 2px, always drawn, so
700
+ nothing shifts as the spy moves down the page. */
701
+ .toc-item {
702
+ @apply block truncate py-0.5 pr-2 pl-2.5;
703
+ border-left-width: 2px;
704
+ border-left-style: solid;
705
+ transition:
706
+ color var(--dur-micro) var(--ease-out),
707
+ border-color var(--dur-micro) var(--ease-out);
708
+ }
709
+ .toc-item:hover {
710
+ text-decoration: underline;
711
+ }
712
+ /* Names are what you want once you have decided to leave, not while you are
713
+ reading. They are clipped rather than removed, so the panel can measure
714
+ them the moment it opens and a screen reader can always read them. */
715
+ .toc-name {
716
+ @apply block truncate opacity-0;
717
+ transition: opacity var(--dur-micro) var(--ease-out);
718
+ }
719
+ .toc-dock:hover .toc-name,
720
+ .toc-panel:focus-within .toc-name {
721
+ @apply opacity-100;
722
+ }
723
+
724
+ /* Anything that pins itself over scrolling content is translucent, not
725
+ opaque: the reader can see that there is more underneath. */
726
+ .sticky-bar {
727
+ background: color-mix(in srgb, var(--bg) 80%, transparent);
728
+ backdrop-filter: blur(8px);
729
+ -webkit-backdrop-filter: blur(8px);
730
+ }
731
+
732
+ /* The name row of an entity page stays at the top of the page while the
733
+ page scrolls under it. Transparent in place, so the hero wash shows
734
+ through as it always did; opaque once pinned, so the row is read off
735
+ solid ground and not off whatever is passing beneath. The rule under it
736
+ is a shadow rather than a border: a border would be a pixel of height
737
+ that appears on pinning, and the page would jump by it. Above a table's
738
+ sticky head (z-20) and a readme heading (z-10), below the overlays (z-40). */
739
+ .page-bar {
740
+ @apply sticky top-0 z-30;
741
+ transition: background-color var(--dur-micro) var(--ease-out);
742
+ }
743
+ .page-bar[data-stuck] {
744
+ background: var(--bg);
745
+ box-shadow: 0 1px 0 var(--border);
746
+ }
747
+ /* The row measures itself into --page-header-h on the box it scrolls in,
748
+ and that box treats the covered strip as not-visible: an anchor scrolled
749
+ to lands under the row's bottom edge rather than behind it. */
750
+ :has(> .page-bar) {
751
+ scroll-padding-top: var(--page-header-h, 0px);
752
+ }
753
+
754
+ /* A section's heading stays at the top of its section while the section
755
+ scrolls under it, just below the pinned name row - the same footing a
756
+ readme's h2 has. Sticky within the section, so it leaves when the section
757
+ does; opaque, so where two share a parent the later one simply covers the
758
+ first. The padding is what keeps the text off the row's rule when pinned,
759
+ and the negative margin gives it back, so nothing moves in place.
760
+
761
+ It pins a gutter higher than it reads, because sticky measures from the
762
+ scrollport's padding box: at `top: 0` a page with no header of its own
763
+ leaves the box's own gutter uncovered, and the rows scrolling through that
764
+ strip are the one thing a pinned heading exists to stop. Padding puts the
765
+ text back where it was, so a page that does have a header - which covers
766
+ the strip anyway, being the higher layer - is unchanged. */
767
+ .section-head {
768
+ position: sticky;
769
+ top: calc(var(--page-header-h, 0px) - var(--gutter));
770
+ z-index: 10;
771
+ padding-top: calc(var(--gutter) + 4px);
772
+ margin-top: calc(-1 * (var(--gutter) + 4px));
773
+ background: var(--bg);
774
+ /* The rule a readme's h2 has had all along, which is the footing this
775
+ heading was written to share. Unpinned it divides the page; pinned it is
776
+ the edge the section passes under, without which the content slides
777
+ against the words. A shadow rather than a border, for the reason the
778
+ page bar gives: a border is a pixel of height, and the section would
779
+ move by it. */
780
+ box-shadow: 0 1px 0 var(--border);
781
+ }
782
+
783
+ /* The flow page's one bar. Wraps rather than scrolls: a control that has
784
+ gone off the right-hand edge of a toolbar is a control nobody finds. */
785
+ .flow-toolbar {
786
+ @apply mt-2 flex flex-wrap items-center gap-2;
787
+ }
788
+
789
+ /* A chapter header on the step rail. Sticky, because a chapter is how the
790
+ reader knows where they are in a forty-step sequence, and a heading that
791
+ scrolls away stops answering that at exactly the point it is asked. */
792
+ .chapter-row {
793
+ @apply sticky top-0 z-10 flex w-full items-center gap-1.5 border-y px-2 py-1 text-left border-line;
794
+ background: color-mix(in srgb, var(--surface) 92%, transparent);
795
+ backdrop-filter: blur(8px);
796
+ -webkit-backdrop-filter: blur(8px);
797
+ }
798
+ .chapter-row:hover {
799
+ background: var(--surface-2);
800
+ }
801
+
802
+ /* The matching-steps pill rides above the rail and must survive the rail
803
+ scrolling itself to a match, or it scrolls out of sight the moment it
804
+ becomes useful. */
805
+ .pill-rail {
806
+ @apply mono sticky top-0 z-10 flex items-center gap-1.5 border-b px-2 py-1.5 border-line;
807
+ background: color-mix(in srgb, var(--bg) 80%, transparent);
808
+ backdrop-filter: blur(8px);
809
+ -webkit-backdrop-filter: blur(8px);
810
+ }
811
+
812
+ /* One line with a bit of character, never an illustration block. The dashed
813
+ border and the 4% glow are the whole of the decoration.
814
+
815
+ It is a sentence, so it is set like one: sans, ranged left, at the size of
816
+ the rows it stands in for. Centred monospace made the best lines in the
817
+ app - "nobody is listening - this event falls silent" - read as debug
818
+ output, which is the opposite of what they are for. */
819
+ .empty {
820
+ @apply glow rounded-card border border-dashed border-line-strong px-4 py-3 text-muted;
821
+ font-size: var(--text-meta);
822
+ }
823
+
824
+ /* ---------------------------------------------------------------------
825
+ Identifiers. Every id, path and type name in the app is an <Ident>, and
826
+ every <Ident> is this button: it looks like text until the pointer is on
827
+ it, then it looks like something you can take.
828
+ --------------------------------------------------------------------- */
829
+ .ident {
830
+ @apply trunc cursor-copy rounded-[4px] text-left align-baseline;
831
+ transition:
832
+ background-color var(--dur-micro) var(--ease-out),
833
+ color var(--dur-micro) var(--ease-out);
834
+ }
835
+ .ident:hover {
836
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
837
+ color: var(--accent);
838
+ }
839
+ /* Above and centred so it never covers the id it is confirming. */
840
+ .ident-tip {
841
+ @apply mono pointer-events-none absolute bottom-full left-1/2 z-30 mb-1 rounded-[4px] border px-1.5 py-px whitespace-nowrap;
842
+ transform: translateX(-50%);
843
+ border-color: var(--accent);
844
+ color: var(--accent);
845
+ background: var(--bg);
846
+ box-shadow: var(--shadow-xs);
847
+ }
848
+
849
+ /* ---------------------------------------------------------------------
850
+ Tables. Density lives in --row-h; the sticky pair keeps the header and
851
+ the first column on screen while a wide table scrolls under them.
852
+ --------------------------------------------------------------------- */
853
+ .tbl {
854
+ @apply w-full;
855
+ border-collapse: separate;
856
+ border-spacing: 0;
857
+ }
858
+ .tbl td {
859
+ height: var(--row-h);
860
+ }
861
+ .tbl th {
862
+ height: var(--row-h-head);
863
+ }
864
+ /* The row rule is drawn on the cells, not on the row: `border-collapse:
865
+ separate` is what makes a sticky column possible, and a separated table
866
+ never paints a border set on <tr>. */
867
+ .tbl thead th,
868
+ .tbl tbody td {
869
+ border-bottom: 1px solid var(--border);
870
+ }
871
+ .tbl tbody tr:last-child td {
872
+ border-bottom: 0;
873
+ }
874
+ .tbl tbody tr {
875
+ transition: background-color var(--dur-micro) var(--ease-out);
876
+ }
877
+ .tbl tbody tr:hover {
878
+ background: var(--surface);
879
+ }
880
+ /* The header is translucent over its own rows, and opaque where a sticky
881
+ first column passes under it - two translucent layers stack into a smear. */
882
+ .tbl-sticky thead th {
883
+ @apply sticky top-0 z-20;
884
+ background: color-mix(in srgb, var(--bg) 80%, transparent);
885
+ backdrop-filter: blur(8px);
886
+ -webkit-backdrop-filter: blur(8px);
887
+ }
888
+ .tbl-sticky tbody td:first-child,
889
+ .tbl-sticky thead th:first-child {
890
+ @apply sticky left-0 z-10;
891
+ background: var(--bg);
892
+ }
893
+ .tbl-sticky thead th:first-child {
894
+ z-index: 30;
895
+ }
896
+ .tbl-sticky tbody tr:hover td:first-child {
897
+ background: var(--surface);
898
+ }
899
+
900
+ /* Striping is off by default and a toolbar toggle away, because a table of
901
+ six rows does not need help being read and a table of six hundred does.
902
+ The stripe is painted on the cells for the same reason the row rule is:
903
+ a separated table never paints a background set on <tr> under a sticky
904
+ column. Parity comes from the row's display position rather than from
905
+ nth-child, so a virtualized body's spacer rows cannot invert it. */
906
+ .tbl-zebra tbody tr[data-even="false"] td {
907
+ background: var(--surface-2);
908
+ }
909
+ .tbl-zebra tbody tr:hover td {
910
+ background: var(--surface);
911
+ }
912
+
913
+ /* A group's heading row, tinted apart from the leaves under it. Painted on
914
+ the cell, and after the sticky-column rule, for the same reason the
915
+ stripe is: under a pinned first column a <tr> background never shows. */
916
+ .tbl tbody tr.tbl-group td,
917
+ .tbl-sticky tbody tr.tbl-group td:first-child {
918
+ background: var(--surface-2);
919
+ }
920
+
921
+ /* A column edge you can take hold of. Four pixels wide and invisible until
922
+ the pointer is on the header, so the header is not a row of handles. */
923
+ .col-resize {
924
+ @apply absolute top-0 right-0 z-10 h-full w-1 cursor-col-resize touch-none opacity-0 select-none;
925
+ background: var(--accent);
926
+ transition: opacity var(--dur-micro) var(--ease-out);
927
+ }
928
+ th:hover > .col-resize,
929
+ .col-resize-on {
930
+ @apply opacity-100;
931
+ }
932
+
933
+ /* The header's own button. The chevron is in the DOM at every width and
934
+ shown only where it means something - the column under the pointer, or
935
+ the column the table is actually sorted by - so a header row is a row of
936
+ words rather than a row of arrows. */
937
+ .th-sort {
938
+ @apply mono flex w-full items-center gap-1 rounded-[4px] text-left uppercase text-muted;
939
+ transition: color var(--dur-micro) var(--ease-out);
940
+ }
941
+ .th-sort:hover {
942
+ color: var(--fg);
943
+ }
944
+ .th-sort > .sort-mark {
945
+ @apply shrink-0 opacity-0;
946
+ transition: opacity var(--dur-micro) var(--ease-out);
947
+ }
948
+ .th-sort:hover > .sort-mark,
949
+ .th-sort:focus-visible > .sort-mark,
950
+ .th-sort[data-sorted="true"] > .sort-mark {
951
+ @apply opacity-100;
952
+ }
953
+ .th-sort[data-sorted="true"] {
954
+ color: var(--accent);
955
+ }
956
+ /* The rank of a secondary sort key. Only ever drawn when there are two. */
957
+ .sort-rank {
958
+ @apply tnum shrink-0 text-[9px] leading-none;
959
+ }
960
+
961
+ /* Cell content is a box of its own so a resized column clips its contents
962
+ rather than refusing to narrow, and so auto-fit has something whose
963
+ natural width it can measure. */
964
+ .cell-body {
965
+ @apply block min-w-0 overflow-hidden;
966
+ }
967
+ .cell-body-nowrap {
968
+ @apply truncate;
969
+ }
970
+ /* Set while a column is being measured for auto-fit: every cell in it is
971
+ briefly allowed its natural width, and read back in the same frame. */
972
+ .cell-measure .cell-body {
973
+ width: max-content;
974
+ max-width: none;
975
+ overflow: visible;
976
+ white-space: nowrap;
977
+ }
978
+
979
+ /* A row's actions: present in the DOM, invisible until the row is under the
980
+ pointer or something inside it has focus, so a table of forty rows is not
981
+ also a table of eighty buttons. */
982
+ .row-actions {
983
+ @apply flex items-center gap-1 opacity-0;
984
+ transition: opacity var(--dur-micro) var(--ease-out);
985
+ }
986
+ tr:hover .row-actions,
987
+ .row:hover .row-actions,
988
+ .card:hover .row-actions,
989
+ .row-actions:focus-within {
990
+ @apply opacity-100;
991
+ }
992
+ .row-action {
993
+ @apply mono flex items-center gap-1 rounded-control border px-1.5 py-px border-line text-muted;
994
+ transition:
995
+ background-color var(--dur-micro) var(--ease-out),
996
+ color var(--dur-micro) var(--ease-out);
997
+ }
998
+ .row-action:hover {
999
+ @apply bg-surface text-ink;
1000
+ }
1001
+
1002
+ /* A heading you can link to. `.anchored` is a trigger and nothing else - it
1003
+ sets no layout, so it can be dropped onto a row that already has one, or
1004
+ onto a markdown <h2> that must keep its own rule underneath. The chain is
1005
+ in the DOM at rest, invisible, so keyboard focus can still reach it: a
1006
+ page with nine sections must not be a page with nine chain icons. */
1007
+ .anchor-link {
1008
+ @apply inline-flex shrink-0 items-center rounded-[4px] p-0.5 opacity-0 text-muted;
1009
+ transition:
1010
+ opacity var(--dur-micro) var(--ease-out),
1011
+ background-color var(--dur-micro) var(--ease-out),
1012
+ color var(--dur-micro) var(--ease-out);
1013
+ }
1014
+ .anchored:hover .anchor-link,
1015
+ .anchor-link:focus-visible {
1016
+ @apply opacity-100;
1017
+ }
1018
+ .anchor-link:hover {
1019
+ color: var(--accent);
1020
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
1021
+ }
1022
+
1023
+ /* The sidebar tree obeys the same density switch as the tables. */
1024
+ .tree-row {
1025
+ min-height: var(--row-h-tree);
1026
+ }
1027
+
1028
+ /* ---------------------------------------------------------------------
1029
+ The sidebar's own furniture. The tree is ONE list: sections are told
1030
+ apart by 12px of air and a label, never by a border or a card. Anything
1031
+ here that looks like a box would make the reader count boxes instead of
1032
+ reading names.
1033
+ --------------------------------------------------------------------- */
1034
+
1035
+ /* A section header. It is a button because every section folds, and it keeps
1036
+ its count when folded - a closed section that also hid its size would be
1037
+ indistinguishable from an empty one. */
1038
+ .sb-section {
1039
+ @apply mono sticky top-0 z-10 flex w-full items-center gap-1.5 px-3 py-1.5 text-left uppercase text-muted;
1040
+ background: color-mix(in srgb, var(--bg) 80%, transparent);
1041
+ backdrop-filter: blur(8px);
1042
+ -webkit-backdrop-filter: blur(8px);
1043
+ }
1044
+
1045
+ /* The count that rides at the end of a header or a group row. Faint and
1046
+ tabular: it annotates the name, it is not another word in it. */
1047
+ .sb-count {
1048
+ @apply mono tnum ml-auto shrink-0;
1049
+ color: var(--fg-faint);
1050
+ }
1051
+
1052
+ /* A context this flow reaches, in that context's own colour. Square, like
1053
+ every other context marker in the app. */
1054
+ .sb-reach {
1055
+ @apply inline-block size-1.5 shrink-0 rounded-[1px];
1056
+ background: var(--ctx);
1057
+ }
1058
+
1059
+ /* How far a flow has been made good on. Round, so it can never be read as a
1060
+ seventh context beside the square reach marks. */
1061
+ .sb-health {
1062
+ @apply inline-block size-2 shrink-0 rounded-full;
1063
+ }
1064
+
1065
+ /* How a bounded context is rated, at the end of its row. Ten pixels, no
1066
+ border, no background: at this size the badge is a footnote on the name,
1067
+ and a bordered chip here reads as a control the reader can press. */
1068
+ .sb-class {
1069
+ @apply shrink-0 lowercase;
1070
+ font-size: 10px;
1071
+ line-height: 14px;
1072
+ color: var(--fg-muted);
1073
+ }
1074
+
1075
+ /* Secondary text inside a row: 13px comfortable, 12px compact. */
1076
+ .meta {
1077
+ color: var(--fg-muted);
1078
+ font-size: var(--text-meta);
1079
+ }
1080
+
1081
+ /* ---- Field tree: a shape opened under a schema row -----------------------
1082
+
1083
+ One shape per level, each a step in from the last, with a guide line
1084
+ down its left edge so a reader four levels deep can see where each
1085
+ level began. The header names the shape; the rows under it are its
1086
+ fields, drawn like the table rows above but without the table - they
1087
+ are an aside to a row, not a table of their own. */
1088
+ .shape {
1089
+ @apply mt-1 mb-0.5 ml-[5px] border-l border-line-strong pl-3;
1090
+ }
1091
+ .shape-root {
1092
+ width: 0;
1093
+ min-width: 100%;
1094
+ }
1095
+ /* One line of the shape's doc, cut where the row ends; the whole of it is
1096
+ on the shape's own page, one click away. */
1097
+ .shape-doc {
1098
+ @apply mb-1 truncate pl-5;
1099
+ }
1100
+ .shape-head {
1101
+ @apply mono flex min-w-0 flex-wrap items-center gap-x-2 gap-y-0.5 py-0.5;
1102
+ font-size: var(--text-sm);
1103
+ }
1104
+ .field-tree {
1105
+ @apply flex flex-col;
1106
+ }
1107
+ .field-row {
1108
+ @apply flex min-w-0 items-center gap-x-3 rounded-control py-0.5 pr-2;
1109
+ font-size: var(--text-sm);
1110
+ min-height: 24px;
1111
+ }
1112
+ .field-row:hover {
1113
+ background: color-mix(in srgb, var(--accent) 5%, transparent);
1114
+ }
1115
+ .field-row[data-open] > .mono:first-of-type {
1116
+ color: var(--fg);
1117
+ }
1118
+ /* The chevron, or the room for one: rows with and without a shape to open
1119
+ keep their names on one line. */
1120
+ .field-toggle {
1121
+ @apply inline-flex size-4 shrink-0 items-center justify-center rounded-control text-muted;
1122
+ }
1123
+ button.field-toggle:hover {
1124
+ color: var(--fg);
1125
+ background: var(--surface-2);
1126
+ }
1127
+ /* A word beside a type - "list of", "optional" - in the tone of a footnote,
1128
+ not a chip: it qualifies the type, it is not a status of its own. */
1129
+ .type-mark {
1130
+ @apply mono shrink-0 rounded-[4px] px-1 leading-4;
1131
+ font-size: 11px;
1132
+ color: var(--fg-faint);
1133
+ background: var(--surface-2);
1134
+ }
1135
+ }
1136
+
1137
+ /* ---------------------------------------------------------------------------
1138
+ Motion. One system: 150 micro, 250 panel and page, 400 narrative.
1139
+ --------------------------------------------------------------------------- */
1140
+
1141
+ @keyframes panel-in {
1142
+ from {
1143
+ opacity: 0;
1144
+ transform: translateX(16px);
1145
+ }
1146
+ }
1147
+ @keyframes palette-in {
1148
+ from {
1149
+ opacity: 0;
1150
+ transform: scale(0.98);
1151
+ }
1152
+ }
1153
+ @keyframes fade-in {
1154
+ from {
1155
+ opacity: 0;
1156
+ }
1157
+ }
1158
+ @keyframes row-in {
1159
+ from {
1160
+ opacity: 0;
1161
+ transform: translateY(4px);
1162
+ }
1163
+ }
1164
+ @keyframes bar-fill {
1165
+ from {
1166
+ transform: scaleX(0);
1167
+ }
1168
+ }
1169
+ /* Anything that also LEAVES or moves - a route, the palette, the drawer, the
1170
+ sheet, the toast, the light under a tab - is not here: that is Motion, in
1171
+ lib/motion.tsx, on the same tokens. */
1172
+ @utility panel-in {
1173
+ animation: panel-in var(--dur-panel) var(--ease-out) both;
1174
+ }
1175
+ @utility palette-in {
1176
+ animation: palette-in var(--dur-panel) var(--ease-out) both;
1177
+ }
1178
+ @utility overlay-in {
1179
+ animation: fade-in var(--dur-panel) var(--ease-out) both;
1180
+ }
1181
+ /* 20ms per row, capped at ten rows by the --i the row sets. */
1182
+ @utility stagger-in {
1183
+ animation: row-in var(--dur-panel) var(--ease-out) both;
1184
+ animation-delay: calc(var(--i, 0) * 20ms);
1185
+ }
1186
+ @utility bar-fill {
1187
+ transform-origin: left center;
1188
+ animation: bar-fill var(--dur-narrative) var(--ease-in-out) both;
1189
+ }
1190
+
1191
+ /* Everything that moves, moves for 150ms unless it says otherwise. */
1192
+ @utility t-micro {
1193
+ transition-duration: var(--dur-micro);
1194
+ transition-timing-function: var(--ease-out);
1195
+ }
1196
+ /* Flow playback dims and undims the steps it is not on. */
1197
+ @utility t-narrative {
1198
+ transition:
1199
+ opacity var(--dur-narrative) var(--ease-in-out),
1200
+ background-color var(--dur-narrative) var(--ease-in-out);
1201
+ }
1202
+
1203
+ @media (prefers-reduced-motion: reduce) {
1204
+ /* Opacity may still cross-fade; nothing is allowed to travel, scale, or
1205
+ repeat. Anything mid-flight lands on its final frame immediately. */
1206
+ *,
1207
+ *::before,
1208
+ *::after {
1209
+ animation-duration: 1ms !important;
1210
+ animation-delay: 0ms !important;
1211
+ animation-iteration-count: 1 !important;
1212
+ transition-property:
1213
+ opacity, background-color, border-color, color, fill, stroke !important;
1214
+ transition-duration: 1ms !important;
1215
+ scroll-behavior: auto !important;
1216
+ }
1217
+ }
1218
+
1219
+ /* ---------------------------------------------------------------------------
1220
+ Base
1221
+ --------------------------------------------------------------------------- */
1222
+
1223
+ html,
1224
+ body,
1225
+ #root {
1226
+ height: 100%;
1227
+ overflow: hidden;
1228
+ }
1229
+
1230
+ /* Keep the app shell anchored to the viewport. A long page owns its scroll in
1231
+ its pane; its descendants must not enlarge the document scrollport. */
1232
+ #root {
1233
+ position: fixed;
1234
+ inset: 0;
1235
+ }
1236
+
1237
+ body {
1238
+ background: var(--bg);
1239
+ color: var(--fg);
1240
+ font-family: var(--font-sans);
1241
+ font-size: var(--text-base);
1242
+ line-height: 1.5;
1243
+ -webkit-font-smoothing: antialiased;
1244
+ }
1245
+
1246
+ h1,
1247
+ h2,
1248
+ h3 {
1249
+ line-height: 1.2;
1250
+ letter-spacing: -0.01em;
1251
+ }
1252
+
1253
+ ::selection {
1254
+ background: color-mix(in srgb, var(--accent) 20%, transparent);
1255
+ }
1256
+
1257
+ /* Keyboard focus is a ring, not an outline hugging the glyphs, and it is
1258
+ drawn on top of neighbouring rows rather than clipped by them. */
1259
+ :focus-visible {
1260
+ outline: 2px solid color-mix(in srgb, var(--accent) 40%, transparent);
1261
+ outline-offset: 2px;
1262
+ border-radius: var(--radius-control);
1263
+ position: relative;
1264
+ z-index: 1;
1265
+ }
1266
+
1267
+ /* Inner panes only - `html` and `body` never scroll in this shell. */
1268
+ .pane::-webkit-scrollbar,
1269
+ [class*="overflow-y-auto"]::-webkit-scrollbar,
1270
+ [class*="overflow-x-auto"]::-webkit-scrollbar,
1271
+ [class*="overflow-auto"]::-webkit-scrollbar {
1272
+ width: 8px;
1273
+ height: 8px;
1274
+ }
1275
+ .pane::-webkit-scrollbar-thumb,
1276
+ [class*="overflow-y-auto"]::-webkit-scrollbar-thumb,
1277
+ [class*="overflow-x-auto"]::-webkit-scrollbar-thumb,
1278
+ [class*="overflow-auto"]::-webkit-scrollbar-thumb {
1279
+ background: var(--scroll-thumb);
1280
+ border-radius: 999px;
1281
+ }
1282
+ .pane::-webkit-scrollbar-thumb:hover,
1283
+ [class*="overflow-y-auto"]::-webkit-scrollbar-thumb:hover,
1284
+ [class*="overflow-x-auto"]::-webkit-scrollbar-thumb:hover,
1285
+ [class*="overflow-auto"]::-webkit-scrollbar-thumb:hover {
1286
+ background: var(--scroll-thumb-hover);
1287
+ }
1288
+ .pane::-webkit-scrollbar-track,
1289
+ [class*="overflow-y-auto"]::-webkit-scrollbar-track,
1290
+ [class*="overflow-x-auto"]::-webkit-scrollbar-track,
1291
+ [class*="overflow-auto"]::-webkit-scrollbar-track {
1292
+ background: transparent;
1293
+ }
1294
+ [class*="overflow-y-auto"],
1295
+ [class*="overflow-x-auto"],
1296
+ [class*="overflow-auto"] {
1297
+ scrollbar-width: thin;
1298
+ scrollbar-color: var(--scroll-thumb) transparent;
1299
+ }
1300
+
1301
+ /* One exception, and it sits after the rule it overrides rather than inside
1302
+ the components layer, which an unlayered rule would outrank: the strip a tab
1303
+ list scrolls inside has no scrollbar at all. The fade and the chevron at its
1304
+ edge already say the row continues, and an 8px bar under eight tabs said it
1305
+ a second time in the space the tabs were short of. */
1306
+ .tab-scroll,
1307
+ .tab-scroll[class*="overflow-x-auto"] {
1308
+ scrollbar-width: none;
1309
+ }
1310
+ .tab-scroll::-webkit-scrollbar {
1311
+ height: 0;
1312
+ width: 0;
1313
+ }
1314
+
1315
+ /* ---------------------------------------------------------------------------
1316
+ A document drawn by somebody else's component - the AsyncAPI reference.
1317
+
1318
+ It brings its own stylesheet, and that stylesheet has no dark mode at all:
1319
+ not a variant this site could set, none. So the sheet it is drawn on is
1320
+ always light, and `color-scheme` says so too, or the browser paints the form
1321
+ controls inside it for the other theme.
1322
+
1323
+ The tokens are re-declared rather than merely painted over because the two
1324
+ stylesheets share a class name: `.prose` is Tailwind Typography's, which the
1325
+ component's markup uses, and it is also this site's own. Its rules reach into
1326
+ the document and colour it with whatever `--fg` currently means - which, in
1327
+ the dark theme, is near-white text on a white sheet.
1328
+ --------------------------------------------------------------------------- */
1329
+ .spec-sheet {
1330
+ color-scheme: light;
1331
+ background: var(--bg);
1332
+ color: var(--fg);
1333
+ }
1334
+
1335
+ /* ---------------------------------------------------------------------------
1336
+ Markdown prose. Capped at the prose width; tables and canvases are not.
1337
+ --------------------------------------------------------------------------- */
1338
+ .prose {
1339
+ max-width: var(--w-prose);
1340
+ color: var(--fg);
1341
+ font-size: var(--text-base);
1342
+ line-height: 1.5;
1343
+ }
1344
+ .prose h1 {
1345
+ font-size: var(--text-lg);
1346
+ font-weight: 600;
1347
+ margin: 20px 0 8px;
1348
+ }
1349
+ /* A readme's section heading stays at the top of the readme while its
1350
+ section scrolls under it, just below the page's pinned name row. Sticky
1351
+ within the readme, so every heading pins at the same edge and the next one
1352
+ simply covers the last - which is why it is opaque. */
1353
+ /* Pinned a gutter higher than it reads, for the reason `.section-head` is:
1354
+ sticky measures from the scrollport's padding box, so at `top: 0` the box's
1355
+ own gutter is left uncovered and prose scrolls visibly through the strip
1356
+ above the heading. The padding puts the text back where it was. */
1357
+ .prose h2 {
1358
+ position: sticky;
1359
+ top: calc(var(--page-header-h, 0px) - var(--gutter));
1360
+ z-index: 10;
1361
+ background: var(--bg);
1362
+ font-size: var(--text-md);
1363
+ font-weight: 600;
1364
+ margin: calc(24px - var(--gutter)) 0 8px;
1365
+ padding-top: var(--gutter);
1366
+ padding-bottom: 4px;
1367
+ border-bottom: 1px solid var(--border);
1368
+ }
1369
+ .prose h3 {
1370
+ font-size: var(--text-base);
1371
+ font-weight: 600;
1372
+ margin: 16px 0 4px;
1373
+ }
1374
+ /* A readme heading keeps its own block layout and its own rule; the chain
1375
+ simply rides at the end of the line. */
1376
+ .prose .anchor-link {
1377
+ margin-left: 6px;
1378
+ vertical-align: middle;
1379
+ }
1380
+ .prose p {
1381
+ margin: 8px 0;
1382
+ color: var(--fg);
1383
+ }
1384
+ .prose ul,
1385
+ .prose ol {
1386
+ margin: 8px 0;
1387
+ padding-left: 20px;
1388
+ list-style: disc;
1389
+ }
1390
+ .prose ol {
1391
+ list-style: decimal;
1392
+ }
1393
+ .prose li {
1394
+ margin: 4px 0;
1395
+ }
1396
+ .prose a {
1397
+ color: var(--accent);
1398
+ text-decoration: underline;
1399
+ text-underline-offset: 2px;
1400
+ }
1401
+ .prose code {
1402
+ font-family: var(--font-mono);
1403
+ font-size: var(--text-xs);
1404
+ background: var(--surface-2);
1405
+ border: 1px solid var(--border);
1406
+ border-radius: 4px;
1407
+ padding: 0 4px;
1408
+ }
1409
+ .prose pre {
1410
+ font-family: var(--font-mono);
1411
+ font-size: var(--text-xs);
1412
+ background: var(--surface);
1413
+ border: 1px solid var(--border);
1414
+ border-radius: var(--radius-control);
1415
+ padding: 12px;
1416
+ overflow-x: auto;
1417
+ margin: 12px 0;
1418
+ }
1419
+ .prose pre code {
1420
+ background: none;
1421
+ border: none;
1422
+ padding: 0;
1423
+ }
1424
+ /* A markdown table that stayed a markdown table - three rows or fewer, or an
1425
+ author who asked for it. The `:not(.tbl)` is what keeps these element-level
1426
+ rules off the enhanced ones: an element selector outranks a utility class,
1427
+ so without it a README's table would quietly repaint the app's. */
1428
+ .prose table:not(.tbl) {
1429
+ border-collapse: separate;
1430
+ border-spacing: 0;
1431
+ margin: 12px 0;
1432
+ font-size: var(--text-xs);
1433
+ width: 100%;
1434
+ border: 1px solid var(--border);
1435
+ border-radius: var(--radius-control);
1436
+ overflow: hidden;
1437
+ }
1438
+ .prose table:not(.tbl) th,
1439
+ .prose table:not(.tbl) td {
1440
+ border-bottom: 1px solid var(--border);
1441
+ padding: 6px 8px;
1442
+ text-align: left;
1443
+ }
1444
+ .prose table:not(.tbl) tr:last-child td {
1445
+ border-bottom: 0;
1446
+ }
1447
+ .prose table:not(.tbl) th {
1448
+ background: var(--surface);
1449
+ font-weight: 600;
1450
+ }
1451
+ .prose blockquote {
1452
+ border-left: 2px solid var(--border-strong);
1453
+ padding-left: 12px;
1454
+ color: var(--fg-muted);
1455
+ margin: 12px 0;
1456
+ }
1457
+
1458
+ /* GitHub admonitions. `> [!NOTE]` and its four siblings arrive in any README
1459
+ written on GitHub; left as a blockquote they read as a quotation, which is
1460
+ the opposite of what the author meant. One box, tinted from the --callout
1461
+ colour the component sets, so five kinds need one rule. */
1462
+ .prose .callout {
1463
+ margin: 12px 0;
1464
+ padding: 12px;
1465
+ border: 1px solid color-mix(in srgb, var(--callout) 28%, var(--border));
1466
+ border-left: 3px solid var(--callout);
1467
+ border-radius: var(--radius-control);
1468
+ background: color-mix(in srgb, var(--callout) 5%, transparent);
1469
+ }
1470
+ .prose .callout-title {
1471
+ display: flex;
1472
+ align-items: center;
1473
+ gap: 8px;
1474
+ margin: 0 0 4px;
1475
+ color: var(--callout);
1476
+ font-weight: 600;
1477
+ font-size: var(--text-sm);
1478
+ }
1479
+ .prose .callout > :last-child {
1480
+ margin-bottom: 0;
1481
+ }
1482
+
1483
+ /* ---------------------------------------------------------------------------
1484
+ A mermaid diagram, drawn into the page by the library rather than by this
1485
+ app. Mermaid names a node's text `.label`, and so does this app - where it
1486
+ means a mono uppercase eyebrow over a section. The utility wins by
1487
+ cascade order and shouts every state name in a diagram, so the transform
1488
+ is undone inside the figure and nowhere else.
1489
+ --------------------------------------------------------------------------- */
1490
+ .mermaid-figure :is(.label, .labelBkg, .nodeLabel, .edgeLabel) {
1491
+ text-transform: none;
1492
+ }
1493
+
1494
+ /* ---------------------------------------------------------------------------
1495
+ React Flow. It ships its chrome - pane, dot grid, minimap, rubber-band
1496
+ selection - painted from --xy-* custom properties, so the canvases are
1497
+ themed here, once, from the app's own tokens: the .dark block above swaps
1498
+ the tokens and every --xy-* follows without a second declaration. Nodes and
1499
+ edges are this app's components and carry their own paint; what this block
1500
+ owns is the chrome, and the one thing React Flow will not do for a custom
1501
+ node, which is draw its selected state.
1502
+ --------------------------------------------------------------------------- */
1503
+ .react-flow {
1504
+ --xy-background-color: var(--flow-pane);
1505
+ --xy-background-pattern-color: var(--flow-dot);
1506
+ --xy-node-border-radius: var(--radius-node);
1507
+ --xy-edge-stroke: var(--border-strong);
1508
+ --xy-edge-stroke-selected: var(--accent);
1509
+ --xy-edge-label-background-color: var(--flow-card);
1510
+ --xy-edge-label-color: var(--fg-muted);
1511
+ --xy-minimap-background-color: var(--flow-card);
1512
+ --xy-minimap-mask-background-color: color-mix(
1513
+ in srgb,
1514
+ var(--flow-pane) 72%,
1515
+ transparent
1516
+ );
1517
+ --xy-minimap-mask-stroke-color: var(--border-strong);
1518
+ --xy-selection-background-color: color-mix(
1519
+ in srgb,
1520
+ var(--accent) 8%,
1521
+ transparent
1522
+ );
1523
+ --xy-selection-border: 1px solid
1524
+ color-mix(in srgb, var(--accent) 60%, transparent);
1525
+ font-family: var(--font-sans);
1526
+ }
1527
+
1528
+ /* Selection is the ring, on the node's own box. The event pill is the one
1529
+ node that is not the shape of its box - it is a capsule, and at fit zooms
1530
+ it shrinks to an icon inside the box - so it draws the ring itself. */
1531
+ .react-flow__node.selected:not(.react-flow__node-event) {
1532
+ border-radius: var(--radius-node);
1533
+ box-shadow: var(--ring);
1534
+ }
1535
+
1536
+ /* A routed edge turns corners; round joins keep them from growing spurs at
1537
+ the thicker lit width. Caps stay flat because the dashed status markers
1538
+ are measured for flat caps. */
1539
+ .react-flow__edge-path {
1540
+ stroke-linejoin: round;
1541
+ }
1542
+
1543
+ .react-flow__minimap {
1544
+ border-radius: var(--radius-node);
1545
+ box-shadow: var(--shadow-card);
1546
+ overflow: hidden;
1547
+ }