@cosmoops/connect-sdk 0.9.0 → 0.16.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/CHANGELOG.md +566 -0
- package/README.md +113 -42
- package/dist/cjs/cli/doctor.js +3 -3
- package/dist/cjs/cli/init.js +3 -3
- package/dist/cjs/client/components/access-gate.js +20 -1
- package/dist/cjs/client/components/ai-screens.js +73 -0
- package/dist/cjs/client/components/ai-widgets.js +37 -0
- package/dist/cjs/client/components/app-shell.js +75 -41
- package/dist/cjs/client/components/apps-grid.js +3 -2
- package/dist/cjs/client/components/ask-ai.js +50 -5
- package/dist/cjs/client/components/confirm-dialog.js +40 -0
- package/dist/cjs/client/components/cursor-pagination.js +22 -0
- package/dist/cjs/client/components/filter-tabs.js +24 -0
- package/dist/cjs/client/components/grievance-notice.js +30 -0
- package/dist/cjs/client/components/link-button.js +17 -10
- package/dist/cjs/client/components/logo.js +4 -3
- package/dist/cjs/client/components/nav.js +41 -0
- package/dist/cjs/client/components/plan-pricing.js +27 -0
- package/dist/cjs/client/components/plans.js +1 -1
- package/dist/cjs/client/components/platform-gate.js +48 -0
- package/dist/cjs/client/components/shell-context.js +149 -0
- package/dist/cjs/client/components/side-nav.js +49 -23
- package/dist/cjs/client/components/sidebar.js +127 -0
- package/dist/cjs/client/components/subscribe-dialog.js +2 -2
- package/dist/cjs/client/components/subscription.js +1 -1
- package/dist/cjs/client/components/user-menu.js +96 -37
- package/dist/cjs/client/components/workspace-switcher.js +100 -0
- package/dist/cjs/client/index.js +72 -2
- package/dist/cjs/client/provider.js +57 -2
- package/dist/cjs/client/use-checkout.js +9 -5
- package/dist/cjs/errors.js +1 -1
- package/dist/cjs/index.js +16 -1
- package/dist/cjs/mcp/index.js +90 -0
- package/dist/cjs/mcp/tools.js +369 -0
- package/dist/cjs/next/index.js +2 -2
- package/dist/cjs/server/client.js +25 -2
- package/dist/cjs/server/handler.js +203 -18
- package/dist/cjs/server/http.js +6 -2
- package/dist/cjs/server/resources/acting-as.js +16 -0
- package/dist/cjs/server/resources/ai.js +22 -2
- package/dist/cjs/server/resources/data.js +374 -0
- package/dist/cjs/server/resources/identity.js +7 -3
- package/dist/cjs/server/resources/kv.js +84 -0
- package/dist/cjs/server/resources/platform.js +43 -0
- package/dist/cjs/server/resources/sessions.js +1 -1
- package/dist/cjs/server/resources/workspaces.js +74 -0
- package/dist/cjs/store-schema.js +66 -0
- package/dist/cjs/store.js +152 -0
- package/dist/cjs/ui/empty-state.js +28 -0
- package/dist/cjs/ui/index.js +44 -0
- package/dist/cjs/ui/page-header.js +27 -0
- package/dist/cjs/ui/page-shell.js +44 -0
- package/dist/cjs/ui/section.js +20 -0
- package/dist/cjs/ui/setting-row.js +14 -0
- package/dist/cjs/ui/status-chip.js +17 -0
- package/dist/esm/cli/doctor.js +3 -3
- package/dist/esm/cli/init.js +3 -3
- package/dist/esm/client/components/access-gate.js +20 -1
- package/dist/esm/client/components/ai-screens.js +69 -0
- package/dist/esm/client/components/ai-widgets.js +37 -1
- package/dist/esm/client/components/app-shell.js +81 -47
- package/dist/esm/client/components/apps-grid.js +3 -2
- package/dist/esm/client/components/ask-ai.js +51 -6
- package/dist/esm/client/components/confirm-dialog.js +37 -0
- package/dist/esm/client/components/cursor-pagination.js +19 -0
- package/dist/esm/client/components/filter-tabs.js +21 -0
- package/dist/esm/client/components/grievance-notice.js +27 -0
- package/dist/esm/client/components/link-button.js +17 -10
- package/dist/esm/client/components/logo.js +4 -3
- package/dist/esm/client/components/nav.js +40 -0
- package/dist/esm/client/components/plan-pricing.js +25 -0
- package/dist/esm/client/components/plans.js +2 -2
- package/dist/esm/client/components/platform-gate.js +45 -0
- package/dist/esm/client/components/shell-context.js +141 -0
- package/dist/esm/client/components/side-nav.js +48 -21
- package/dist/esm/client/components/sidebar.js +120 -0
- package/dist/esm/client/components/subscribe-dialog.js +3 -3
- package/dist/esm/client/components/subscription.js +2 -2
- package/dist/esm/client/components/user-menu.js +95 -38
- package/dist/esm/client/components/workspace-switcher.js +97 -0
- package/dist/esm/client/index.js +47 -4
- package/dist/esm/client/provider.js +53 -2
- package/dist/esm/client/use-checkout.js +9 -5
- package/dist/esm/errors.js +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/mcp/index.js +85 -0
- package/dist/esm/mcp/tools.js +366 -0
- package/dist/esm/next/index.js +2 -2
- package/dist/esm/server/client.js +25 -2
- package/dist/esm/server/handler.js +203 -18
- package/dist/esm/server/http.js +6 -2
- package/dist/esm/server/resources/acting-as.js +13 -0
- package/dist/esm/server/resources/ai.js +22 -2
- package/dist/esm/server/resources/data.js +369 -0
- package/dist/esm/server/resources/identity.js +7 -3
- package/dist/esm/server/resources/kv.js +80 -0
- package/dist/esm/server/resources/platform.js +39 -0
- package/dist/esm/server/resources/sessions.js +1 -1
- package/dist/esm/server/resources/workspaces.js +70 -0
- package/dist/esm/store-schema.js +62 -0
- package/dist/esm/store.js +141 -0
- package/dist/esm/ui/empty-state.js +25 -0
- package/dist/esm/ui/index.js +34 -0
- package/dist/esm/ui/page-header.js +24 -0
- package/dist/esm/ui/page-shell.js +41 -0
- package/dist/esm/ui/section.js +17 -0
- package/dist/esm/ui/setting-row.js +11 -0
- package/dist/esm/ui/status-chip.js +14 -0
- package/dist/types/client/components/ai-screens.d.ts +25 -0
- package/dist/types/client/components/ai-widgets.d.ts +34 -1
- package/dist/types/client/components/app-shell.d.ts +65 -27
- package/dist/types/client/components/apps-grid.d.ts +11 -2
- package/dist/types/client/components/ask-ai.d.ts +47 -1
- package/dist/types/client/components/confirm-dialog.d.ts +34 -0
- package/dist/types/client/components/cursor-pagination.d.ts +27 -0
- package/dist/types/client/components/filter-tabs.d.ts +26 -0
- package/dist/types/client/components/grievance-notice.d.ts +31 -0
- package/dist/types/client/components/link-button.d.ts +32 -9
- package/dist/types/client/components/logo.d.ts +18 -2
- package/dist/types/client/components/nav.d.ts +24 -0
- package/dist/types/client/components/plan-pricing.d.ts +10 -0
- package/dist/types/client/components/platform-gate.d.ts +53 -0
- package/dist/types/client/components/shell-context.d.ts +116 -0
- package/dist/types/client/components/side-nav.d.ts +27 -13
- package/dist/types/client/components/sidebar.d.ts +95 -0
- package/dist/types/client/components/user-menu.d.ts +68 -22
- package/dist/types/client/components/workspace-switcher.d.ts +49 -0
- package/dist/types/client/index.d.ts +29 -5
- package/dist/types/client/provider.d.ts +109 -6
- package/dist/types/client/use-checkout.d.ts +8 -4
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/mcp/index.d.ts +74 -0
- package/dist/types/mcp/tools.d.ts +107 -0
- package/dist/types/next/index.d.ts +1 -1
- package/dist/types/server/client.d.ts +25 -2
- package/dist/types/server/http.d.ts +8 -2
- package/dist/types/server/index.d.ts +4 -0
- package/dist/types/server/resources/acting-as.d.ts +11 -0
- package/dist/types/server/resources/ai.d.ts +20 -2
- package/dist/types/server/resources/data.d.ts +258 -0
- package/dist/types/server/resources/kv.d.ts +63 -0
- package/dist/types/server/resources/platform.d.ts +32 -0
- package/dist/types/server/resources/workspaces.d.ts +55 -0
- package/dist/types/store-schema.d.ts +52 -0
- package/dist/types/store.d.ts +96 -0
- package/dist/types/types.d.ts +369 -16
- package/dist/types/ui/empty-state.d.ts +34 -0
- package/dist/types/ui/index.d.ts +40 -0
- package/dist/types/ui/page-header.d.ts +34 -0
- package/dist/types/ui/page-shell.d.ts +39 -0
- package/dist/types/ui/section.d.ts +26 -0
- package/dist/types/ui/setting-row.d.ts +18 -0
- package/dist/types/ui/status-chip.d.ts +36 -0
- package/package.json +15 -2
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Building blocks for `data.commit` - the transform sentinels, and the two
|
|
3
|
+
* ways to say "only if nothing has moved".
|
|
4
|
+
*
|
|
5
|
+
* Isomorphic and dependency-free, so a Server Component and a client component
|
|
6
|
+
* building the same write use the same constructors. They are functions rather
|
|
7
|
+
* than object literals you write by hand because the sentinel shape is an
|
|
8
|
+
* encoding, not an API: `{ __transform: "increment", by: 1 }` is what goes over
|
|
9
|
+
* the wire, and nothing outside this file should have to know that.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Add to a number, atomically.
|
|
13
|
+
*
|
|
14
|
+
* **The one operation you genuinely cannot do yourself.** Increments are
|
|
15
|
+
* commutative, so two concurrent `increment(500)` calls both land - where
|
|
16
|
+
* read-add-write from your own process loses one of them however carefully it
|
|
17
|
+
* retries, because the read and the write are two round trips with a gap in
|
|
18
|
+
* between. Use this for balances, counters and tallies, and reach for a
|
|
19
|
+
* precondition only when the new value depends on the old one in a way
|
|
20
|
+
* addition cannot express.
|
|
21
|
+
*
|
|
22
|
+
* Negative to subtract; there is no separate `decrement`.
|
|
23
|
+
*/
|
|
24
|
+
export function increment(by) {
|
|
25
|
+
return { __transform: "increment", by };
|
|
26
|
+
}
|
|
27
|
+
/** Connect's own write time, rather than a clock reading from your process. */
|
|
28
|
+
export function serverTimestamp() {
|
|
29
|
+
return { __transform: "serverTimestamp" };
|
|
30
|
+
}
|
|
31
|
+
/** Adds the values not already in the array. Order is not preserved. */
|
|
32
|
+
export function arrayUnion(...values) {
|
|
33
|
+
return { __transform: "arrayUnion", values };
|
|
34
|
+
}
|
|
35
|
+
/** Removes every occurrence of each value. */
|
|
36
|
+
export function arrayRemove(...values) {
|
|
37
|
+
return { __transform: "arrayRemove", values };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Removes the field entirely.
|
|
41
|
+
*
|
|
42
|
+
* `merge` only - a `replace` already drops every field it does not list, so
|
|
43
|
+
* asking it to delete one is a contradiction rather than a narrowing.
|
|
44
|
+
*/
|
|
45
|
+
export function deleteField() {
|
|
46
|
+
return { __transform: "delete" };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* "Only if this document has not changed since I read it."
|
|
50
|
+
*
|
|
51
|
+
* Pass the document a read returned, and the precondition is built from its
|
|
52
|
+
* `_updateTime`. Taking the document rather than the timestamp is deliberate:
|
|
53
|
+
* a caller who has to reach for `doc.data._updateTime` themselves is one typo
|
|
54
|
+
* away from a precondition that silently does nothing.
|
|
55
|
+
*
|
|
56
|
+
* Throws rather than degrading when the document carries no `_updateTime` -
|
|
57
|
+
* that only happens for a document you built yourself rather than read, and a
|
|
58
|
+
* precondition that quietly became "no precondition" is worse than a crash at
|
|
59
|
+
* the call site.
|
|
60
|
+
*/
|
|
61
|
+
export function unchangedSince(document) {
|
|
62
|
+
const updateTime = document.data._updateTime;
|
|
63
|
+
if (typeof updateTime !== "string") {
|
|
64
|
+
throw new Error(`Document "${document.id}" has no _updateTime, so there is no version to hold it to. Preconditions are built from documents you read back, not from ones you constructed.`);
|
|
65
|
+
}
|
|
66
|
+
return { updateTime };
|
|
67
|
+
}
|
|
68
|
+
/** "Only if this document exists" / "only if it does not." */
|
|
69
|
+
export function documentExists(exists = true) {
|
|
70
|
+
return { exists };
|
|
71
|
+
}
|
|
72
|
+
/** Firestore treats these as a range scan, which constrains where they may sit. */
|
|
73
|
+
const RANGE_OPERATORS = new Set(["!=", "not-in", "<", "<=", ">", ">="]);
|
|
74
|
+
/** Served by an equality-shaped index entry - `in` is a disjunction of equalities. */
|
|
75
|
+
const EQUALITY_OPERATORS = new Set(["==", "in"]);
|
|
76
|
+
/**
|
|
77
|
+
* The composite index a query shape needs, or `null` when Firestore's
|
|
78
|
+
* automatic single-field indexes already serve it.
|
|
79
|
+
*
|
|
80
|
+
* Use it to generate your Store tab declarations from the queries your code
|
|
81
|
+
* actually makes, rather than transcribing them by hand and discovering the
|
|
82
|
+
* gap in production:
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* const needed = MY_QUERY_SHAPES.map(storeIndexFor).filter(Boolean);
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* **Field order is Firestore's, not yours.** Equality fields first (sorted, so
|
|
89
|
+
* the order you passed them in cannot produce two different declarations for
|
|
90
|
+
* one query), then the `array-contains` field, then the field the query orders
|
|
91
|
+
* by. Declaring the same shape twice in two orders builds two indexes, one of
|
|
92
|
+
* which is dead weight on every document write.
|
|
93
|
+
*
|
|
94
|
+
* Connect runs the same derivation server-side and is the authority: a query
|
|
95
|
+
* with no matching declared index is refused with the index it needs spelled
|
|
96
|
+
* out, so a disagreement here surfaces on the first call rather than silently.
|
|
97
|
+
*
|
|
98
|
+
* Throws for a query Firestore cannot serve at all - two `array-contains`
|
|
99
|
+
* filters, ranges over two fields, or a range ordered by a different field.
|
|
100
|
+
*/
|
|
101
|
+
export function storeIndexFor(query) {
|
|
102
|
+
const filters = query.filters ?? [];
|
|
103
|
+
const orderBy = query.orderBy ?? null;
|
|
104
|
+
const equalities = filters.filter((filter) => EQUALITY_OPERATORS.has(filter.op));
|
|
105
|
+
const arrayContains = filters.filter((filter) => filter.op === "array-contains");
|
|
106
|
+
const rangeFields = [
|
|
107
|
+
...new Set(filters.filter((filter) => RANGE_OPERATORS.has(filter.op)).map((f) => f.field)),
|
|
108
|
+
];
|
|
109
|
+
if (arrayContains.length > 1) {
|
|
110
|
+
throw new Error(`A query may use "array-contains" once; this one uses it ${arrayContains.length} times.`);
|
|
111
|
+
}
|
|
112
|
+
if (rangeFields.length > 1) {
|
|
113
|
+
throw new Error(`A query may range over one field; this one ranges over ${rangeFields.join(", ")}.`);
|
|
114
|
+
}
|
|
115
|
+
const rangeField = rangeFields[0] ?? null;
|
|
116
|
+
if (rangeField && orderBy && orderBy.field !== rangeField) {
|
|
117
|
+
throw new Error(`A range filter on "${rangeField}" must be ordered by "${rangeField}" - Firestore cannot order a range scan by "${orderBy.field}".`);
|
|
118
|
+
}
|
|
119
|
+
const ordered = orderBy?.field ?? rangeField;
|
|
120
|
+
const distinct = new Set([...filters.map((filter) => filter.field), ...(ordered ? [ordered] : [])]);
|
|
121
|
+
if (filters.length <= 1 && distinct.size <= 1)
|
|
122
|
+
return null;
|
|
123
|
+
const fields = [
|
|
124
|
+
...[...new Set(equalities.map((filter) => filter.field))]
|
|
125
|
+
.sort()
|
|
126
|
+
.map((field) => ({ field, direction: "asc" })),
|
|
127
|
+
...arrayContains.map((filter) => ({ field: filter.field, direction: "array-contains" })),
|
|
128
|
+
];
|
|
129
|
+
if (ordered)
|
|
130
|
+
fields.push({ field: ordered, direction: orderBy?.direction ?? "asc" });
|
|
131
|
+
return fields;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* An index as one comparable string - `"status:asc,createdAt:desc"`.
|
|
135
|
+
*
|
|
136
|
+
* Deduplicate a generated list on this before declaring it: a dozen query
|
|
137
|
+
* shapes across a screen usually collapse to two or three distinct indexes.
|
|
138
|
+
*/
|
|
139
|
+
export function storeIndexSignature(fields) {
|
|
140
|
+
return fields.map((entry) => `${entry.field}:${entry.direction}`).join(",");
|
|
141
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Card } from "@heroui/react";
|
|
3
|
+
/**
|
|
4
|
+
* What a list, grid or table shows when it has nothing to show.
|
|
5
|
+
*
|
|
6
|
+
* **The two empty states are different facts and the copy must say so.** "You
|
|
7
|
+
* have no contacts" and "no contacts match this filter" call for different
|
|
8
|
+
* words and a different action - create the thing, versus undo the filter
|
|
9
|
+
* hiding it. Both render through here; the caller decides which by what it
|
|
10
|
+
* passes as `action`. Getting this wrong costs a user real time: an
|
|
11
|
+
* unfiltered-looking empty state in front of a filtered list reads as data
|
|
12
|
+
* loss.
|
|
13
|
+
*
|
|
14
|
+
* `variant` chooses the container by what the state stands *in*: `card` (the
|
|
15
|
+
* default), `dashed` for a region that will be filled in place - a file grid,
|
|
16
|
+
* a board column - where the outline is what says "things appear here", and
|
|
17
|
+
* `plain` for a slot already inside a card that would otherwise nest two
|
|
18
|
+
* borders.
|
|
19
|
+
*/
|
|
20
|
+
export function ConnectEmptyState({ icon, title, description, action, variant = "card", className, }) {
|
|
21
|
+
const body = (_jsxs("div", { className: "flex flex-col items-center text-center", children: [icon && (_jsx("span", { "aria-hidden": true, className: "mb-3 text-muted [&>svg]:size-8", children: icon })), _jsx("p", { className: "text-sm font-medium text-foreground", children: title }), description && _jsx("p", { className: "mt-1 max-w-md text-sm text-muted", children: description }), action && _jsx("div", { className: "mt-3", children: action })] }));
|
|
22
|
+
if (variant === "plain")
|
|
23
|
+
return _jsx("div", { className: className, children: body });
|
|
24
|
+
return (_jsx(Card, { className: `p-5 sm:p-8 ${variant === "dashed" ? "border-dashed" : ""} ${className ?? ""}`, children: body }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-composition primitives - the layer between HeroUI and your features.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately **not** `"use client"`. Everything here is presentational, with
|
|
5
|
+
* no hooks and no state, so it renders in a Server Component and stays out of
|
|
6
|
+
* the browser bundle. That is the whole reason this is a separate entry point
|
|
7
|
+
* from `@cosmoops/connect-sdk/client`: that module's barrel carries a
|
|
8
|
+
* `"use client"` directive, so re-exporting a `<Card>` wrapper through it
|
|
9
|
+
* would turn every page shell in your app into a client boundary for no gain.
|
|
10
|
+
*
|
|
11
|
+
* The interactive counterparts - a confirm dialog, cursor pagination, filter
|
|
12
|
+
* tabs - hold state and live in `/client` alongside the rest of the browser
|
|
13
|
+
* half.
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { ConnectPageShell, ConnectSection } from "@cosmoops/connect-sdk/ui";
|
|
17
|
+
*
|
|
18
|
+
* export default function ContactsPage() {
|
|
19
|
+
* return (
|
|
20
|
+
* <ConnectPageShell title="Contacts" description="Everyone you do business with.">
|
|
21
|
+
* <ConnectPageShell.Async fallback={<TableSkeleton />}>
|
|
22
|
+
* <ContactsTable />
|
|
23
|
+
* </ConnectPageShell.Async>
|
|
24
|
+
* </ConnectPageShell>
|
|
25
|
+
* );
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export { ConnectPageShell } from "./page-shell.js";
|
|
30
|
+
export { ConnectPageHeader } from "./page-header.js";
|
|
31
|
+
export { ConnectSection } from "./section.js";
|
|
32
|
+
export { ConnectEmptyState } from "./empty-state.js";
|
|
33
|
+
export { ConnectSettingRow } from "./setting-row.js";
|
|
34
|
+
export { ConnectStatusChip } from "./status-chip.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The title / description / action block a page opens with.
|
|
4
|
+
*
|
|
5
|
+
* The structure is the point, not the styling. The action is a peer of the
|
|
6
|
+
* **title**, not of the block, so nothing below the first line can move it -
|
|
7
|
+
* the common hand-rolled shape puts the action after the description, which
|
|
8
|
+
* makes the vertical position of a page's primary control a function of how
|
|
9
|
+
* long someone wrote the paragraph above it. On a phone that pushes it most of
|
|
10
|
+
* a thumb's reach down the screen.
|
|
11
|
+
*
|
|
12
|
+
* Mobile-first, and the two axes are separate decisions: the title row is a
|
|
13
|
+
* `justify-between` at every width (title left, action right, where a thumb
|
|
14
|
+
* expects it), while the description is clamped to two lines on a phone and
|
|
15
|
+
* released from `sm` up.
|
|
16
|
+
*
|
|
17
|
+
* An `h2`, because the document's `h1` is the application itself.
|
|
18
|
+
*
|
|
19
|
+
* No hooks and no state, so this renders from a Server Component - which is
|
|
20
|
+
* why it lives in `@cosmoops/connect-sdk/ui` rather than `/client`.
|
|
21
|
+
*/
|
|
22
|
+
export function ConnectPageHeader({ title, description, action, clamp = true, className, }) {
|
|
23
|
+
return (_jsxs("header", { className: `flex items-start justify-between gap-3 ${className ?? ""}`, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h2", { className: "flex items-center gap-2 text-lg font-semibold", children: title }), description && (_jsx("p", { className: `mt-2 text-sm text-muted ${clamp ? "line-clamp-2 sm:line-clamp-none" : ""}`, children: description }))] }), action && _jsx("div", { className: "shrink-0", children: action })] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense } from "react";
|
|
3
|
+
import { ConnectPageHeader } from "./page-header.js";
|
|
4
|
+
/**
|
|
5
|
+
* The frame a page opens with: its vertical rhythm, its header, and the
|
|
6
|
+
* suspended regions beneath.
|
|
7
|
+
*
|
|
8
|
+
* **The header is a prop, so it renders in the static shell** - above and
|
|
9
|
+
* outside every `<Suspense>` boundary. That is the one bug this component
|
|
10
|
+
* exists to make unrepresentable: rendering the header inside the async child
|
|
11
|
+
* makes a page's own title wait on a data read, and under Next's partial
|
|
12
|
+
* prefetching the route's shared App Shell loses it as well. There is no
|
|
13
|
+
* arrangement of `children` that reaches the header slot.
|
|
14
|
+
*
|
|
15
|
+
* A detail route is the honest exception, and it has its own door:
|
|
16
|
+
* `ConnectPageShell.Header` renders the identical header from inside an async
|
|
17
|
+
* child, for a page whose title is data it does not have yet. Pair it with a
|
|
18
|
+
* skeleton header in the fallback. The rule is that a *static* title must
|
|
19
|
+
* never be made to wait, not that every title can be static.
|
|
20
|
+
*
|
|
21
|
+
* `space-y-5` is the page gap, owned here so it has exactly one definition.
|
|
22
|
+
*/
|
|
23
|
+
export function ConnectPageShell({ title, description, action, clamp, above, children, }) {
|
|
24
|
+
return (_jsxs("div", { className: "space-y-5", children: [above, title !== undefined && (_jsx(ConnectPageHeader, { title: title, description: description, action: action, clamp: clamp })), children] }));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The page header, for a detail route that can only name itself once its data
|
|
28
|
+
* has loaded. The same component the shell renders from its props, so the two
|
|
29
|
+
* cannot drift apart.
|
|
30
|
+
*/
|
|
31
|
+
ConnectPageShell.Header = ConnectPageHeader;
|
|
32
|
+
/**
|
|
33
|
+
* A suspended region of the page.
|
|
34
|
+
*
|
|
35
|
+
* The donut: a static shell with async children behind their own boundaries,
|
|
36
|
+
* so a slow read costs its own section rather than the whole page. Give each
|
|
37
|
+
* independent region its own `Async` and they stream separately.
|
|
38
|
+
*/
|
|
39
|
+
ConnectPageShell.Async = function ConnectPageShellAsync({ fallback = null, children, }) {
|
|
40
|
+
return _jsx(Suspense, { fallback: fallback, children: children });
|
|
41
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Card } from "@heroui/react";
|
|
3
|
+
/**
|
|
4
|
+
* One titled block inside a page: a card, its heading, what it is for, and the
|
|
5
|
+
* one control that acts on it.
|
|
6
|
+
*
|
|
7
|
+
* The heading is an `h3` - `ConnectPageHeader` renders the page title as an
|
|
8
|
+
* `h2`, so a block *inside* a page is a level below it. Hand-rolled sections
|
|
9
|
+
* reliably use `h2`, which makes every settings block a sibling of the page it
|
|
10
|
+
* sits in and flattens the outline a screen reader announces.
|
|
11
|
+
*
|
|
12
|
+
* As in the page header, the action is a peer of the title rather than of the
|
|
13
|
+
* block, so its position does not move with the length of the description.
|
|
14
|
+
*/
|
|
15
|
+
export function ConnectSection({ title, description, action, titleId, className, bodyClassName, children, }) {
|
|
16
|
+
return (_jsxs(Card, { className: className ?? "p-4 sm:p-5", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { id: titleId, className: "text-base font-semibold", children: title }), description && _jsx("p", { className: "mt-1 text-sm text-muted", children: description })] }), action && _jsx("div", { className: "shrink-0", children: action })] }), children && _jsx("div", { className: bodyClassName ?? "mt-4 space-y-4", children: children })] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* One labelled setting inside a `ConnectSection`.
|
|
4
|
+
*
|
|
5
|
+
* Stacks on a phone and goes side-by-side from `sm` up, rather than shrinking
|
|
6
|
+
* a two-column row until neither column is readable. The control keeps its
|
|
7
|
+
* intrinsic width so truncation lands on the description.
|
|
8
|
+
*/
|
|
9
|
+
export function ConnectSettingRow({ title, description, action, children, className, }) {
|
|
10
|
+
return (_jsxs("div", { className: `flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between ${className ?? ""}`, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: title }), description && _jsx("p", { className: "mt-1 text-sm text-muted", children: description }), children && _jsx("div", { className: "mt-3", children: children })] }), action && _jsx("div", { className: "shrink-0 sm:pl-4", children: action })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Chip } from "@heroui/react";
|
|
3
|
+
/**
|
|
4
|
+
* One status, rendered the way every status should be.
|
|
5
|
+
*
|
|
6
|
+
* Domain knowledge stays in the domain: each caller exports its own
|
|
7
|
+
* `ConnectStatusPresentation` next to the union it describes, and this renders
|
|
8
|
+
* it. A status whose *rendering* carries real logic - a scheduled cancellation
|
|
9
|
+
* outranking a running trial, say - keeps its own component and calls this for
|
|
10
|
+
* the final chip. A map cannot express precedence.
|
|
11
|
+
*/
|
|
12
|
+
export function ConnectStatusChip({ status, presentation, className, }) {
|
|
13
|
+
return (_jsx(Chip, { color: presentation.tone[status], size: "sm", variant: "soft", className: className, children: presentation.label[status] }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ConnectScreen } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The declared screen a live path is on, or `null` if none matches.
|
|
4
|
+
*
|
|
5
|
+
* `null` is a normal outcome, not an error: an application need only describe
|
|
6
|
+
* the screens it wants the assistant to know about, and an undescribed one
|
|
7
|
+
* simply costs the assistant its orientation rather than costing the user an
|
|
8
|
+
* answer.
|
|
9
|
+
*/
|
|
10
|
+
export declare function matchConnectScreen(screens: readonly ConnectScreen[], path: string): ConnectScreen | null;
|
|
11
|
+
/**
|
|
12
|
+
* Whether an href the model produced may be rendered as a link.
|
|
13
|
+
*
|
|
14
|
+
* Same reasoning as Connect's own `showActions` check, and the same conclusion:
|
|
15
|
+
* an assistant that can emit an arbitrary URL is a redirect the model can be
|
|
16
|
+
* talked into, and retrieved content shares a context window with its
|
|
17
|
+
* tool-calling authority. So a link is drawn only when it resolves to a screen
|
|
18
|
+
* *your application declared* - which makes an external destination structurally
|
|
19
|
+
* impossible, since no route pattern has a scheme or a host.
|
|
20
|
+
*
|
|
21
|
+
* The protocol-relative form is refused separately: `//evil.example.com` has no
|
|
22
|
+
* scheme for a path matcher to trip over, and would otherwise reach an anchor
|
|
23
|
+
* as an absolute destination.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isLinkableConnectScreen(screens: readonly ConnectScreen[], href: string): boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { AiChartInput, AiDetailsInput, AiMetricsInput, AiSourcesInput, AiStepsInput, AiTableInput, AiTimelineInput } from "../../types.js";
|
|
2
|
+
import type { AiChartInput, AiDetailsInput, AiMetricsInput, AiSourcesInput, AiStepsInput, AiActionsInput, AiTableInput, AiTimelineInput, ConnectScreen } from "../../types.js";
|
|
3
3
|
/**
|
|
4
4
|
* `showTable`. Sorting and CSV are what a table is *for* - once an answer is
|
|
5
5
|
* tabular the next thing anyone wants is to reorder it or take it elsewhere,
|
|
@@ -40,3 +40,36 @@ export declare function AiSteps({ title, steps, renderDetail, }: Readonly<AiStep
|
|
|
40
40
|
* without is still a named row - never a marker with nothing behind it.
|
|
41
41
|
*/
|
|
42
42
|
export declare function AiSources({ sources }: Readonly<AiSourcesInput>): import("react").JSX.Element;
|
|
43
|
+
/**
|
|
44
|
+
* Where to go next, as links into *your* application.
|
|
45
|
+
*
|
|
46
|
+
* ## Why links and not navigation
|
|
47
|
+
*
|
|
48
|
+
* The obvious version of "the assistant can act" moves the user. It is also the
|
|
49
|
+
* version that breaks the property the whole design rests on: nothing happens
|
|
50
|
+
* without a click. Retrieved content shares a context window with the model's
|
|
51
|
+
* tool-calling authority, so an auto-navigate is a redirect primitive that
|
|
52
|
+
* content can reach. A link cannot be, and loses nothing - the user was going
|
|
53
|
+
* to click something anyway.
|
|
54
|
+
*
|
|
55
|
+
* ## Why every href is checked against your declared screens
|
|
56
|
+
*
|
|
57
|
+
* The model can emit any string. Only your application knows which strings are
|
|
58
|
+
* real routes, which is why `screens` is a prop rather than something Connect
|
|
59
|
+
* could supply. A link that does not resolve is *dropped* rather than rendered
|
|
60
|
+
* broken - the prose around it still stands, so a missing link reads as terse
|
|
61
|
+
* rather than wrong.
|
|
62
|
+
*
|
|
63
|
+
* `renderLink` is how this stays router-agnostic. Given one, a link is your
|
|
64
|
+
* framework's - a Next `<Link>`, a TanStack `<Link>` - and navigation is a
|
|
65
|
+
* client transition. Without one it falls back to an anchor, which works
|
|
66
|
+
* everywhere and costs a document load.
|
|
67
|
+
*/
|
|
68
|
+
export declare function AiActions({ title, actions, screens, renderLink, }: Readonly<AiActionsInput & {
|
|
69
|
+
screens: readonly ConnectScreen[];
|
|
70
|
+
renderLink?: (props: {
|
|
71
|
+
href: string;
|
|
72
|
+
className: string;
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
}) => ReactNode;
|
|
75
|
+
}>): import("react").JSX.Element | null;
|
|
@@ -11,34 +11,51 @@ export interface AppShellProps {
|
|
|
11
11
|
homeHref?: string;
|
|
12
12
|
/** The sidebar's destinations - items, sections, or a mix. See `ConnectNav`. */
|
|
13
13
|
nav: ConnectNav;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* A second nav group, pinned to the foot of the rail above the account row.
|
|
16
|
+
*
|
|
17
|
+
* For the routes a product has but does not want competing with its own:
|
|
18
|
+
* settings, help, changelog, status. They are the same `ConnectNav` shape
|
|
19
|
+
* and get the same active resolution and audience filtering - the only
|
|
20
|
+
* difference is where they sit, which is the difference between "what this
|
|
21
|
+
* product is for" and "everything else".
|
|
22
|
+
*
|
|
23
|
+
* A route belongs up top if a user goes there to *do the job*, and down here
|
|
24
|
+
* if they go there to change how the job works. Putting Settings in the
|
|
25
|
+
* primary list is what makes a nine-item rail out of a five-item product.
|
|
26
|
+
*/
|
|
27
|
+
secondaryNav?: ConnectNav;
|
|
28
|
+
/** Current path. Pass `usePathname()` in Next; see `ConnectSidebarNavProps.activePath`. */
|
|
15
29
|
activePath?: string;
|
|
16
30
|
/** See `ConnectLinkRenderer` - how this app renders a link. Defaults to a plain `<a>`. */
|
|
17
31
|
renderLink?: ConnectLinkRenderer;
|
|
32
|
+
/** Extra rows pinned to the bottom of the sidebar, above the account row. See `ConnectSidebarRow`. */
|
|
33
|
+
sidebarFooter?: ReactNode;
|
|
18
34
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
35
|
+
* The workspace switcher under the brand. On by default, and it hides itself
|
|
36
|
+
* for an account with only one workspace - so the reason to pass `false` is
|
|
37
|
+
* not "most users have one" (already handled) but "this application renders
|
|
38
|
+
* its own".
|
|
22
39
|
*/
|
|
23
|
-
|
|
24
|
-
/** Your own controls in the top bar, left of the Connect ones. */
|
|
25
|
-
headerActions?: ReactNode;
|
|
26
|
-
/** Extra content pinned to the bottom of the sidebar, above the fold-away. */
|
|
27
|
-
sidebarFooter?: ReactNode;
|
|
40
|
+
workspaceSwitcher?: false;
|
|
28
41
|
/** Rendered below `children`, inside the scrolling content pane. */
|
|
29
42
|
footer?: ReactNode;
|
|
30
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Start the rail collapsed. **Defaults to `true`** - see the component's
|
|
45
|
+
* own doc comment. Ignored when `collapsed` is supplied, and overridden by
|
|
46
|
+
* a stored preference from the user's last visit.
|
|
47
|
+
*/
|
|
31
48
|
defaultCollapsed?: boolean;
|
|
32
49
|
/** Drive the rail yourself. Supply `onCollapsedChange` with it. */
|
|
33
50
|
collapsed?: boolean;
|
|
34
51
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
35
52
|
/** Remove the collapse control and pin the rail expanded. */
|
|
36
53
|
collapsible?: boolean;
|
|
37
|
-
/** The app launcher in the
|
|
54
|
+
/** The app launcher, in the sidebar footer. On by default. */
|
|
38
55
|
showAppsGrid?: boolean;
|
|
39
|
-
/** "Continue with Connect" / "Create account" in the
|
|
56
|
+
/** "Continue with Connect" / "Create account" in the sidebar footer when signed out. On by default. */
|
|
40
57
|
showAuthButtons?: boolean;
|
|
41
|
-
/** Passed to `
|
|
58
|
+
/** Passed to `ConnectUserButton` - the SDK has no theme provider, so this is host-controlled. */
|
|
42
59
|
theme?: ThemeMode;
|
|
43
60
|
onThemeChange?: (theme: ThemeMode) => void;
|
|
44
61
|
/**
|
|
@@ -48,7 +65,7 @@ export interface AppShellProps {
|
|
|
48
65
|
* gate asks for, and an app that mounts a shell and forgets the gate is an
|
|
49
66
|
* app that lets a suspended workspace keep working.
|
|
50
67
|
*
|
|
51
|
-
* The chrome is never gated: the sidebar, the lockup, the
|
|
68
|
+
* The chrome is never gated: the sidebar, the lockup, the account row and
|
|
52
69
|
* sign-out stay reachable in every blocked state, so a blocked customer can
|
|
53
70
|
* always get to the page that unblocks them.
|
|
54
71
|
*
|
|
@@ -83,23 +100,21 @@ export interface AppShellProps {
|
|
|
83
100
|
children: ReactNode;
|
|
84
101
|
}
|
|
85
102
|
/**
|
|
86
|
-
* The application frame every CosmoOps application platform runs inside:
|
|
87
|
-
*
|
|
88
|
-
*
|
|
103
|
+
* The application frame every CosmoOps application platform runs inside: one
|
|
104
|
+
* rail down the side holding the brand, the workspace, the navigation and the
|
|
105
|
+
* account, and your pages in the rest.
|
|
89
106
|
*
|
|
90
107
|
* This is the piece each partner was otherwise writing by hand, and writing
|
|
91
|
-
* slightly differently - which is how a family of applications stops looking
|
|
92
|
-
* one product. Everything that should be identical across platforms
|
|
93
|
-
*
|
|
94
|
-
* account menu, the access gate) lives here; everything that genuinely
|
|
95
|
-
* differs is a prop:
|
|
108
|
+
* slightly differently - which is how a family of applications stops looking
|
|
109
|
+
* like one product. Everything that should be identical across platforms
|
|
110
|
+
* lives here; everything that genuinely differs is a prop:
|
|
96
111
|
*
|
|
97
112
|
* ```tsx
|
|
98
113
|
* "use client";
|
|
99
114
|
* import Link from "next/link";
|
|
100
115
|
* import { usePathname } from "next/navigation";
|
|
101
116
|
* import { AppShell } from "@cosmoops/connect-sdk/client";
|
|
102
|
-
* import { LayoutDashboard, Truck } from "lucide-react";
|
|
117
|
+
* import { LayoutDashboard, Settings, Truck } from "lucide-react";
|
|
103
118
|
*
|
|
104
119
|
* export function Shell({ children }: { children: React.ReactNode }) {
|
|
105
120
|
* return (
|
|
@@ -110,7 +125,10 @@ export interface AppShellProps {
|
|
|
110
125
|
* renderLink={(props) => <Link {...props} />}
|
|
111
126
|
* nav={[
|
|
112
127
|
* { label: "Overview", href: "/", icon: <LayoutDashboard className="size-5" /> },
|
|
113
|
-
* { label: "
|
|
128
|
+
* { label: "Shipments", href: "/shipments", icon: <Truck className="size-5" /> },
|
|
129
|
+
* ]}
|
|
130
|
+
* secondaryNav={[
|
|
131
|
+
* { label: "Settings", href: "/settings", icon: <Settings className="size-5" /> },
|
|
114
132
|
* ]}
|
|
115
133
|
* >
|
|
116
134
|
* {children}
|
|
@@ -119,13 +137,33 @@ export interface AppShellProps {
|
|
|
119
137
|
* }
|
|
120
138
|
* ```
|
|
121
139
|
*
|
|
140
|
+
* **There is no top bar.** There used to be one, carrying a page title, the
|
|
141
|
+
* launcher and the account menu - and it was a second, half-width strip of
|
|
142
|
+
* chrome saying things the rail was better placed to say. The launcher and
|
|
143
|
+
* the account row moved into the rail's footer, where they are one press from
|
|
144
|
+
* wherever the pointer already is; the page title moved into the page, where
|
|
145
|
+
* `ConnectPageShell` renders it next to that page's own actions and
|
|
146
|
+
* breadcrumbs. What is left is a single column of chrome and a full-height
|
|
147
|
+
* content pane, which is what every application this shell is modelled on
|
|
148
|
+
* settled at.
|
|
149
|
+
*
|
|
150
|
+
* The one exception is below `md`, where a slim bar carries the drawer
|
|
151
|
+
* trigger. A phone has no rail to put it in, and a navigation you cannot open
|
|
152
|
+
* is not a smaller navigation.
|
|
153
|
+
*
|
|
154
|
+
* **The rail starts collapsed.** `defaultCollapsed` is `true`: the icon rail
|
|
155
|
+
* is the resting state, and expanding is the deliberate act. That is the right
|
|
156
|
+
* default because the rail's job is to be *there*, not to be read - a person
|
|
157
|
+
* learns five glyphs in a day and then wants the 200px back for their data.
|
|
158
|
+
* A stored preference from the user's last visit always wins over it.
|
|
159
|
+
*
|
|
122
160
|
* There is no `isSignedIn` branch anywhere in here, and there should be none
|
|
123
|
-
* in yours: the auth buttons render nothing once a session exists, the
|
|
124
|
-
*
|
|
161
|
+
* in yours: the auth buttons render nothing once a session exists, the account
|
|
162
|
+
* row renders nothing without one, and the launcher renders nothing when
|
|
125
163
|
* signed out. The swap happens inside the components.
|
|
126
164
|
*
|
|
127
165
|
* It renders no provider of its own - mount `ConnectProvider` above it, once,
|
|
128
166
|
* so a page with a shell, a plan table and a usage meter still makes one
|
|
129
167
|
* session read.
|
|
130
168
|
*/
|
|
131
|
-
export declare function AppShell({ name, icon, homeHref, nav, activePath, renderLink,
|
|
169
|
+
export declare function AppShell({ name, icon, homeHref, nav, secondaryNav, activePath, renderLink, sidebarFooter, workspaceSwitcher, footer, defaultCollapsed, collapsed: controlledCollapsed, onCollapsedChange, collapsible, showAppsGrid, showAuthButtons, theme, onThemeChange, gate, gateExemptRoutes, askAi, className, contentClassName, children, }: AppShellProps): import("react").JSX.Element;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
export interface ConnectAppsGridProps {
|
|
2
2
|
/** Extra classes on the trigger button. */
|
|
3
3
|
className?: string;
|
|
4
|
-
/** Trigger label for screen readers. */
|
|
4
|
+
/** Trigger label for screen readers, and the collapsed rail's tooltip. */
|
|
5
5
|
label?: string;
|
|
6
|
+
/**
|
|
7
|
+
* How the trigger is drawn.
|
|
8
|
+
*
|
|
9
|
+
* - `"row"` - a full-width sidebar row, glyph on the rail's shared 32px
|
|
10
|
+
* centre. The default, since the launcher lives in the sidebar footer now
|
|
11
|
+
* that there is no top bar to hold it.
|
|
12
|
+
* - `"icon"` - the square accent button, for a bar of your own.
|
|
13
|
+
*/
|
|
14
|
+
variant?: "row" | "icon";
|
|
6
15
|
}
|
|
7
16
|
/**
|
|
8
17
|
* The Google-style app launcher: a grid icon in your top bar that opens a
|
|
@@ -17,4 +26,4 @@ export interface ConnectAppsGridProps {
|
|
|
17
26
|
* Renders nothing when signed out: a launcher with nothing to launch is just a
|
|
18
27
|
* button that opens an empty box.
|
|
19
28
|
*/
|
|
20
|
-
export declare function ConnectAppsGrid({ className, label }: ConnectAppsGridProps): import("react").JSX.Element | null;
|
|
29
|
+
export declare function ConnectAppsGrid({ className, label, variant }: ConnectAppsGridProps): import("react").JSX.Element | null;
|
|
@@ -1,5 +1,51 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
1
2
|
import { type StreamdownProps } from "streamdown";
|
|
3
|
+
import type { ConnectScreen } from "../../types.js";
|
|
2
4
|
export interface ConnectAskAiProps {
|
|
5
|
+
/**
|
|
6
|
+
* The screens in your application, described for the assistant.
|
|
7
|
+
*
|
|
8
|
+
* Without these it answers from general knowledge about software, because
|
|
9
|
+
* Connect has no idea what your product is - see `ConnectScreen`. With them,
|
|
10
|
+
* "what can I do here?" answers in your own words, the empty panel offers
|
|
11
|
+
* your own opening questions, and `showActions` can link into your routes.
|
|
12
|
+
*
|
|
13
|
+
* Prose and links only: Connect never uses them to decide what anyone may do,
|
|
14
|
+
* and never executes anything on your behalf.
|
|
15
|
+
*/
|
|
16
|
+
screens?: readonly ConnectScreen[];
|
|
17
|
+
/**
|
|
18
|
+
* The route the user is currently on.
|
|
19
|
+
*
|
|
20
|
+
* **Usually omit this.** Left unset, the panel resolves the live path itself
|
|
21
|
+
* through `useActivePath`, which reads `location.pathname` from a
|
|
22
|
+
* `useSyncExternalStore` whose server snapshot is empty - so it works inside
|
|
23
|
+
* a shared App Shell, where `usePathname()` is refused outright
|
|
24
|
+
* (`CLIENT_HOOK_DYNAMIC`) because URL data cannot live in an artifact shared
|
|
25
|
+
* across URLs. That is the same reason `ConnectAppShell` recommends leaving
|
|
26
|
+
* its own `activePath` unset in a Next app.
|
|
27
|
+
*
|
|
28
|
+
* Pass it only where your router's path is not `location.pathname` - a
|
|
29
|
+
* hash router, or a panel embedded under a basePath your routes do not
|
|
30
|
+
* include.
|
|
31
|
+
*/
|
|
32
|
+
activePath?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Renders an action link with your router, so following one is a client
|
|
35
|
+
* transition rather than a document load. Omit it and links are plain
|
|
36
|
+
* anchors, which work everywhere.
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* renderLink={({ href, className, children }) => (
|
|
40
|
+
* <Link href={href} className={className}>{children}</Link>
|
|
41
|
+
* )}
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
renderLink?: (props: {
|
|
45
|
+
href: string;
|
|
46
|
+
className: string;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
}) => ReactNode;
|
|
3
49
|
/** Panel heading. Defaults to `"Ask AI"`. */
|
|
4
50
|
title?: string;
|
|
5
51
|
/** Byline over each assistant turn. Defaults to `"Assistant"`. */
|
|
@@ -70,4 +116,4 @@ export interface ConnectAskAiProps {
|
|
|
70
116
|
* Neither is something the browser can discover, so both surface as a message
|
|
71
117
|
* in the panel rather than as a hidden button.
|
|
72
118
|
*/
|
|
73
|
-
export declare function ConnectAskAi({ title, assistantName, placeholder, hint, defaultOpen, markdownPlugins, }: ConnectAskAiProps): import("react").JSX.Element | null;
|
|
119
|
+
export declare function ConnectAskAi({ title, assistantName, placeholder, hint, defaultOpen, markdownPlugins, screens, activePath, renderLink, }: ConnectAskAiProps): import("react").JSX.Element | null;
|