@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
@@ -0,0 +1,223 @@
1
+ import { Fragment } from "react";
2
+ import { Link, useLocation } from "react-router";
3
+ import { ChevronRight } from "lucide-react";
4
+ import { catalog, index } from "../data";
5
+ import { enumsOf } from "../catalog";
6
+ import { adrNumber } from "../lib/adr";
7
+ import { selectionLabel, selectionTrail } from "../selection/model";
8
+ import { selectionPath } from "../selection/pages";
9
+ import { useSelectionStore } from "../selection/store";
10
+ import { paths } from "../routes";
11
+
12
+ interface Crumb {
13
+ label: string;
14
+ to: string;
15
+ }
16
+
17
+ export function crumbsFor(pathname: string): Crumb[] {
18
+ const parts = pathname.split("/").filter(Boolean);
19
+ if (parts.length === 0) return [];
20
+
21
+ if (parts[0] === "flows") {
22
+ const crumbs: Crumb[] = [{ label: "flows", to: "/flows" }];
23
+ const slug = parts[1];
24
+ if (slug) {
25
+ const flow = index.flowBySlug.get(slug);
26
+ crumbs.push({ label: flow?.slug ?? slug, to: `/flows/${slug}` });
27
+ }
28
+ return crumbs;
29
+ }
30
+
31
+ if (parts[0] === "adrs") {
32
+ const crumbs: Crumb[] = [{ label: "decisions", to: paths.adrs() }];
33
+ const slug = parts[1];
34
+ if (slug) {
35
+ const adr = index.adrBySlug.get(slug);
36
+ crumbs.push({
37
+ label: adr ? adrNumber(adr) : slug,
38
+ to: `/adrs/${slug}`,
39
+ });
40
+ }
41
+ return crumbs;
42
+ }
43
+
44
+ if (parts[0] === "graph") return [{ label: "graph", to: "/graph" }];
45
+
46
+ if (parts[0] === "map") return [{ label: "map", to: "/map" }];
47
+
48
+ if (parts[0] === "settings")
49
+ return [{ label: "settings", to: paths.settings() }];
50
+
51
+ if (parts[0] === "changes")
52
+ return [{ label: "changes", to: paths.changes() }];
53
+
54
+ if (parts[0] === "externals") {
55
+ const slug = parts[1];
56
+ if (!slug) return [];
57
+ const external = (catalog.externals ?? []).find(
58
+ (candidate) => candidate.slug === slug,
59
+ );
60
+ return [
61
+ {
62
+ label: external?.name ?? slug,
63
+ to: paths.external(slug),
64
+ },
65
+ ];
66
+ }
67
+
68
+ if (parts[0] === "language")
69
+ return [{ label: "language", to: paths.language() }];
70
+
71
+ if (parts[0] === "problems")
72
+ return [{ label: "problems", to: paths.problems() }];
73
+
74
+ if (parts[0] === "registry") {
75
+ const crumbs: Crumb[] = [{ label: "registry", to: paths.registry() }];
76
+ const slug = parts[1];
77
+ if (slug) {
78
+ const module = index.moduleBySlug.get(slug);
79
+ crumbs.push({ label: module?.name ?? slug, to: paths.module(slug) });
80
+ }
81
+ return crumbs;
82
+ }
83
+
84
+ if (parts[0] === "c") {
85
+ const [, contextId, serviceSlug, aggregateSlug, eventSlug] = parts;
86
+ const crumbs: Crumb[] = [];
87
+ if (!contextId) return crumbs;
88
+ crumbs.push({ label: contextId, to: `/c/${contextId}` });
89
+ if (!serviceSlug) return crumbs;
90
+ crumbs.push({ label: serviceSlug, to: `/c/${contextId}/${serviceSlug}` });
91
+ if (!aggregateSlug) return crumbs;
92
+
93
+ // "data" is a literal too: a store hangs off its service, and the segment
94
+ // after it is the store slug, not an event of an aggregate called "data".
95
+ if (aggregateSlug === "data") {
96
+ const storeSlug = parts[4];
97
+ if (!storeSlug) return crumbs;
98
+ const service = catalog.contexts
99
+ .find((c) => c.id === contextId)
100
+ ?.services.find((s) => s.slug === serviceSlug);
101
+ const store = (catalog.stores ?? []).find(
102
+ (s) => s.slug === storeSlug && s.owner === service?.id,
103
+ );
104
+ crumbs.push({
105
+ label: store?.name ?? storeSlug,
106
+ to: paths.store(contextId, serviceSlug, storeSlug),
107
+ });
108
+ return crumbs;
109
+ }
110
+
111
+ crumbs.push({
112
+ label: aggregateSlug,
113
+ to: `/c/${contextId}/${serviceSlug}/${aggregateSlug}`,
114
+ });
115
+ if (!eventSlug) return crumbs;
116
+ const aggregate = catalog.contexts
117
+ .find((c) => c.id === contextId)
118
+ ?.services.find((s) => s.slug === serviceSlug)
119
+ ?.aggregates.find((a) => a.slug === aggregateSlug);
120
+
121
+ // "vo", "entity" and "enum" are literals, not slugs: one more segment follows.
122
+ if (eventSlug === "vo" || eventSlug === "entity" || eventSlug === "enum") {
123
+ const blockSlug = parts[5];
124
+ if (!blockSlug) return crumbs;
125
+ const list =
126
+ eventSlug === "vo"
127
+ ? aggregate?.valueObjects
128
+ : eventSlug === "entity"
129
+ ? aggregate?.entities
130
+ : aggregate
131
+ ? enumsOf(aggregate)
132
+ : undefined;
133
+ const block = list?.find((b) => b.slug === blockSlug);
134
+ crumbs.push({
135
+ label: block?.name ?? blockSlug,
136
+ to: `/c/${contextId}/${serviceSlug}/${aggregateSlug}/${eventSlug}/${blockSlug}`,
137
+ });
138
+ return crumbs;
139
+ }
140
+
141
+ const event = aggregate?.events.find((e) => e.slug === eventSlug);
142
+ crumbs.push({
143
+ label: event?.name ?? eventSlug,
144
+ to: `/c/${contextId}/${serviceSlug}/${aggregateSlug}/${eventSlug}`,
145
+ });
146
+ return crumbs;
147
+ }
148
+
149
+ return [];
150
+ }
151
+
152
+ export function Breadcrumbs() {
153
+ const { pathname } = useLocation();
154
+ const crumbs = crumbsFor(pathname);
155
+
156
+ const selection = useSelectionStore((s) => s.selection);
157
+ const select = useSelectionStore((s) => s.select);
158
+
159
+ // The route says where the reader is; the selection says what they are
160
+ // looking at, and the two are often not the same thing - an event picked
161
+ // while a flow is open, say. The selection trail is appended rather than
162
+ // substituted so neither answer is lost.
163
+ const alreadyOnRoute =
164
+ selection !== null &&
165
+ selection.kind !== "flow-step" &&
166
+ selectionPath(selection) === pathname;
167
+ // Only the part of the trail the route does not already say. Repeating
168
+ // "shop > oms" either side of the marker is noise, not information.
169
+ const onRoute = new Set(crumbs.map((c) => c.to));
170
+ const full = selection && !alreadyOnRoute ? selectionTrail(selection) : [];
171
+ const trail = full.filter(
172
+ (step) =>
173
+ step.kind === "flow-step" || !onRoute.has(selectionPath(step) ?? ""),
174
+ );
175
+
176
+ return (
177
+ <div className="mono flex min-w-0 items-center gap-1 truncate">
178
+ <Link to="/" className="hover:underline text-muted">
179
+ portolan
180
+ </Link>
181
+ {crumbs.map((crumb, i) => (
182
+ <Fragment key={crumb.to}>
183
+ <ChevronRight size={11} aria-hidden className="text-line-strong" />
184
+ <Link
185
+ to={crumb.to}
186
+ className={`truncate hover:underline ${i === crumbs.length - 1 && trail.length === 0 ? "text-ink" : "text-muted"}`}
187
+ >
188
+ {crumb.label}
189
+ </Link>
190
+ </Fragment>
191
+ ))}
192
+
193
+ {trail.length > 0 ? (
194
+ <>
195
+ <span
196
+ aria-hidden
197
+ title="selected"
198
+ className="mx-1 size-1.5 shrink-0"
199
+ style={{ background: "var(--accent)" }}
200
+ />
201
+ {trail.map((step, i) => (
202
+ <Fragment key={step.id}>
203
+ {i > 0 ? (
204
+ <ChevronRight
205
+ size={11}
206
+ aria-hidden
207
+ className="text-line-strong"
208
+ />
209
+ ) : null}
210
+ <button
211
+ type="button"
212
+ onClick={() => select(step.id, "breadcrumb")}
213
+ className={`truncate hover:underline ${i === trail.length - 1 ? "text-ink" : "text-muted"}`}
214
+ >
215
+ {selectionLabel(step)}
216
+ </button>
217
+ </Fragment>
218
+ ))}
219
+ </>
220
+ ) : null}
221
+ </div>
222
+ );
223
+ }
@@ -0,0 +1,146 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
3
+ import { GitCommitHorizontal } from "lucide-react";
4
+ import {
5
+ buildHref,
6
+ buildInfo,
7
+ buildLabel,
8
+ buildTitle,
9
+ } from "../lib/build-info";
10
+ import { absoluteTime, relativeTime } from "../lib/format";
11
+
12
+ const BOX =
13
+ "mono flex items-center gap-1.5 rounded-control border px-2 py-1.5 border-line text-muted";
14
+
15
+ /**
16
+ * What is deployed, and where to go to see it: the run that built this bundle,
17
+ * or the commit it was built from when nobody built it but you. It is stamped
18
+ * once, in the top bar, where it is one control among five and its border
19
+ * makes it one of them; a second copy in the sidebar said nothing new.
20
+ *
21
+ * `compact` is the phone form: the icon alone, and everything the wide stamp
22
+ * says in line - the commit, when it was built, the branch and the run - behind
23
+ * a tap. The stamp is not a control a reader reaches for often enough to spend
24
+ * a fifth of a phone's top bar on, but it is the one they reach for when they
25
+ * are asking "is what I am looking at the build I think it is", and a title
26
+ * attribute is not an answer on a device with no pointer to hover.
27
+ */
28
+ export function BuildStamp({ compact = false }: { compact?: boolean }) {
29
+ const [, force] = useState(0);
30
+
31
+ // The stamp reads as relative time; nudge it once a minute.
32
+ useEffect(() => {
33
+ const t = setInterval(() => force((n) => n + 1), 60_000);
34
+ return () => clearInterval(t);
35
+ }, []);
36
+
37
+ // The stamp names a commit, so it opens the commit; the run that built it
38
+ // is one click further, in the checks on that page.
39
+ const href = buildHref();
40
+
41
+ if (compact) return <CompactStamp href={href} />;
42
+
43
+ const body = (
44
+ <>
45
+ <GitCommitHorizontal size={16} aria-hidden className="shrink-0" />
46
+ {buildLabel()}
47
+ {buildInfo.builtAt && (
48
+ <>
49
+ <span aria-hidden className="text-line-strong">
50
+ ·
51
+ </span>
52
+ {relativeTime(buildInfo.builtAt)}
53
+ </>
54
+ )}
55
+ </>
56
+ );
57
+
58
+ if (!href)
59
+ return (
60
+ <span className={BOX} title={buildTitle()}>
61
+ {body}
62
+ </span>
63
+ );
64
+ return (
65
+ <a
66
+ href={href}
67
+ target="_blank"
68
+ rel="noreferrer"
69
+ title={buildTitle()}
70
+ className={`${BOX} transition-colors hover:border-line-strong hover:bg-surface hover:text-ink`}
71
+ >
72
+ {body}
73
+ </a>
74
+ );
75
+ }
76
+
77
+ function CompactStamp({ href }: { href: string | null }) {
78
+ return (
79
+ <Popover className="shrink-0">
80
+ <PopoverButton
81
+ aria-label={`Build — ${buildTitle()}`}
82
+ title={buildTitle()}
83
+ className={({ open }) =>
84
+ `flex size-8 items-center justify-center rounded-control border t-micro transition-colors border-line hover:bg-surface ${
85
+ open ? "text-accent" : "text-muted hover:text-ink"
86
+ }`
87
+ }
88
+ >
89
+ <GitCommitHorizontal size={16} aria-hidden />
90
+ </PopoverButton>
91
+ <PopoverPanel
92
+ anchor={{ to: "bottom end", gap: 4, padding: 8 }}
93
+ className="palette-in z-50 w-64 rounded-control border bg-canvas p-2 border-line-strong shadow-md focus:outline-none"
94
+ >
95
+ <div className="label mb-1.5 px-1">build</div>
96
+ <dl className="mono grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 px-1 text-muted">
97
+ <dt>commit</dt>
98
+ {/* `buildLabel` ends a dirty tree in a "+", which is what the wide
99
+ stamp has room to say. Here the word says it, so the sign would
100
+ be saying it twice. */}
101
+ <dd className="truncate text-ink">
102
+ {buildInfo.shortCommit || buildLabel()}
103
+ {buildInfo.dirty ? (
104
+ <span className="ml-1.5 text-muted">uncommitted</span>
105
+ ) : null}
106
+ </dd>
107
+ {buildInfo.branch ? (
108
+ <>
109
+ <dt>branch</dt>
110
+ <dd className="truncate text-ink">{buildInfo.branch}</dd>
111
+ </>
112
+ ) : null}
113
+ {buildInfo.buildNumber ? (
114
+ <>
115
+ <dt>run</dt>
116
+ <dd className="truncate text-ink">#{buildInfo.buildNumber}</dd>
117
+ </>
118
+ ) : null}
119
+ <dt>built</dt>
120
+ <dd className="text-ink">
121
+ {buildInfo.builtAt ? (
122
+ <>
123
+ {relativeTime(buildInfo.builtAt)}
124
+ <div className="text-muted">
125
+ {absoluteTime(buildInfo.builtAt)}
126
+ </div>
127
+ </>
128
+ ) : (
129
+ "at an unrecorded time"
130
+ )}
131
+ </dd>
132
+ </dl>
133
+ {href ? (
134
+ <a
135
+ href={href}
136
+ target="_blank"
137
+ rel="noreferrer"
138
+ className="mono mt-2 block rounded-control px-1 py-1 text-accent hover:bg-surface"
139
+ >
140
+ open on the forge ↗
141
+ </a>
142
+ ) : null}
143
+ </PopoverPanel>
144
+ </Popover>
145
+ );
146
+ }
@@ -0,0 +1,292 @@
1
+ // ⌘K. Everything the catalog names, in one list.
2
+ //
3
+ // Enter sets the selection first and navigates second, and only when the page
4
+ // already on screen cannot show what was picked. Choosing an event while its
5
+ // service page is open therefore opens the panel and leaves the reader where
6
+ // they were, which is the whole point of holding selection globally.
7
+ //
8
+ // Rows carry the same kind icons as the sidebar tree, and the same taxonomy
9
+ // narrows the list: "e: item" searches events, "vo: money" value objects.
10
+
11
+ import { Fragment, useMemo, useRef, useState } from "react";
12
+ import {
13
+ Combobox,
14
+ ComboboxInput,
15
+ ComboboxOption,
16
+ ComboboxOptions,
17
+ } from "@headlessui/react";
18
+ import { useLocation, useNavigate } from "react-router";
19
+ import { Modal } from "../components/Overlay";
20
+ import { catalog } from "../data";
21
+ import { paletteItems, search } from "../lib/palette";
22
+ import type { PaletteHit, PaletteItem } from "../lib/palette";
23
+ import { recentSections } from "../lib/palette-recent";
24
+ import { useTrailStore } from "../trail/store";
25
+ import { usePinsStore } from "./pins";
26
+ import { KIND_LABEL, KIND_PREFIXES, canonicalPrefix } from "../lib/kinds";
27
+ import type { Kind } from "../lib/kinds";
28
+ import { ctxStyle } from "../lib/context-color";
29
+ import { KindIcon } from "../components/kind";
30
+ import { pageContains, selectionPath } from "../selection/pages";
31
+ import { selectionHash } from "../selection/hash";
32
+ import { selectionFor } from "../selection/model";
33
+ import { useSelectionStore } from "../selection/store";
34
+
35
+ const ITEMS = paletteItems(catalog);
36
+
37
+ /** The prefixes offered under an empty input. Kinds, not every spelling. */
38
+ const HINT_KINDS: Kind[] = [
39
+ "event",
40
+ "vo",
41
+ "entity",
42
+ "aggregate",
43
+ "command",
44
+ "query",
45
+ ];
46
+
47
+ /** One row of the list, drawn the same whether it was searched for or recalled. */
48
+ function Row({ hit: { item, excerpt } }: { hit: PaletteHit }) {
49
+ return (
50
+ <ComboboxOption
51
+ value={{ item, ...(excerpt ? { excerpt } : {}) }}
52
+ /* The 2px edge is always drawn and only changes colour, so the row
53
+ under the cursor never nudges the text beside it. */
54
+ className={({ focus }) =>
55
+ `flex w-full cursor-pointer flex-col gap-0.5 border-l-2 px-4 py-1.5 text-left t-micro transition-colors ${
56
+ focus ? "bg-raised border-accent" : "border-transparent"
57
+ }`
58
+ }
59
+ >
60
+ {() => (
61
+ <>
62
+ <span className="flex w-full items-center gap-2">
63
+ <span className="flex shrink-0" style={ctxStyle(item.context)}>
64
+ <KindIcon
65
+ kind={item.kind}
66
+ {...(item.context ? { contextId: item.context } : {})}
67
+ />
68
+ </span>
69
+ <span
70
+ className="mono shrink-0"
71
+ title={item.name}
72
+ style={
73
+ item.kind === "event"
74
+ ? { color: "var(--kind-event)" }
75
+ : undefined
76
+ }
77
+ >
78
+ {item.name}
79
+ </span>
80
+ <span className="mono truncate text-muted" title={item.detail}>
81
+ {item.detail}
82
+ </span>
83
+ <span className="mono ml-auto flex shrink-0 items-center gap-2 text-muted">
84
+ {item.badge ? (
85
+ <span className="rounded-[4px] border px-1 border-line">
86
+ {item.badge}
87
+ </span>
88
+ ) : null}
89
+ <span className="label">{KIND_LABEL[item.kind]}</span>
90
+ </span>
91
+ </span>
92
+ {/* Why this row is here. Sans, not mono: the line above is
93
+ identifiers, this one is a sentence, and the two must not be
94
+ mistaken for each other. */}
95
+ {excerpt ? (
96
+ <span className="w-full truncate pl-6 text-xs text-muted">
97
+ {excerpt.before}
98
+ <span style={{ color: "var(--accent)" }}>{excerpt.match}</span>
99
+ {excerpt.after}
100
+ </span>
101
+ ) : null}
102
+ </>
103
+ )}
104
+ </ComboboxOption>
105
+ );
106
+ }
107
+
108
+ export function CommandPalette({
109
+ open,
110
+ onClose,
111
+ }: {
112
+ open: boolean;
113
+ onClose: () => void;
114
+ }) {
115
+ const [query, setQuery] = useState("");
116
+ // Only so a hint chip can hand the caret back after filling the prefix in.
117
+ const inputRef = useRef<HTMLInputElement | null>(null);
118
+ const navigate = useNavigate();
119
+ const location = useLocation();
120
+ const setSelection = useSelectionStore((s) => s.set);
121
+
122
+ const result = useMemo(() => search(ITEMS, open ? query : ""), [open, query]);
123
+ const results = open ? result.hits : [];
124
+
125
+ // Under an empty input: where the reader has been and what they pinned,
126
+ // when they have either. The full index is what a reader with neither
127
+ // gets, and what typing anything switches back to.
128
+ const visits = useTrailStore((s) => s.visits);
129
+ const pins = usePinsStore((s) => s.pins);
130
+ const sections = useMemo(() => {
131
+ if (!open || query.trim() !== "") return null;
132
+ const found = recentSections(visits, pins, ITEMS, location.pathname);
133
+ return found.length > 0 ? found : null;
134
+ }, [open, query, visits, pins, location.pathname]);
135
+ const shown = sections
136
+ ? sections.reduce((n, s) => n + s.hits.length, 0)
137
+ : results.length;
138
+
139
+ const close = (): void => {
140
+ onClose();
141
+ // Cleared on the way out rather than on the way in: the palette unmounts,
142
+ // and a query left behind would flash under the next ⌘K before the effect
143
+ // that cleared it ran.
144
+ setQuery("");
145
+ };
146
+
147
+ const commit = (item: PaletteItem): void => {
148
+ close();
149
+
150
+ if (item.selectId) {
151
+ const selection = selectionFor(item.selectId);
152
+
153
+ // This page can already point at it: select in place and move nobody.
154
+ if (pageContains(location.pathname, selection)) {
155
+ setSelection(selection, "palette");
156
+ return;
157
+ }
158
+
159
+ // Otherwise carry the selection in the URL rather than writing it to the
160
+ // store first. Two writers - this handler and SelectionSync - would race
161
+ // for the same history entry, and the sync's `replace` would land with
162
+ // the pathname it had before the navigation, undoing it. A selection in
163
+ // the URL wins on arrival, so one write does both jobs.
164
+ const to = selectionPath(selection) ?? item.path;
165
+ if (to) navigate(`${to}${selectionHash(selection)}`);
166
+ else setSelection(selection, "palette");
167
+ return;
168
+ }
169
+
170
+ // Rows the selection model does not resolve - value objects, entities,
171
+ // commands, queries, flows, decisions - simply go to their page.
172
+ if (item.path && item.path !== location.pathname) navigate(item.path);
173
+ };
174
+
175
+ return (
176
+ <Modal open={open} onClose={close} label="Command palette">
177
+ {/* The combobox owns the list: which row is active, keeping it in view,
178
+ ↑↓ and ⏎, and the aria wiring that makes the input announce the row
179
+ under it. Escape is caught on the way DOWN, before the combobox can
180
+ take it - inside a modal there is nothing to close but the modal, and
181
+ a first Escape that only closes an invisible listbox reads as an
182
+ Escape that did nothing. */}
183
+ <div
184
+ className="flex min-h-0 flex-col"
185
+ onKeyDownCapture={(e) => {
186
+ if (e.key !== "Escape") return;
187
+ e.preventDefault();
188
+ e.stopPropagation();
189
+ close();
190
+ }}
191
+ >
192
+ <Combobox<PaletteHit | null>
193
+ value={null}
194
+ onChange={(hit) => {
195
+ if (hit) commit(hit.item);
196
+ }}
197
+ immediate
198
+ >
199
+ {/* The one accent glow the palette gets: 4%, radial, behind the input. */}
200
+ <ComboboxInput
201
+ ref={inputRef}
202
+ autoFocus
203
+ value={query}
204
+ onChange={(e) => setQuery(e.target.value)}
205
+ placeholder="jump to anything — or narrow with e: vo: ent: agg: cmd: q:"
206
+ spellCheck={false}
207
+ aria-label="Search the catalog"
208
+ className="glow mono w-full shrink-0 border-b bg-transparent px-4 py-3 text-sm outline-none border-line placeholder:text-muted"
209
+ />
210
+
211
+ <div className="mono flex shrink-0 flex-wrap items-center gap-x-2 gap-y-1 border-b px-4 py-2 border-line text-muted">
212
+ {result.kind ? (
213
+ <>
214
+ <span>only</span>
215
+ <span
216
+ className="chip"
217
+ style={{
218
+ borderColor: "var(--accent)",
219
+ color: "var(--accent)",
220
+ }}
221
+ >
222
+ <KindIcon kind={result.kind} />
223
+ {KIND_LABEL[result.kind]}
224
+ </span>
225
+ {result.term ? <span>matching “{result.term}”</span> : null}
226
+ </>
227
+ ) : (
228
+ <>
229
+ <span>narrow to</span>
230
+ {HINT_KINDS.map((kind) => (
231
+ <button
232
+ key={kind}
233
+ type="button"
234
+ onClick={() => {
235
+ setQuery(`${canonicalPrefix(kind)}: `);
236
+ inputRef.current?.focus();
237
+ }}
238
+ className="inline-flex items-center gap-1 rounded-control px-0.5 t-micro transition-colors hover:text-ink"
239
+ title={`${KIND_PREFIXES[kind].join(" / ")} — ${KIND_LABEL[kind]}`}
240
+ >
241
+ <KindIcon kind={kind} />
242
+ {canonicalPrefix(kind)}:
243
+ </button>
244
+ ))}
245
+ </>
246
+ )}
247
+ </div>
248
+
249
+ {shown === 0 ? (
250
+ <div className="glow mono px-4 py-8 text-center text-muted">
251
+ no match
252
+ </div>
253
+ ) : (
254
+ /* `static` because the list is the palette: there is no closed
255
+ state to render, and the modal around it is what "closed" means
256
+ here. */
257
+ <ComboboxOptions static className="min-h-0 flex-1 overflow-y-auto">
258
+ {sections
259
+ ? sections.map((section) => (
260
+ <Fragment key={section.title}>
261
+ {/* A heading, not an option: ↑↓ skip it, and the
262
+ list reads as two short lists rather than one. */}
263
+ <div className="label sticky top-0 z-10 px-4 pt-2 pb-1 bg-canvas">
264
+ {section.title}
265
+ </div>
266
+ {section.hits.map((hit) => (
267
+ <Row key={`${hit.item.kind}:${hit.item.id}`} hit={hit} />
268
+ ))}
269
+ </Fragment>
270
+ ))
271
+ : results.map((hit) => (
272
+ <Row key={`${hit.item.kind}:${hit.item.id}`} hit={hit} />
273
+ ))}
274
+ </ComboboxOptions>
275
+ )}
276
+ </Combobox>
277
+
278
+ <div className="mono flex shrink-0 items-center gap-3 border-t px-4 py-2 border-line text-muted">
279
+ <span>↑↓ move</span>
280
+ <span>⏎ select</span>
281
+ <span>esc close</span>
282
+ <span className="ml-auto">
283
+ {shown} shown
284
+ {!sections && result.truncated > 0
285
+ ? ` · +${result.truncated} more`
286
+ : ""}
287
+ </span>
288
+ </div>
289
+ </div>
290
+ </Modal>
291
+ );
292
+ }