@shortlink-org/portolan 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -205
- package/cli/init.mjs +285 -0
- package/cli/init.test.mjs +158 -0
- package/cli/portolan.mjs +37 -73
- package/index.html +19 -0
- package/package.json +5 -10
- package/plugins/README.md +66 -27
- package/plugins/portolan-go.wasm +0 -0
- package/portolan.json +52 -73
- package/public/landing/cats/bug-hunter.webp +0 -0
- package/public/landing/cats/compass-nap.webp +0 -0
- package/public/landing/cats/diagrammer.webp +0 -0
- package/public/landing/cats/docs-reader.webp +0 -0
- package/public/landing/cats/laptop.webp +0 -0
- package/public/landing/cats/map-inspector.webp +0 -0
- package/public/landing/cats/server-break.webp +0 -0
- package/public/landing/cats/star-mapper.webp +0 -0
- package/public/landing/cats/system-builder.webp +0 -0
- package/public/landing/cats/thread-tangle.webp +0 -0
- package/public/og.png +0 -0
- package/public/readme-header.webp +0 -0
- package/schema/portolan.schema.json +180 -0
- package/scripts/builtin-plugins.mjs +14 -0
- package/scripts/delivery-presets.mjs +353 -0
- package/scripts/gen.mjs +37 -4
- package/scripts/history.mjs +126 -0
- package/scripts/history.test.mjs +110 -0
- package/scripts/host-plugins/fetch-bsr.mjs +351 -0
- package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
- package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
- package/scripts/host-plugins/fetch-csr.mjs +386 -0
- package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
- package/scripts/host-plugins/fetch-git.mjs +379 -0
- package/scripts/host-plugins/fetch-git.test.mjs +210 -0
- package/scripts/local-api.mjs +22 -4
- package/scripts/local-api.test.mjs +84 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +6 -2
- package/scripts/plugin-host.mjs +77 -18
- package/scripts/plugin-host.test.mjs +50 -1
- package/scripts/plugin-wasm-worker.mjs +6 -3
- package/scripts/run-builtin.mjs +6 -26
- package/scripts/schema.mjs +6 -1
- package/src/app/App.tsx +24 -429
- package/src/app/Breadcrumbs.test.ts +7 -0
- package/src/app/Breadcrumbs.tsx +15 -2
- package/src/app/CatalogApp.tsx +436 -0
- package/src/app/Sidebar.tsx +0 -1
- package/src/chat/ChatPanel.tsx +98 -41
- package/src/chat/Composer.tsx +4 -2
- package/src/chat/Conversation.tsx +79 -15
- package/src/chat/Header.tsx +43 -9
- package/src/chat/Starter.tsx +64 -3
- package/src/chat/Steps.tsx +1 -1
- package/src/chat/flags.test.ts +51 -14
- package/src/chat/flags.ts +8 -2
- package/src/chat/page-context.test.ts +51 -0
- package/src/chat/page-context.ts +128 -0
- package/src/chat/prompt.test.ts +49 -1
- package/src/chat/prompt.ts +42 -1
- package/src/chat/tools.ts +7 -2
- package/src/chat/transport.ts +16 -4
- package/src/components/ProblemRow.tsx +182 -0
- package/src/data.ts +20 -5
- package/src/er/ErCanvas.tsx +1 -1
- package/src/graph/DependencyGraph.tsx +10 -1
- package/src/graph/FocusedEventGraph.tsx +1 -1
- package/src/graph/GraphToolbar.tsx +2 -4
- package/src/index.css +220 -2
- package/src/landing/DraggableReveal.tsx +168 -0
- package/src/landing/EstateGraph.tsx +28 -0
- package/src/landing/FlowPlayback.tsx +282 -0
- package/src/landing/HeroMap.tsx +128 -0
- package/src/landing/LandingPage.tsx +757 -0
- package/src/landing/ProductFrame.tsx +68 -0
- package/src/landing/ProductTour.tsx +371 -0
- package/src/landing/catalog.ts +25 -0
- package/src/landing/motion.tsx +52 -0
- package/src/lib/all-problems.ts +28 -0
- package/src/lib/local-api.ts +35 -1
- package/src/lib/setup-info.test.ts +8 -0
- package/src/lib/setup-info.ts +3 -2
- package/src/lib/source-code.ts +2 -1
- package/src/likec4/C4View.tsx +5 -0
- package/src/likec4/InteractiveView.tsx +5 -1
- package/src/map/ContextMapGraph.tsx +47 -20
- package/src/pages/ContextMap.tsx +5 -1
- package/src/pages/FlowDetail.tsx +24 -7
- package/src/pages/Overview.tsx +68 -12
- package/src/pages/Problems.tsx +6 -193
- package/src/pages/Settings.tsx +133 -108
- package/src/pages/settings/DeliverySettings.tsx +229 -0
- package/src/pages/settings/PreferencesSettings.tsx +101 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +13 -1
- package/vite.config.ts +12 -8
- package/catalog/enum_test.go +0 -46
- package/catalog/model.go +0 -932
- package/catalog/roundtrip_test.go +0 -185
- package/catalog/via_test.go +0 -38
- package/go.mod +0 -14
- package/go.sum +0 -14
- package/internal/commands/cargo.go +0 -154
- package/internal/commands/commands.go +0 -143
- package/internal/commands/commands_test.go +0 -323
- package/internal/commands/gradle.go +0 -77
- package/internal/commands/justfile.go +0 -82
- package/internal/commands/makefile.go +0 -106
- package/internal/commands/maven.go +0 -93
- package/internal/commands/packagejson.go +0 -159
- package/internal/commands/pyproject.go +0 -169
- package/internal/commands/taskfile.go +0 -101
- package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
- package/internal/commands/testdata/estate/Makefile +0 -33
- package/internal/commands/testdata/estate/Taskfile.yml +0 -21
- package/internal/commands/testdata/estate/build.gradle.kts +0 -21
- package/internal/commands/testdata/estate/justfile +0 -20
- package/internal/commands/testdata/estate/package.json +0 -13
- package/internal/commands/testdata/estate/pom.xml +0 -29
- package/internal/commands/testdata/estate/pyproject.toml +0 -27
- package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
- package/internal/commands/testdata/golden/commands.json +0 -305
- package/internal/gohttp/analyze.go +0 -2500
- package/internal/gohttp/endpoints.go +0 -1067
- package/internal/gohttp/roots.go +0 -320
- package/internal/gohttp/typed.go +0 -143
- package/internal/goscan/constants.go +0 -85
- package/internal/goscan/goscan_test.go +0 -227
- package/internal/goscan/names.go +0 -52
- package/internal/goscan/parse_test.go +0 -11
- package/internal/goscan/source.go +0 -36
- package/internal/goscan/tree.go +0 -155
- package/internal/goscan/types.go +0 -99
- package/internal/wsdl/ids.go +0 -127
- package/internal/wsdl/ids_test.go +0 -21
- package/internal/wsdl/model.go +0 -62
- package/internal/wsdl/parse.go +0 -920
- package/internal/wsdl/parse_test.go +0 -133
- package/plugin/describe.go +0 -107
- package/plugin/describe_test.go +0 -114
- package/plugin/protocol.go +0 -117
- package/plugin/schematest/schematest.go +0 -126
- package/plugins/extract-adr/describe.go +0 -19
- package/plugins/extract-adr/describe_test.go +0 -11
- package/plugins/extract-adr/extract.go +0 -153
- package/plugins/extract-adr/extract_test.go +0 -350
- package/plugins/extract-adr/history.go +0 -99
- package/plugins/extract-adr/main.go +0 -65
- package/plugins/extract-adr/parse.go +0 -642
- package/plugins/extract-adr/parse_test.go +0 -405
- package/plugins/extract-asyncapi/describe.go +0 -19
- package/plugins/extract-asyncapi/describe_test.go +0 -11
- package/plugins/extract-asyncapi/extract.go +0 -315
- package/plugins/extract-asyncapi/extract_test.go +0 -197
- package/plugins/extract-asyncapi/main.go +0 -48
- package/plugins/extract-asyncapi/spec.go +0 -151
- package/plugins/extract-commands/describe.go +0 -19
- package/plugins/extract-commands/describe_test.go +0 -11
- package/plugins/extract-commands/extract.go +0 -79
- package/plugins/extract-commands/extract_test.go +0 -92
- package/plugins/extract-commands/main.go +0 -55
- package/plugins/extract-csr/avro.go +0 -258
- package/plugins/extract-csr/describe.go +0 -19
- package/plugins/extract-csr/describe_test.go +0 -11
- package/plugins/extract-csr/extract.go +0 -338
- package/plugins/extract-csr/extract_test.go +0 -374
- package/plugins/extract-csr/jsonschema.go +0 -343
- package/plugins/extract-csr/lock.go +0 -25
- package/plugins/extract-csr/main.go +0 -84
- package/plugins/extract-csr/subject.go +0 -102
- package/plugins/extract-flows/describe.go +0 -19
- package/plugins/extract-flows/describe_test.go +0 -11
- package/plugins/extract-flows/extract.go +0 -91
- package/plugins/extract-flows/main.go +0 -45
- package/plugins/extract-flows/parse.go +0 -593
- package/plugins/extract-flows/parse_test.go +0 -204
- package/plugins/extract-glossary/describe.go +0 -19
- package/plugins/extract-glossary/describe_test.go +0 -11
- package/plugins/extract-glossary/extract.go +0 -115
- package/plugins/extract-glossary/extract_test.go +0 -220
- package/plugins/extract-glossary/main.go +0 -59
- package/plugins/extract-glossary/parse.go +0 -214
- package/plugins/extract-glossary/parse_test.go +0 -203
- package/plugins/extract-go/aggregate.go +0 -214
- package/plugins/extract-go/client.go +0 -409
- package/plugins/extract-go/client_test.go +0 -266
- package/plugins/extract-go/describe.go +0 -19
- package/plugins/extract-go/describe_test.go +0 -11
- package/plugins/extract-go/enum.go +0 -195
- package/plugins/extract-go/enum_test.go +0 -82
- package/plugins/extract-go/event.go +0 -99
- package/plugins/extract-go/extract.go +0 -191
- package/plugins/extract-go/extract_test.go +0 -261
- package/plugins/extract-go/flow.go +0 -1441
- package/plugins/extract-go/flow_test.go +0 -609
- package/plugins/extract-go/httpclient.go +0 -174
- package/plugins/extract-go/httpclient_test.go +0 -296
- package/plugins/extract-go/ids.go +0 -92
- package/plugins/extract-go/layout.go +0 -225
- package/plugins/extract-go/layout_test.go +0 -125
- package/plugins/extract-go/lifecycle.go +0 -324
- package/plugins/extract-go/lifecycle_test.go +0 -108
- package/plugins/extract-go/main.go +0 -79
- package/plugins/extract-go/operation.go +0 -157
- package/plugins/extract-go/source.go +0 -316
- package/plugins/extract-go/transport.go +0 -321
- package/plugins/extract-go/transport_test.go +0 -145
- package/plugins/extract-go/wiring.go +0 -434
- package/plugins/extract-go-nats/describe.go +0 -19
- package/plugins/extract-go-nats/describe_test.go +0 -11
- package/plugins/extract-go-nats/extract.go +0 -177
- package/plugins/extract-go-nats/extract_test.go +0 -389
- package/plugins/extract-go-nats/index.go +0 -379
- package/plugins/extract-go-nats/main.go +0 -42
- package/plugins/extract-go-nats/resolve.go +0 -161
- package/plugins/extract-go-nats/sites.go +0 -224
- package/plugins/extract-graphql/describe.go +0 -19
- package/plugins/extract-graphql/describe_test.go +0 -11
- package/plugins/extract-graphql/extract.go +0 -433
- package/plugins/extract-graphql/extract_test.go +0 -256
- package/plugins/extract-graphql/ids.go +0 -49
- package/plugins/extract-graphql/lex.go +0 -237
- package/plugins/extract-graphql/main.go +0 -51
- package/plugins/extract-graphql/parse.go +0 -621
- package/plugins/extract-graphql/parse_test.go +0 -122
- package/plugins/extract-http-clients/describe.go +0 -19
- package/plugins/extract-http-clients/describe_test.go +0 -11
- package/plugins/extract-http-clients/extract.go +0 -705
- package/plugins/extract-http-clients/extract_test.go +0 -1263
- package/plugins/extract-http-clients/main.go +0 -42
- package/plugins/extract-openapi/describe.go +0 -19
- package/plugins/extract-openapi/describe_test.go +0 -11
- package/plugins/extract-openapi/discover.go +0 -243
- package/plugins/extract-openapi/extract.go +0 -526
- package/plugins/extract-openapi/extract_test.go +0 -545
- package/plugins/extract-openapi/main.go +0 -75
- package/plugins/extract-openapi/spec.go +0 -350
- package/plugins/extract-project/describe.go +0 -19
- package/plugins/extract-project/describe_test.go +0 -11
- package/plugins/extract-project/extract.go +0 -221
- package/plugins/extract-project/extract_test.go +0 -109
- package/plugins/extract-project/main.go +0 -41
- package/plugins/extract-proto/ast.go +0 -125
- package/plugins/extract-proto/consumes.go +0 -77
- package/plugins/extract-proto/describe.go +0 -19
- package/plugins/extract-proto/extract.go +0 -293
- package/plugins/extract-proto/extract_test.go +0 -459
- package/plugins/extract-proto/ids.go +0 -89
- package/plugins/extract-proto/ids_test.go +0 -57
- package/plugins/extract-proto/lex.go +0 -285
- package/plugins/extract-proto/main.go +0 -100
- package/plugins/extract-proto/module.go +0 -120
- package/plugins/extract-proto/parse.go +0 -720
- package/plugins/extract-proto/parse_test.go +0 -307
- package/plugins/extract-proto/provides.go +0 -236
- package/plugins/extract-proto/resolve.go +0 -222
- package/plugins/extract-proto/resolve_test.go +0 -119
- package/plugins/extract-redis/describe.go +0 -19
- package/plugins/extract-redis/describe_test.go +0 -11
- package/plugins/extract-redis/extract.go +0 -183
- package/plugins/extract-redis/extract_test.go +0 -168
- package/plugins/extract-redis/keyspaces.go +0 -469
- package/plugins/extract-redis/main.go +0 -44
- package/plugins/extract-river/describe.go +0 -19
- package/plugins/extract-river/describe_test.go +0 -11
- package/plugins/extract-river/extract.go +0 -507
- package/plugins/extract-river/extract_test.go +0 -132
- package/plugins/extract-river/main.go +0 -42
- package/plugins/extract-sql/ddl.go +0 -893
- package/plugins/extract-sql/ddl_test.go +0 -401
- package/plugins/extract-sql/describe.go +0 -19
- package/plugins/extract-sql/describe_test.go +0 -11
- package/plugins/extract-sql/layout.go +0 -221
- package/plugins/extract-sql/layout_test.go +0 -55
- package/plugins/extract-sql/lineage.go +0 -107
- package/plugins/extract-sql/main.go +0 -141
- package/plugins/extract-sql/maps.go +0 -564
- package/plugins/extract-sql/maps_java.go +0 -117
- package/plugins/extract-sql/maps_rust.go +0 -333
- package/plugins/extract-sql/maps_rust_test.go +0 -70
- package/plugins/extract-sql/maps_test.go +0 -204
- package/plugins/extract-sql/maps_ts.go +0 -398
- package/plugins/extract-sql/maps_ts_test.go +0 -136
- package/plugins/extract-sql/projection.go +0 -70
- package/plugins/extract-sql/projection_test.go +0 -50
- package/plugins/extract-sql/store.go +0 -420
- package/plugins/extract-sql/store_test.go +0 -233
- package/plugins/extract-sql/view.go +0 -295
- package/plugins/extract-watermill/describe.go +0 -19
- package/plugins/extract-watermill/describe_test.go +0 -11
- package/plugins/extract-watermill/extract.go +0 -1228
- package/plugins/extract-watermill/extract_test.go +0 -234
- package/plugins/extract-watermill/main.go +0 -41
- package/plugins/extract-wsdl/describe.go +0 -19
- package/plugins/extract-wsdl/describe_test.go +0 -11
- package/plugins/extract-wsdl/extract.go +0 -148
- package/plugins/extract-wsdl/extract_test.go +0 -45
- package/plugins/extract-wsdl/main.go +0 -53
- package/plugins/fetch-bsr/auth.go +0 -114
- package/plugins/fetch-bsr/bsr.go +0 -240
- package/plugins/fetch-bsr/cache.go +0 -66
- package/plugins/fetch-bsr/describe.go +0 -19
- package/plugins/fetch-bsr/fetch.go +0 -192
- package/plugins/fetch-bsr/fetch_test.go +0 -465
- package/plugins/fetch-bsr/lock.go +0 -71
- package/plugins/fetch-bsr/main.go +0 -117
- package/plugins/fetch-csr/auth.go +0 -95
- package/plugins/fetch-csr/cache.go +0 -69
- package/plugins/fetch-csr/describe.go +0 -19
- package/plugins/fetch-csr/describe_test.go +0 -11
- package/plugins/fetch-csr/fetch.go +0 -254
- package/plugins/fetch-csr/fetch_test.go +0 -494
- package/plugins/fetch-csr/lock.go +0 -95
- package/plugins/fetch-csr/main.go +0 -122
- package/plugins/fetch-csr/registry.go +0 -209
- package/plugins/fetch-git/cache.go +0 -65
- package/plugins/fetch-git/describe.go +0 -19
- package/plugins/fetch-git/describe_test.go +0 -11
- package/plugins/fetch-git/fetch.go +0 -181
- package/plugins/fetch-git/fetch_test.go +0 -332
- package/plugins/fetch-git/git.go +0 -169
- package/plugins/fetch-git/lock.go +0 -72
- package/plugins/fetch-git/main.go +0 -127
- package/plugins/fetch-git/offline.go +0 -39
- package/plugins/fetch-git/pin.go +0 -90
- package/plugins/fetch-git/pin_test.go +0 -74
- package/plugins/gen-backstage/describe.go +0 -17
- package/plugins/gen-backstage/main.go +0 -22
- package/plugins/gen-backstage/plugin.go +0 -473
- package/plugins/gen-backstage/plugin_test.go +0 -145
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +0 -162
- package/plugins/gen-markdown/aggregate.go +0 -362
- package/plugins/gen-markdown/canonical.go +0 -93
- package/plugins/gen-markdown/context.go +0 -90
- package/plugins/gen-markdown/coverage_test.go +0 -89
- package/plugins/gen-markdown/describe.go +0 -19
- package/plugins/gen-markdown/describe_test.go +0 -11
- package/plugins/gen-markdown/external.go +0 -71
- package/plugins/gen-markdown/flow.go +0 -277
- package/plugins/gen-markdown/glossary.go +0 -76
- package/plugins/gen-markdown/glossary_test.go +0 -148
- package/plugins/gen-markdown/llms.go +0 -302
- package/plugins/gen-markdown/main.go +0 -22
- package/plugins/gen-markdown/markdown.go +0 -254
- package/plugins/gen-markdown/markdown_test.go +0 -100
- package/plugins/gen-markdown/module.go +0 -107
- package/plugins/gen-markdown/plugin.go +0 -39
- package/plugins/gen-markdown/quality_test.go +0 -176
- package/plugins/gen-markdown/redis_test.go +0 -38
- package/plugins/gen-markdown/render.go +0 -344
- package/plugins/gen-markdown/render_test.go +0 -186
- package/plugins/gen-markdown/service.go +0 -456
- package/plugins/gen-markdown/source.go +0 -177
- package/plugins/gen-markdown/store.go +0 -201
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +0 -17
- package/plugins/gen-mermaid/main.go +0 -22
- package/plugins/gen-mermaid/plugin.go +0 -104
- package/plugins/gen-mermaid/plugin_test.go +0 -33
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +0 -261
- package/plugins/openapi/ids_test.go +0 -98
- package/plugins/verify-codeowners/describe.go +0 -19
- package/plugins/verify-codeowners/describe_test.go +0 -11
- package/plugins/verify-codeowners/main.go +0 -75
- package/plugins/verify-codeowners/match.go +0 -85
- package/plugins/verify-codeowners/match_test.go +0 -47
- package/plugins/verify-codeowners/owners.go +0 -164
- package/plugins/verify-codeowners/owners_test.go +0 -225
- package/plugins/verify-codeowners/parse.go +0 -90
- package/plugins/verify-codeowners/parse_test.go +0 -62
- package/plugins/verify-otel/describe.go +0 -19
- package/plugins/verify-otel/describe_test.go +0 -11
- package/plugins/verify-otel/main.go +0 -53
- package/plugins/verify-otel/match.go +0 -336
- package/plugins/verify-otel/otlp.go +0 -200
- package/plugins/verify-otel/verify.go +0 -734
- package/plugins/verify-otel/verify_test.go +0 -460
- /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
- /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// The tour's second stop: one real flow, played.
|
|
2
|
+
//
|
|
3
|
+
// The steps on the left are the flow's own, in order, with the status each
|
|
4
|
+
// one has in the catalog. The canvas on the right is the flow's participants,
|
|
5
|
+
// placed by elk, with one line per direction a message travels. The playback
|
|
6
|
+
// walks the steps: the current one is lit on the list, and its line and its
|
|
7
|
+
// two ends are lit on the canvas while everything else dims - the reading a
|
|
8
|
+
// finger would do down a sequence diagram, done for the reader.
|
|
9
|
+
//
|
|
10
|
+
// It plays while it is on screen and stops when it is not, when the reader
|
|
11
|
+
// pauses it, when they pick a step, and when they asked for less motion.
|
|
12
|
+
|
|
13
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
14
|
+
import { Link } from "react-router";
|
|
15
|
+
import { Background, ReactFlow, ReactFlowProvider } from "@xyflow/react";
|
|
16
|
+
import type { Edge } from "@xyflow/react";
|
|
17
|
+
import "@xyflow/react/dist/style.css";
|
|
18
|
+
import { useInView } from "motion/react";
|
|
19
|
+
import { ArrowRight, Pause, Play } from "lucide-react";
|
|
20
|
+
import type { Flow, FlowNode, Step } from "../catalog";
|
|
21
|
+
import { DiagramSkeleton } from "../components/DiagramSkeleton";
|
|
22
|
+
import { catalog } from "../data";
|
|
23
|
+
import { nodeTypes } from "../graph/nodes";
|
|
24
|
+
import type { ServiceNodeData } from "../graph/nodes";
|
|
25
|
+
import { EDGE_W, EDGE_W_LIT } from "../graph/theme";
|
|
26
|
+
import { useElkFlow } from "../graph/useElkFlow";
|
|
27
|
+
import type { FlowSpec } from "../graph/useElkFlow";
|
|
28
|
+
import { worstStatus } from "../lib/event-graph";
|
|
29
|
+
import { LayoutGroup, m, transitions, useReducedMotion } from "../lib/motion";
|
|
30
|
+
import { paths } from "../routes";
|
|
31
|
+
import { catalogTo } from "./catalog";
|
|
32
|
+
|
|
33
|
+
const FLOW_ID = "flow.cart-checkout";
|
|
34
|
+
const STEP_MS = 1600;
|
|
35
|
+
const LANE_NODE_W = 144;
|
|
36
|
+
|
|
37
|
+
/** The steps in reading order, whatever frames they sit in. */
|
|
38
|
+
function flatSteps(nodes: readonly FlowNode[]): Step[] {
|
|
39
|
+
const out: Step[] = [];
|
|
40
|
+
for (const node of nodes) {
|
|
41
|
+
if (node.type === "step") out.push(node);
|
|
42
|
+
else if (node.type === "parallel")
|
|
43
|
+
for (const branch of node.branches) out.push(...flatSteps(branch));
|
|
44
|
+
else if (node.type === "alt")
|
|
45
|
+
for (const branch of node.branches) out.push(...flatSteps(branch.steps));
|
|
46
|
+
else out.push(...flatSteps(node.steps));
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const laneId = (step: Step): string => `${step.from}->${step.to}`;
|
|
52
|
+
|
|
53
|
+
/** What the box says under the eyebrow: a service by its id, anything else by what it is. */
|
|
54
|
+
function participantData(
|
|
55
|
+
participant: Flow["participants"][number],
|
|
56
|
+
): ServiceNodeData {
|
|
57
|
+
return {
|
|
58
|
+
label: participant.label ?? participant.id,
|
|
59
|
+
context: participant.context,
|
|
60
|
+
ghost: participant.kind === "unknown",
|
|
61
|
+
kind: "service",
|
|
62
|
+
role: participant.kind,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function Canvas({ flow, steps, at }: { flow: Flow; steps: Step[]; at: number }) {
|
|
67
|
+
const spec = useMemo<FlowSpec>(() => {
|
|
68
|
+
const lanes = new Map<string, Step[]>();
|
|
69
|
+
for (const step of steps) {
|
|
70
|
+
const lane = lanes.get(laneId(step));
|
|
71
|
+
if (lane) lane.push(step);
|
|
72
|
+
else lanes.set(laneId(step), [step]);
|
|
73
|
+
}
|
|
74
|
+
// Narrow boxes and no labels for elk to leave room for: the label a lane
|
|
75
|
+
// shows is the current step's, put on at render time, and the whole
|
|
76
|
+
// picture has to fit a box half the width of the graph page's.
|
|
77
|
+
return {
|
|
78
|
+
nodes: flow.participants.map((p) => ({
|
|
79
|
+
id: p.id,
|
|
80
|
+
width: LANE_NODE_W,
|
|
81
|
+
data: participantData(p),
|
|
82
|
+
})),
|
|
83
|
+
edges: [...lanes.entries()].map(([id, lane]) => ({
|
|
84
|
+
id,
|
|
85
|
+
source: lane[0]!.from,
|
|
86
|
+
target: lane[0]!.to,
|
|
87
|
+
label: "",
|
|
88
|
+
status: worstStatus(lane.map((s) => s.status)),
|
|
89
|
+
})),
|
|
90
|
+
direction: "RIGHT",
|
|
91
|
+
layerSpacing: 48,
|
|
92
|
+
};
|
|
93
|
+
}, [flow, steps]);
|
|
94
|
+
const state = useElkFlow(spec);
|
|
95
|
+
|
|
96
|
+
const active = steps[at] ?? null;
|
|
97
|
+
const lit = active ? laneId(active) : null;
|
|
98
|
+
|
|
99
|
+
const nodes = useMemo(
|
|
100
|
+
() =>
|
|
101
|
+
state.nodes.map((node) => ({
|
|
102
|
+
...node,
|
|
103
|
+
style: {
|
|
104
|
+
...node.style,
|
|
105
|
+
opacity:
|
|
106
|
+
!active || node.id === active.from || node.id === active.to
|
|
107
|
+
? 1
|
|
108
|
+
: 0.4,
|
|
109
|
+
},
|
|
110
|
+
})),
|
|
111
|
+
[state.nodes, active],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const edges: Edge[] = useMemo(
|
|
115
|
+
() =>
|
|
116
|
+
state.edges.map((edge) => {
|
|
117
|
+
const on = edge.id === lit;
|
|
118
|
+
return {
|
|
119
|
+
...edge,
|
|
120
|
+
label: on ? (active?.label ?? "") : "",
|
|
121
|
+
style: {
|
|
122
|
+
...edge.style,
|
|
123
|
+
opacity: lit && !on ? 0.3 : 1,
|
|
124
|
+
strokeWidth: on ? EDGE_W_LIT : EDGE_W,
|
|
125
|
+
},
|
|
126
|
+
zIndex: on ? 10 : 0,
|
|
127
|
+
};
|
|
128
|
+
}),
|
|
129
|
+
[state.edges, lit, active],
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div className="canvas-motion relative h-[340px] overflow-hidden rounded-card border border-line">
|
|
134
|
+
{state.ready ? null : <DiagramSkeleton />}
|
|
135
|
+
<ReactFlow
|
|
136
|
+
nodes={nodes}
|
|
137
|
+
edges={edges}
|
|
138
|
+
nodeTypes={nodeTypes}
|
|
139
|
+
nodesDraggable={false}
|
|
140
|
+
nodesConnectable={false}
|
|
141
|
+
elementsSelectable={false}
|
|
142
|
+
zoomOnScroll={false}
|
|
143
|
+
preventScrolling={false}
|
|
144
|
+
proOptions={{ hideAttribution: true }}
|
|
145
|
+
fitView
|
|
146
|
+
fitViewOptions={{ padding: 0.18, maxZoom: 1.1 }}
|
|
147
|
+
minZoom={0.3}
|
|
148
|
+
maxZoom={1.6}
|
|
149
|
+
key={state.ready ? `fit-${state.nodes.length}` : "pending"}
|
|
150
|
+
>
|
|
151
|
+
<Background gap={20} size={2} />
|
|
152
|
+
</ReactFlow>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function FlowPlayback() {
|
|
158
|
+
const flow = useMemo(
|
|
159
|
+
() => catalog.flows.find((f) => f.id === FLOW_ID) ?? catalog.flows[0] ?? null,
|
|
160
|
+
[],
|
|
161
|
+
);
|
|
162
|
+
const steps = useMemo(() => (flow ? flatSteps(flow.steps) : []), [flow]);
|
|
163
|
+
const [at, setAt] = useState(0);
|
|
164
|
+
const [playing, setPlaying] = useState(true);
|
|
165
|
+
const box = useRef<HTMLDivElement>(null);
|
|
166
|
+
const inView = useInView(box, { amount: 0.4 });
|
|
167
|
+
const reduced = useReducedMotion();
|
|
168
|
+
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (!playing || !inView || reduced || steps.length < 2) return;
|
|
171
|
+
const every = window.setInterval(
|
|
172
|
+
() => setAt((i) => (i + 1) % steps.length),
|
|
173
|
+
STEP_MS,
|
|
174
|
+
);
|
|
175
|
+
return () => window.clearInterval(every);
|
|
176
|
+
}, [playing, inView, reduced, steps.length]);
|
|
177
|
+
|
|
178
|
+
if (!flow) {
|
|
179
|
+
return (
|
|
180
|
+
<div className="p-6 text-sm text-muted">
|
|
181
|
+
The example catalog has no flows to play.
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const verified = steps.filter((s) => s.status === "verified").length;
|
|
187
|
+
const health =
|
|
188
|
+
verified === steps.length
|
|
189
|
+
? "status-verified"
|
|
190
|
+
: steps.some((s) => s.status === "unresolved")
|
|
191
|
+
? "status-unresolved"
|
|
192
|
+
: "status-declared";
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div ref={box} className="p-4 sm:p-5">
|
|
196
|
+
<div className="mb-4 flex flex-wrap items-start justify-between gap-3">
|
|
197
|
+
<div className="min-w-0">
|
|
198
|
+
<div className="mono truncate text-faint">flow / {flow.slug}</div>
|
|
199
|
+
<div className="mt-1 text-md font-semibold text-ink">{flow.name}</div>
|
|
200
|
+
</div>
|
|
201
|
+
<div className="flex items-center gap-2">
|
|
202
|
+
<span className={`chip ${health}`}>
|
|
203
|
+
{verified} / {steps.length} verified
|
|
204
|
+
</span>
|
|
205
|
+
<button
|
|
206
|
+
type="button"
|
|
207
|
+
className="tbtn size-8 justify-center p-0"
|
|
208
|
+
onClick={() => setPlaying((p) => !p)}
|
|
209
|
+
aria-label={playing ? "Pause playback" : "Play the flow"}
|
|
210
|
+
aria-pressed={playing}
|
|
211
|
+
>
|
|
212
|
+
{playing ? <Pause size={14} /> : <Play size={14} />}
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
<div className="grid gap-4 lg:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]">
|
|
217
|
+
<LayoutGroup id="flow-playback">
|
|
218
|
+
<ol
|
|
219
|
+
className="overflow-hidden rounded-card border border-line bg-canvas"
|
|
220
|
+
aria-label={`Steps of ${flow.name}`}
|
|
221
|
+
>
|
|
222
|
+
{steps.map((step, index) => {
|
|
223
|
+
const on = index === at;
|
|
224
|
+
return (
|
|
225
|
+
<li key={step.id} className="relative border-b border-line last:border-b-0">
|
|
226
|
+
{on ? (
|
|
227
|
+
<m.span
|
|
228
|
+
layoutId="flow-cursor"
|
|
229
|
+
transition={transitions.settle}
|
|
230
|
+
className="absolute inset-0 bg-accent/8"
|
|
231
|
+
aria-hidden
|
|
232
|
+
/>
|
|
233
|
+
) : null}
|
|
234
|
+
<button
|
|
235
|
+
type="button"
|
|
236
|
+
onClick={() => {
|
|
237
|
+
setAt(index);
|
|
238
|
+
setPlaying(false);
|
|
239
|
+
}}
|
|
240
|
+
aria-current={on ? "step" : undefined}
|
|
241
|
+
className={`relative grid w-full grid-cols-[26px_minmax(0,1fr)_auto] items-center gap-3 px-3 py-2 text-left t-narrative ${
|
|
242
|
+
on ? "opacity-100" : "opacity-70 hover:opacity-100"
|
|
243
|
+
}`}
|
|
244
|
+
>
|
|
245
|
+
<span className="mono tnum text-faint">
|
|
246
|
+
{String(index + 1).padStart(2, "0")}
|
|
247
|
+
</span>
|
|
248
|
+
<span className="min-w-0">
|
|
249
|
+
<span className="block truncate font-medium text-ink">
|
|
250
|
+
{step.label ?? step.ref ?? step.kind}
|
|
251
|
+
</span>
|
|
252
|
+
<span className="mono block truncate text-muted">
|
|
253
|
+
{step.from} → {step.to}
|
|
254
|
+
</span>
|
|
255
|
+
</span>
|
|
256
|
+
<span className={`chip status-${step.status}`}>
|
|
257
|
+
{step.kind}
|
|
258
|
+
</span>
|
|
259
|
+
</button>
|
|
260
|
+
</li>
|
|
261
|
+
);
|
|
262
|
+
})}
|
|
263
|
+
</ol>
|
|
264
|
+
</LayoutGroup>
|
|
265
|
+
<ReactFlowProvider>
|
|
266
|
+
<Canvas flow={flow} steps={steps} at={at} />
|
|
267
|
+
</ReactFlowProvider>
|
|
268
|
+
</div>
|
|
269
|
+
<div className="mt-3 flex items-center justify-between gap-3">
|
|
270
|
+
<span className="mono truncate text-faint">
|
|
271
|
+
{steps[at]?.line ?? flow.source ?? ""}
|
|
272
|
+
</span>
|
|
273
|
+
<Link
|
|
274
|
+
to={catalogTo(paths.flow(flow.slug))}
|
|
275
|
+
className="inline-flex shrink-0 items-center gap-1 text-sm text-accent hover:underline"
|
|
276
|
+
>
|
|
277
|
+
Open the flow <ArrowRight size={13} />
|
|
278
|
+
</Link>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// The hero's picture: the example estate's context map, drawn by the same
|
|
2
|
+
// component that draws it on the map page, with elk placing the domains and
|
|
3
|
+
// one line per relationship.
|
|
4
|
+
//
|
|
5
|
+
// It tours itself until the reader takes over. Every few seconds the next
|
|
6
|
+
// domain is selected - which lights its lines and dims the rest, exactly as a
|
|
7
|
+
// click would - and a card names it. A pointer over the map pauses the tour;
|
|
8
|
+
// a click ends it, because from then on the selection is the reader's.
|
|
9
|
+
|
|
10
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
11
|
+
import { Link } from "react-router";
|
|
12
|
+
import { ArrowRight, Boxes } from "lucide-react";
|
|
13
|
+
import { catalog } from "../data";
|
|
14
|
+
import { contextVar } from "../lib/context-color";
|
|
15
|
+
import { contextMap } from "../lib/context-map";
|
|
16
|
+
import { contextStats } from "../lib/derive";
|
|
17
|
+
import { AnimatePresence, m, rise, useReducedMotion } from "../lib/motion";
|
|
18
|
+
import { ContextMapPane } from "../map/ContextMapGraph";
|
|
19
|
+
import { paths } from "../routes";
|
|
20
|
+
import { useSelectionStore } from "../selection/store";
|
|
21
|
+
import { catalogTo, useSelectionCleared } from "./catalog";
|
|
22
|
+
|
|
23
|
+
const FIRST_MS = 1400;
|
|
24
|
+
const EVERY_MS = 2800;
|
|
25
|
+
|
|
26
|
+
export function HeroMap() {
|
|
27
|
+
const relations = useMemo(() => contextMap(catalog), []);
|
|
28
|
+
const selectionId = useSelectionStore((s) => s.selection?.id ?? null);
|
|
29
|
+
const select = useSelectionStore((s) => s.select);
|
|
30
|
+
const reduced = useReducedMotion();
|
|
31
|
+
const [touched, setTouched] = useState(false);
|
|
32
|
+
const [hovering, setHovering] = useState(false);
|
|
33
|
+
const at = useRef(0);
|
|
34
|
+
useSelectionCleared();
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (touched || hovering || reduced) return;
|
|
38
|
+
const ids = catalog.contexts.map((c) => c.id);
|
|
39
|
+
if (ids.length === 0) return;
|
|
40
|
+
const tick = () => {
|
|
41
|
+
select(ids[at.current % ids.length]!, "diagram");
|
|
42
|
+
at.current += 1;
|
|
43
|
+
};
|
|
44
|
+
let every = 0;
|
|
45
|
+
const first = window.setTimeout(() => {
|
|
46
|
+
tick();
|
|
47
|
+
every = window.setInterval(tick, EVERY_MS);
|
|
48
|
+
}, FIRST_MS);
|
|
49
|
+
return () => {
|
|
50
|
+
window.clearTimeout(first);
|
|
51
|
+
window.clearInterval(every);
|
|
52
|
+
};
|
|
53
|
+
}, [touched, hovering, reduced, select]);
|
|
54
|
+
|
|
55
|
+
const context = catalog.contexts.find((c) => c.id === selectionId) ?? null;
|
|
56
|
+
const stats = context ? contextStats(context) : null;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
className="relative h-full"
|
|
61
|
+
onPointerDown={() => setTouched(true)}
|
|
62
|
+
onPointerEnter={() => setHovering(true)}
|
|
63
|
+
onPointerLeave={() => setHovering(false)}
|
|
64
|
+
>
|
|
65
|
+
{/* The map stops above the strip, so the strip never covers a domain;
|
|
66
|
+
the frame's dotted ground runs on beneath both. */}
|
|
67
|
+
<div className="absolute inset-x-0 top-0 bottom-[68px]">
|
|
68
|
+
<ContextMapPane
|
|
69
|
+
catalog={catalog}
|
|
70
|
+
relations={relations}
|
|
71
|
+
zoomOnScroll={false}
|
|
72
|
+
compactLayout
|
|
73
|
+
showExport={false}
|
|
74
|
+
inactiveEdgeOpacity={0.36}
|
|
75
|
+
inactiveNodeOpacity={0.78}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
<AnimatePresence mode="wait">
|
|
79
|
+
{context && stats ? (
|
|
80
|
+
<m.div
|
|
81
|
+
key={context.id}
|
|
82
|
+
{...rise}
|
|
83
|
+
className="pointer-events-none absolute right-3 bottom-3 left-3 z-10 flex items-center gap-3 rounded-card border border-line bg-canvas/95 px-3 py-2 shadow-md backdrop-blur"
|
|
84
|
+
>
|
|
85
|
+
<span
|
|
86
|
+
className="flow-tile shrink-0"
|
|
87
|
+
style={{ color: contextVar(context.id), width: 28, height: 28 }}
|
|
88
|
+
>
|
|
89
|
+
<Boxes size={15} aria-hidden />
|
|
90
|
+
</span>
|
|
91
|
+
<div className="min-w-0 flex-1">
|
|
92
|
+
<div className="flex items-baseline gap-2">
|
|
93
|
+
<span className="truncate font-semibold text-ink">
|
|
94
|
+
{context.name}
|
|
95
|
+
</span>
|
|
96
|
+
<span className="mono shrink-0 text-faint">
|
|
97
|
+
{context.id}
|
|
98
|
+
{context.classification ? ` · ${context.classification}` : ""}
|
|
99
|
+
</span>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="truncate text-sm text-muted">
|
|
102
|
+
{context.summary || "\u00a0"}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="mono hidden shrink-0 gap-x-3 text-faint md:flex">
|
|
106
|
+
<span>
|
|
107
|
+
<span className="tnum text-ink">{stats.services}</span> services
|
|
108
|
+
</span>
|
|
109
|
+
<span>
|
|
110
|
+
<span className="tnum text-ink">{stats.aggregates}</span>{" "}
|
|
111
|
+
aggregates
|
|
112
|
+
</span>
|
|
113
|
+
<span>
|
|
114
|
+
<span className="tnum text-ink">{stats.events}</span> events
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
<Link
|
|
118
|
+
to={catalogTo(paths.context(context.id))}
|
|
119
|
+
className="pointer-events-auto inline-flex shrink-0 items-center gap-1 text-sm text-accent hover:underline"
|
|
120
|
+
>
|
|
121
|
+
Open <ArrowRight size={13} />
|
|
122
|
+
</Link>
|
|
123
|
+
</m.div>
|
|
124
|
+
) : null}
|
|
125
|
+
</AnimatePresence>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|