@protobase/ui 0.33.4 → 0.34.1
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/dist/account/account-security-page.js +16 -6
- package/dist/account/connected-apps-section.js +93 -0
- package/dist/app/app.js +20 -7
- package/dist/app/charts/chart-view.js +1 -1
- package/dist/app/create/create-page.js +2 -2
- package/dist/app/create/create-route.js +1 -1
- package/dist/app/delete/use-delete-flow.js +1 -1
- package/dist/app/displays/board-display.js +1 -1
- package/dist/app/displays/calendar-display.js +1 -1
- package/dist/app/displays/map-display.js +1 -1
- package/dist/app/displays/record-card.js +1 -1
- package/dist/app/displays/timeline-display.js +1 -1
- package/dist/app/displays/use-board-column.js +1 -1
- package/dist/app/displays/use-map-records.js +1 -1
- package/dist/app/embedded-list/embedded-list.js +1 -1
- package/dist/app/embedded-list/use-embedded-rows.js +1 -1
- package/dist/app/export/export-dialog.js +1 -1
- package/dist/app/file-field.js +1 -1
- package/dist/app/gallery/gallery-section.js +1 -1
- package/dist/app/gallery/lightbox.js +1 -1
- package/dist/app/gallery/use-gallery-photos.js +1 -1
- package/dist/app/hand-order/hand-sorted-list.js +1 -1
- package/dist/app/hand-order/use-hand-order.js +1 -1
- package/dist/app/import/import-dialog.js +1 -1
- package/dist/app/incoming-card.js +1 -1
- package/dist/app/list-page.js +3 -3
- package/dist/app/map/map-section.js +1 -1
- package/dist/app/meta-gate.js +2 -2
- package/dist/app/pages/action-inputs/action-inputs-dialog.js +2 -2
- package/dist/app/pages/blocks/card-row.js +1 -1
- package/dist/app/pages/blocks/field.js +1 -1
- package/dist/app/pages/blocks/modal-form.js +1 -1
- package/dist/app/pages/blocks/record-card.js +1 -1
- package/dist/app/pages/blocks/record-cards.js +1 -1
- package/dist/app/pages/blocks/stat.js +1 -1
- package/dist/app/pages/composed-page.js +1 -1
- package/dist/app/pages/field-value.js +1 -1
- package/dist/app/pages/use-run-action.js +1 -1
- package/dist/app/record-chart/record-chart-section.js +1 -1
- package/dist/app/record-lines.js +1 -1
- package/dist/app/record-page.js +5 -5
- package/dist/app/record-row/record-row-body.js +1 -1
- package/dist/app/record-row/saved-record-row.js +1 -1
- package/dist/app/related-cards/related-card.js +1 -1
- package/dist/app/related-section.js +2 -2
- package/dist/app/saved/use-favorite.js +1 -1
- package/dist/app/search/navigation-hits.js +1 -1
- package/dist/app/search/use-global-search.js +1 -1
- package/dist/app/searches/save-search-button.js +1 -1
- package/dist/app/searches/saved-search-rows.js +1 -1
- package/dist/app/searches/saved-searches.js +1 -1
- package/dist/app/searches/search-controls.js +1 -1
- package/dist/app/searches/update-search-dialog.js +1 -1
- package/dist/app/searches/use-save-search.js +1 -1
- package/dist/app/searches/use-saved-searches.js +1 -1
- package/dist/app/source-notice.js +1 -1
- package/dist/app/use-app-address.js +1 -1
- package/dist/app/use-incoming-counts.js +1 -1
- package/dist/auth/auth-gate.js +1 -1
- package/dist/auth/auth-provider.js +4 -0
- package/dist/auth/sign-in-prompt.js +2 -2
- package/dist/connect/client-name.js +9 -0
- package/dist/connect/client-publisher.js +13 -0
- package/dist/connect/connect-page.js +236 -0
- package/dist/connect/interaction-guard.js +77 -0
- package/dist/connect/redirect-target.js +36 -0
- package/dist/connect/signed-request.js +14 -0
- package/dist/connect/use-connect-address.js +11 -0
- package/dist/data/use-live-updates.js +1 -1
- package/dist/forms/public-form-page.js +2 -2
- package/dist/index.js +3 -3
- package/dist/live/use-filter-config.js +1 -1
- package/dist/live/use-reference-search.js +1 -1
- package/dist/package.js +1 -1
- package/dist/plugins/plugin-gate.js +1 -1
- package/dist/shell/assets/app-Bh3A_oyl.js +70 -0
- package/dist/shell/assets/{index-BAYZFH-l.js → index-B7S1UwQw.js} +2 -2
- package/dist/shell/assets/index-BX850JXd.css +2 -0
- package/dist/shell/assets/{records-map-D2XP8QkT.js → records-map-DH1o1e94.js} +2 -2
- package/dist/shell/assets/shared-protobase-ui-yYsrs03Z.js +2 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-Bh3A_oyl.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-BAYZFH-l.js.map → index-B7S1UwQw.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-D2XP8QkT.js.map → records-map-DH1o1e94.js.map} +1 -1
- package/dist/shell-source-maps/assets/{shared-protobase-ui-C5AR2k3j.js.map → shared-protobase-ui-yYsrs03Z.js.map} +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-B9bnfmK5.js +0 -70
- package/dist/shell/assets/index-BrgzNBVN.css +0 -2
- package/dist/shell/assets/shared-protobase-ui-C5AR2k3j.js +0 -2
- package/dist/shell-source-maps/assets/app-B9bnfmK5.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { keys } from "../../data/query-keys.js";
|
|
3
3
|
import { useClient } from "../../data/api-provider.js";
|
|
4
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
4
5
|
import { fileShown, shownImage } from "../../live/file-values.js";
|
|
5
6
|
import { isMarkdown } from "../../markdown/markdown-hint.js";
|
|
6
7
|
import { contactFormat } from "../../live/contact-links.js";
|
|
7
8
|
import { displayValue, valueTitle } from "../../live/display-value.js";
|
|
8
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
9
9
|
import { AppLink } from "../use-app-address.js";
|
|
10
10
|
import { FileImage } from "../../live/file-image.js";
|
|
11
11
|
import { useShownWidget } from "../field-types/shown-widget.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useClient, useResourceClient } from "../../data/api-provider.js";
|
|
2
|
+
import { useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
2
3
|
import { text } from "./messages.js";
|
|
3
4
|
import { useToast } from "../../toasts/toast-provider.js";
|
|
4
|
-
import { useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
5
5
|
import { useAppAddress } from "../use-app-address.js";
|
|
6
6
|
import { useProjectUi } from "./project-ui.js";
|
|
7
7
|
import { can } from "../permissions.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
2
|
import { Segmented } from "../../primitives/segmented.js";
|
|
3
3
|
import { viewerZone } from "../../format/zone.js";
|
|
4
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
4
5
|
import { humanize } from "../../live/naming.js";
|
|
5
6
|
import { Section } from "../../record-view/section.js";
|
|
6
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
7
7
|
import { useAggregates } from "../../data/use-aggregates.js";
|
|
8
8
|
import { PeriodChart } from "../charts/period-chart.js";
|
|
9
9
|
import { chartPoints, rangeBuckets } from "./chart-points.js";
|
package/dist/app/record-lines.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { usePermissions } from "./meta-gate.js";
|
|
1
2
|
import { InlineChildTable } from "../inline-child-table/inline-child-table.js";
|
|
2
3
|
import { Section } from "../record-view/section.js";
|
|
3
|
-
import { usePermissions } from "./meta-gate.js";
|
|
4
4
|
import { can } from "./permissions.js";
|
|
5
5
|
import { text } from "./record-messages.js";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
package/dist/app/record-page.js
CHANGED
|
@@ -3,11 +3,13 @@ import { Skeleton } from "../primitives/skeleton.js";
|
|
|
3
3
|
import { inApp } from "../app-shell/breadcrumb.js";
|
|
4
4
|
import { useAuth } from "../auth/auth-provider.js";
|
|
5
5
|
import { Button, buttonClass } from "../primitives/button.js";
|
|
6
|
+
import { namedIcon } from "./named-icon.js";
|
|
7
|
+
import { text } from "./messages.js";
|
|
8
|
+
import { useNavigationGuard } from "./router.js";
|
|
6
9
|
import { usePauseFocusRefresh } from "../data/focus-refresh.js";
|
|
7
10
|
import { keys } from "../data/query-keys.js";
|
|
8
11
|
import { useClient } from "../data/api-provider.js";
|
|
9
|
-
import {
|
|
10
|
-
import { useNavigationGuard } from "./router.js";
|
|
12
|
+
import { Notice, useAdminMeta, usePermissions } from "./meta-gate.js";
|
|
11
13
|
import { useOpenedRecord } from "../data/use-record.js";
|
|
12
14
|
import { humanize } from "../live/naming.js";
|
|
13
15
|
import { Field } from "../record-view/field.js";
|
|
@@ -21,7 +23,6 @@ import { contactFormat } from "../live/contact-links.js";
|
|
|
21
23
|
import { toneFor } from "../live/columns-from-model.js";
|
|
22
24
|
import { displayValue, valueTitle } from "../live/display-value.js";
|
|
23
25
|
import { filledFields } from "./filled-fields.js";
|
|
24
|
-
import { Notice, useAdminMeta, usePermissions } from "./meta-gate.js";
|
|
25
26
|
import { AppLink, useAppAddress } from "./use-app-address.js";
|
|
26
27
|
import { GroupSection } from "../record-view/group-section.js";
|
|
27
28
|
import { RecordTabs } from "../record-view/record-tabs.js";
|
|
@@ -41,7 +42,6 @@ import { useFocusFirstInvalid } from "./use-focus-first-invalid.js";
|
|
|
41
42
|
import { can, fieldAccess } from "./permissions.js";
|
|
42
43
|
import { ValueBadge } from "./value-badge.js";
|
|
43
44
|
import { FieldValue } from "./pages/field-value.js";
|
|
44
|
-
import { namedIcon } from "./named-icon.js";
|
|
45
45
|
import { text as text$1 } from "./record-messages.js";
|
|
46
46
|
import { useDeleteFlow } from "./delete/use-delete-flow.js";
|
|
47
47
|
import { useRestore } from "./delete/use-restore.js";
|
|
@@ -69,8 +69,8 @@ import { Fragment, useEffect, useMemo } from "react";
|
|
|
69
69
|
import { Check } from "lucide-react";
|
|
70
70
|
import { isApiError } from "@protobase/client";
|
|
71
71
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
72
|
-
import { useQueryClient } from "@tanstack/react-query";
|
|
73
72
|
import { enumLabel } from "@protobase/schema";
|
|
73
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
74
74
|
//#region src/app/record-page.tsx
|
|
75
75
|
const hidden = (model, name) => name === model.tenant || model.primaryKey.includes(name);
|
|
76
76
|
/**
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { formatTag } from "../../locale/active-locale.js";
|
|
2
2
|
import { useIsXs } from "../../lib/use-media-query.js";
|
|
3
3
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
4
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
4
5
|
import { humanize } from "../../live/naming.js";
|
|
5
6
|
import { displaysOf, loadedFields } from "../displays/list-displays.js";
|
|
6
7
|
import { defaultOrderBy } from "../list-order.js";
|
|
7
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
8
8
|
import { AppLink } from "../use-app-address.js";
|
|
9
9
|
import { ErrorBanner } from "../error-banner.js";
|
|
10
10
|
import { viewAllAddress } from "../embedded-list/embedded-display.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
2
3
|
import { humanize } from "../../live/naming.js";
|
|
3
4
|
import { listedResource } from "../current-address.js";
|
|
4
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
5
5
|
import { ErrorBanner } from "../error-banner.js";
|
|
6
6
|
import { savedScope } from "../saved/saved-records.js";
|
|
7
7
|
import { useSavedRecords } from "../saved/use-saved-records.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
|
-
import { recordKey, recordTitle } from "../../live/model-helpers.js";
|
|
3
2
|
import { useAdminMeta } from "../meta-gate.js";
|
|
3
|
+
import { recordKey, recordTitle } from "../../live/model-helpers.js";
|
|
4
4
|
import { AppLink } from "../use-app-address.js";
|
|
5
5
|
import { RecordScope } from "../pages/record-scope.js";
|
|
6
6
|
import { ActionButton } from "../pages/blocks/action.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Skeleton } from "../primitives/skeleton.js";
|
|
2
2
|
import { keys } from "../data/query-keys.js";
|
|
3
3
|
import { useClient } from "../data/api-provider.js";
|
|
4
|
+
import { useAdminMeta, usePermissions } from "./meta-gate.js";
|
|
4
5
|
import { Section } from "../record-view/section.js";
|
|
5
6
|
import { listColumnNames } from "../live/columns-from-model.js";
|
|
6
|
-
import { useAdminMeta, usePermissions } from "./meta-gate.js";
|
|
7
7
|
import { ErrorBanner } from "./error-banner.js";
|
|
8
8
|
import { countQuery } from "./pages/use-layout-data.js";
|
|
9
9
|
import { RelatedCards } from "./related-cards/related-cards.js";
|
|
@@ -17,8 +17,8 @@ import { HandSortedList } from "./hand-order/hand-sorted-list.js";
|
|
|
17
17
|
import { ReorderButton } from "./hand-order/reorder-button.js";
|
|
18
18
|
import { useState } from "react";
|
|
19
19
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
-
import { useQuery } from "@tanstack/react-query";
|
|
21
20
|
import { createWhere, printFilter } from "@protobase/schema";
|
|
21
|
+
import { useQuery } from "@tanstack/react-query";
|
|
22
22
|
//#region src/app/related-section.tsx
|
|
23
23
|
const where$1 = createWhere();
|
|
24
24
|
const and = (...parts) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useClient } from "../../data/api-provider.js";
|
|
2
|
-
import { useToast } from "../../toasts/toast-provider.js";
|
|
3
2
|
import { useAdminMeta } from "../meta-gate.js";
|
|
3
|
+
import { useToast } from "../../toasts/toast-provider.js";
|
|
4
4
|
import { can } from "../permissions.js";
|
|
5
5
|
import { useRefreshResource } from "../pages/use-layout-data.js";
|
|
6
6
|
import { savedFor, withSaved, withoutSaved } from "./saved-records.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { humanize } from "../../live/naming.js";
|
|
2
1
|
import { allowEverything } from "../../data/meta-data.js";
|
|
2
|
+
import { humanize } from "../../live/naming.js";
|
|
3
3
|
import { text } from "../create/messages.js";
|
|
4
4
|
import { createOffered } from "../create/create-offered.js";
|
|
5
5
|
import { navFromMeta } from "../nav-from-meta.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useClient } from "../../data/api-provider.js";
|
|
2
1
|
import { useRouter } from "../router.js";
|
|
2
|
+
import { useClient } from "../../data/api-provider.js";
|
|
3
3
|
import { useAdminMeta } from "../meta-gate.js";
|
|
4
4
|
import { useDebounced } from "../../lib/use-debounced.js";
|
|
5
5
|
import { navigationHits } from "./navigation-hits.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button } from "../../primitives/button.js";
|
|
2
|
-
import { text } from "./messages.js";
|
|
3
2
|
import { SignInPrompt } from "../../auth/sign-in-prompt.js";
|
|
3
|
+
import { text } from "./messages.js";
|
|
4
4
|
import { SearchNameDialog } from "./search-name-dialog.js";
|
|
5
5
|
import { UpdateSearchDialog } from "./update-search-dialog.js";
|
|
6
6
|
import { useSaveSearch } from "./use-save-search.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
2
3
|
import { humanize } from "../../live/naming.js";
|
|
3
4
|
import { recordKey } from "../../live/model-helpers.js";
|
|
4
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
5
5
|
import { AppLink } from "../use-app-address.js";
|
|
6
6
|
import { ErrorBanner } from "../error-banner.js";
|
|
7
7
|
import { text } from "./messages.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
2
|
import { PageHeader } from "../../app-shell/page-header.js";
|
|
3
|
+
import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
3
4
|
import { slotted } from "../../locale/slotted.js";
|
|
4
5
|
import { humanize } from "../../live/naming.js";
|
|
5
6
|
import { recordKey } from "../../live/model-helpers.js";
|
|
6
|
-
import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
7
7
|
import { AppLink } from "../use-app-address.js";
|
|
8
8
|
import { ErrorBanner } from "../error-banner.js";
|
|
9
9
|
import { can } from "../permissions.js";
|
|
@@ -6,8 +6,8 @@ import { useRefreshResource } from "../pages/use-layout-data.js";
|
|
|
6
6
|
import { text } from "./messages.js";
|
|
7
7
|
import { useId } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
import { useMutation } from "@tanstack/react-query";
|
|
10
9
|
import { enumLabel } from "@protobase/schema";
|
|
10
|
+
import { useMutation } from "@tanstack/react-query";
|
|
11
11
|
//#region src/app/searches/search-controls.tsx
|
|
12
12
|
const message = (error) => error instanceof Error ? error.message : text("somethingWrong");
|
|
13
13
|
const Control = ({ model, view, name, value, onChange }) => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
2
|
import { Button } from "../../primitives/button.js";
|
|
3
|
+
import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
|
|
3
4
|
import { useClient } from "../../data/api-provider.js";
|
|
4
5
|
import { humanize } from "../../live/naming.js";
|
|
5
6
|
import { defaultOrderBy } from "../list-order.js";
|
|
6
|
-
import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
|
|
7
7
|
import { countQuery } from "../pages/use-layout-data.js";
|
|
8
8
|
import { text } from "./messages.js";
|
|
9
9
|
import { useFilterChips } from "./use-filter-chips.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useClient } from "../../data/api-provider.js";
|
|
2
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
2
3
|
import { humanize } from "../../live/naming.js";
|
|
3
4
|
import { useToast } from "../../toasts/toast-provider.js";
|
|
4
5
|
import { recordKey } from "../../live/model-helpers.js";
|
|
5
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
6
6
|
import { useAppAddress } from "../use-app-address.js";
|
|
7
7
|
import { can } from "../permissions.js";
|
|
8
8
|
import { useRefreshResource } from "../pages/use-layout-data.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keys } from "../../data/query-keys.js";
|
|
2
2
|
import { useClient } from "../../data/api-provider.js";
|
|
3
|
-
import { defaultOrderBy } from "../list-order.js";
|
|
4
3
|
import { useAdminMeta } from "../meta-gate.js";
|
|
4
|
+
import { defaultOrderBy } from "../list-order.js";
|
|
5
5
|
import { useQuery } from "@tanstack/react-query";
|
|
6
6
|
//#region src/app/searches/use-saved-searches.ts
|
|
7
7
|
/** Where the user's saved searches of `resource` are cached; a list key, so a write to the resource refreshes it. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Spinner } from "../primitives/spinner.js";
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
|
-
import { keys } from "../data/query-keys.js";
|
|
4
3
|
import { text } from "./messages.js";
|
|
4
|
+
import { keys } from "../data/query-keys.js";
|
|
5
5
|
import { isApiError } from "@protobase/client";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { useQueryClient } from "@tanstack/react-query";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useIsXs } from "../lib/use-media-query.js";
|
|
2
2
|
import { Link, useRouter } from "./router.js";
|
|
3
|
-
import { changedList, currentAddress, listDefaults, listedResource } from "./current-address.js";
|
|
4
3
|
import { useAdminMeta } from "./meta-gate.js";
|
|
4
|
+
import { changedList, currentAddress, listDefaults, listedResource } from "./current-address.js";
|
|
5
5
|
import { useCallback, useMemo } from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { appPath, readAppPath } from "@protobase/schema";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keys } from "../data/query-keys.js";
|
|
2
2
|
import { useClient } from "../data/api-provider.js";
|
|
3
|
-
import { useQueries } from "@tanstack/react-query";
|
|
4
3
|
import { createWhere, printFilter } from "@protobase/schema";
|
|
4
|
+
import { useQueries } from "@tanstack/react-query";
|
|
5
5
|
//#region src/app/use-incoming-counts.ts
|
|
6
6
|
const where$1 = createWhere();
|
|
7
7
|
/** How many records point at `key` through each relation: `undefined` while loading, left out when the list is refused. */
|
package/dist/auth/auth-gate.js
CHANGED
|
@@ -5,6 +5,7 @@ import { text } from "./messages.js";
|
|
|
5
5
|
import { useAuth } from "./auth-provider.js";
|
|
6
6
|
import { Button } from "../primitives/button.js";
|
|
7
7
|
import { useRouter } from "../app/router.js";
|
|
8
|
+
import { useSignInAddress } from "./use-sign-in-address.js";
|
|
8
9
|
import { EmailCodePage } from "./email-code-page.js";
|
|
9
10
|
import { FirstRunPage } from "./first-run-page.js";
|
|
10
11
|
import { readResetLink, withoutResetLink } from "./reset-link.js";
|
|
@@ -17,7 +18,6 @@ import { SignUpPage } from "./sign-up-page.js";
|
|
|
17
18
|
import { readStaffLink, withoutStaffLink } from "./staff-link.js";
|
|
18
19
|
import { StaffSignInPage } from "./staff-sign-in-page.js";
|
|
19
20
|
import { TwoFactorPage } from "./two-factor-page.js";
|
|
20
|
-
import { useSignInAddress } from "./use-sign-in-address.js";
|
|
21
21
|
import { readInvitationLink, withoutInvitationLink } from "../organizations/invitation-link.js";
|
|
22
22
|
import { InvitationPage } from "../organizations/invitation-page.js";
|
|
23
23
|
import { BeforeSignIn } from "../plugins/before-sign-in.js";
|
|
@@ -18,6 +18,7 @@ const AuthProvider = ({ session: given, origin, children }) => {
|
|
|
18
18
|
const [state, setState] = useState({ kind: "loading" });
|
|
19
19
|
const [signInMethods, setSignInMethods] = useState(["password"]);
|
|
20
20
|
const [passwordReset, setPasswordReset] = useState(false);
|
|
21
|
+
const [mcp, setMcp] = useState(false);
|
|
21
22
|
const [canSignUp, setCanSignUp] = useState(false);
|
|
22
23
|
const [passkeyDomains, setPasskeyDomains] = useState([]);
|
|
23
24
|
const [socialProviders, setSocialProviders] = useState([]);
|
|
@@ -57,6 +58,7 @@ const AuthProvider = ({ session: given, origin, children }) => {
|
|
|
57
58
|
session.status().then(async (status) => {
|
|
58
59
|
setSignInMethods(status.signInMethods);
|
|
59
60
|
setPasswordReset(status.passwordReset);
|
|
61
|
+
setMcp(Boolean(status.mcp));
|
|
60
62
|
setCanSignUp(Boolean(status.signUp));
|
|
61
63
|
setPasskeyDomains(status.passkeyDomains ?? []);
|
|
62
64
|
setSocialProviders(status.socialProviders);
|
|
@@ -81,6 +83,7 @@ const AuthProvider = ({ session: given, origin, children }) => {
|
|
|
81
83
|
session,
|
|
82
84
|
signInMethods,
|
|
83
85
|
passwordReset,
|
|
86
|
+
mcp,
|
|
84
87
|
canSignUp,
|
|
85
88
|
passkeyDomains,
|
|
86
89
|
socialProviders,
|
|
@@ -132,6 +135,7 @@ const AuthProvider = ({ session: given, origin, children }) => {
|
|
|
132
135
|
session,
|
|
133
136
|
signInMethods,
|
|
134
137
|
passwordReset,
|
|
138
|
+
mcp,
|
|
135
139
|
canSignUp,
|
|
136
140
|
passkeyDomains,
|
|
137
141
|
socialProviders,
|
|
@@ -2,9 +2,9 @@ import { RouteLink } from "../primitives/route-link.js";
|
|
|
2
2
|
import { text } from "./messages.js";
|
|
3
3
|
import { useAuth } from "./auth-provider.js";
|
|
4
4
|
import { Button, buttonClass } from "../primitives/button.js";
|
|
5
|
-
import { useSignInAddress } from "./use-sign-in-address.js";
|
|
6
|
-
import { ResponsiveDialog } from "../primitives/responsive-dialog.js";
|
|
7
5
|
import { namedIcon } from "../app/named-icon.js";
|
|
6
|
+
import { ResponsiveDialog } from "../primitives/responsive-dialog.js";
|
|
7
|
+
import { useSignInAddress } from "./use-sign-in-address.js";
|
|
8
8
|
import { useId } from "react";
|
|
9
9
|
import { Check, LogIn } from "lucide-react";
|
|
10
10
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const hidden = /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u115f\u1160\u180e\u200b-\u200f\u2028-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff]/g;
|
|
2
|
+
/** A client's own name as one line of plain text, without characters that hide or turn text, cut to `clientNameLength`. */
|
|
3
|
+
const clientName = (name) => {
|
|
4
|
+
const plain = (name ?? "").replace(hidden, " ").replace(/\s+/g, " ").trim();
|
|
5
|
+
if (!plain) return void 0;
|
|
6
|
+
return plain.length > 60 ? `${plain.slice(0, 59)}…` : plain;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { clientName };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/connect/client-publisher.ts
|
|
2
|
+
/**
|
|
3
|
+
* Who published a client: the host of its id when the id is an https URL, a client metadata document the server
|
|
4
|
+
* fetched from there, the one identity of a client the server checked. `undefined` for a client that registered
|
|
5
|
+
* itself, whose id the server chose, and whose name says nothing about who made it.
|
|
6
|
+
*/
|
|
7
|
+
const clientPublisher = (clientId) => {
|
|
8
|
+
if (!URL.canParse(clientId)) return void 0;
|
|
9
|
+
const url = new URL(clientId);
|
|
10
|
+
return url.protocol === "https:" ? url.host : void 0;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { clientPublisher };
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Spinner } from "../primitives/spinner.js";
|
|
2
|
+
import { authMessage } from "../auth/auth-messages.js";
|
|
3
|
+
import { useAuth } from "../auth/auth-provider.js";
|
|
4
|
+
import { clientName } from "./client-name.js";
|
|
5
|
+
import { clientPublisher } from "./client-publisher.js";
|
|
6
|
+
import { Button } from "../primitives/button.js";
|
|
7
|
+
import { useRouter } from "../app/router.js";
|
|
8
|
+
import { Notice } from "../app/meta-gate.js";
|
|
9
|
+
import { isFramed, useArmedAfterFocus, useGuardedPress } from "./interaction-guard.js";
|
|
10
|
+
import { isSafeTarget, redirectTarget } from "./redirect-target.js";
|
|
11
|
+
import { signedRequestFor } from "./signed-request.js";
|
|
12
|
+
import { useConnectAddress } from "./use-connect-address.js";
|
|
13
|
+
import { useEffect, useState } from "react";
|
|
14
|
+
import { AuthError } from "@protobase/client";
|
|
15
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
//#region src/connect/connect-page.tsx
|
|
17
|
+
const refusals = {
|
|
18
|
+
invalid_signature: "This request has expired: connect again from your app.",
|
|
19
|
+
STAFF_CANNOT_CONNECT: "Staff signed in as someone cannot allow apps to act for them.",
|
|
20
|
+
SIGN_IN_SETUP_REQUIRED: "Set up what this app requires for signing in first, then connect again from your app.",
|
|
21
|
+
ADDRESS_NOT_VERIFIED: "Confirm your address first: sign out, then sign in with a code emailed to you, and connect again from your app."
|
|
22
|
+
};
|
|
23
|
+
const refusalOf = (failure) => {
|
|
24
|
+
if (!(failure instanceof AuthError)) throw failure;
|
|
25
|
+
return (failure.code && refusals[failure.code]) ?? authMessage(failure);
|
|
26
|
+
};
|
|
27
|
+
const goWith = (assign) => (url) => {
|
|
28
|
+
if (!isSafeTarget(url)) throw new AuthError("The app asked to be sent somewhere this page will not go.", 400);
|
|
29
|
+
assign(url);
|
|
30
|
+
};
|
|
31
|
+
const TargetText = ({ target }) => target.kind === "site" ? /* @__PURE__ */ jsxs(Fragment$1, { children: ["the website ", /* @__PURE__ */ jsx("strong", {
|
|
32
|
+
className: "font-medium text-foreground",
|
|
33
|
+
children: target.host
|
|
34
|
+
})] }) : target.kind === "this-computer" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
35
|
+
"an app on this computer (port ",
|
|
36
|
+
target.port,
|
|
37
|
+
")"
|
|
38
|
+
] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: ["an app on this device that opens ", /* @__PURE__ */ jsx("code", {
|
|
39
|
+
className: "break-all text-foreground",
|
|
40
|
+
children: target.uri
|
|
41
|
+
})] });
|
|
42
|
+
const Request = ({ asking, appName, email, onAnswer, onSignOut }) => {
|
|
43
|
+
const [busy, setBusy] = useState(false);
|
|
44
|
+
const armed = useArmedAfterFocus(500);
|
|
45
|
+
const answer = (accept) => {
|
|
46
|
+
setBusy(true);
|
|
47
|
+
onAnswer(accept).finally(() => setBusy(false));
|
|
48
|
+
};
|
|
49
|
+
const allow = useGuardedPress(armed && !busy, () => answer(true));
|
|
50
|
+
const name = clientName(asking.client.client_name);
|
|
51
|
+
const publisher = clientPublisher(asking.client.client_id);
|
|
52
|
+
const { target } = asking;
|
|
53
|
+
return /* @__PURE__ */ jsx("div", {
|
|
54
|
+
className: "mx-auto flex w-full max-w-lg flex-col gap-4 px-4 py-10",
|
|
55
|
+
children: /* @__PURE__ */ jsxs("section", {
|
|
56
|
+
"aria-label": "Allow an app",
|
|
57
|
+
className: "rounded-lg border bg-background p-5",
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ jsxs("h1", {
|
|
60
|
+
className: "text-[16px] font-semibold",
|
|
61
|
+
children: [
|
|
62
|
+
"Allow an app to act as you in ",
|
|
63
|
+
appName,
|
|
64
|
+
"?"
|
|
65
|
+
]
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ jsxs("p", {
|
|
68
|
+
className: "mt-3",
|
|
69
|
+
children: [
|
|
70
|
+
"An app that calls itself",
|
|
71
|
+
" ",
|
|
72
|
+
/* @__PURE__ */ jsxs("bdi", {
|
|
73
|
+
className: "inline-block max-w-full truncate align-bottom font-medium",
|
|
74
|
+
title: name,
|
|
75
|
+
children: [
|
|
76
|
+
"“",
|
|
77
|
+
name ?? "no name",
|
|
78
|
+
"”"
|
|
79
|
+
]
|
|
80
|
+
}),
|
|
81
|
+
" ",
|
|
82
|
+
"asks to act as you here."
|
|
83
|
+
]
|
|
84
|
+
}),
|
|
85
|
+
publisher && /* @__PURE__ */ jsxs("p", {
|
|
86
|
+
className: "mt-1 text-muted-foreground",
|
|
87
|
+
children: [
|
|
88
|
+
"Published by ",
|
|
89
|
+
publisher,
|
|
90
|
+
"."
|
|
91
|
+
]
|
|
92
|
+
}),
|
|
93
|
+
target ? /* @__PURE__ */ jsxs("p", {
|
|
94
|
+
className: "mt-3 text-muted-foreground",
|
|
95
|
+
children: [
|
|
96
|
+
"Allowing sends you on to ",
|
|
97
|
+
/* @__PURE__ */ jsx(TargetText, { target }),
|
|
98
|
+
"."
|
|
99
|
+
]
|
|
100
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
101
|
+
role: "alert",
|
|
102
|
+
className: "mt-3 text-danger-text",
|
|
103
|
+
children: "This app asked to be sent somewhere this page will not go, so it cannot be allowed."
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ jsx("p", {
|
|
106
|
+
className: "mt-3 text-muted-foreground",
|
|
107
|
+
children: "It can then read and change what you can, with your current role and organization, until you disconnect it from your account page."
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ jsxs("div", {
|
|
110
|
+
className: "mt-5 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
111
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
112
|
+
disabled: busy,
|
|
113
|
+
onClick: () => answer(false),
|
|
114
|
+
children: "Deny"
|
|
115
|
+
}), target && /* @__PURE__ */ jsx(Button, {
|
|
116
|
+
variant: "primary",
|
|
117
|
+
loading: busy,
|
|
118
|
+
disabled: !armed || busy,
|
|
119
|
+
...allow,
|
|
120
|
+
children: "Allow"
|
|
121
|
+
})]
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ jsxs("p", {
|
|
124
|
+
className: "mt-4 border-t pt-3 text-xs text-muted-foreground",
|
|
125
|
+
children: [
|
|
126
|
+
"Signed in as ",
|
|
127
|
+
email,
|
|
128
|
+
".",
|
|
129
|
+
" ",
|
|
130
|
+
/* @__PURE__ */ jsx("button", {
|
|
131
|
+
type: "button",
|
|
132
|
+
className: "underline underline-offset-2 hover:text-foreground",
|
|
133
|
+
onClick: onSignOut,
|
|
134
|
+
children: "Not you? Sign out"
|
|
135
|
+
})
|
|
136
|
+
]
|
|
137
|
+
})
|
|
138
|
+
]
|
|
139
|
+
})
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const Connecting = ({ oauthQuery, appName, email, assign }) => {
|
|
143
|
+
const { session, signOut } = useAuth();
|
|
144
|
+
const { path, basePath } = useRouter();
|
|
145
|
+
const go = goWith(assign);
|
|
146
|
+
const signedRequest = (url) => signedRequestFor(url, {
|
|
147
|
+
origin: window.location.origin,
|
|
148
|
+
pathname: `${basePath}${path}`
|
|
149
|
+
});
|
|
150
|
+
const [state, setState] = useState({ kind: "loading" });
|
|
151
|
+
const fail = (failure) => setState({
|
|
152
|
+
kind: "failed",
|
|
153
|
+
message: refusalOf(failure)
|
|
154
|
+
});
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
let current = true;
|
|
157
|
+
const ask = async () => {
|
|
158
|
+
const { url } = await session.connect.resume(oauthQuery);
|
|
159
|
+
const query = signedRequest(url);
|
|
160
|
+
if (query === void 0) return go(url);
|
|
161
|
+
const params = new URLSearchParams(query);
|
|
162
|
+
const client = await session.connect.client(params.get("client_id") ?? "");
|
|
163
|
+
if (current) setState({
|
|
164
|
+
kind: "asking",
|
|
165
|
+
asking: {
|
|
166
|
+
oauthQuery: query,
|
|
167
|
+
client,
|
|
168
|
+
target: redirectTarget(params.get("redirect_uri") ?? "")
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
ask().catch((failure) => current && fail(failure));
|
|
173
|
+
return () => {
|
|
174
|
+
current = false;
|
|
175
|
+
};
|
|
176
|
+
}, [session, oauthQuery]);
|
|
177
|
+
const answer = async (accept, asking) => {
|
|
178
|
+
try {
|
|
179
|
+
const { url } = await session.connect.answer(accept, asking.oauthQuery);
|
|
180
|
+
if (signedRequest(url) !== void 0) await signOut();
|
|
181
|
+
go(url);
|
|
182
|
+
} catch (failure) {
|
|
183
|
+
fail(failure);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
if (state.kind === "failed") return /* @__PURE__ */ jsx(Notice, {
|
|
187
|
+
title: "This app cannot be connected",
|
|
188
|
+
children: state.message
|
|
189
|
+
});
|
|
190
|
+
if (state.kind === "loading") return /* @__PURE__ */ jsxs("div", {
|
|
191
|
+
className: "flex min-h-48 items-center justify-center gap-2 text-muted-foreground",
|
|
192
|
+
children: [/* @__PURE__ */ jsx(Spinner, {}), " Loading"]
|
|
193
|
+
});
|
|
194
|
+
return /* @__PURE__ */ jsx(Request, {
|
|
195
|
+
asking: state.asking,
|
|
196
|
+
appName,
|
|
197
|
+
email,
|
|
198
|
+
onAnswer: (accept) => answer(accept, state.asking),
|
|
199
|
+
onSignOut: () => void signOut()
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* The connect page: someone signed in allows an outside app, such as an MCP client, to act as them, from the request
|
|
204
|
+
* the server signed and sent here. Inside another page's frame it shows only that it cannot be used there and calls
|
|
205
|
+
* nothing, whatever headers the host sent; Allow takes a click only once the page has been in front for a moment.
|
|
206
|
+
*/
|
|
207
|
+
const ConnectPage = ({ email, appName, isFramed: isFramed$1 = isFramed, assign = (url) => window.location.assign(url) }) => {
|
|
208
|
+
const [framed] = useState(isFramed$1);
|
|
209
|
+
const { oauthQuery } = useConnectAddress();
|
|
210
|
+
if (framed) return /* @__PURE__ */ jsxs(Notice, {
|
|
211
|
+
title: "This page cannot be used inside another site",
|
|
212
|
+
children: [
|
|
213
|
+
/* @__PURE__ */ jsx("a", {
|
|
214
|
+
href: window.location.href,
|
|
215
|
+
target: "_blank",
|
|
216
|
+
rel: "noopener",
|
|
217
|
+
className: "underline underline-offset-2",
|
|
218
|
+
children: "Open it on its own"
|
|
219
|
+
}),
|
|
220
|
+
" ",
|
|
221
|
+
"to go on."
|
|
222
|
+
]
|
|
223
|
+
});
|
|
224
|
+
if (!oauthQuery) return /* @__PURE__ */ jsx(Notice, {
|
|
225
|
+
title: "Nothing to connect",
|
|
226
|
+
children: "Connect again from your app."
|
|
227
|
+
});
|
|
228
|
+
return /* @__PURE__ */ jsx(Connecting, {
|
|
229
|
+
oauthQuery,
|
|
230
|
+
appName,
|
|
231
|
+
email,
|
|
232
|
+
assign
|
|
233
|
+
}, oauthQuery);
|
|
234
|
+
};
|
|
235
|
+
//#endregion
|
|
236
|
+
export { ConnectPage };
|