@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,436 @@
|
|
|
1
|
+
import { Suspense, lazy, useCallback, useEffect, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Navigate,
|
|
4
|
+
Route,
|
|
5
|
+
Routes,
|
|
6
|
+
useLocation,
|
|
7
|
+
useNavigate,
|
|
8
|
+
} from "react-router";
|
|
9
|
+
import type { Location } from "react-router";
|
|
10
|
+
import {
|
|
11
|
+
Panel,
|
|
12
|
+
ResizeHandle,
|
|
13
|
+
SavedGroup,
|
|
14
|
+
useCanvasResize,
|
|
15
|
+
usePanelRef,
|
|
16
|
+
} from "./panels";
|
|
17
|
+
import { FlowDetail } from "../pages/FlowDetail";
|
|
18
|
+
import { FlowIndex } from "../pages/FlowIndex";
|
|
19
|
+
import { AdrIndex } from "../pages/AdrIndex";
|
|
20
|
+
import { Language } from "../pages/Language";
|
|
21
|
+
import { AdrDetail } from "../pages/AdrDetail";
|
|
22
|
+
import { Overview } from "../pages/Overview";
|
|
23
|
+
import { ContextMap } from "../pages/ContextMap";
|
|
24
|
+
import { ContextPage } from "../pages/ContextPage";
|
|
25
|
+
import { ServicePage } from "../pages/ServicePage";
|
|
26
|
+
import { AggregatePage } from "../pages/AggregatePage";
|
|
27
|
+
import { BlockPage } from "../pages/BlockPage";
|
|
28
|
+
import { EnumPage } from "../pages/EnumPage";
|
|
29
|
+
import { EventPage } from "../pages/EventPage";
|
|
30
|
+
import { StorePage } from "../pages/StorePage";
|
|
31
|
+
import { GraphPage } from "../pages/GraphPage";
|
|
32
|
+
import { Problems } from "../pages/Problems";
|
|
33
|
+
import { Settings } from "../pages/Settings";
|
|
34
|
+
import { Changes } from "../pages/Changes";
|
|
35
|
+
import { RegistryIndex } from "../pages/RegistryIndex";
|
|
36
|
+
import { ModulePage } from "../pages/ModulePage";
|
|
37
|
+
import { ExternalPage } from "../pages/ExternalPage";
|
|
38
|
+
import { NotFoundPage } from "../pages/NotFound";
|
|
39
|
+
import { CatalogFailure } from "../pages/CatalogFailure";
|
|
40
|
+
import { activeCatalogProfile, catalogError } from "../data";
|
|
41
|
+
import { SidePanel } from "../components/Overlay";
|
|
42
|
+
import { Empty } from "../components/PageHeader";
|
|
43
|
+
import { WithDetail } from "../selection/DetailPanel";
|
|
44
|
+
import { SelectionSync } from "../selection/sync";
|
|
45
|
+
import { Trail } from "../trail/Trail";
|
|
46
|
+
import { TrailRecorder } from "../trail/record";
|
|
47
|
+
import { HashScroll } from "./HashScroll";
|
|
48
|
+
import { CommandPalette } from "./CommandPalette";
|
|
49
|
+
import { Sidebar } from "./Sidebar";
|
|
50
|
+
import { TopBar } from "./TopBar";
|
|
51
|
+
import { DensityProvider } from "./density";
|
|
52
|
+
import { useNarrow, usePhone } from "./responsive";
|
|
53
|
+
import { ShortcutsSheet, useShortcuts } from "./shortcuts";
|
|
54
|
+
import { Toaster } from "./toast";
|
|
55
|
+
import { PeekLayer } from "../peek/PeekLayer";
|
|
56
|
+
import { useUiStore } from "./ui-store";
|
|
57
|
+
import { ForgeAccessProvider } from "./forge-access";
|
|
58
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
59
|
+
import { queryClient } from "./query-client";
|
|
60
|
+
import { AnimatePresence, m, page } from "../lib/motion";
|
|
61
|
+
import { useChatUi } from "../chat/store";
|
|
62
|
+
import { projectPreview } from "../lib/project-preview";
|
|
63
|
+
|
|
64
|
+
// The chat is a chunk of its own, and a build with VITE_CHAT=off has no such
|
|
65
|
+
// chunk: the test is on the literal Vite substitutes, so the import below is
|
|
66
|
+
// dead code to the bundler and is dropped with everything it would pull in.
|
|
67
|
+
const ChatPanel =
|
|
68
|
+
import.meta.env.VITE_CHAT !== "off"
|
|
69
|
+
? lazy(() => import("../chat/ChatPanel"))
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* What the click shows while that chunk is on its way: the same sheet, at the
|
|
74
|
+
* same width, with a word in it. A fallback of nothing meant the first "ask"
|
|
75
|
+
* on a slow line did nothing for a second and the reader clicked again.
|
|
76
|
+
*/
|
|
77
|
+
function ChatLoading() {
|
|
78
|
+
const phone = usePhone();
|
|
79
|
+
return (
|
|
80
|
+
<SidePanel
|
|
81
|
+
open
|
|
82
|
+
onClose={() => useChatUi.getState().setOpen(false)}
|
|
83
|
+
side="right"
|
|
84
|
+
label="Ask the catalog"
|
|
85
|
+
width={phone ? "100vw" : "min(560px,92vw)"}
|
|
86
|
+
>
|
|
87
|
+
<div className="h-full bg-canvas p-gutter text-ink">
|
|
88
|
+
<Empty>loading the chat…</Empty>
|
|
89
|
+
</div>
|
|
90
|
+
</SidePanel>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Every route, once. Rendered inside a pane on wide layouts and alone below.
|
|
96
|
+
*
|
|
97
|
+
* The location is a prop, not read from the router: while a page is on its
|
|
98
|
+
* way out the router already says the next address, and the page leaving
|
|
99
|
+
* must keep drawing the one it was.
|
|
100
|
+
*/
|
|
101
|
+
function AppRoutes({ location }: { location: Location }) {
|
|
102
|
+
return (
|
|
103
|
+
<Routes location={location}>
|
|
104
|
+
<Route path="/" element={<Overview />} />
|
|
105
|
+
<Route path="/flows" element={<FlowIndex />} />
|
|
106
|
+
<Route
|
|
107
|
+
path="/flows/:flow"
|
|
108
|
+
element={
|
|
109
|
+
<WithDetail id="flow">
|
|
110
|
+
<FlowDetail />
|
|
111
|
+
</WithDetail>
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
114
|
+
<Route path="/adrs" element={<AdrIndex />} />
|
|
115
|
+
<Route path="/language" element={<Language />} />
|
|
116
|
+
<Route path="/adrs/:adr" element={<AdrDetail />} />
|
|
117
|
+
<Route path="/problems" element={<Problems />} />
|
|
118
|
+
<Route path="/settings/*" element={<Settings />} />
|
|
119
|
+
<Route path="/changes" element={<Changes />} />
|
|
120
|
+
<Route path="/externals/:external" element={<ExternalPage />} />
|
|
121
|
+
{/* A module sits at the estate level, not under a service: it is
|
|
122
|
+
published by one and read by four, so hanging it off a service would
|
|
123
|
+
put one entity at four URLs. */}
|
|
124
|
+
<Route path="/registry" element={<RegistryIndex />} />
|
|
125
|
+
<Route
|
|
126
|
+
path="/registry/:module"
|
|
127
|
+
element={
|
|
128
|
+
<WithDetail id="module">
|
|
129
|
+
<ModulePage />
|
|
130
|
+
</WithDetail>
|
|
131
|
+
}
|
|
132
|
+
/>
|
|
133
|
+
<Route
|
|
134
|
+
path="/map"
|
|
135
|
+
element={
|
|
136
|
+
<WithDetail id="map">
|
|
137
|
+
<ContextMap />
|
|
138
|
+
</WithDetail>
|
|
139
|
+
}
|
|
140
|
+
/>
|
|
141
|
+
<Route
|
|
142
|
+
path="/c/:context"
|
|
143
|
+
element={
|
|
144
|
+
<WithDetail id="context">
|
|
145
|
+
<ContextPage />
|
|
146
|
+
</WithDetail>
|
|
147
|
+
}
|
|
148
|
+
/>
|
|
149
|
+
<Route
|
|
150
|
+
path="/c/:context/:service"
|
|
151
|
+
element={
|
|
152
|
+
<WithDetail id="service">
|
|
153
|
+
<ServicePage />
|
|
154
|
+
</WithDetail>
|
|
155
|
+
}
|
|
156
|
+
/>
|
|
157
|
+
{/* Before the aggregate routes: "data" is a literal sitting where an
|
|
158
|
+
aggregate slug would, and "/c/x/y/data/z" would otherwise be read as
|
|
159
|
+
the event "z" of an aggregate called "data". */}
|
|
160
|
+
<Route
|
|
161
|
+
path="/c/:context/:service/data/:store"
|
|
162
|
+
element={
|
|
163
|
+
<WithDetail id="store">
|
|
164
|
+
<StorePage />
|
|
165
|
+
</WithDetail>
|
|
166
|
+
}
|
|
167
|
+
/>
|
|
168
|
+
<Route
|
|
169
|
+
path="/c/:context/:service/:aggregate"
|
|
170
|
+
element={<AggregatePage />}
|
|
171
|
+
/>
|
|
172
|
+
{/* The two literal segments come first: a block page must not be
|
|
173
|
+
read as an event whose slug happens to be "vo". */}
|
|
174
|
+
<Route
|
|
175
|
+
path="/c/:context/:service/:aggregate/vo/:block"
|
|
176
|
+
element={<BlockPage kind="vo" />}
|
|
177
|
+
/>
|
|
178
|
+
<Route
|
|
179
|
+
path="/c/:context/:service/:aggregate/entity/:block"
|
|
180
|
+
element={<BlockPage kind="entity" />}
|
|
181
|
+
/>
|
|
182
|
+
<Route
|
|
183
|
+
path="/c/:context/:service/:aggregate/enum/:enum"
|
|
184
|
+
element={<EnumPage />}
|
|
185
|
+
/>
|
|
186
|
+
<Route
|
|
187
|
+
path="/c/:context/:service/:aggregate/:event"
|
|
188
|
+
element={
|
|
189
|
+
<WithDetail id="event">
|
|
190
|
+
<EventPage />
|
|
191
|
+
</WithDetail>
|
|
192
|
+
}
|
|
193
|
+
/>
|
|
194
|
+
<Route
|
|
195
|
+
path="/graph"
|
|
196
|
+
element={
|
|
197
|
+
<WithDetail id="graph">
|
|
198
|
+
<GraphPage />
|
|
199
|
+
</WithDetail>
|
|
200
|
+
}
|
|
201
|
+
/>
|
|
202
|
+
<Route path="/index.html" element={<Navigate to="/" replace />} />
|
|
203
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
204
|
+
</Routes>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The catalog tree as an overlay, below the narrow breakpoint. It is the same
|
|
210
|
+
* Sidebar; only the box around it changes, so nothing about the tree has two
|
|
211
|
+
* implementations.
|
|
212
|
+
*/
|
|
213
|
+
function SidebarDrawer() {
|
|
214
|
+
const open = useUiStore((s) => s.drawer);
|
|
215
|
+
const setDrawer = useUiStore((s) => s.setDrawer);
|
|
216
|
+
const { pathname } = useLocation();
|
|
217
|
+
|
|
218
|
+
// Navigating is what the tree is for, so a click that moves the reader ends
|
|
219
|
+
// the drawer's job. Without this the page they asked for is behind a scrim.
|
|
220
|
+
useEffect(() => setDrawer(false), [pathname, setDrawer]);
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<SidePanel
|
|
224
|
+
open={open}
|
|
225
|
+
onClose={() => setDrawer(false)}
|
|
226
|
+
side="left"
|
|
227
|
+
label="Catalog"
|
|
228
|
+
width="min(320px,85vw)"
|
|
229
|
+
>
|
|
230
|
+
<Sidebar />
|
|
231
|
+
</SidePanel>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function Shell() {
|
|
236
|
+
const [palette, setPalette] = useState(false);
|
|
237
|
+
const [help, setHelp] = useState(false);
|
|
238
|
+
const chatOpen = useChatUi((s) => s.open);
|
|
239
|
+
const [railed, setRailed] = useState(false);
|
|
240
|
+
const location = useLocation();
|
|
241
|
+
const navigate = useNavigate();
|
|
242
|
+
const { pathname } = location;
|
|
243
|
+
const settle = useCanvasResize();
|
|
244
|
+
const sidebarRef = usePanelRef();
|
|
245
|
+
const narrow = useNarrow();
|
|
246
|
+
const toggleDrawer = useUiStore((s) => s.toggleDrawer);
|
|
247
|
+
const setDrawer = useUiStore((s) => s.setDrawer);
|
|
248
|
+
const toggleDetail = useUiStore((s) => s.toggleDetail);
|
|
249
|
+
const revealNonce = useUiStore((s) => s.revealNonce);
|
|
250
|
+
|
|
251
|
+
// Keep the selected catalog shareable even though most route builders do
|
|
252
|
+
// not know about presentation state. Changing the profile itself reloads
|
|
253
|
+
// the bundle; ordinary navigation only restores its URL marker.
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
const params = new URLSearchParams(location.search);
|
|
256
|
+
if (params.get("catalog") === activeCatalogProfile.id) return;
|
|
257
|
+
params.set("catalog", activeCatalogProfile.id);
|
|
258
|
+
navigate(
|
|
259
|
+
{ pathname: location.pathname, search: `?${params}`, hash: location.hash },
|
|
260
|
+
{ replace: true },
|
|
261
|
+
);
|
|
262
|
+
}, [location.hash, location.pathname, location.search, navigate]);
|
|
263
|
+
|
|
264
|
+
// Collapsed is a fact about pixels, not about who dragged: the rail appears
|
|
265
|
+
// whether the reader dragged past the minimum or pressed a rail button.
|
|
266
|
+
const onSidebarResize = useCallback(
|
|
267
|
+
(size: { inPixels: number }) => setRailed(size.inPixels <= 56),
|
|
268
|
+
[],
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// The rail's buttons ask the panel to expand; `railed` then follows from the
|
|
272
|
+
// resize that answers, so the flag can never disagree with the layout.
|
|
273
|
+
//
|
|
274
|
+
// `expand()` restores the width the panel had before it collapsed, which
|
|
275
|
+
// after a drag all the way to the edge is the minimum - a tree too narrow to
|
|
276
|
+
// read the names in. Anything at or under the minimum is treated as "no
|
|
277
|
+
// remembered width" and goes back to the default instead.
|
|
278
|
+
const expandSidebar = useCallback(() => {
|
|
279
|
+
const panel = sidebarRef.current;
|
|
280
|
+
if (!panel) return;
|
|
281
|
+
panel.expand();
|
|
282
|
+
if (panel.getSize().asPercentage <= 13) panel.resize("18");
|
|
283
|
+
}, [sidebarRef]);
|
|
284
|
+
|
|
285
|
+
// A layout restored from a previous session can arrive already collapsed,
|
|
286
|
+
// and no resize fires for a size that never changed - so the rail has to be
|
|
287
|
+
// read off the panel once on mount, or the full tree renders into 48px.
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
setRailed(sidebarRef.current?.isCollapsed() ?? false);
|
|
290
|
+
}, [sidebarRef]);
|
|
291
|
+
|
|
292
|
+
// "[" means the same thing at both widths - show me the tree, or stop
|
|
293
|
+
// showing it - and reaches for whichever mechanism is on screen.
|
|
294
|
+
const toggleSidebar = useCallback(() => {
|
|
295
|
+
if (narrow) {
|
|
296
|
+
toggleDrawer();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const panel = sidebarRef.current;
|
|
300
|
+
if (!panel) return;
|
|
301
|
+
if (panel.isCollapsed()) expandSidebar();
|
|
302
|
+
else panel.collapse();
|
|
303
|
+
}, [narrow, toggleDrawer, sidebarRef, expandSidebar]);
|
|
304
|
+
|
|
305
|
+
// "Reveal in tree" selects; the tree then opens its own ancestors and scrolls
|
|
306
|
+
// itself. The one thing it cannot do from inside a collapsed pane is become
|
|
307
|
+
// visible, which is this.
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
if (revealNonce === 0) return;
|
|
310
|
+
if (narrow) setDrawer(true);
|
|
311
|
+
else if (sidebarRef.current?.isCollapsed()) expandSidebar();
|
|
312
|
+
}, [revealNonce, narrow, setDrawer, sidebarRef, expandSidebar]);
|
|
313
|
+
|
|
314
|
+
useShortcuts(
|
|
315
|
+
{
|
|
316
|
+
openPalette: () => setPalette(true),
|
|
317
|
+
openHelp: () => setHelp(true),
|
|
318
|
+
toggleSidebar,
|
|
319
|
+
toggleDetail,
|
|
320
|
+
},
|
|
321
|
+
// While a modal owns the keyboard the global bindings stand down; ⌘K and
|
|
322
|
+
// Esc are handled inside the modals themselves.
|
|
323
|
+
!palette && !help && !chatOpen,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<div className="flex h-full flex-col bg-canvas text-ink">
|
|
328
|
+
<SelectionSync />
|
|
329
|
+
<TrailRecorder />
|
|
330
|
+
<HashScroll />
|
|
331
|
+
<TopBar
|
|
332
|
+
onOpenPalette={() => setPalette(true)}
|
|
333
|
+
onOpenHelp={() => setHelp(true)}
|
|
334
|
+
onToggleSidebar={toggleSidebar}
|
|
335
|
+
narrow={narrow}
|
|
336
|
+
/>
|
|
337
|
+
{projectPreview ? (
|
|
338
|
+
<div className="shrink-0 border-b border-declared/40 bg-declared/10 px-gutter py-2 text-center text-sm text-declared">
|
|
339
|
+
Temporary project preview · the repository has not been changed · expires after 15 minutes
|
|
340
|
+
</div>
|
|
341
|
+
) : null}
|
|
342
|
+
{/* Under the bar, above everything: the trail is about the whole shell,
|
|
343
|
+
not about the page inside it. */}
|
|
344
|
+
<Trail />
|
|
345
|
+
|
|
346
|
+
{narrow ? (
|
|
347
|
+
<>
|
|
348
|
+
{/* `key` on the route content is what makes the page transition
|
|
349
|
+
fire: a new pathname is a new element. The old one leaves first,
|
|
350
|
+
on the micro duration, then the new one rises. */}
|
|
351
|
+
<AnimatePresence mode="wait">
|
|
352
|
+
<m.main
|
|
353
|
+
key={pathname}
|
|
354
|
+
{...page}
|
|
355
|
+
className="min-h-0 flex-1 overflow-hidden"
|
|
356
|
+
>
|
|
357
|
+
<AppRoutes location={location} />
|
|
358
|
+
</m.main>
|
|
359
|
+
</AnimatePresence>
|
|
360
|
+
<SidebarDrawer />
|
|
361
|
+
</>
|
|
362
|
+
) : (
|
|
363
|
+
<SavedGroup
|
|
364
|
+
id="portolan:shell"
|
|
365
|
+
orientation="horizontal"
|
|
366
|
+
className="min-h-0 flex-1"
|
|
367
|
+
>
|
|
368
|
+
<Panel
|
|
369
|
+
id="sidebar"
|
|
370
|
+
defaultSize="18"
|
|
371
|
+
minSize="12"
|
|
372
|
+
maxSize="28"
|
|
373
|
+
collapsible
|
|
374
|
+
collapsedSize="48px"
|
|
375
|
+
className="h-full text-sm"
|
|
376
|
+
panelRef={sidebarRef}
|
|
377
|
+
onResize={onSidebarResize}
|
|
378
|
+
>
|
|
379
|
+
<Sidebar railed={railed} onExpand={expandSidebar} />
|
|
380
|
+
</Panel>
|
|
381
|
+
|
|
382
|
+
<ResizeHandle id="shell" />
|
|
383
|
+
|
|
384
|
+
{/* The main pane owns a canvas on most routes, so a drag here has to
|
|
385
|
+
reach the diagram - debounced, once the reader lets go. */}
|
|
386
|
+
<Panel id="main" className="h-full min-w-0" onResize={settle}>
|
|
387
|
+
{/* The detail rail rides along with every page that draws a
|
|
388
|
+
diagram, so a selection made anywhere has somewhere to be read. */}
|
|
389
|
+
<AnimatePresence mode="wait">
|
|
390
|
+
<m.main
|
|
391
|
+
key={pathname}
|
|
392
|
+
{...page}
|
|
393
|
+
className="h-full overflow-hidden"
|
|
394
|
+
>
|
|
395
|
+
<AppRoutes location={location} />
|
|
396
|
+
</m.main>
|
|
397
|
+
</AnimatePresence>
|
|
398
|
+
</Panel>
|
|
399
|
+
</SavedGroup>
|
|
400
|
+
)}
|
|
401
|
+
|
|
402
|
+
<CommandPalette open={palette} onClose={() => setPalette(false)} />
|
|
403
|
+
<ShortcutsSheet open={help} onClose={() => setHelp(false)} />
|
|
404
|
+
{ChatPanel && chatOpen ? (
|
|
405
|
+
<Suspense fallback={<ChatLoading />}>
|
|
406
|
+
<ChatPanel />
|
|
407
|
+
</Suspense>
|
|
408
|
+
) : null}
|
|
409
|
+
{/* The one thing the app says out loud, and it says it here rather than
|
|
410
|
+
in the sidebar: a pin can be taken from a page whose tree is folded
|
|
411
|
+
away to a 48px rail. */}
|
|
412
|
+
<Toaster />
|
|
413
|
+
{/* A card on hover, for any row that names an entity. Mounted once,
|
|
414
|
+
here, because the rows it answers for are on every page. */}
|
|
415
|
+
<PeekLayer />
|
|
416
|
+
</div>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function CatalogApp() {
|
|
421
|
+
// A catalog that failed validation is the catalog app's whole answer. The
|
|
422
|
+
// public landing page is routed before this module is loaded.
|
|
423
|
+
if (catalogError) {
|
|
424
|
+
return <CatalogFailure error={catalogError} />;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return (
|
|
428
|
+
<DensityProvider>
|
|
429
|
+
<QueryClientProvider client={queryClient}>
|
|
430
|
+
<ForgeAccessProvider>
|
|
431
|
+
<Shell />
|
|
432
|
+
</ForgeAccessProvider>
|
|
433
|
+
</QueryClientProvider>
|
|
434
|
+
</DensityProvider>
|
|
435
|
+
);
|
|
436
|
+
}
|
package/src/app/Sidebar.tsx
CHANGED
package/src/chat/ChatPanel.tsx
CHANGED
|
@@ -16,41 +16,56 @@ import { ACTION_ACCENT, Notice } from "./Notice";
|
|
|
16
16
|
import { useChatRoute } from "./prefs";
|
|
17
17
|
import { useChatUi } from "./store";
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
function ChatBody({
|
|
20
|
+
onClose,
|
|
21
|
+
onSettings,
|
|
22
|
+
embedded,
|
|
23
|
+
}: {
|
|
24
|
+
onClose?: () => void;
|
|
25
|
+
onSettings: () => void;
|
|
26
|
+
embedded: boolean;
|
|
27
|
+
}) {
|
|
22
28
|
const route = useChatRoute();
|
|
23
|
-
const phone = usePhone();
|
|
24
|
-
const [settings, setSettings] = useState(false);
|
|
25
|
-
const close = () => setOpen(false);
|
|
26
29
|
const answering = route.kind === "proxy" || route.kind === "own";
|
|
27
30
|
|
|
28
31
|
return (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
32
|
+
<div className="flex h-full flex-col bg-canvas text-ink">
|
|
33
|
+
{answering ? (
|
|
34
|
+
<Conversation
|
|
35
|
+
route={route}
|
|
36
|
+
onOwnKey={onSettings}
|
|
37
|
+
onSettings={onSettings}
|
|
38
|
+
seeded={embedded}
|
|
39
|
+
{...(onClose ? { onClose } : {})}
|
|
40
|
+
/>
|
|
41
|
+
) : (
|
|
42
|
+
<>
|
|
43
|
+
<Header
|
|
44
|
+
route={route}
|
|
45
|
+
onSettings={onSettings}
|
|
46
|
+
{...(onClose ? { onClose } : {})}
|
|
47
|
+
/>
|
|
48
|
+
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-4">
|
|
49
|
+
{embedded ? (
|
|
50
|
+
<>
|
|
51
|
+
<Notice tone="info" title="bring the model you already use">
|
|
52
|
+
Connect any OpenAI-compatible endpoint. Its address and key
|
|
53
|
+
stay in this browser; Portolan supplies the catalog context.
|
|
54
|
+
</Notice>
|
|
55
|
+
<div className="mt-5">
|
|
56
|
+
<ModelForm />
|
|
57
|
+
</div>
|
|
58
|
+
</>
|
|
59
|
+
) : (
|
|
49
60
|
<Notice
|
|
50
61
|
tone="info"
|
|
51
62
|
title="nothing answers yet"
|
|
52
63
|
actions={
|
|
53
|
-
<button
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
onClick={onSettings}
|
|
67
|
+
className={ACTION_ACCENT}
|
|
68
|
+
>
|
|
54
69
|
<KeyRound size={13} aria-hidden /> set a model
|
|
55
70
|
</button>
|
|
56
71
|
}
|
|
@@ -59,21 +74,63 @@ export default function ChatPanel() {
|
|
|
59
74
|
yours: any OpenAI-compatible endpoint and, usually, a key. Both
|
|
60
75
|
stay in this browser.
|
|
61
76
|
</Notice>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ModelModal({ open, onClose }: { open: boolean; onClose: () => void }) {
|
|
86
|
+
return (
|
|
87
|
+
<Modal
|
|
88
|
+
open={open}
|
|
89
|
+
onClose={onClose}
|
|
90
|
+
label="Model settings"
|
|
91
|
+
width="min(540px,92vw)"
|
|
92
|
+
>
|
|
93
|
+
<div className="overflow-y-auto p-4">
|
|
94
|
+
<ModelForm onDone={onClose} />
|
|
95
|
+
</div>
|
|
96
|
+
</Modal>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function ChatSurface({ embedded = false }: { embedded?: boolean }) {
|
|
101
|
+
const [settings, setSettings] = useState(false);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<>
|
|
105
|
+
<ChatBody embedded={embedded} onSettings={() => setSettings(true)} />
|
|
106
|
+
<ModelModal open={settings} onClose={() => setSettings(false)} />
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default function ChatPanel() {
|
|
112
|
+
const open = useChatUi((s) => s.open);
|
|
113
|
+
const setOpen = useChatUi((s) => s.setOpen);
|
|
114
|
+
const phone = usePhone();
|
|
115
|
+
const [settings, setSettings] = useState(false);
|
|
116
|
+
const close = () => setOpen(false);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
<SidePanel
|
|
121
|
+
open={open}
|
|
122
|
+
onClose={close}
|
|
123
|
+
side="right"
|
|
124
|
+
label="Ask the catalog"
|
|
125
|
+
width={phone ? "100vw" : "min(560px,92vw)"}
|
|
72
126
|
>
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
127
|
+
<ChatBody
|
|
128
|
+
embedded={false}
|
|
129
|
+
onClose={close}
|
|
130
|
+
onSettings={() => setSettings(true)}
|
|
131
|
+
/>
|
|
132
|
+
</SidePanel>
|
|
133
|
+
<ModelModal open={settings} onClose={() => setSettings(false)} />
|
|
77
134
|
</>
|
|
78
135
|
);
|
|
79
136
|
}
|
package/src/chat/Composer.tsx
CHANGED
|
@@ -13,17 +13,19 @@ export function Composer({
|
|
|
13
13
|
busy,
|
|
14
14
|
onSend,
|
|
15
15
|
onStop,
|
|
16
|
+
focusOnMount = true,
|
|
16
17
|
}: {
|
|
17
18
|
busy: boolean;
|
|
18
19
|
onSend: (question: string) => void;
|
|
19
20
|
onStop: () => void;
|
|
21
|
+
focusOnMount?: boolean;
|
|
20
22
|
}) {
|
|
21
23
|
const [draft, setDraft] = useState("");
|
|
22
24
|
const field = useRef<HTMLTextAreaElement>(null);
|
|
23
25
|
|
|
24
26
|
useEffect(() => {
|
|
25
|
-
field.current?.focus();
|
|
26
|
-
}, []);
|
|
27
|
+
if (focusOnMount) field.current?.focus();
|
|
28
|
+
}, [focusOnMount]);
|
|
27
29
|
|
|
28
30
|
// Grow with the text, up to the limit; shrink back when it is deleted.
|
|
29
31
|
useEffect(() => {
|