@ekanos/sdk 0.1.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.
Files changed (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/components/base-activation-dialog.d.ts +41 -0
  4. package/dist/components/base-activation-dialog.js +41 -0
  5. package/dist/components/base-activation-dialog.js.map +1 -0
  6. package/dist/components/base-activation-form.d.ts +36 -0
  7. package/dist/components/base-activation-form.js +21 -0
  8. package/dist/components/base-activation-form.js.map +1 -0
  9. package/dist/components/base-marketplace-tile.d.ts +34 -0
  10. package/dist/components/base-marketplace-tile.js +40 -0
  11. package/dist/components/base-marketplace-tile.js.map +1 -0
  12. package/dist/components/current-return-path.d.ts +19 -0
  13. package/dist/components/current-return-path.js +26 -0
  14. package/dist/components/current-return-path.js.map +1 -0
  15. package/dist/components/index.d.ts +40 -0
  16. package/dist/components/index.js +38 -0
  17. package/dist/components/index.js.map +1 -0
  18. package/dist/components/oauth-activation-form.d.ts +50 -0
  19. package/dist/components/oauth-activation-form.js +119 -0
  20. package/dist/components/oauth-activation-form.js.map +1 -0
  21. package/dist/components/widgets/ai-prompt-chip.d.ts +31 -0
  22. package/dist/components/widgets/ai-prompt-chip.js +38 -0
  23. package/dist/components/widgets/ai-prompt-chip.js.map +1 -0
  24. package/dist/components/widgets/ask-icon.d.ts +3 -0
  25. package/dist/components/widgets/ask-icon.js +5 -0
  26. package/dist/components/widgets/ask-icon.js.map +1 -0
  27. package/dist/components/widgets/use-animated-height.d.ts +12 -0
  28. package/dist/components/widgets/use-animated-height.js +39 -0
  29. package/dist/components/widgets/use-animated-height.js.map +1 -0
  30. package/dist/components/widgets/widget-ask-bar.d.ts +18 -0
  31. package/dist/components/widgets/widget-ask-bar.js +62 -0
  32. package/dist/components/widgets/widget-ask-bar.js.map +1 -0
  33. package/dist/components/widgets/widget-context.d.ts +38 -0
  34. package/dist/components/widgets/widget-context.js +4 -0
  35. package/dist/components/widgets/widget-context.js.map +1 -0
  36. package/dist/components/widgets/widget-state-components.d.ts +5 -0
  37. package/dist/components/widgets/widget-state-components.js +19 -0
  38. package/dist/components/widgets/widget-state-components.js.map +1 -0
  39. package/dist/components/widgets/widget.d.ts +91 -0
  40. package/dist/components/widgets/widget.js +205 -0
  41. package/dist/components/widgets/widget.js.map +1 -0
  42. package/dist/context/egress.d.ts +30 -0
  43. package/dist/context/egress.js +81 -0
  44. package/dist/context/egress.js.map +1 -0
  45. package/dist/context/errors.d.ts +57 -0
  46. package/dist/context/errors.js +78 -0
  47. package/dist/context/errors.js.map +1 -0
  48. package/dist/context/index.d.ts +19 -0
  49. package/dist/context/index.js +20 -0
  50. package/dist/context/index.js.map +1 -0
  51. package/dist/context/require-context.d.ts +26 -0
  52. package/dist/context/require-context.js +36 -0
  53. package/dist/context/require-context.js.map +1 -0
  54. package/dist/context/storage-validation.d.ts +71 -0
  55. package/dist/context/storage-validation.js +132 -0
  56. package/dist/context/storage-validation.js.map +1 -0
  57. package/dist/context/types.d.ts +11 -0
  58. package/dist/context/types.js +2 -0
  59. package/dist/context/types.js.map +1 -0
  60. package/dist/hooks/activation-actions-context.d.ts +80 -0
  61. package/dist/hooks/activation-actions-context.js +31 -0
  62. package/dist/hooks/activation-actions-context.js.map +1 -0
  63. package/dist/hooks/ask-assistant-context.d.ts +32 -0
  64. package/dist/hooks/ask-assistant-context.js +56 -0
  65. package/dist/hooks/ask-assistant-context.js.map +1 -0
  66. package/dist/hooks/fetch-integration-storage.d.ts +48 -0
  67. package/dist/hooks/fetch-integration-storage.js +68 -0
  68. package/dist/hooks/fetch-integration-storage.js.map +1 -0
  69. package/dist/hooks/index.d.ts +28 -0
  70. package/dist/hooks/index.js +32 -0
  71. package/dist/hooks/index.js.map +1 -0
  72. package/dist/hooks/use-activate-integration.d.ts +15 -0
  73. package/dist/hooks/use-activate-integration.js +61 -0
  74. package/dist/hooks/use-activate-integration.js.map +1 -0
  75. package/dist/hooks/use-oauth-connection-status.d.ts +360 -0
  76. package/dist/hooks/use-oauth-connection-status.js +108 -0
  77. package/dist/hooks/use-oauth-connection-status.js.map +1 -0
  78. package/dist/hooks/use-widget-events.d.ts +20 -0
  79. package/dist/hooks/use-widget-events.js +51 -0
  80. package/dist/hooks/use-widget-events.js.map +1 -0
  81. package/dist/hooks/widget-event-context.d.ts +22 -0
  82. package/dist/hooks/widget-event-context.js +75 -0
  83. package/dist/hooks/widget-event-context.js.map +1 -0
  84. package/dist/hooks/widget-event-types.d.ts +55 -0
  85. package/dist/hooks/widget-event-types.js +9 -0
  86. package/dist/hooks/widget-event-types.js.map +1 -0
  87. package/dist/index.d.ts +19 -0
  88. package/dist/index.js +19 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/integration/cron.d.ts +29 -0
  91. package/dist/integration/cron.js +102 -0
  92. package/dist/integration/cron.js.map +1 -0
  93. package/dist/integration/define-integration.d.ts +28 -0
  94. package/dist/integration/define-integration.js +41 -0
  95. package/dist/integration/define-integration.js.map +1 -0
  96. package/dist/integration/event-surfaces.d.ts +15 -0
  97. package/dist/integration/event-surfaces.js +45 -0
  98. package/dist/integration/event-surfaces.js.map +1 -0
  99. package/dist/integration/index.d.ts +20 -0
  100. package/dist/integration/index.js +23 -0
  101. package/dist/integration/index.js.map +1 -0
  102. package/dist/integration/types.d.ts +7 -0
  103. package/dist/integration/types.js +2 -0
  104. package/dist/integration/types.js.map +1 -0
  105. package/dist/mcp/guards.d.ts +40 -0
  106. package/dist/mcp/guards.js +99 -0
  107. package/dist/mcp/guards.js.map +1 -0
  108. package/dist/mcp/index.d.ts +22 -0
  109. package/dist/mcp/index.js +22 -0
  110. package/dist/mcp/index.js.map +1 -0
  111. package/dist/mcp/ownership.d.ts +74 -0
  112. package/dist/mcp/ownership.js +83 -0
  113. package/dist/mcp/ownership.js.map +1 -0
  114. package/dist/mcp/types.d.ts +145 -0
  115. package/dist/mcp/types.js +2 -0
  116. package/dist/mcp/types.js.map +1 -0
  117. package/dist/testing/index.d.ts +16 -0
  118. package/dist/testing/index.js +19 -0
  119. package/dist/testing/index.js.map +1 -0
  120. package/dist/testing/invoke.d.ts +77 -0
  121. package/dist/testing/invoke.js +83 -0
  122. package/dist/testing/invoke.js.map +1 -0
  123. package/dist/testing/mock-context.d.ts +79 -0
  124. package/dist/testing/mock-context.js +206 -0
  125. package/dist/testing/mock-context.js.map +1 -0
  126. package/dist/types/icon.d.ts +12 -0
  127. package/dist/types/icon.js +2 -0
  128. package/dist/types/icon.js.map +1 -0
  129. package/dist/types/index.d.ts +10 -0
  130. package/dist/types/index.js +8 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/integration.d.ts +258 -0
  133. package/dist/types/integration.js +2 -0
  134. package/dist/types/integration.js.map +1 -0
  135. package/dist/types/widget-ask-context.d.ts +36 -0
  136. package/dist/types/widget-ask-context.js +2 -0
  137. package/dist/types/widget-ask-context.js.map +1 -0
  138. package/dist/types/workspace-target.d.ts +33 -0
  139. package/dist/types/workspace-target.js +2 -0
  140. package/dist/types/workspace-target.js.map +1 -0
  141. package/package.json +116 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vastly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @ekanos/sdk
2
+
3
+ The official SDK for building Ekanos integrations.
4
+
5
+ ## Entrypoints
6
+
7
+ | Import | Runs on | Contents |
8
+ |---|---|---|
9
+ | `@ekanos/sdk` | anywhere | Types only — the integration contract (`IntegrationMetadata`, component prop types, activation/health/data types) and the capability context types (`IntegrationContext`, `ScopedStore`, `StorageSchemas`, …) |
10
+ | `@ekanos/sdk/components` | client | `Widget` compound API + `WidgetContext`, `BaseActivationForm`, `OAuthActivationForm`, `BaseMarketplaceTile`, `AiPromptChip` — implemented here (the inversion); UI primitives from `@ekanos/ui` |
11
+ | `@ekanos/sdk/hooks` | client | `useActivateIntegration`/`useDeactivateIntegration` (host-injected actions via `IntegrationActivationProvider`), `useOAuthConnectionStatus`, widget event bus, `useAskAssistant` |
12
+ | `@ekanos/sdk/mcp` | server | `ToolModule`/`ToolContext` contract + membership/ownership guards — implemented here |
13
+ | `@ekanos/sdk/context` | anywhere (isomorphic, dependency-pure) | Capability-context shared runtime: `EgressDeniedError`, `SecretAccessError`, `StorageValidationError`, the `isEgressAllowed` matcher, the storage validators — one implementation reused by the production wrapper and the mock |
14
+ | `@ekanos/sdk/testing` | anywhere (tests) | `createMockContext()` — the in-memory `IntegrationContext` harness (schema-validated stores, tiered secrets, allowlist-enforcing fetch stub, capturing logger) |
15
+ | `@ekanos/sdk/integration` | anywhere (isomorphic, dependency-pure) | The partner authoring contract: `defineIntegration()` (validate + deep-freeze, storage schemas per ruling 1, https-origin egress per ruling 3, security classifications only as a `proposes` block), `validateIntegrationDefinitions()` (cross-definition slug/widget/tool collision check), and `IntegrationDefinitionSchema` for the gate/dev-mcp validators |
16
+
17
+ `/context`, `/testing`, and `/integration` import nothing from `@kit/*` and
18
+ carry no `server-only` marker — they are self-contained by construction and
19
+ the pack test typechecks all three in a clean room (alongside the root). The
20
+ capability context spec is
21
+ [`docs/devex/capability-context-proposal.md`](../../docs/devex/capability-context-proposal.md).
22
+ Partners never extend `BaseIntegration` — `defineIntegration()` is the v1
23
+ contract, and the host adapts the definition via
24
+ `registerPartnerIntegration` in `@kit/integrations-core` (typed there as a
25
+ structural twin, held in parity by
26
+ `src/integration/__tests__/partner-definition-parity.test-d.ts`).
27
+
28
+ ## Rules
29
+
30
+ - The root entrypoint is types-only and must stay importable from client code.
31
+ Never add a value export to it.
32
+ - Additions anywhere require a second consumer and an entry in
33
+ [`docs/devex/sdk-export-map.md`](../../docs/devex/sdk-export-map.md) — the
34
+ export map is the source of truth for what is public and why.
35
+ - The inversion landed: this package OWNS the `/components`, `/hooks`, and
36
+ `/mcp` implementations and `@kit/integrations-core` re-exports them. Never
37
+ import `@kit/*` from SDK source — UI comes from `@ekanos/ui`, host services
38
+ arrive through injection seams (`IntegrationActivationProvider`,
39
+ `ToolContext`). There is no `/server` entrypoint: first-party transitional
40
+ accessors live in `@kit/integrations-core/server` (core depends on the SDK
41
+ now, so the SDK cannot re-export core without a package cycle).
42
+
43
+ ## Publish pipeline
44
+
45
+ Workspace consumers resolve raw `src/*.ts` via `exports`; the published
46
+ artifact is compiled ESM + declarations in `dist/`, wired through
47
+ `publishConfig` (applied by `pnpm pack`/`pnpm publish`). The tarball ships
48
+ `dist` + `README.md` only.
49
+
50
+ **Runtime dependencies + publish order.** The SDK depends on
51
+ `@ekanos/integration-schema` (the dependency-pure canonical schema — one
52
+ contract shared with `@kit/integrations-core`) and `@ekanos/ui` (the
53
+ published slice of the host design system that `/components` renders
54
+ through). Publish **schema and ui first**, then the SDK: the packed SDK
55
+ declares both as normal dependencies, so they must already be on the registry
56
+ for a fresh `npm install @ekanos/sdk` to resolve. The `pack:test` proves this
57
+ offline by packing all three, installing ONLY the SDK tarball (schema + ui
58
+ pulled transitively) and executing a real
59
+ `import("@ekanos/sdk/integration")`.
60
+
61
+ | Script | What it does |
62
+ |---|---|
63
+ | `pnpm --filter @ekanos/sdk build` | `tsc -p tsconfig.build.json` → `dist/` (ESM + `.d.ts` + maps; `__tests__` excluded) |
64
+ | `pnpm --filter @ekanos/sdk test` | Vitest over `src/**/__tests__` — matcher semantics, storage schema enforcement, secret tier rules, mock recording surfaces |
65
+ | `pnpm --filter @ekanos/sdk api-report` | Regenerates [`api-report.md`](./api-report.md), the checked-in snapshot of every entrypoint's exports |
66
+ | `pnpm --filter @ekanos/sdk api-report:check` | Fails on drift between the surface and `api-report.md` |
67
+ | `pnpm --filter @ekanos/sdk pack:test` | Clean-room test: pack the SDK (and `@ekanos/integration-schema` + `@ekanos/ui`), install ONLY the SDK tarball outside the workspace with the declared peers, typecheck a consumer of EVERY published entrypoint (`skipLibCheck: false`), esbuild-bundle the client and server graphs, grep dist for `@kit/*` import specifiers, and EXECUTE `import("@ekanos/sdk/integration")` in real Node |
68
+
69
+ The pack test was the forcing function for the SDK inversion
70
+ ([export map](../../docs/devex/sdk-export-map.md), review outcome 6), and the
71
+ inversion landed: every published entrypoint (root, `/components`, `/hooks`,
72
+ `/mcp`, `/context`, `/testing`, `/integration`) is typechecked in the clean
73
+ room and must be GREEN — no skips remain. Nothing is a v1 contract until it
74
+ passes.
75
+
76
+ Publishing itself is a deliberate human step: flip `"private": true` and run
77
+ `pnpm publish` (`publishConfig.access` is already `public`, version stays
78
+ `0.0.0` until then).
@@ -0,0 +1,41 @@
1
+ import { ReactNode } from 'react';
2
+ interface BaseActivationDialogProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ title: string;
6
+ description: string;
7
+ children: ReactNode;
8
+ isLoading?: boolean;
9
+ error?: string | null;
10
+ success?: boolean;
11
+ successTitle?: string;
12
+ successMessage?: string;
13
+ onSuccessClose?: () => void;
14
+ maxWidth?: string;
15
+ /** @deprecated Use `ActivationInline` instead for inline rendering */
16
+ inline?: boolean;
17
+ }
18
+ export declare function BaseActivationDialog({ isOpen, onClose, title, description, children, isLoading, error, success, successTitle, successMessage, onSuccessClose, maxWidth, inline, }: BaseActivationDialogProps): import("react").JSX.Element;
19
+ export declare function ActivationInline({ isLoading, error, success, successTitle, successMessage, onClose, children, }: {
20
+ isLoading?: boolean;
21
+ error?: string | null;
22
+ success?: boolean;
23
+ successTitle?: string;
24
+ successMessage?: string;
25
+ onClose: () => void;
26
+ children: ReactNode;
27
+ }): import("react").JSX.Element;
28
+ export declare function ActivationDialog({ isOpen, onClose, title, description, children, isLoading, error, success, successTitle, successMessage, maxWidth, }: {
29
+ isOpen: boolean;
30
+ onClose: () => void;
31
+ title: string;
32
+ description: string;
33
+ children: ReactNode;
34
+ isLoading?: boolean;
35
+ error?: string | null;
36
+ success?: boolean;
37
+ successTitle?: string;
38
+ successMessage?: string;
39
+ maxWidth?: string;
40
+ }): import("react").JSX.Element;
41
+ export {};
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Alert, AlertDescription } from '@ekanos/ui/alert';
4
+ import { Button } from '@ekanos/ui/button';
5
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@ekanos/ui/dialog';
6
+ import { Icon } from '@ekanos/ui/icon';
7
+ import { If } from '@ekanos/ui/if';
8
+ import { Separator } from '@ekanos/ui/separator';
9
+ import { Trans } from '@ekanos/ui/trans';
10
+ import { cn } from '@ekanos/ui/utils';
11
+ function SuccessContent({ successTitle = 'Integration Activated!', successMessage = 'Your integration is now active and ready to use.', onClose, showSeparator = false, }) {
12
+ return (_jsxs("div", { className: "flex flex-col items-center space-y-4 py-6", children: [_jsx("div", { className: "bg-success/10 flex h-16 w-16 items-center justify-center rounded-full", children: _jsx(Icon, { name: "fa-light fa-circle-check", className: "text-success h-8 w-8" }) }), _jsxs("div", { className: "text-center", children: [_jsx("h3", { className: "text-lg font-semibold", children: successTitle }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: successMessage })] }), showSeparator && _jsx(Separator, {}), _jsxs("div", { className: "flex gap-2", children: [showSeparator && (_jsx(Button, { variant: "outline", onClick: onClose, children: _jsx(Trans, { i18nKey: "common:stayOnMarketplace", defaults: "Stay on Marketplace" }) })), _jsx(Button, { onClick: onClose, children: _jsx(Trans, { i18nKey: "common:continue", defaults: "Continue" }) })] })] }));
13
+ }
14
+ function ErrorAlert({ error }) {
15
+ return (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) }));
16
+ }
17
+ function LoadingOverlay() {
18
+ return (_jsx("div", { className: "bg-background/80 fixed inset-0 z-[1000] flex items-center justify-center", children: _jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx(Icon, { name: "fa-light fa-spinner", className: "text-primary h-8 w-8 animate-spin" }), _jsx("span", { className: "text-muted-foreground text-sm", children: "Loading..." })] }) }));
19
+ }
20
+ export function BaseActivationDialog({ isOpen, onClose, title, description, children, isLoading = false, error = null, success = false, successTitle, successMessage, onSuccessClose, maxWidth = 'sm:max-w-[600px]', inline = false, }) {
21
+ const handleClose = () => {
22
+ if (success && onSuccessClose) {
23
+ onSuccessClose();
24
+ }
25
+ else {
26
+ onClose();
27
+ }
28
+ };
29
+ if (inline) {
30
+ return (_jsx(ActivationInline, { isLoading: isLoading, error: error, success: success, successTitle: successTitle, successMessage: successMessage, onClose: handleClose, children: children }));
31
+ }
32
+ return (_jsx(ActivationDialog, { isOpen: isOpen, onClose: handleClose, title: title, description: description, isLoading: isLoading, error: error, success: success, successTitle: successTitle, successMessage: successMessage, maxWidth: maxWidth, children: children }));
33
+ }
34
+ // --- Explicit Variants ---
35
+ export function ActivationInline({ isLoading = false, error = null, success = false, successTitle, successMessage, onClose, children, }) {
36
+ return (_jsxs("div", { className: cn('space-y-4', isLoading && 'pointer-events-none opacity-60'), "aria-busy": isLoading, children: [_jsx(If, { condition: !!error, children: _jsx(ErrorAlert, { error: error }) }), _jsx(If, { condition: success, fallback: children, children: _jsx(SuccessContent, { successTitle: successTitle, successMessage: successMessage, onClose: onClose }) })] }));
37
+ }
38
+ export function ActivationDialog({ isOpen, onClose, title, description, children, isLoading = false, error = null, success = false, successTitle, successMessage, maxWidth = 'sm:max-w-[600px]', }) {
39
+ return (_jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: _jsxs(DialogContent, { className: `${maxWidth} max-h-[80vh] overflow-y-auto`, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: description })] }), _jsx(If, { condition: !!error, children: _jsx(ErrorAlert, { error: error }) }), _jsx(If, { condition: success, fallback: children, children: _jsx(SuccessContent, { successTitle: successTitle, successMessage: successMessage, onClose: onClose, showSeparator: true }) }), isLoading && _jsx(LoadingOverlay, {})] }) }));
40
+ }
41
+ //# sourceMappingURL=base-activation-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-activation-dialog.js","sourceRoot":"","sources":["../../src/components/base-activation-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { ReactNode } from 'react';\n\nimport { Alert, AlertDescription } from '@ekanos/ui/alert';\nimport { Button } from '@ekanos/ui/button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@ekanos/ui/dialog';\nimport { Icon } from '@ekanos/ui/icon';\nimport { If } from '@ekanos/ui/if';\nimport { Separator } from '@ekanos/ui/separator';\nimport { Trans } from '@ekanos/ui/trans';\nimport { cn } from '@ekanos/ui/utils';\n\ninterface BaseActivationDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n description: string;\n children: ReactNode;\n isLoading?: boolean;\n error?: string | null;\n success?: boolean;\n successTitle?: string;\n successMessage?: string;\n onSuccessClose?: () => void;\n maxWidth?: string;\n /** @deprecated Use `ActivationInline` instead for inline rendering */\n inline?: boolean;\n}\n\nfunction SuccessContent({\n successTitle = 'Integration Activated!',\n successMessage = 'Your integration is now active and ready to use.',\n onClose,\n showSeparator = false,\n}: {\n successTitle?: string;\n successMessage?: string;\n onClose: () => void;\n showSeparator?: boolean;\n}) {\n return (\n <div className=\"flex flex-col items-center space-y-4 py-6\">\n <div className=\"bg-success/10 flex h-16 w-16 items-center justify-center rounded-full\">\n <Icon\n name=\"fa-light fa-circle-check\"\n className=\"text-success h-8 w-8\"\n />\n </div>\n <div className=\"text-center\">\n <h3 className=\"text-lg font-semibold\">{successTitle}</h3>\n <p className=\"text-muted-foreground mt-1 text-sm\">{successMessage}</p>\n </div>\n {showSeparator && <Separator />}\n <div className=\"flex gap-2\">\n {showSeparator && (\n <Button variant=\"outline\" onClick={onClose}>\n <Trans\n i18nKey=\"common:stayOnMarketplace\"\n defaults=\"Stay on Marketplace\"\n />\n </Button>\n )}\n <Button onClick={onClose}>\n <Trans i18nKey=\"common:continue\" defaults=\"Continue\" />\n </Button>\n </div>\n </div>\n );\n}\n\nfunction ErrorAlert({ error }: { error: string }) {\n return (\n <Alert variant=\"destructive\">\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n );\n}\n\nfunction LoadingOverlay() {\n return (\n <div className=\"bg-background/80 fixed inset-0 z-[1000] flex items-center justify-center\">\n <div className=\"flex flex-col items-center gap-2\">\n <Icon\n name=\"fa-light fa-spinner\"\n className=\"text-primary h-8 w-8 animate-spin\"\n />\n <span className=\"text-muted-foreground text-sm\">Loading...</span>\n </div>\n </div>\n );\n}\n\nexport function BaseActivationDialog({\n isOpen,\n onClose,\n title,\n description,\n children,\n isLoading = false,\n error = null,\n success = false,\n successTitle,\n successMessage,\n onSuccessClose,\n maxWidth = 'sm:max-w-[600px]',\n inline = false,\n}: BaseActivationDialogProps) {\n const handleClose = () => {\n if (success && onSuccessClose) {\n onSuccessClose();\n } else {\n onClose();\n }\n };\n\n if (inline) {\n return (\n <ActivationInline\n isLoading={isLoading}\n error={error}\n success={success}\n successTitle={successTitle}\n successMessage={successMessage}\n onClose={handleClose}\n >\n {children}\n </ActivationInline>\n );\n }\n\n return (\n <ActivationDialog\n isOpen={isOpen}\n onClose={handleClose}\n title={title}\n description={description}\n isLoading={isLoading}\n error={error}\n success={success}\n successTitle={successTitle}\n successMessage={successMessage}\n maxWidth={maxWidth}\n >\n {children}\n </ActivationDialog>\n );\n}\n\n// --- Explicit Variants ---\n\nexport function ActivationInline({\n isLoading = false,\n error = null,\n success = false,\n successTitle,\n successMessage,\n onClose,\n children,\n}: {\n isLoading?: boolean;\n error?: string | null;\n success?: boolean;\n successTitle?: string;\n successMessage?: string;\n onClose: () => void;\n children: ReactNode;\n}) {\n return (\n <div\n className={cn('space-y-4', isLoading && 'pointer-events-none opacity-60')}\n aria-busy={isLoading}\n >\n <If condition={!!error}>\n <ErrorAlert error={error!} />\n </If>\n <If condition={success} fallback={children}>\n <SuccessContent\n successTitle={successTitle}\n successMessage={successMessage}\n onClose={onClose}\n />\n </If>\n </div>\n );\n}\n\nexport function ActivationDialog({\n isOpen,\n onClose,\n title,\n description,\n children,\n isLoading = false,\n error = null,\n success = false,\n successTitle,\n successMessage,\n maxWidth = 'sm:max-w-[600px]',\n}: {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n description: string;\n children: ReactNode;\n isLoading?: boolean;\n error?: string | null;\n success?: boolean;\n successTitle?: string;\n successMessage?: string;\n maxWidth?: string;\n}) {\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className={`${maxWidth} max-h-[80vh] overflow-y-auto`}>\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n\n <If condition={!!error}>\n <ErrorAlert error={error!} />\n </If>\n\n <If condition={success} fallback={children}>\n <SuccessContent\n successTitle={successTitle}\n successMessage={successMessage}\n onClose={onClose}\n showSeparator\n />\n </If>\n\n {isLoading && <LoadingOverlay />}\n </DialogContent>\n </Dialog>\n );\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { ReactNode } from 'react';
2
+ import { FieldValues, UseFormReturn } from 'react-hook-form';
3
+ export interface BaseActivationFormProps<TFieldValues extends FieldValues = FieldValues> {
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ title: string;
7
+ description: string;
8
+ /**
9
+ * Generic rather than `UseFormReturn<any>`: with current react-hook-form
10
+ * typings a concrete `UseFormReturn<T>` is NOT assignable to
11
+ * `UseFormReturn<any>` (the `control._options.validate` parameter is
12
+ * checked contravariantly), which broke every out-of-repo consumer — the
13
+ * clean-room pack test caught it. Inference from the `form` prop keeps
14
+ * every call site source-compatible.
15
+ */
16
+ form: UseFormReturn<TFieldValues, any, any>;
17
+ onSubmit: (data: TFieldValues) => Promise<void> | void;
18
+ children: ReactNode;
19
+ isLoading?: boolean;
20
+ error?: string | null;
21
+ success?: boolean;
22
+ successTitle?: string;
23
+ successMessage?: string;
24
+ onSuccessClose?: () => void;
25
+ submitButtonText?: string;
26
+ submitButtonLoadingText?: string;
27
+ submitButtonDisabled?: boolean;
28
+ /** @deprecated No longer needed — cancel button is hidden by default */
29
+ showCancelButton?: boolean;
30
+ /** @deprecated No longer needed — submit button is centered by default */
31
+ centerSubmitButton?: boolean;
32
+ maxWidth?: string;
33
+ /** Render inline instead of in a dialog */
34
+ inline?: boolean;
35
+ }
36
+ export declare function BaseActivationForm<TFieldValues extends FieldValues = FieldValues>({ isOpen, onClose, title, description, form, onSubmit, children, isLoading, error, success, successTitle, successMessage, onSuccessClose, submitButtonText, submitButtonLoadingText, submitButtonDisabled, maxWidth, inline, }: BaseActivationFormProps<TFieldValues>): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTransition } from 'react';
4
+ import { Button } from '@ekanos/ui/button';
5
+ import { Form } from '@ekanos/ui/form';
6
+ import { Icon } from '@ekanos/ui/icon';
7
+ import { BaseActivationDialog } from './base-activation-dialog.js';
8
+ export function BaseActivationForm({ isOpen, onClose, title, description, form, onSubmit, children, isLoading = false, error = null, success = false, successTitle, successMessage, onSuccessClose, submitButtonText = 'Activate Integration', submitButtonLoadingText, submitButtonDisabled = false, maxWidth, inline = false, }) {
9
+ const [pending, startTransition] = useTransition();
10
+ const loading = pending || isLoading;
11
+ const handleSubmit = (data) => {
12
+ startTransition(async () => {
13
+ await onSubmit(data);
14
+ });
15
+ };
16
+ const formContent = (_jsx(Form, Object.assign({}, form, { children: _jsxs("form", { onSubmit: form.handleSubmit(handleSubmit), className: "space-y-6", children: [children, _jsx("div", { className: "flex justify-center pt-4", children: _jsxs(Button, { type: "submit", disabled: loading || submitButtonDisabled, children: [loading && (_jsx(Icon, { name: "fa-light fa-spinner", className: "mr-2 h-4 w-4 animate-spin" })), loading && submitButtonLoadingText
17
+ ? submitButtonLoadingText
18
+ : submitButtonText] }) })] }) })));
19
+ return (_jsx(BaseActivationDialog, { isOpen: isOpen, onClose: onClose, title: title, description: description, isLoading: loading, error: error, success: success, successTitle: successTitle, successMessage: successMessage, onSuccessClose: onSuccessClose, maxWidth: maxWidth, inline: inline, children: formContent }));
20
+ }
21
+ //# sourceMappingURL=base-activation-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-activation-form.js","sourceRoot":"","sources":["../../src/components/base-activation-form.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAa,aAAa,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAuChE,MAAM,UAAU,kBAAkB,CAEhC,EACA,MAAM,EACN,OAAO,EACP,KAAK,EACL,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,SAAS,GAAG,KAAK,EACjB,KAAK,GAAG,IAAI,EACZ,OAAO,GAAG,KAAK,EACf,YAAY,EACZ,cAAc,EACd,cAAc,EACd,gBAAgB,GAAG,sBAAsB,EACzC,uBAAuB,EACvB,oBAAoB,GAAG,KAAK,EAC5B,QAAQ,EACR,MAAM,GAAG,KAAK,GACwB;IACtC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,GAAG,aAAa,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,OAAO,IAAI,SAAS,CAAC;IAErC,MAAM,YAAY,GAAG,CAAC,IAAkB,EAAE,EAAE;QAC1C,eAAe,CAAC,KAAK,IAAI,EAAE;YACzB,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAClB,KAAC,IAAI,oBAAK,IAAI,cACZ,gBAAM,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,SAAS,EAAC,WAAW,aACnE,QAAQ,EAET,cAAK,SAAS,EAAC,0BAA0B,YACvC,MAAC,MAAM,IAAC,IAAI,EAAC,QAAQ,EAAC,QAAQ,EAAE,OAAO,IAAI,oBAAoB,aAC5D,OAAO,IAAI,CACV,KAAC,IAAI,IACH,IAAI,EAAC,qBAAqB,EAC1B,SAAS,EAAC,2BAA2B,GACrC,CACH,EACA,OAAO,IAAI,uBAAuB;gCACjC,CAAC,CAAC,uBAAuB;gCACzB,CAAC,CAAC,gBAAgB,IACb,GACL,IACD,IACF,CACR,CAAC;IAEF,OAAO,CACL,KAAC,oBAAoB,IACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,OAAO,EAClB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,EAC9B,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,YAEb,WAAW,GACS,CACxB,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { ReactNode, useTransition } from 'react';\n\nimport { Button } from '@ekanos/ui/button';\nimport { Form } from '@ekanos/ui/form';\nimport { Icon } from '@ekanos/ui/icon';\nimport { FieldValues, UseFormReturn } from 'react-hook-form';\n\nimport { BaseActivationDialog } from './base-activation-dialog';\n\nexport interface BaseActivationFormProps<\n TFieldValues extends FieldValues = FieldValues,\n> {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n description: string;\n /**\n * Generic rather than `UseFormReturn<any>`: with current react-hook-form\n * typings a concrete `UseFormReturn<T>` is NOT assignable to\n * `UseFormReturn<any>` (the `control._options.validate` parameter is\n * checked contravariantly), which broke every out-of-repo consumer — the\n * clean-room pack test caught it. Inference from the `form` prop keeps\n * every call site source-compatible.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n form: UseFormReturn<TFieldValues, any, any>;\n onSubmit: (data: TFieldValues) => Promise<void> | void;\n children: ReactNode;\n isLoading?: boolean;\n error?: string | null;\n success?: boolean;\n successTitle?: string;\n successMessage?: string;\n onSuccessClose?: () => void;\n submitButtonText?: string;\n submitButtonLoadingText?: string;\n submitButtonDisabled?: boolean;\n /** @deprecated No longer needed — cancel button is hidden by default */\n showCancelButton?: boolean;\n /** @deprecated No longer needed — submit button is centered by default */\n centerSubmitButton?: boolean;\n maxWidth?: string;\n /** Render inline instead of in a dialog */\n inline?: boolean;\n}\n\nexport function BaseActivationForm<\n TFieldValues extends FieldValues = FieldValues,\n>({\n isOpen,\n onClose,\n title,\n description,\n form,\n onSubmit,\n children,\n isLoading = false,\n error = null,\n success = false,\n successTitle,\n successMessage,\n onSuccessClose,\n submitButtonText = 'Activate Integration',\n submitButtonLoadingText,\n submitButtonDisabled = false,\n maxWidth,\n inline = false,\n}: BaseActivationFormProps<TFieldValues>) {\n const [pending, startTransition] = useTransition();\n const loading = pending || isLoading;\n\n const handleSubmit = (data: TFieldValues) => {\n startTransition(async () => {\n await onSubmit(data);\n });\n };\n\n const formContent = (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(handleSubmit)} className=\"space-y-6\">\n {children}\n\n <div className=\"flex justify-center pt-4\">\n <Button type=\"submit\" disabled={loading || submitButtonDisabled}>\n {loading && (\n <Icon\n name=\"fa-light fa-spinner\"\n className=\"mr-2 h-4 w-4 animate-spin\"\n />\n )}\n {loading && submitButtonLoadingText\n ? submitButtonLoadingText\n : submitButtonText}\n </Button>\n </div>\n </form>\n </Form>\n );\n\n return (\n <BaseActivationDialog\n isOpen={isOpen}\n onClose={onClose}\n title={title}\n description={description}\n isLoading={loading}\n error={error}\n success={success}\n successTitle={successTitle}\n successMessage={successMessage}\n onSuccessClose={onSuccessClose}\n maxWidth={maxWidth}\n inline={inline}\n >\n {formContent}\n </BaseActivationDialog>\n );\n}\n"]}
@@ -0,0 +1,34 @@
1
+ import type { MarketplaceTileProps } from '../types/integration.js';
2
+ export interface ProductCategory {
3
+ id: string;
4
+ name: string;
5
+ slug: string;
6
+ description?: string;
7
+ }
8
+ export type BaseMarketplaceTileProps = MarketplaceTileProps & {
9
+ /** The display name of the product */
10
+ name: string;
11
+ /** Full description of the product (used for detail pages) */
12
+ description: string;
13
+ /** URL-friendly identifier for the product */
14
+ slug: string;
15
+ /** Short summary text displayed on the tile (1-2 sentences) */
16
+ summary?: string;
17
+ /** Name of the company/brand that provides this product */
18
+ brandName?: string;
19
+ /** Path to the product logo image */
20
+ logoPath?: string;
21
+ /** Text for the call-to-action button */
22
+ ctaText?: string;
23
+ /** External URL for the CTA (opens in new tab when set) */
24
+ ctaLink?: string;
25
+ /** Array of categories this product belongs to */
26
+ categories?: ProductCategory[];
27
+ /** Hex color from product schema for tile theming */
28
+ productColor?: string;
29
+ /** Additional CSS classes to apply to the tile */
30
+ className?: string;
31
+ /** Internal navigation URL for the integration detail page */
32
+ href?: string;
33
+ };
34
+ export declare function BaseMarketplaceTile(props: BaseMarketplaceTileProps): import("react").JSX.Element;
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import Link from 'next/link';
4
+ import { Badge } from '@ekanos/ui/badge';
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@ekanos/ui/card';
6
+ import { Icon } from '@ekanos/ui/icon';
7
+ import { If } from '@ekanos/ui/if';
8
+ import { cn } from '@ekanos/ui/utils';
9
+ export function BaseMarketplaceTile(props) {
10
+ const { name, summary, brandName, logoPath, categories = [], productColor, className, href, ctaLink, } = props;
11
+ const displayTitle = name || 'Unknown Product';
12
+ const displaySummary = summary;
13
+ const displayBrandName = brandName || displayTitle;
14
+ const cardContent = (_jsxs(Card, { className: cn('group bg-card relative flex h-full w-full cursor-pointer flex-col overflow-hidden border-0 pt-3 pb-2 shadow-sm transition-all duration-300 hover:shadow-md', className), children: [_jsxs(CardHeader, { className: "flex flex-col items-center pt-6 pb-3 text-center", children: [_jsx("div", { className: "mb-3", children: logoPath ? (_jsxs("div", { className: "flex h-16 w-16 items-center justify-center", children: [_jsx("img", { src: logoPath, alt: `${displayBrandName} logo`, width: 64, height: 64, className: "max-h-full max-w-full object-contain", onError: (e) => {
15
+ const target = e.target;
16
+ target.style.display = 'none';
17
+ if (target.nextSibling) {
18
+ target.nextSibling.style.display = 'flex';
19
+ }
20
+ } }), _jsx("div", { className: "bg-border ring-border/50 hidden h-full w-full items-center justify-center rounded-lg text-2xl font-semibold ring-1", children: displayBrandName.charAt(0).toUpperCase() })] })) : (_jsx("div", { className: "bg-border ring-border/50 flex h-16 w-16 items-center justify-center rounded-lg border-2 text-2xl font-semibold ring-1", children: displayBrandName.charAt(0).toUpperCase() })) }), _jsx(CardTitle, { className: "line-clamp-2 text-xl leading-tight font-semibold", children: displayTitle }), _jsx(If, { condition: categories.length > 0, children: _jsxs("div", { className: "mt-2 flex flex-wrap justify-center gap-1", children: [categories.slice(0, 2).map((cat) => (_jsx(Badge, { variant: "secondary", className: "px-2 py-0.5 text-xs font-medium", style: {
21
+ backgroundColor: productColor
22
+ ? `${productColor}66`
23
+ : undefined,
24
+ }, children: cat.name }, cat.id))), _jsx(If, { condition: categories.length > 2, children: _jsxs(Badge, { variant: "secondary", className: "px-2 py-0.5 text-xs font-medium", style: {
25
+ backgroundColor: productColor
26
+ ? `${productColor}66`
27
+ : undefined,
28
+ }, children: ["+", categories.length - 2] }) })] }) })] }), _jsx(CardContent, { className: "flex-1 pb-8 text-center", children: _jsx(If, { condition: displaySummary, children: _jsx(CardDescription, { className: "text-muted-foreground line-clamp-2 text-base leading-relaxed", children: displaySummary }) }) }), _jsx(If, { condition: displayBrandName !== displayTitle, children: _jsx("p", { className: "text-muted-foreground/70 absolute right-3 bottom-3 text-sm", children: displayBrandName }) }), _jsx(If, { condition: !!ctaLink, children: _jsx("div", { className: "text-muted-foreground/60 group-hover:text-foreground absolute bottom-3 left-3 transition-colors", "aria-hidden": "true", children: _jsx(Icon, { name: "fa-light fa-arrow-up-right-from-square", className: "h-4 w-4" }) }) })] }));
29
+ // External link: opens in new tab
30
+ if (ctaLink) {
31
+ return (_jsx("a", { href: ctaLink, target: "_blank", rel: "noopener noreferrer", className: "block h-full", "aria-label": `${displayTitle} (opens in new tab)`, children: cardContent }));
32
+ }
33
+ // Internal link: uses Next.js Link
34
+ if (href) {
35
+ return (_jsx(Link, { href: href, className: "block h-full", children: cardContent }));
36
+ }
37
+ // Fallback: render without Link (for backwards compatibility)
38
+ return cardContent;
39
+ }
40
+ //# sourceMappingURL=base-marketplace-tile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-marketplace-tile.js","sourceRoot":"","sources":["../../src/components/base-marketplace-tile.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport Link from 'next/link';\n\nimport { Badge } from '@ekanos/ui/badge';\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from '@ekanos/ui/card';\nimport { Icon } from '@ekanos/ui/icon';\nimport { If } from '@ekanos/ui/if';\nimport { cn } from '@ekanos/ui/utils';\n\nimport type { MarketplaceTileProps } from '../types/integration';\n\n// Type for product category\nexport interface ProductCategory {\n id: string;\n name: string;\n slug: string;\n description?: string;\n}\n\nexport type BaseMarketplaceTileProps = MarketplaceTileProps & {\n /** The display name of the product */\n name: string;\n /** Full description of the product (used for detail pages) */\n description: string;\n /** URL-friendly identifier for the product */\n slug: string;\n /** Short summary text displayed on the tile (1-2 sentences) */\n summary?: string;\n /** Name of the company/brand that provides this product */\n brandName?: string;\n /** Path to the product logo image */\n logoPath?: string;\n /** Text for the call-to-action button */\n ctaText?: string;\n /** External URL for the CTA (opens in new tab when set) */\n ctaLink?: string;\n\n /** Array of categories this product belongs to */\n categories?: ProductCategory[];\n\n /** Hex color from product schema for tile theming */\n productColor?: string;\n /** Additional CSS classes to apply to the tile */\n className?: string;\n\n /** Internal navigation URL for the integration detail page */\n href?: string;\n};\n\nexport function BaseMarketplaceTile(props: BaseMarketplaceTileProps) {\n const {\n name,\n summary,\n brandName,\n logoPath,\n categories = [],\n productColor,\n className,\n href,\n ctaLink,\n } = props;\n\n const displayTitle = name || 'Unknown Product';\n const displaySummary = summary;\n const displayBrandName = brandName || displayTitle;\n\n const cardContent = (\n <Card\n className={cn(\n 'group bg-card relative flex h-full w-full cursor-pointer flex-col overflow-hidden border-0 pt-3 pb-2 shadow-sm transition-all duration-300 hover:shadow-md',\n className,\n )}\n >\n {/* Header: Centered Logo, Title, Categories */}\n <CardHeader className=\"flex flex-col items-center pt-6 pb-3 text-center\">\n {/* Logo */}\n <div className=\"mb-3\">\n {logoPath ? (\n <div className=\"flex h-16 w-16 items-center justify-center\">\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={logoPath}\n alt={`${displayBrandName} logo`}\n width={64}\n height={64}\n className=\"max-h-full max-w-full object-contain\"\n onError={(e) => {\n const target = e.target as HTMLImageElement;\n target.style.display = 'none';\n if (target.nextSibling) {\n (target.nextSibling as HTMLElement).style.display = 'flex';\n }\n }}\n />\n <div className=\"bg-border ring-border/50 hidden h-full w-full items-center justify-center rounded-lg text-2xl font-semibold ring-1\">\n {displayBrandName.charAt(0).toUpperCase()}\n </div>\n </div>\n ) : (\n <div className=\"bg-border ring-border/50 flex h-16 w-16 items-center justify-center rounded-lg border-2 text-2xl font-semibold ring-1\">\n {displayBrandName.charAt(0).toUpperCase()}\n </div>\n )}\n </div>\n\n {/* Title */}\n <CardTitle className=\"line-clamp-2 text-xl leading-tight font-semibold\">\n {displayTitle}\n </CardTitle>\n\n {/* Category Badges - Under Title */}\n <If condition={categories.length > 0}>\n <div className=\"mt-2 flex flex-wrap justify-center gap-1\">\n {categories.slice(0, 2).map((cat) => (\n <Badge\n key={cat.id}\n variant=\"secondary\"\n className=\"px-2 py-0.5 text-xs font-medium\"\n style={{\n backgroundColor: productColor\n ? `${productColor}66`\n : undefined,\n }}\n >\n {cat.name}\n </Badge>\n ))}\n <If condition={categories.length > 2}>\n <Badge\n variant=\"secondary\"\n className=\"px-2 py-0.5 text-xs font-medium\"\n style={{\n backgroundColor: productColor\n ? `${productColor}66`\n : undefined,\n }}\n >\n +{categories.length - 2}\n </Badge>\n </If>\n </div>\n </If>\n </CardHeader>\n\n {/* Content: Summary */}\n <CardContent className=\"flex-1 pb-8 text-center\">\n <If condition={displaySummary}>\n <CardDescription className=\"text-muted-foreground line-clamp-2 text-base leading-relaxed\">\n {displaySummary}\n </CardDescription>\n </If>\n </CardContent>\n\n {/* Brand Name - Bottom Right */}\n <If condition={displayBrandName !== displayTitle}>\n <p className=\"text-muted-foreground/70 absolute right-3 bottom-3 text-sm\">\n {displayBrandName}\n </p>\n </If>\n\n {/* External Link Indicator */}\n <If condition={!!ctaLink}>\n <div\n className=\"text-muted-foreground/60 group-hover:text-foreground absolute bottom-3 left-3 transition-colors\"\n aria-hidden=\"true\"\n >\n <Icon\n name=\"fa-light fa-arrow-up-right-from-square\"\n className=\"h-4 w-4\"\n />\n </div>\n </If>\n </Card>\n );\n\n // External link: opens in new tab\n if (ctaLink) {\n return (\n <a\n href={ctaLink}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block h-full\"\n aria-label={`${displayTitle} (opens in new tab)`}\n >\n {cardContent}\n </a>\n );\n }\n\n // Internal link: uses Next.js Link\n if (href) {\n return (\n <Link href={href} className=\"block h-full\">\n {cardContent}\n </Link>\n );\n }\n\n // Fallback: render without Link (for backwards compatibility)\n return cardContent;\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The path an OAuth connect route should send the browser back to.
3
+ *
4
+ * Includes the QUERY STRING, which every call site used to drop by reading
5
+ * `window.location.pathname` alone. That mattered the moment activation moved
6
+ * into a modal addressed by `?integration=<slug>` (plan 067): the browser has
7
+ * to land back on a URL that re-opens the modal, because the activation form
8
+ * is what reads `?oauth_success=true` on mount and auto-activates
9
+ * (`use-oauth-connection-status.ts`). Return to a bare path and the provider
10
+ * token is stored while the integration is silently never activated — no
11
+ * error, no toast, the row just stays "Ready to connect".
12
+ *
13
+ * `oauth_success` is stripped so a retry after a completed round-trip does
14
+ * not ask the next return to re-announce a stale success.
15
+ *
16
+ * Any NEW OAuth-style activation form must use this rather than reaching for
17
+ * `window.location.pathname` again.
18
+ */
19
+ export declare function currentReturnPath(fallback?: string): string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The path an OAuth connect route should send the browser back to.
3
+ *
4
+ * Includes the QUERY STRING, which every call site used to drop by reading
5
+ * `window.location.pathname` alone. That mattered the moment activation moved
6
+ * into a modal addressed by `?integration=<slug>` (plan 067): the browser has
7
+ * to land back on a URL that re-opens the modal, because the activation form
8
+ * is what reads `?oauth_success=true` on mount and auto-activates
9
+ * (`use-oauth-connection-status.ts`). Return to a bare path and the provider
10
+ * token is stored while the integration is silently never activated — no
11
+ * error, no toast, the row just stays "Ready to connect".
12
+ *
13
+ * `oauth_success` is stripped so a retry after a completed round-trip does
14
+ * not ask the next return to re-announce a stale success.
15
+ *
16
+ * Any NEW OAuth-style activation form must use this rather than reaching for
17
+ * `window.location.pathname` again.
18
+ */
19
+ export function currentReturnPath(fallback = '/home') {
20
+ if (typeof window === 'undefined')
21
+ return fallback;
22
+ const url = new URL(window.location.href);
23
+ url.searchParams.delete('oauth_success');
24
+ return `${url.pathname}${url.search}`;
25
+ }
26
+ //# sourceMappingURL=current-return-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-return-path.js","sourceRoot":"","sources":["../../src/components/current-return-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAQ,GAAG,OAAO;IAClD,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,QAAQ,CAAC;IAEnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1C,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAEzC,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;AACxC,CAAC","sourcesContent":["/**\n * The path an OAuth connect route should send the browser back to.\n *\n * Includes the QUERY STRING, which every call site used to drop by reading\n * `window.location.pathname` alone. That mattered the moment activation moved\n * into a modal addressed by `?integration=<slug>` (plan 067): the browser has\n * to land back on a URL that re-opens the modal, because the activation form\n * is what reads `?oauth_success=true` on mount and auto-activates\n * (`use-oauth-connection-status.ts`). Return to a bare path and the provider\n * token is stored while the integration is silently never activated — no\n * error, no toast, the row just stays \"Ready to connect\".\n *\n * `oauth_success` is stripped so a retry after a completed round-trip does\n * not ask the next return to re-announce a stale success.\n *\n * Any NEW OAuth-style activation form must use this rather than reaching for\n * `window.location.pathname` again.\n */\nexport function currentReturnPath(fallback = '/home'): string {\n if (typeof window === 'undefined') return fallback;\n\n const url = new URL(window.location.href);\n url.searchParams.delete('oauth_success');\n\n return `${url.pathname}${url.search}`;\n}\n"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @ekanos/sdk/components — client-side building blocks.
3
+ *
4
+ * Import from client components only. `Widget` is the compound API every
5
+ * dashboard widget renders through; the activation forms are the two
6
+ * sanctioned activation UX bases (credential-style and OAuth-style), and
7
+ * `BaseMarketplaceTile` is the matching base for the marketplace tile.
8
+ *
9
+ * The SDK OWNS these implementations — the pre-inversion re-exports through
10
+ * `@kit/integrations-core/components/*` are gone; core now re-exports FROM
11
+ * here for first-party code (one source of truth, two consumption paths —
12
+ * DEVELOPER_EXPERIENCE_PLAN.md §5.1). UI primitives come from `@ekanos/ui`
13
+ * (the published slice of the host design system), never `@kit/ui`.
14
+ *
15
+ * Server seam: nothing here imports a server module. `OAuthActivationForm`
16
+ * performs activation through the host-injected actions seam
17
+ * (`IntegrationActivationProvider`, @ekanos/sdk/hooks) — a server-action
18
+ * REFERENCE bound by the host, not server code shipped to the browser.
19
+ *
20
+ * `WidgetContext` is the single context identity both sides share: the
21
+ * host's `DashboardWidgetProvider` (host chrome — persistence callbacks,
22
+ * config hierarchy) provides it, the SDK's `Widget.*` reads it.
23
+ *
24
+ * Surface discipline: additions require an entry in
25
+ * docs/devex/sdk-export-map.md ("`@ekanos/sdk/components`").
26
+ */
27
+ export { Widget } from './widgets/widget.js';
28
+ export { WidgetContext, type WidgetActions, type WidgetContextValue, type WidgetHealth, type WidgetMeta, type WidgetRenderState, type WidgetState, } from './widgets/widget-context.js';
29
+ export { AiPromptChip } from './widgets/ai-prompt-chip.js';
30
+ export { AskIcon } from './widgets/ask-icon.js';
31
+ export { WidgetAskBar } from './widgets/widget-ask-bar.js';
32
+ export { WidgetDataLoading } from './widgets/widget-state-components.js';
33
+ export { BaseActivationForm } from './base-activation-form.js';
34
+ export type { BaseActivationFormProps } from './base-activation-form.js';
35
+ export { ActivationDialog, ActivationInline, BaseActivationDialog, } from './base-activation-dialog.js';
36
+ export { BaseMarketplaceTile } from './base-marketplace-tile.js';
37
+ export type { BaseMarketplaceTileProps } from './base-marketplace-tile.js';
38
+ export { OAuthActivationForm } from './oauth-activation-form.js';
39
+ export type { OAuthActivationFormConfig } from './oauth-activation-form.js';
40
+ export { currentReturnPath } from './current-return-path.js';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @ekanos/sdk/components — client-side building blocks.
3
+ *
4
+ * Import from client components only. `Widget` is the compound API every
5
+ * dashboard widget renders through; the activation forms are the two
6
+ * sanctioned activation UX bases (credential-style and OAuth-style), and
7
+ * `BaseMarketplaceTile` is the matching base for the marketplace tile.
8
+ *
9
+ * The SDK OWNS these implementations — the pre-inversion re-exports through
10
+ * `@kit/integrations-core/components/*` are gone; core now re-exports FROM
11
+ * here for first-party code (one source of truth, two consumption paths —
12
+ * DEVELOPER_EXPERIENCE_PLAN.md §5.1). UI primitives come from `@ekanos/ui`
13
+ * (the published slice of the host design system), never `@kit/ui`.
14
+ *
15
+ * Server seam: nothing here imports a server module. `OAuthActivationForm`
16
+ * performs activation through the host-injected actions seam
17
+ * (`IntegrationActivationProvider`, @ekanos/sdk/hooks) — a server-action
18
+ * REFERENCE bound by the host, not server code shipped to the browser.
19
+ *
20
+ * `WidgetContext` is the single context identity both sides share: the
21
+ * host's `DashboardWidgetProvider` (host chrome — persistence callbacks,
22
+ * config hierarchy) provides it, the SDK's `Widget.*` reads it.
23
+ *
24
+ * Surface discipline: additions require an entry in
25
+ * docs/devex/sdk-export-map.md ("`@ekanos/sdk/components`").
26
+ */
27
+ export { Widget } from './widgets/widget.js';
28
+ export { WidgetContext, } from './widgets/widget-context.js';
29
+ export { AiPromptChip } from './widgets/ai-prompt-chip.js';
30
+ export { AskIcon } from './widgets/ask-icon.js';
31
+ export { WidgetAskBar } from './widgets/widget-ask-bar.js';
32
+ export { WidgetDataLoading } from './widgets/widget-state-components.js';
33
+ export { BaseActivationForm } from './base-activation-form.js';
34
+ export { ActivationDialog, ActivationInline, BaseActivationDialog, } from './base-activation-dialog.js';
35
+ export { BaseMarketplaceTile } from './base-marketplace-tile.js';
36
+ export { OAuthActivationForm } from './oauth-activation-form.js';
37
+ export { currentReturnPath } from './current-return-path.js';
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EACL,aAAa,GAOd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["/**\n * @ekanos/sdk/components — client-side building blocks.\n *\n * Import from client components only. `Widget` is the compound API every\n * dashboard widget renders through; the activation forms are the two\n * sanctioned activation UX bases (credential-style and OAuth-style), and\n * `BaseMarketplaceTile` is the matching base for the marketplace tile.\n *\n * The SDK OWNS these implementations — the pre-inversion re-exports through\n * `@kit/integrations-core/components/*` are gone; core now re-exports FROM\n * here for first-party code (one source of truth, two consumption paths —\n * DEVELOPER_EXPERIENCE_PLAN.md §5.1). UI primitives come from `@ekanos/ui`\n * (the published slice of the host design system), never `@kit/ui`.\n *\n * Server seam: nothing here imports a server module. `OAuthActivationForm`\n * performs activation through the host-injected actions seam\n * (`IntegrationActivationProvider`, @ekanos/sdk/hooks) — a server-action\n * REFERENCE bound by the host, not server code shipped to the browser.\n *\n * `WidgetContext` is the single context identity both sides share: the\n * host's `DashboardWidgetProvider` (host chrome — persistence callbacks,\n * config hierarchy) provides it, the SDK's `Widget.*` reads it.\n *\n * Surface discipline: additions require an entry in\n * docs/devex/sdk-export-map.md (\"`@ekanos/sdk/components`\").\n */\n\nexport { Widget } from './widgets/widget';\nexport {\n WidgetContext,\n type WidgetActions,\n type WidgetContextValue,\n type WidgetHealth,\n type WidgetMeta,\n type WidgetRenderState,\n type WidgetState,\n} from './widgets/widget-context';\nexport { AiPromptChip } from './widgets/ai-prompt-chip';\nexport { AskIcon } from './widgets/ask-icon';\nexport { WidgetAskBar } from './widgets/widget-ask-bar';\nexport { WidgetDataLoading } from './widgets/widget-state-components';\n\nexport { BaseActivationForm } from './base-activation-form';\nexport type { BaseActivationFormProps } from './base-activation-form';\nexport {\n ActivationDialog,\n ActivationInline,\n BaseActivationDialog,\n} from './base-activation-dialog';\n\nexport { BaseMarketplaceTile } from './base-marketplace-tile';\nexport type { BaseMarketplaceTileProps } from './base-marketplace-tile';\n\nexport { OAuthActivationForm } from './oauth-activation-form';\nexport type { OAuthActivationFormConfig } from './oauth-activation-form';\n\nexport { currentReturnPath } from './current-return-path';\n"]}
@@ -0,0 +1,50 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type ConnectionCheckAction, type OAuthErrorParam } from '../hooks/use-oauth-connection-status.js';
3
+ import type { ActivationFormProps } from '../types/integration.js';
4
+ export interface OAuthActivationFormConfig {
5
+ /** Integration slug — used for activation, the connect route, and ids. */
6
+ slug: string;
7
+ /** Human name used in default button/banner copy, e.g. 'QuickBooks'. */
8
+ displayName: string;
9
+ title: string;
10
+ description: string;
11
+ successMessage: string;
12
+ /** Defaults to "You're all set!" */
13
+ successTitle?: string;
14
+ /** Defaults to `/api/integrations/<slug>/connect`. */
15
+ connectPath?: string;
16
+ /** Defaults to `<displayName> account connected`. */
17
+ connectedBanner?: string;
18
+ /** Defaults to `Connect with <displayName>`. */
19
+ connectButtonText?: string;
20
+ /** Defaults to `Activate <displayName>`. */
21
+ activateButtonText?: string;
22
+ /** externalId is `<prefix>_<timestamp>`; prefix defaults to the slug. */
23
+ externalIdPrefix?: string;
24
+ /**
25
+ * Whether returning from OAuth should immediately activate (default `true`).
26
+ *
27
+ * Set `false` for integrations that expose post-connect configuration — the
28
+ * user needs a chance to review or change it before activating, which
29
+ * auto-activation skips right past. Connecting then leaves the form on an
30
+ * "Activate <X>" button as a deliberate second step.
31
+ */
32
+ autoActivateOnConnect?: boolean;
33
+ }
34
+ type OAuthActivationFormProps = ActivationFormProps & {
35
+ config: OAuthActivationFormConfig;
36
+ checkConnection: ConnectionCheckAction;
37
+ errorParam?: OAuthErrorParam;
38
+ /** Extra content (feature showcases, permission badges) rendered above the connected banner. */
39
+ children?: ReactNode;
40
+ };
41
+ /**
42
+ * Shared activation form for OAuth-backed integrations: shows a "Connect
43
+ * with X" button that redirects to the integration's connect route, then
44
+ * auto-activates once the OAuth redirect returns with a live connection.
45
+ *
46
+ * Integrations with real activation fields (API keys, domains, …) should
47
+ * keep a bespoke form built on BaseActivationForm instead.
48
+ */
49
+ export declare function OAuthActivationForm(props: OAuthActivationFormProps): import("react").JSX.Element;
50
+ export {};