@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,757 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrowRight,
|
|
3
|
+
Bot,
|
|
4
|
+
Braces,
|
|
5
|
+
Check,
|
|
6
|
+
CircleDotDashed,
|
|
7
|
+
Code2,
|
|
8
|
+
Copy,
|
|
9
|
+
FileSearch,
|
|
10
|
+
Files,
|
|
11
|
+
FileText,
|
|
12
|
+
GitBranch,
|
|
13
|
+
Moon,
|
|
14
|
+
Network,
|
|
15
|
+
ShieldCheck,
|
|
16
|
+
Sun,
|
|
17
|
+
Terminal,
|
|
18
|
+
} from "lucide-react";
|
|
19
|
+
import { lazy, Suspense, useState } from "react";
|
|
20
|
+
import { Link } from "react-router";
|
|
21
|
+
import { CompassRose, Wordmark } from "../components/logo";
|
|
22
|
+
import { DiagramSkeleton } from "../components/DiagramSkeleton";
|
|
23
|
+
import { useTheme } from "../app/theme";
|
|
24
|
+
import { useDocumentTitle } from "../app/title";
|
|
25
|
+
import { catalog } from "../data";
|
|
26
|
+
import { m } from "../lib/motion";
|
|
27
|
+
import { paths } from "../routes";
|
|
28
|
+
import { catalogTo } from "./catalog";
|
|
29
|
+
import { DraggableReveal } from "./DraggableReveal";
|
|
30
|
+
import { heroColumn, heroLine, Reveal } from "./motion";
|
|
31
|
+
import { ProductFrame } from "./ProductFrame";
|
|
32
|
+
import { ProductTour } from "./ProductTour";
|
|
33
|
+
|
|
34
|
+
const REPOSITORY = "https://github.com/shortlink-org/portolan";
|
|
35
|
+
const EXAMPLE_DOCS = "https://shortlink-org.github.io/portolan/docs/example";
|
|
36
|
+
const exampleTo = catalogTo(paths.overview());
|
|
37
|
+
|
|
38
|
+
// The hero's map carries elk and React Flow. The words arrive first; the map
|
|
39
|
+
// follows a moment later into a frame that already has its height.
|
|
40
|
+
const HeroMap = lazy(() =>
|
|
41
|
+
import("./HeroMap").then((mod) => ({ default: mod.HeroMap })),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const EmbeddedChat =
|
|
45
|
+
import.meta.env.VITE_CHAT !== "off"
|
|
46
|
+
? lazy(() =>
|
|
47
|
+
import("../chat/ChatPanel").then((mod) => ({
|
|
48
|
+
default: mod.ChatSurface,
|
|
49
|
+
})),
|
|
50
|
+
)
|
|
51
|
+
: null;
|
|
52
|
+
|
|
53
|
+
function Header() {
|
|
54
|
+
const { theme, toggle } = useTheme();
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<header className="sticky top-0 z-50 border-b border-line bg-canvas/85 backdrop-blur-xl">
|
|
58
|
+
<div className="mx-auto flex h-16 max-w-[1200px] items-center gap-5 px-5 sm:px-8">
|
|
59
|
+
<a href="#top" aria-label="Portolan home">
|
|
60
|
+
<Wordmark size={19} />
|
|
61
|
+
</a>
|
|
62
|
+
<nav
|
|
63
|
+
aria-label="Landing page"
|
|
64
|
+
className="ml-5 hidden items-center gap-5 text-sm text-muted md:flex"
|
|
65
|
+
>
|
|
66
|
+
<a className="hover:text-ink" href="#product">
|
|
67
|
+
Product
|
|
68
|
+
</a>
|
|
69
|
+
<a className="hover:text-ink" href="#how-it-works">
|
|
70
|
+
How it works
|
|
71
|
+
</a>
|
|
72
|
+
<a className="hover:text-ink" href="#ai-output">
|
|
73
|
+
For agents
|
|
74
|
+
</a>
|
|
75
|
+
<a className="hover:text-ink" href="#get-started">
|
|
76
|
+
Get started
|
|
77
|
+
</a>
|
|
78
|
+
</nav>
|
|
79
|
+
<div className="ml-auto flex items-center gap-2">
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
onClick={toggle}
|
|
83
|
+
className="tbtn size-8 justify-center p-0"
|
|
84
|
+
aria-label={`Use ${theme === "dark" ? "light" : "dark"} theme`}
|
|
85
|
+
>
|
|
86
|
+
{theme === "dark" ? <Sun size={15} /> : <Moon size={15} />}
|
|
87
|
+
</button>
|
|
88
|
+
<a
|
|
89
|
+
href={REPOSITORY}
|
|
90
|
+
className="tbtn hidden sm:flex"
|
|
91
|
+
target="_blank"
|
|
92
|
+
rel="noreferrer"
|
|
93
|
+
>
|
|
94
|
+
<GitBranch size={15} /> GitHub
|
|
95
|
+
</a>
|
|
96
|
+
<Link to={exampleTo} className="btn-accent landing-primary">
|
|
97
|
+
Explore example <ArrowRight size={14} />
|
|
98
|
+
</Link>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</header>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function HeroDemo() {
|
|
106
|
+
const services = catalog.contexts.reduce((n, c) => n + c.services.length, 0);
|
|
107
|
+
const stats: Array<[number, string]> = [
|
|
108
|
+
[catalog.contexts.length, "contexts"],
|
|
109
|
+
[services, "services"],
|
|
110
|
+
[catalog.flows.length, "flows"],
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<ProductFrame
|
|
115
|
+
title="Context map · example estate"
|
|
116
|
+
eyebrow="estate"
|
|
117
|
+
className="landing-product-shadow"
|
|
118
|
+
aside={false}
|
|
119
|
+
>
|
|
120
|
+
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-line bg-canvas px-4 py-3">
|
|
121
|
+
<div className="min-w-0">
|
|
122
|
+
<div className="font-semibold text-ink">Context map</div>
|
|
123
|
+
<div className="mono mt-0.5 truncate text-faint">
|
|
124
|
+
one line per relationship · select a domain · open its catalog
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div className="flex gap-2">
|
|
128
|
+
{stats.map(([value, label]) => (
|
|
129
|
+
<div
|
|
130
|
+
key={label}
|
|
131
|
+
className="rounded-control border border-line bg-canvas px-2 py-1"
|
|
132
|
+
>
|
|
133
|
+
<span className="tnum font-semibold text-ink">{value}</span>{" "}
|
|
134
|
+
<span className="mono text-faint">{label}</span>
|
|
135
|
+
</div>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div className="relative h-[420px] sm:h-[440px] lg:h-[420px]">
|
|
140
|
+
<Suspense fallback={<DiagramSkeleton />}>
|
|
141
|
+
<HeroMap />
|
|
142
|
+
</Suspense>
|
|
143
|
+
</div>
|
|
144
|
+
</ProductFrame>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const inputGroups = [
|
|
149
|
+
{ label: "code", items: ["Go", "TypeScript", "Java", "Rust", "Django"] },
|
|
150
|
+
{ label: "contracts", items: ["OpenAPI", "GraphQL", "Protobuf"] },
|
|
151
|
+
{ label: "runtime + data", items: ["SQL", "OTel"] },
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const features = [
|
|
155
|
+
{
|
|
156
|
+
icon: FileSearch,
|
|
157
|
+
title: "Source-level answers",
|
|
158
|
+
copy: "Open the exact file and line behind an event, field, call or flow step without leaving the architecture context.",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
icon: ShieldCheck,
|
|
162
|
+
title: "Drift caught before merge",
|
|
163
|
+
copy: "Unresolved calls, mismatched schemas, second writers and missing publishers become explicit checks instead of tribal knowledge.",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
icon: GitBranch,
|
|
167
|
+
title: "Made for pull requests",
|
|
168
|
+
copy: "Generated docs, diagrams and exports are ordinary files, while portolan check fails when committed architecture is stale.",
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
const agentDocs = [
|
|
173
|
+
{
|
|
174
|
+
icon: FileText,
|
|
175
|
+
name: "llms.txt",
|
|
176
|
+
label: "Index for retrieval",
|
|
177
|
+
href: `${EXAMPLE_DOCS}/llms.txt`,
|
|
178
|
+
copy: "Contexts, services, aggregates, flows, glossaries and ADRs, linked to page-sized Markdown. An agent starts here and fetches only what the task needs.",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
icon: Files,
|
|
182
|
+
name: "llms-full.txt",
|
|
183
|
+
label: "Complete context",
|
|
184
|
+
href: `${EXAMPLE_DOCS}/llms-full.txt`,
|
|
185
|
+
copy: "Every generated architecture page in one ordered file, for models that have room to reason across the whole estate at once.",
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const quickStartCommand = "npx @shortlink-org/portolan init";
|
|
190
|
+
const setupCommands = [
|
|
191
|
+
quickStartCommand,
|
|
192
|
+
"npm install --save-dev @shortlink-org/portolan",
|
|
193
|
+
"npx portolan generate",
|
|
194
|
+
"npx portolan dev",
|
|
195
|
+
].join("\n");
|
|
196
|
+
|
|
197
|
+
function CopyCommand({
|
|
198
|
+
text,
|
|
199
|
+
label = "Copy",
|
|
200
|
+
className = "",
|
|
201
|
+
}: {
|
|
202
|
+
text: string;
|
|
203
|
+
label?: string;
|
|
204
|
+
className?: string;
|
|
205
|
+
}) {
|
|
206
|
+
const [copied, setCopied] = useState(false);
|
|
207
|
+
|
|
208
|
+
const copy = async () => {
|
|
209
|
+
let written = false;
|
|
210
|
+
if (navigator.clipboard) {
|
|
211
|
+
try {
|
|
212
|
+
await navigator.clipboard.writeText(text);
|
|
213
|
+
written = true;
|
|
214
|
+
} catch {
|
|
215
|
+
// Fall through to the selection-based path used by stricter browsers.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (!written) {
|
|
219
|
+
const field = document.createElement("textarea");
|
|
220
|
+
field.value = text;
|
|
221
|
+
field.readOnly = true;
|
|
222
|
+
field.style.position = "fixed";
|
|
223
|
+
field.style.left = "-9999px";
|
|
224
|
+
document.body.appendChild(field);
|
|
225
|
+
field.select();
|
|
226
|
+
written = document.execCommand("copy");
|
|
227
|
+
field.remove();
|
|
228
|
+
}
|
|
229
|
+
if (written) {
|
|
230
|
+
setCopied(true);
|
|
231
|
+
window.setTimeout(() => setCopied(false), 1600);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<button
|
|
237
|
+
type="button"
|
|
238
|
+
onClick={() => void copy()}
|
|
239
|
+
className={`tbtn shrink-0 ${className}`}
|
|
240
|
+
aria-label={`${label}: ${text}`}
|
|
241
|
+
>
|
|
242
|
+
{copied ? <Check size={14} /> : <Copy size={14} />}
|
|
243
|
+
{copied ? "Copied" : label}
|
|
244
|
+
</button>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const pipeline = [
|
|
249
|
+
{
|
|
250
|
+
icon: Code2,
|
|
251
|
+
label: "01 / read",
|
|
252
|
+
title: "Code and specifications",
|
|
253
|
+
copy: "Existing source, schemas, contracts, ADRs and traces.",
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
icon: Braces,
|
|
257
|
+
label: "02 / extract",
|
|
258
|
+
title: "Local fragments",
|
|
259
|
+
copy: "Each service publishes the architectural facts it owns.",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
icon: CircleDotDashed,
|
|
263
|
+
label: "03 / validate",
|
|
264
|
+
title: "One estate model",
|
|
265
|
+
copy: "Portolan merges the union and checks every relationship.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
icon: Network,
|
|
269
|
+
label: "04 / publish",
|
|
270
|
+
title: "A static catalog",
|
|
271
|
+
copy: "Browse the site or export Markdown, Mermaid and Backstage entities.",
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
export function LandingPage() {
|
|
276
|
+
useDocumentTitle("Architecture from code");
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div className="h-full overflow-y-auto scroll-smooth bg-canvas text-ink">
|
|
280
|
+
<Header />
|
|
281
|
+
<main id="top">
|
|
282
|
+
<section className="landing-hero relative overflow-hidden border-b border-line">
|
|
283
|
+
<div className="landing-orbit landing-orbit-one" aria-hidden />
|
|
284
|
+
<div className="landing-orbit landing-orbit-two" aria-hidden />
|
|
285
|
+
<div className="landing-hero-grid relative mx-auto grid max-w-[1200px] items-center gap-14 px-5 py-20 sm:px-8 sm:py-24 lg:grid-cols-[minmax(0,0.92fr)_minmax(500px,1.08fr)] lg:py-20">
|
|
286
|
+
<m.div
|
|
287
|
+
className="max-w-[680px]"
|
|
288
|
+
variants={heroColumn}
|
|
289
|
+
initial="hidden"
|
|
290
|
+
animate="shown"
|
|
291
|
+
>
|
|
292
|
+
<m.div
|
|
293
|
+
variants={heroLine}
|
|
294
|
+
className="mono mb-6 flex items-center gap-2 text-accent"
|
|
295
|
+
>
|
|
296
|
+
<CompassRose size={15} />
|
|
297
|
+
ARCHITECTURE CATALOG
|
|
298
|
+
</m.div>
|
|
299
|
+
<m.h1
|
|
300
|
+
variants={heroLine}
|
|
301
|
+
className="landing-hero-title max-w-[760px] text-[clamp(3rem,6.4vw,5.8rem)] leading-[0.96] font-semibold tracking-[-0.055em] text-ink"
|
|
302
|
+
>
|
|
303
|
+
Your architecture, read from the code.
|
|
304
|
+
</m.h1>
|
|
305
|
+
<m.p
|
|
306
|
+
variants={heroLine}
|
|
307
|
+
className="mt-7 max-w-[620px] text-[17px] leading-7 text-muted sm:text-[19px] sm:leading-8"
|
|
308
|
+
>
|
|
309
|
+
Portolan turns code, specifications, schemas, traces and ADRs
|
|
310
|
+
into a validated, navigable map of your software estate.
|
|
311
|
+
</m.p>
|
|
312
|
+
<m.div
|
|
313
|
+
variants={heroLine}
|
|
314
|
+
className="mt-8 flex flex-wrap items-center gap-3"
|
|
315
|
+
>
|
|
316
|
+
<Link
|
|
317
|
+
to={exampleTo}
|
|
318
|
+
className="btn-accent landing-primary px-4 py-2.5"
|
|
319
|
+
>
|
|
320
|
+
Explore example catalog <ArrowRight size={15} />
|
|
321
|
+
</Link>
|
|
322
|
+
<a href="#get-started" className="tbtn px-4 py-2.5 text-ink">
|
|
323
|
+
<Terminal size={15} /> Get started
|
|
324
|
+
</a>
|
|
325
|
+
</m.div>
|
|
326
|
+
<m.div
|
|
327
|
+
variants={heroLine}
|
|
328
|
+
className="mono mt-6 flex flex-wrap items-center gap-x-5 gap-y-2 text-faint"
|
|
329
|
+
>
|
|
330
|
+
<span className="flex items-center gap-1.5">
|
|
331
|
+
<Check size={12} className="text-verified" /> no hosted
|
|
332
|
+
backend
|
|
333
|
+
</span>
|
|
334
|
+
<span className="flex items-center gap-1.5">
|
|
335
|
+
<Check size={12} className="text-verified" /> exact source
|
|
336
|
+
links
|
|
337
|
+
</span>
|
|
338
|
+
<a
|
|
339
|
+
href="#ai-output"
|
|
340
|
+
className="flex items-center gap-1.5 hover:text-accent"
|
|
341
|
+
>
|
|
342
|
+
<Check size={12} className="text-verified" /> llms.txt for
|
|
343
|
+
agents
|
|
344
|
+
</a>
|
|
345
|
+
</m.div>
|
|
346
|
+
</m.div>
|
|
347
|
+
<m.div
|
|
348
|
+
className="relative mx-auto w-full max-w-[650px] lg:translate-x-5"
|
|
349
|
+
initial={{ opacity: 0, y: 18 }}
|
|
350
|
+
animate={{ opacity: 1, y: 0 }}
|
|
351
|
+
transition={{ duration: 0.4, ease: [0.2, 0, 0, 1], delay: 0.25 }}
|
|
352
|
+
>
|
|
353
|
+
<DraggableReveal>
|
|
354
|
+
<HeroDemo />
|
|
355
|
+
</DraggableReveal>
|
|
356
|
+
</m.div>
|
|
357
|
+
</div>
|
|
358
|
+
</section>
|
|
359
|
+
|
|
360
|
+
<section aria-label="Built-in inputs" className="border-b border-line">
|
|
361
|
+
<div className="mx-auto flex max-w-[1200px] flex-col gap-5 px-5 py-7 sm:px-8 lg:flex-row lg:items-center">
|
|
362
|
+
<div className="shrink-0">
|
|
363
|
+
<div className="mono text-faint">BUILT-IN INPUTS</div>
|
|
364
|
+
<a
|
|
365
|
+
href={`${REPOSITORY}#plugins`}
|
|
366
|
+
target="_blank"
|
|
367
|
+
rel="noreferrer"
|
|
368
|
+
className="mt-1 inline-flex items-center gap-1 text-sm text-accent hover:underline"
|
|
369
|
+
>
|
|
370
|
+
See every extractor <ArrowRight size={12} />
|
|
371
|
+
</a>
|
|
372
|
+
</div>
|
|
373
|
+
<div className="flex flex-wrap gap-x-7 gap-y-3 lg:ml-auto lg:justify-end">
|
|
374
|
+
{inputGroups.map((group) => (
|
|
375
|
+
<div
|
|
376
|
+
key={group.label}
|
|
377
|
+
className="flex flex-wrap items-center gap-x-3 gap-y-1"
|
|
378
|
+
>
|
|
379
|
+
<span className="mono text-faint">{group.label}</span>
|
|
380
|
+
{group.items.map((input) => (
|
|
381
|
+
<span key={input} className="mono text-muted">
|
|
382
|
+
{input}
|
|
383
|
+
</span>
|
|
384
|
+
))}
|
|
385
|
+
</div>
|
|
386
|
+
))}
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</section>
|
|
390
|
+
|
|
391
|
+
<section id="product" className="scroll-mt-20 border-b border-line">
|
|
392
|
+
<div className="mx-auto max-w-[1200px] px-5 py-20 sm:px-8 sm:py-28">
|
|
393
|
+
<Reveal className="mb-12 max-w-[720px]">
|
|
394
|
+
<div className="label text-accent">THE PRODUCT</div>
|
|
395
|
+
<h2 className="mt-4 text-[clamp(2rem,4vw,3.75rem)] leading-[1.04] font-semibold tracking-[-0.04em]">
|
|
396
|
+
Start wide. Follow the evidence all the way down.
|
|
397
|
+
</h2>
|
|
398
|
+
<p className="mt-5 max-w-[650px] text-[17px] leading-7 text-muted">
|
|
399
|
+
The catalog is not a poster. Every map, relationship and health
|
|
400
|
+
signal leads to the entity, contract and source that produced
|
|
401
|
+
it.
|
|
402
|
+
</p>
|
|
403
|
+
</Reveal>
|
|
404
|
+
<Reveal delay={0.08}>
|
|
405
|
+
<ProductTour />
|
|
406
|
+
</Reveal>
|
|
407
|
+
</div>
|
|
408
|
+
</section>
|
|
409
|
+
|
|
410
|
+
<section
|
|
411
|
+
id="how-it-works"
|
|
412
|
+
className="scroll-mt-20 border-b border-line bg-surface/40"
|
|
413
|
+
>
|
|
414
|
+
<div className="mx-auto max-w-[1200px] px-5 py-20 sm:px-8 sm:py-28">
|
|
415
|
+
<Reveal className="max-w-[760px]">
|
|
416
|
+
<div className="label text-accent">HOW IT WORKS</div>
|
|
417
|
+
<h2 className="mt-4 text-[clamp(2rem,4vw,3.75rem)] leading-[1.04] font-semibold tracking-[-0.04em]">
|
|
418
|
+
Architecture documentation that begins with evidence.
|
|
419
|
+
</h2>
|
|
420
|
+
</Reveal>
|
|
421
|
+
<div className="mt-12 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
422
|
+
{pipeline.map((item, index) => {
|
|
423
|
+
const Icon = item.icon;
|
|
424
|
+
return (
|
|
425
|
+
<Reveal
|
|
426
|
+
key={item.title}
|
|
427
|
+
delay={index * 0.07}
|
|
428
|
+
className="relative rounded-card border border-line bg-canvas p-5 shadow-xs"
|
|
429
|
+
>
|
|
430
|
+
<div className="flex items-center justify-between">
|
|
431
|
+
<span className="flow-tile text-accent">
|
|
432
|
+
<Icon size={14} />
|
|
433
|
+
</span>
|
|
434
|
+
<span className="mono text-faint">{item.label}</span>
|
|
435
|
+
</div>
|
|
436
|
+
<h3 className="mt-8 text-md font-semibold">{item.title}</h3>
|
|
437
|
+
<p className="mt-2 text-sm text-muted">{item.copy}</p>
|
|
438
|
+
{index < pipeline.length - 1 ? (
|
|
439
|
+
<ArrowRight
|
|
440
|
+
size={16}
|
|
441
|
+
className="absolute top-1/2 -right-[11px] z-10 hidden -translate-y-1/2 rounded-full bg-canvas text-faint xl:block"
|
|
442
|
+
aria-hidden
|
|
443
|
+
/>
|
|
444
|
+
) : null}
|
|
445
|
+
</Reveal>
|
|
446
|
+
);
|
|
447
|
+
})}
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
</section>
|
|
451
|
+
|
|
452
|
+
<section className="border-b border-line">
|
|
453
|
+
<div className="mx-auto max-w-[1200px] px-5 py-20 sm:px-8 sm:py-28">
|
|
454
|
+
<Reveal className="grid items-end gap-5 md:grid-cols-2">
|
|
455
|
+
<div>
|
|
456
|
+
<div className="label text-accent">WHY PORTOLAN</div>
|
|
457
|
+
<h2 className="mt-4 max-w-[700px] text-[clamp(2rem,4vw,3.75rem)] leading-[1.04] font-semibold tracking-[-0.04em]">
|
|
458
|
+
Evidence that travels with the change.
|
|
459
|
+
</h2>
|
|
460
|
+
</div>
|
|
461
|
+
<p className="max-w-[520px] text-[17px] leading-7 text-muted md:justify-self-end">
|
|
462
|
+
Every view keeps its source, validation status and reviewable
|
|
463
|
+
artifact close enough to act on.
|
|
464
|
+
</p>
|
|
465
|
+
</Reveal>
|
|
466
|
+
<div className="mt-12 grid gap-4 md:grid-cols-3">
|
|
467
|
+
{features.map((feature, index) => {
|
|
468
|
+
const Icon = feature.icon;
|
|
469
|
+
return (
|
|
470
|
+
<Reveal
|
|
471
|
+
key={feature.title}
|
|
472
|
+
delay={(index % 3) * 0.07}
|
|
473
|
+
className="rounded-card border border-line p-5 transition-colors hover:border-line-strong hover:bg-surface"
|
|
474
|
+
>
|
|
475
|
+
<span className="flow-tile text-accent">
|
|
476
|
+
<Icon size={14} />
|
|
477
|
+
</span>
|
|
478
|
+
<h3 className="mt-7 text-md font-semibold">
|
|
479
|
+
{feature.title}
|
|
480
|
+
</h3>
|
|
481
|
+
<p className="mt-2 text-sm leading-6 text-muted">
|
|
482
|
+
{feature.copy}
|
|
483
|
+
</p>
|
|
484
|
+
</Reveal>
|
|
485
|
+
);
|
|
486
|
+
})}
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
</section>
|
|
490
|
+
|
|
491
|
+
<section
|
|
492
|
+
id="ai-output"
|
|
493
|
+
aria-label="Architecture output for coding agents"
|
|
494
|
+
className="relative overflow-hidden border-b border-line bg-surface/40"
|
|
495
|
+
>
|
|
496
|
+
<div
|
|
497
|
+
aria-hidden
|
|
498
|
+
className="pointer-events-none absolute top-[-240px] right-[-180px] size-[620px] rounded-full bg-accent/[0.045] blur-3xl"
|
|
499
|
+
/>
|
|
500
|
+
<div className="relative mx-auto max-w-[1200px] px-5 py-20 sm:px-8 sm:py-24">
|
|
501
|
+
<Reveal className="grid gap-7 lg:grid-cols-[minmax(0,1.15fr)_minmax(380px,0.85fr)] lg:items-end">
|
|
502
|
+
<div className="max-w-[720px]">
|
|
503
|
+
<div className="label flex items-center gap-2 text-accent">
|
|
504
|
+
<Bot size={14} /> FOR CODING AGENTS
|
|
505
|
+
</div>
|
|
506
|
+
<h2 className="mt-4 text-[clamp(2rem,4vw,3.75rem)] leading-[1.04] font-semibold tracking-[-0.04em]">
|
|
507
|
+
Give agents the map before they change the territory.
|
|
508
|
+
</h2>
|
|
509
|
+
</div>
|
|
510
|
+
<div className="max-w-[520px] lg:justify-self-end">
|
|
511
|
+
<p className="text-[17px] leading-7 text-muted">
|
|
512
|
+
Portolan turns the same measured catalog people browse into
|
|
513
|
+
model-readable context. The built-in chat answers with links,
|
|
514
|
+
cards and diagrams — not a second AI summary that can drift.
|
|
515
|
+
</p>
|
|
516
|
+
<div className="mono mt-5 flex flex-wrap gap-x-5 gap-y-2 text-faint">
|
|
517
|
+
<span className="flex items-center gap-1.5">
|
|
518
|
+
<Check size={12} className="text-verified" /> same catalog
|
|
519
|
+
</span>
|
|
520
|
+
<span className="flex items-center gap-1.5">
|
|
521
|
+
<Check size={12} className="text-verified" /> commit stamped
|
|
522
|
+
</span>
|
|
523
|
+
<span className="flex items-center gap-1.5">
|
|
524
|
+
<Check size={12} className="text-verified" /> source linked
|
|
525
|
+
</span>
|
|
526
|
+
</div>
|
|
527
|
+
<a
|
|
528
|
+
href="#catalog-chat"
|
|
529
|
+
className="mt-6 inline-flex items-center gap-1.5 text-sm font-medium text-accent hover:underline"
|
|
530
|
+
>
|
|
531
|
+
Try the live chat{" "}
|
|
532
|
+
<ArrowRight size={13} className="rotate-90" />
|
|
533
|
+
</a>
|
|
534
|
+
</div>
|
|
535
|
+
</Reveal>
|
|
536
|
+
|
|
537
|
+
<div className="mt-12 grid min-w-0 gap-4 lg:grid-cols-[300px_minmax(0,1fr)]">
|
|
538
|
+
<Reveal className="flex min-w-0 flex-col rounded-card border border-line bg-canvas p-4 shadow-xs sm:p-5">
|
|
539
|
+
<div className="flex items-center justify-between gap-3 border-b border-line pb-4">
|
|
540
|
+
<div>
|
|
541
|
+
<div className="label text-accent">MODEL CONTEXT</div>
|
|
542
|
+
<div className="mono mt-1 text-faint">
|
|
543
|
+
generated together
|
|
544
|
+
</div>
|
|
545
|
+
</div>
|
|
546
|
+
<span className="flow-tile text-accent">
|
|
547
|
+
<Files size={14} />
|
|
548
|
+
</span>
|
|
549
|
+
</div>
|
|
550
|
+
|
|
551
|
+
<div className="mt-4 space-y-3">
|
|
552
|
+
{agentDocs.map((doc) => {
|
|
553
|
+
const Icon = doc.icon;
|
|
554
|
+
return (
|
|
555
|
+
<a
|
|
556
|
+
key={doc.name}
|
|
557
|
+
href={doc.href}
|
|
558
|
+
target="_blank"
|
|
559
|
+
rel="noreferrer"
|
|
560
|
+
className="group block rounded-card border border-line bg-surface/55 p-4 transition-colors hover:border-line-strong hover:bg-raised"
|
|
561
|
+
>
|
|
562
|
+
<div className="flex items-start gap-3">
|
|
563
|
+
<Icon
|
|
564
|
+
size={15}
|
|
565
|
+
className="mt-0.5 shrink-0 text-accent"
|
|
566
|
+
/>
|
|
567
|
+
<div className="min-w-0 flex-1">
|
|
568
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
569
|
+
<h3 className="mono font-semibold text-ink">
|
|
570
|
+
{doc.name}
|
|
571
|
+
</h3>
|
|
572
|
+
<ArrowRight
|
|
573
|
+
size={12}
|
|
574
|
+
className="text-faint transition-transform group-hover:translate-x-0.5 group-hover:text-accent"
|
|
575
|
+
/>
|
|
576
|
+
</div>
|
|
577
|
+
<div className="mono mt-1 text-faint">
|
|
578
|
+
{doc.label}
|
|
579
|
+
</div>
|
|
580
|
+
</div>
|
|
581
|
+
</div>
|
|
582
|
+
<p className="mt-3 text-sm leading-6 text-muted">
|
|
583
|
+
{doc.copy}
|
|
584
|
+
</p>
|
|
585
|
+
</a>
|
|
586
|
+
);
|
|
587
|
+
})}
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
<div className="mono mt-auto flex items-start gap-2 border-t border-line pt-4 text-faint">
|
|
591
|
+
<Bot size={13} className="mt-0.5 shrink-0 text-accent" />
|
|
592
|
+
<span>choose the index or load the whole estate</span>
|
|
593
|
+
</div>
|
|
594
|
+
</Reveal>
|
|
595
|
+
|
|
596
|
+
{EmbeddedChat ? (
|
|
597
|
+
<div id="catalog-chat" className="min-w-0 scroll-mt-20">
|
|
598
|
+
<Reveal delay={0.08} className="min-w-0">
|
|
599
|
+
<DraggableReveal label="Movable catalog chat">
|
|
600
|
+
<ProductFrame
|
|
601
|
+
title="Ask the catalog · portolan"
|
|
602
|
+
eyebrow="agents"
|
|
603
|
+
aside={false}
|
|
604
|
+
>
|
|
605
|
+
<div className="h-[605px] overflow-hidden bg-canvas">
|
|
606
|
+
<Suspense
|
|
607
|
+
fallback={
|
|
608
|
+
<div className="mono flex h-full items-center justify-center text-muted">
|
|
609
|
+
loading the catalog chat…
|
|
610
|
+
</div>
|
|
611
|
+
}
|
|
612
|
+
>
|
|
613
|
+
<EmbeddedChat embedded />
|
|
614
|
+
</Suspense>
|
|
615
|
+
</div>
|
|
616
|
+
</ProductFrame>
|
|
617
|
+
</DraggableReveal>
|
|
618
|
+
</Reveal>
|
|
619
|
+
</div>
|
|
620
|
+
) : null}
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
</section>
|
|
624
|
+
|
|
625
|
+
<section id="get-started" className="scroll-mt-20 border-b border-line">
|
|
626
|
+
<div className="mx-auto grid max-w-[1200px] grid-cols-1 items-center gap-12 px-5 py-20 sm:px-8 sm:py-28 lg:grid-cols-[minmax(0,0.9fr)_minmax(440px,1.1fr)]">
|
|
627
|
+
<Reveal className="min-w-0">
|
|
628
|
+
<div className="label text-accent">GET STARTED</div>
|
|
629
|
+
<h2 className="mt-4 text-[clamp(2rem,4vw,3.75rem)] leading-[1.04] font-semibold tracking-[-0.04em]">
|
|
630
|
+
Start with one command.
|
|
631
|
+
</h2>
|
|
632
|
+
<p className="mt-5 max-w-[580px] text-[17px] leading-7 text-muted">
|
|
633
|
+
Run the initializer at a repository root. It detects projects,
|
|
634
|
+
specifications and toolchains, then proposes the manifest.
|
|
635
|
+
</p>
|
|
636
|
+
<div className="mt-7 flex min-w-0 items-center gap-3 rounded-card border border-line bg-surface p-2 pl-3 shadow-xs">
|
|
637
|
+
<Terminal size={15} className="shrink-0 text-accent" />
|
|
638
|
+
<code className="mono min-w-0 flex-1 truncate text-sm text-ink">
|
|
639
|
+
{quickStartCommand}
|
|
640
|
+
</code>
|
|
641
|
+
<CopyCommand text={quickStartCommand} label="Copy" />
|
|
642
|
+
</div>
|
|
643
|
+
<div className="mt-4 flex flex-wrap gap-3">
|
|
644
|
+
<Link
|
|
645
|
+
to={exampleTo}
|
|
646
|
+
className="btn-accent landing-primary px-4 py-2.5"
|
|
647
|
+
>
|
|
648
|
+
Open the example <ArrowRight size={15} />
|
|
649
|
+
</Link>
|
|
650
|
+
<a
|
|
651
|
+
href={`${REPOSITORY}#use-it-in-your-project`}
|
|
652
|
+
className="tbtn px-4 py-2.5 text-ink"
|
|
653
|
+
target="_blank"
|
|
654
|
+
rel="noreferrer"
|
|
655
|
+
>
|
|
656
|
+
Read the guide
|
|
657
|
+
</a>
|
|
658
|
+
</div>
|
|
659
|
+
</Reveal>
|
|
660
|
+
<Reveal delay={0.1} className="min-w-0">
|
|
661
|
+
<DraggableReveal
|
|
662
|
+
handleClassName="right-24 left-0"
|
|
663
|
+
label="Movable setup terminal"
|
|
664
|
+
>
|
|
665
|
+
<div className="min-w-0 overflow-hidden rounded-[14px] border border-line bg-[#090c10] shadow-md">
|
|
666
|
+
<div className="flex h-11 items-center gap-1.5 border-b border-white/10 px-4">
|
|
667
|
+
<span className="size-2 rounded-full bg-white/15" />
|
|
668
|
+
<span className="size-2 rounded-full bg-white/15" />
|
|
669
|
+
<span className="size-2 rounded-full bg-white/15" />
|
|
670
|
+
<span className="mono ml-auto text-white/35">
|
|
671
|
+
next steps
|
|
672
|
+
</span>
|
|
673
|
+
<CopyCommand
|
|
674
|
+
text={setupCommands}
|
|
675
|
+
label="Copy all"
|
|
676
|
+
className="h-7 border-white/10 bg-white/5 px-2 text-white/60 hover:bg-white/10 hover:text-white"
|
|
677
|
+
/>
|
|
678
|
+
</div>
|
|
679
|
+
<pre className="overflow-x-auto p-5 text-[13px] leading-7 text-white/75 sm:p-7">
|
|
680
|
+
<code>
|
|
681
|
+
<span className="text-white/35">$</span> npx
|
|
682
|
+
@shortlink-org/portolan init{"\n"}
|
|
683
|
+
<span className="text-[#72d5c4]">✓</span> projects and
|
|
684
|
+
specifications detected{"\n"}
|
|
685
|
+
<span className="text-[#72d5c4]">✓</span> portolan.json
|
|
686
|
+
ready{"\n\n"}
|
|
687
|
+
<span className="text-white/35">$</span> npm install
|
|
688
|
+
--save-dev @shortlink-org/portolan{"\n"}
|
|
689
|
+
<span className="text-white/35">$</span> npx portolan
|
|
690
|
+
generate{"\n"}
|
|
691
|
+
<span className="text-[#72d5c4]">✓</span> catalog merged
|
|
692
|
+
and validated{"\n\n"}
|
|
693
|
+
<span className="text-white/35">$</span> npx portolan dev
|
|
694
|
+
{"\n"}
|
|
695
|
+
<span className="text-[#72d5c4]">✓</span> site ready at
|
|
696
|
+
http://localhost:5173
|
|
697
|
+
</code>
|
|
698
|
+
</pre>
|
|
699
|
+
</div>
|
|
700
|
+
</DraggableReveal>
|
|
701
|
+
</Reveal>
|
|
702
|
+
</div>
|
|
703
|
+
</section>
|
|
704
|
+
|
|
705
|
+
<section className="landing-final relative overflow-hidden">
|
|
706
|
+
<Reveal className="mx-auto max-w-[920px] px-5 py-24 text-center sm:px-8 sm:py-32">
|
|
707
|
+
<CompassRose size={28} className="mx-auto text-accent" />
|
|
708
|
+
<h2 className="mt-7 text-[clamp(2.4rem,5vw,4.8rem)] leading-[1] font-semibold tracking-[-0.05em]">
|
|
709
|
+
Make the architecture explorable.
|
|
710
|
+
</h2>
|
|
711
|
+
<p className="mx-auto mt-5 max-w-[600px] text-[17px] leading-7 text-muted">
|
|
712
|
+
Give every diagram, flow and dependency a path back to the code
|
|
713
|
+
that made it true.
|
|
714
|
+
</p>
|
|
715
|
+
<div className="mt-8 flex flex-wrap justify-center gap-3">
|
|
716
|
+
<Link
|
|
717
|
+
to={exampleTo}
|
|
718
|
+
className="btn-accent landing-primary px-4 py-2.5"
|
|
719
|
+
>
|
|
720
|
+
Explore example catalog <ArrowRight size={15} />
|
|
721
|
+
</Link>
|
|
722
|
+
<a
|
|
723
|
+
href={REPOSITORY}
|
|
724
|
+
target="_blank"
|
|
725
|
+
rel="noreferrer"
|
|
726
|
+
className="tbtn px-4 py-2.5 text-ink"
|
|
727
|
+
>
|
|
728
|
+
<GitBranch size={15} /> View on GitHub
|
|
729
|
+
</a>
|
|
730
|
+
</div>
|
|
731
|
+
</Reveal>
|
|
732
|
+
</section>
|
|
733
|
+
</main>
|
|
734
|
+
|
|
735
|
+
<footer className="border-t border-line">
|
|
736
|
+
<div className="mx-auto flex max-w-[1200px] flex-col gap-4 px-5 py-7 text-sm text-muted sm:flex-row sm:items-center sm:px-8">
|
|
737
|
+
<Wordmark />
|
|
738
|
+
<span className="sm:ml-3">Architecture from code and evidence.</span>
|
|
739
|
+
<div className="flex gap-5 sm:ml-auto">
|
|
740
|
+
<a className="hover:text-ink" href={`${REPOSITORY}#readme`}>
|
|
741
|
+
Docs
|
|
742
|
+
</a>
|
|
743
|
+
<a
|
|
744
|
+
className="hover:text-ink"
|
|
745
|
+
href={`${REPOSITORY}/blob/main/LICENSE`}
|
|
746
|
+
>
|
|
747
|
+
MIT License
|
|
748
|
+
</a>
|
|
749
|
+
<a className="hover:text-ink" href={REPOSITORY}>
|
|
750
|
+
GitHub
|
|
751
|
+
</a>
|
|
752
|
+
</div>
|
|
753
|
+
</div>
|
|
754
|
+
</footer>
|
|
755
|
+
</div>
|
|
756
|
+
);
|
|
757
|
+
}
|