@declarion/react 0.1.64 → 0.1.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist-lib/api/agents.d.ts +22 -3
- package/dist-lib/api/auth-redirect.d.ts +3 -0
- package/dist-lib/api/auth.d.ts +14 -0
- package/dist-lib/api/client.d.ts +13 -0
- package/dist-lib/api/events.d.ts +17 -0
- package/dist-lib/components/agents/AgentChatPanel.d.ts +2 -1
- package/dist-lib/components/fields/RichTextField.d.ts +2 -0
- package/dist-lib/components/fields/index.d.ts +1 -0
- package/dist-lib/components/file-widgets/CircleImage.d.ts +2 -0
- package/dist-lib/components/file-widgets/DocumentCard.d.ts +2 -0
- package/dist-lib/components/file-widgets/FileList.d.ts +2 -0
- package/dist-lib/components/file-widgets/Grid.d.ts +2 -0
- package/dist-lib/components/file-widgets/ImageWidget.d.ts +12 -0
- package/dist-lib/components/file-widgets/LabeledSlots.d.ts +2 -0
- package/dist-lib/components/file-widgets/SquareImage.d.ts +2 -0
- package/dist-lib/components/file-widgets/WideBanner.d.ts +2 -0
- package/dist-lib/components/file-widgets/builtins.d.ts +1 -0
- package/dist-lib/components/file-widgets/cropper.d.ts +14 -0
- package/dist-lib/components/file-widgets/draft-scope.d.ts +22 -0
- package/dist-lib/components/file-widgets/helpers.d.ts +12 -0
- package/dist-lib/components/file-widgets/host.d.ts +19 -0
- package/dist-lib/components/file-widgets/index.d.ts +16 -0
- package/dist-lib/components/file-widgets/payload.d.ts +21 -0
- package/dist-lib/components/file-widgets/registry.d.ts +5 -0
- package/dist-lib/components/file-widgets/types.d.ts +15 -0
- package/dist-lib/components/file-widgets/upload.d.ts +25 -0
- package/dist-lib/components/file-widgets/value.d.ts +4 -0
- package/dist-lib/components/layout/ImpersonationBanner.d.ts +11 -0
- package/dist-lib/components/layout/ImpersonationFrame.d.ts +11 -0
- package/dist-lib/components/layout/ImpersonationStartModal.d.ts +22 -0
- package/dist-lib/components/layout/TenantChip.d.ts +9 -1
- package/dist-lib/components/list/QuickPeek.d.ts +13 -6
- package/dist-lib/components/pages/SmartDetailPage.d.ts +1 -1
- package/dist-lib/components/pages/SmartListPage.d.ts +1 -1
- package/dist-lib/components/primitives/Fields.d.ts +3 -3
- package/dist-lib/components/primitives/MenuItem.d.ts +2 -1
- package/dist-lib/components/shell/CommandPalette.d.ts +2 -1
- package/dist-lib/declarion-react.css +1 -1
- package/dist-lib/hooks/useAgentConversation.d.ts +11 -2
- package/dist-lib/hooks/useImpersonation.d.ts +23 -0
- package/dist-lib/hooks/useVersionWatcher.d.ts +30 -0
- package/dist-lib/index.d.ts +6 -0
- package/dist-lib/index.js +7043 -3406
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/rich-text.d.ts +14 -0
- package/dist-lib/lib/versionTracker.d.ts +66 -0
- package/dist-lib/types/api.d.ts +20 -0
- package/dist-lib/types/files.d.ts +76 -0
- package/dist-lib/types/schema.d.ts +11 -0
- package/dist-lib/vite/fingerprint-plugin.d.ts +45 -0
- package/dist-lib/vite/index.d.ts +21 -0
- package/dist-lib/vite/index.js +52 -0
- package/dist-lib/vite/index.js.map +1 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -12,6 +12,38 @@ pnpm add @declarion/react
|
|
|
12
12
|
pnpm add react react-dom
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Subpath exports
|
|
16
|
+
|
|
17
|
+
| Import | Purpose |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `@declarion/react` | Main React entry — components, hooks, types |
|
|
20
|
+
| `@declarion/react/styles.css` | Pre-compiled Tailwind output; import once at the SPA root |
|
|
21
|
+
| `@declarion/react/vite` | Node-only Vite plugin — opt into client staleness handling at build time |
|
|
22
|
+
|
|
23
|
+
## Vite plugin: build identity for client staleness handling
|
|
24
|
+
|
|
25
|
+
The SDK ships with a tiny Vite plugin that emits `bundle-fingerprint.txt` next to `index.html` after every production build. The Declarion server reads it at boot and folds it into the build identity stamped on every `/api/*` response (`X-Declarion-Version`). The SDK's built-in version watcher detects drift on every API response, on focus/online/60s probe, and either silently invalidates the schema query (schema-only changes) or prompts the user to reload (asset/binary changes). Cross-tab coordination via `BroadcastChannel`.
|
|
26
|
+
|
|
27
|
+
Add the plugin to your consumer app's `vite.config.ts`:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { defineConfig } from "vite";
|
|
31
|
+
import react from "@vitejs/plugin-react";
|
|
32
|
+
import { declarionFingerprintPlugin } from "@declarion/react/vite";
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
plugins: [react(), declarionFingerprintPlugin()],
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
That's it — there is no client API to wire by hand. The version watcher mounts itself inside `<App>`, the response-header observer is built into `apiFetch`, and the toast slot reuses the existing Sonner `<Toaster/>`. The plugin works in every Declarion deployment mode (local SDK link, npm-pinned, prebuilt prod image) since it runs at consumer build time regardless of where the SDK itself comes from.
|
|
40
|
+
|
|
41
|
+
Consumers that haven't adopted the plugin yet degrade gracefully: the server logs once at boot, falls back to binary-only drift detection, and the rest of the system continues working — drift still gets detected, just classified more conservatively as "asset/binary" so the user gets a reload prompt rather than a silent invalidation.
|
|
42
|
+
|
|
43
|
+
Browser support requires `BroadcastChannel` for cross-tab coordination — Chrome 54+, Firefox 38+, Safari 15.4+. In environments without it, in-tab detection still works; cross-tab fan-out is silently skipped.
|
|
44
|
+
|
|
45
|
+
See [`docs/architecture.md`](https://github.com/disciplinedware/declarion/blob/main/docs/architecture.md) (section "Build identity & client staleness handling") for the full design rationale, fingerprint composition, RFC 7232 details (auth-variant ETag, weak-ETag handling), and the no-persistence invariant that prevents reload loops.
|
|
46
|
+
|
|
15
47
|
## License
|
|
16
48
|
|
|
17
49
|
MIT. See `LICENSE`.
|
package/dist-lib/api/agents.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type AgentContentBlock = {
|
|
|
10
10
|
} | {
|
|
11
11
|
type: "tool_result";
|
|
12
12
|
id: string;
|
|
13
|
+
name?: string;
|
|
13
14
|
output: unknown;
|
|
14
15
|
is_error?: boolean;
|
|
15
16
|
} | {
|
|
@@ -38,6 +39,14 @@ export type AgentContentBlock = {
|
|
|
38
39
|
} | {
|
|
39
40
|
type: "system";
|
|
40
41
|
reason: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: "decision";
|
|
44
|
+
tool_use_id?: string;
|
|
45
|
+
tool_code: string;
|
|
46
|
+
decision: "approved" | "rejected";
|
|
47
|
+
decided_by?: string;
|
|
48
|
+
decided_at?: string;
|
|
49
|
+
note?: string;
|
|
41
50
|
};
|
|
42
51
|
export interface AgentMessage {
|
|
43
52
|
id: string;
|
|
@@ -46,6 +55,7 @@ export interface AgentMessage {
|
|
|
46
55
|
role: AgentRole;
|
|
47
56
|
content: AgentContentBlock[];
|
|
48
57
|
created_at: string;
|
|
58
|
+
streaming?: boolean;
|
|
49
59
|
}
|
|
50
60
|
export type AgentInvocationStatus = "proposed" | "pending_approval" | "approved" | "rejected" | "running" | "succeeded" | "failed" | "canceled" | "interrupted";
|
|
51
61
|
export interface AgentToolInvocation {
|
|
@@ -62,13 +72,15 @@ export interface AgentToolInvocation {
|
|
|
62
72
|
edits?: Record<string, unknown>;
|
|
63
73
|
};
|
|
64
74
|
result?: unknown;
|
|
65
|
-
|
|
75
|
+
audit_operation_id?: string;
|
|
76
|
+
reason?: string;
|
|
77
|
+
confidence?: number;
|
|
66
78
|
}
|
|
67
79
|
export type AgentConversationStatus = "active" | "canceling" | "interrupted" | "closed";
|
|
68
80
|
export interface AgentConversation {
|
|
69
81
|
id: string;
|
|
70
82
|
tenant_id: string;
|
|
71
|
-
|
|
83
|
+
created_by?: string;
|
|
72
84
|
agent_code: string;
|
|
73
85
|
agent_version?: string;
|
|
74
86
|
title?: string;
|
|
@@ -84,9 +96,16 @@ export interface AgentConversation {
|
|
|
84
96
|
catalog_snapshot?: unknown;
|
|
85
97
|
active_instance?: string;
|
|
86
98
|
}
|
|
87
|
-
export declare function
|
|
99
|
+
export declare function createConversation(input: {
|
|
100
|
+
agent_code?: string;
|
|
101
|
+
context?: Record<string, unknown>;
|
|
102
|
+
}): Promise<AgentConversation>;
|
|
103
|
+
export declare function sendMessage(text: string, conversationId: string, _seq?: number): Promise<AgentMessage>;
|
|
104
|
+
export declare function sendElicitResponse(conversationId: string, _seq: number | undefined, toolUseId: string, data: Record<string, unknown>): Promise<AgentMessage>;
|
|
88
105
|
export declare function approveInvocation(id: string, edits?: Record<string, unknown>): Promise<void>;
|
|
89
106
|
export declare function rejectInvocation(id: string, note?: string): Promise<void>;
|
|
90
107
|
export declare function cancelConversation(id: string): Promise<void>;
|
|
91
108
|
export declare function listConversations(): Promise<AgentConversation[]>;
|
|
109
|
+
export declare function rerunMessageTurn(conversationId: string): Promise<void>;
|
|
110
|
+
export declare function listPendingInvocations(conversationId: string): Promise<AgentToolInvocation[]>;
|
|
92
111
|
export declare function loadHistory(conversationId: string): Promise<AgentMessage[]>;
|
package/dist-lib/api/auth.d.ts
CHANGED
|
@@ -9,3 +9,17 @@ export declare function createTenant(code: string, name: string): Promise<Tenant
|
|
|
9
9
|
export declare function logout(): Promise<void>;
|
|
10
10
|
export declare function listSSOProviders(tenantCode?: string): Promise<SSOProvider[]>;
|
|
11
11
|
export declare function ssoAuthorizeUrl(slug: string, tenantCode?: string, redirect?: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Impersonation client surface. Each call dispatches through
|
|
14
|
+
* POST /api/actions/{code} like every other auth handler. The server
|
|
15
|
+
* sets / clears the impersonation cookies via the LoginResult
|
|
16
|
+
* HTTPCustomizer; the client receives the public response shape
|
|
17
|
+
* (user / tenant / expires_at) and reloads the auth context.
|
|
18
|
+
*/
|
|
19
|
+
export interface ImpersonationStartResponse extends LoginResponse {
|
|
20
|
+
impersonation_session_id: string;
|
|
21
|
+
session_expires_at: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function impersonationStart(targetUserId: string, reason: string, durationMinutes?: number): Promise<ImpersonationStartResponse>;
|
|
24
|
+
export declare function impersonationStop(): Promise<LoginResponse>;
|
|
25
|
+
export declare function impersonationRevoke(sessionId: string): Promise<void>;
|
package/dist-lib/api/client.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
/** Signals that auth.refresh failed terminally (refresh cookie missing, token
|
|
2
|
+
* expired/reused, session revoked). Callers MUST stop retrying and route the
|
|
3
|
+
* user back to /login — retrying produces an infinite 401/4xx storm against
|
|
4
|
+
* the rate-limited refresh endpoint. Distinct from generic network errors so
|
|
5
|
+
* useSSE can branch on it. */
|
|
6
|
+
export declare class RefreshDeadError extends Error {
|
|
7
|
+
status: number;
|
|
8
|
+
constructor(status: number);
|
|
9
|
+
}
|
|
1
10
|
export declare function ensureRefresh(): Promise<void>;
|
|
2
11
|
type PersistentAuthFailureHandler = () => void;
|
|
3
12
|
/** Replace the handler invoked on persistent 401. Intended for tests
|
|
4
13
|
* and non-browser consumers. Returns the previous handler so callers
|
|
5
14
|
* can restore it if needed. */
|
|
6
15
|
export declare function setPersistentAuthFailureHandler(h: PersistentAuthFailureHandler): PersistentAuthFailureHandler;
|
|
16
|
+
/** Returns the currently registered persistent-auth-failure handler. useSSE
|
|
17
|
+
* reads this lazily so a setPersistentAuthFailureHandler() call from a test
|
|
18
|
+
* is observed by the SSE retry loop without re-importing the module. */
|
|
19
|
+
export declare function getPersistentAuthFailureHandler(): PersistentAuthFailureHandler;
|
|
7
20
|
export declare function apiFetch<T>(path: string, options?: RequestInit): Promise<T>;
|
|
8
21
|
export declare function callHandler<R = unknown>(code: string, args?: unknown): Promise<R>;
|
|
9
22
|
export {};
|
package/dist-lib/api/events.d.ts
CHANGED
|
@@ -2,8 +2,25 @@ export type SSEEvent = {
|
|
|
2
2
|
type: "entity_changed";
|
|
3
3
|
entity: string;
|
|
4
4
|
ids: string[];
|
|
5
|
+
objects?: Array<{
|
|
6
|
+
object_id?: string;
|
|
7
|
+
pk?: Record<string, string>;
|
|
8
|
+
}>;
|
|
5
9
|
action: "created" | "updated" | "deleted" | "restored";
|
|
6
10
|
} | {
|
|
7
11
|
type: "session_refresh";
|
|
8
12
|
};
|
|
13
|
+
export declare const agentBus: EventTarget;
|
|
14
|
+
export declare const AGENT_FRAME_EVENT = "agent-frame";
|
|
15
|
+
export declare const AGENT_MESSAGE_CHANGED_EVENT = "agent-message-changed";
|
|
16
|
+
export interface AgentFrameDetail {
|
|
17
|
+
convId: string;
|
|
18
|
+
kind: string;
|
|
19
|
+
turnId?: string;
|
|
20
|
+
payload?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export declare function emitAgentFrame(detail: AgentFrameDetail): void;
|
|
23
|
+
export declare class SSEAuthError extends Error {
|
|
24
|
+
constructor();
|
|
25
|
+
}
|
|
9
26
|
export declare function streamEvents(signal?: AbortSignal): AsyncGenerator<SSEEvent>;
|
|
@@ -2,5 +2,6 @@ export interface AgentChatPanelProps {
|
|
|
2
2
|
open: boolean;
|
|
3
3
|
onClose: () => void;
|
|
4
4
|
context?: string;
|
|
5
|
+
title?: string;
|
|
5
6
|
}
|
|
6
|
-
export declare function AgentChatPanel({ open, onClose, context }: AgentChatPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export declare function AgentChatPanel({ open, onClose, context, title }: AgentChatPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -2,6 +2,7 @@ import type { EntityField } from "../../types/schema";
|
|
|
2
2
|
export type RefsMap = Record<string, Record<string, Record<string, unknown>>>;
|
|
3
3
|
export interface FieldRendererProps {
|
|
4
4
|
field: EntityField;
|
|
5
|
+
entityCode?: string;
|
|
5
6
|
fieldName: string;
|
|
6
7
|
value: unknown;
|
|
7
8
|
mode: "display" | "edit";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { formatBytes } from "./upload";
|
|
2
|
+
import type { FileWidgetProps } from "./types";
|
|
3
|
+
export interface ImageWidgetVisualConfig {
|
|
4
|
+
shape: "square" | "circle" | "wide";
|
|
5
|
+
aspectRatio?: string;
|
|
6
|
+
width?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ImageWidgetInternalProps extends FileWidgetProps {
|
|
9
|
+
visual: ImageWidgetVisualConfig;
|
|
10
|
+
}
|
|
11
|
+
export declare function ImageWidget(props: ImageWidgetInternalProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export { formatBytes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerBuiltinFileWidgets(): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface CropperOptions {
|
|
3
|
+
aspectRatio?: number;
|
|
4
|
+
file: File;
|
|
5
|
+
}
|
|
6
|
+
export interface CropperResult {
|
|
7
|
+
blob: Blob;
|
|
8
|
+
filename: string;
|
|
9
|
+
}
|
|
10
|
+
export type ImageCropper = (opts: CropperOptions) => Promise<CropperResult>;
|
|
11
|
+
export declare function setImageCropper(c: ImageCropper | null): void;
|
|
12
|
+
export declare function getImageCropper(): ImageCropper;
|
|
13
|
+
export declare function setCropperRenderer(fn: ((node: ReactNode) => void) | null): void;
|
|
14
|
+
export declare function getCropperRenderer(): ((node: ReactNode) => void) | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface DraftEntry {
|
|
3
|
+
fileId: string;
|
|
4
|
+
discard: () => Promise<void> | void;
|
|
5
|
+
}
|
|
6
|
+
export interface FileDraftScopeApi {
|
|
7
|
+
registerDraft(entry: DraftEntry): void;
|
|
8
|
+
unregisterDraft(fileId: string): void;
|
|
9
|
+
hasDraft(fileId: string): boolean;
|
|
10
|
+
flushAllDrafts(): Promise<void>;
|
|
11
|
+
markCommitted(fileId: string): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function FileDraftScope({ children }: {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function useFileDraftScope(): FileDraftScopeApi;
|
|
17
|
+
export declare function useDraftRegistration(): {
|
|
18
|
+
register: (fileId: string, discard: () => Promise<void> | void) => void;
|
|
19
|
+
unregister: (fileId: string) => void;
|
|
20
|
+
markCommitted: (fileId: string) => void;
|
|
21
|
+
isDraft: (fileId: string) => boolean;
|
|
22
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HydratedFile } from "./types";
|
|
2
|
+
export interface FocalPoint {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveFileAltText(file: HydratedFile | null, alt: string | undefined, fallback: string): string;
|
|
7
|
+
export declare function previewFileURL(file: HydratedFile): string | undefined;
|
|
8
|
+
export declare function downloadFileURL(file: HydratedFile): string | undefined;
|
|
9
|
+
export declare function preferredListThumbnailURL(file: HydratedFile | null, preferredDerivationKey?: string): string | undefined;
|
|
10
|
+
export declare function readFocalPoint(file: HydratedFile | null): FocalPoint | null;
|
|
11
|
+
export declare function focalPointObjectPosition(point: FocalPoint | null): string | undefined;
|
|
12
|
+
export declare function withFocalPoint(file: HydratedFile, point: FocalPoint): HydratedFile;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { Entity } from "../../types/schema";
|
|
3
|
+
import type { FileFieldDef, FileFieldValue } from "../../types/files";
|
|
4
|
+
import type { RefsMap } from "../../components/fields";
|
|
5
|
+
export declare function getEntityFileField(entity: Pick<Entity, "files">, fieldName: string): FileFieldDef | undefined;
|
|
6
|
+
export declare function getFileFieldValue(record: Record<string, unknown>, fieldName: string): FileFieldValue;
|
|
7
|
+
export declare function getSchemaFieldValue(entity: Pick<Entity, "fields" | "files">, record: Record<string, unknown>, fieldName: string): unknown;
|
|
8
|
+
export declare function serializeSchemaFieldValue(entity: Pick<Entity, "fields" | "files">, fieldName: string, value: unknown): unknown;
|
|
9
|
+
export declare function resolveSchemaFieldLabel(entity: Pick<Entity, "fields" | "files">, fieldName: string, entities?: Record<string, Entity>): string;
|
|
10
|
+
export declare function renderSchemaField(props: {
|
|
11
|
+
entity: Pick<Entity, "fields" | "files">;
|
|
12
|
+
entityCode?: string;
|
|
13
|
+
fieldName: string;
|
|
14
|
+
record: Record<string, unknown>;
|
|
15
|
+
mode: "display" | "edit";
|
|
16
|
+
onChange?: (value: unknown) => void;
|
|
17
|
+
refs?: RefsMap;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}): ReactNode;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { SquareImage } from "./SquareImage";
|
|
2
|
+
export { CircleImage } from "./CircleImage";
|
|
3
|
+
export { WideBanner } from "./WideBanner";
|
|
4
|
+
export { Grid } from "./Grid";
|
|
5
|
+
export { DocumentCard } from "./DocumentCard";
|
|
6
|
+
export { FileList } from "./FileList";
|
|
7
|
+
export { LabeledSlots } from "./LabeledSlots";
|
|
8
|
+
export { registerFileWidget, getFileWidget, hasFileWidget, __resetFileWidgetRegistryForTests, } from "./registry";
|
|
9
|
+
export { getEntityFileField, getFileFieldValue, getSchemaFieldValue, serializeSchemaFieldValue, resolveSchemaFieldLabel, renderSchemaField, } from "./host";
|
|
10
|
+
export { buildFileFieldPatch, buildFilesPayload, collectCommittedFileIds, } from "./payload";
|
|
11
|
+
export { uploadFile, discardFile, validateClientSide, formatBytes, UploadError, } from "./upload";
|
|
12
|
+
export { setImageCropper, getImageCropper, setCropperRenderer, getCropperRenderer, } from "./cropper";
|
|
13
|
+
export type { ImageCropper, CropperOptions, CropperResult, } from "./cropper";
|
|
14
|
+
export { FileDraftScope, useFileDraftScope, useDraftRegistration, } from "./draft-scope";
|
|
15
|
+
export type { FileDraftScopeApi, DraftEntry, } from "./draft-scope";
|
|
16
|
+
export type { FileFieldDef, FileFieldDisplay, FileFieldMode, FileWidget, FileWidgetProps, HydratedFile, UploadProgress, UploadedFile, } from "./types";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Entity } from "../../types/schema";
|
|
2
|
+
import type { FileFieldDef, FileFieldValue } from "../../types/files";
|
|
3
|
+
export interface FileSetItem {
|
|
4
|
+
file_id: string;
|
|
5
|
+
filename?: string;
|
|
6
|
+
slot?: string;
|
|
7
|
+
position?: number;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface FileFieldPatch {
|
|
11
|
+
clear?: true;
|
|
12
|
+
set?: FileSetItem[];
|
|
13
|
+
}
|
|
14
|
+
export declare function buildFileFieldPatch(field: FileFieldDef, value: FileFieldValue): FileFieldPatch;
|
|
15
|
+
export declare function buildFilesPayload(opts: {
|
|
16
|
+
entity: Pick<Entity, "files"> & Partial<Pick<Entity, "fields">>;
|
|
17
|
+
formData: Record<string, unknown>;
|
|
18
|
+
record: Record<string, unknown> | undefined;
|
|
19
|
+
isNew: boolean;
|
|
20
|
+
}): Record<string, FileFieldPatch>;
|
|
21
|
+
export declare function collectCommittedFileIds(payload: Record<string, FileFieldPatch>): string[];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FileWidget } from "./types";
|
|
2
|
+
export declare function registerFileWidget(code: string, widget: FileWidget): void;
|
|
3
|
+
export declare function getFileWidget(code: string): FileWidget | undefined;
|
|
4
|
+
export declare function hasFileWidget(code: string): boolean;
|
|
5
|
+
export declare function __resetFileWidgetRegistryForTests(): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FileFieldDef, FileFieldDisplay, FileFieldMode, FileFieldValue, HydratedFile, UploadProgress, UploadedFile } from "../../types/files";
|
|
2
|
+
export type { FileFieldDef, FileFieldDisplay, FileFieldMode, FileFieldValue, HydratedFile, UploadProgress, UploadedFile, };
|
|
3
|
+
export interface FileWidgetProps {
|
|
4
|
+
field: FileFieldDef;
|
|
5
|
+
fieldName: string;
|
|
6
|
+
entityCode?: string;
|
|
7
|
+
value: FileFieldValue;
|
|
8
|
+
mode: "display" | "edit";
|
|
9
|
+
onChange?: (next: FileFieldValue) => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
alt?: string;
|
|
12
|
+
uploadFieldCode?: string;
|
|
13
|
+
uploadSlotCode?: string;
|
|
14
|
+
}
|
|
15
|
+
export type FileWidget = React.ComponentType<FileWidgetProps>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { UploadProgress, UploadedFile } from "./types";
|
|
2
|
+
export interface UploadOptions {
|
|
3
|
+
file: File;
|
|
4
|
+
entityCode?: string;
|
|
5
|
+
fieldCode?: string;
|
|
6
|
+
slotCode?: string;
|
|
7
|
+
onProgress?: (p: UploadProgress) => void;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export declare class UploadError extends Error {
|
|
11
|
+
code: string;
|
|
12
|
+
status: number;
|
|
13
|
+
constructor(status: number, code: string, message: string);
|
|
14
|
+
}
|
|
15
|
+
export interface UploadEnvelope {
|
|
16
|
+
status: string;
|
|
17
|
+
result: UploadedFile;
|
|
18
|
+
}
|
|
19
|
+
export declare function uploadFile(opts: UploadOptions): Promise<UploadedFile>;
|
|
20
|
+
export declare function discardFile(fileId: string): Promise<void>;
|
|
21
|
+
export declare function validateClientSide(file: File, accepts: string[] | undefined, maxBytes: number | undefined): {
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
} | null;
|
|
25
|
+
export declare function formatBytes(n: number): string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FileFieldValue, HydratedFile } from "./types";
|
|
2
|
+
export declare function isHydratedFile(value: unknown): value is HydratedFile;
|
|
3
|
+
export declare function singleFileFromValue(value: FileFieldValue): HydratedFile | null;
|
|
4
|
+
export declare function fileArrayFromValue(value: FileFieldValue): HydratedFile[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sticky top-of-viewport banner shown for the entire duration of an
|
|
3
|
+
* impersonation session (principle 8: "UI signals impersonation loudly").
|
|
4
|
+
* Never dismissable. Real user, target identity, expiry countdown, and
|
|
5
|
+
* an explicit "End session" button are always visible.
|
|
6
|
+
*
|
|
7
|
+
* Color is driven by the `--impersonation-accent` CSS variable in
|
|
8
|
+
* globals.css. The body[data-impersonation-destructive] flip is wired
|
|
9
|
+
* separately by ImpersonationFrame so the banner + frame stay in lockstep.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ImpersonationBanner(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed 4px viewport overlay frame that surrounds every route. Visible
|
|
3
|
+
* for the duration of an impersonation session (principle 8). The
|
|
4
|
+
* pointer-events:none guarantees the frame never blocks clicks.
|
|
5
|
+
*
|
|
6
|
+
* Color comes from --impersonation-accent (amber by default). When
|
|
7
|
+
* impersonation.allow_destructive=true, this component sets a body
|
|
8
|
+
* attribute that flips the variable to red across the banner, chip,
|
|
9
|
+
* and frame in lockstep (one CSS variable, no parallel state).
|
|
10
|
+
*/
|
|
11
|
+
export declare function ImpersonationFrame(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Open state controlled by the caller. */
|
|
3
|
+
open: boolean;
|
|
4
|
+
/** Target user the admin is about to impersonate. */
|
|
5
|
+
targetUserId: string;
|
|
6
|
+
targetUserLabel?: string;
|
|
7
|
+
/** Closes the modal without dispatching. Caller resets state. */
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
/**
|
|
10
|
+
* Called after the start mutation succeeds. Useful for closing the
|
|
11
|
+
* modal and navigating; auth state is rehydrated by the hook itself.
|
|
12
|
+
*/
|
|
13
|
+
onStarted?: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reason modal for impersonation.start. Enforces a minimum reason
|
|
17
|
+
* length (server-side param impersonation.require_reason_min_length,
|
|
18
|
+
* default 10) before submitting. The "Duration" input is optional and
|
|
19
|
+
* is capped server-side by impersonation.max_duration_minutes.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ImpersonationStartModal({ open, targetUserId, targetUserLabel, onClose, onStarted }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
22
|
+
export {};
|
|
@@ -4,6 +4,14 @@ export interface TenantChipProps {
|
|
|
4
4
|
tenants: Tenant[];
|
|
5
5
|
onSwitch: (tenant: Tenant) => void;
|
|
6
6
|
busy?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* When true, the chip is disabled with a tooltip explaining why.
|
|
9
|
+
* Used during impersonation: cross-tenant targeting at start is
|
|
10
|
+
* supported, but mid-session tenant switching is undefined and
|
|
11
|
+
* locked (plan §"Cross-tenant impersonation").
|
|
12
|
+
*/
|
|
13
|
+
locked?: boolean;
|
|
14
|
+
lockedReason?: string;
|
|
7
15
|
}
|
|
8
16
|
interface EnvTone {
|
|
9
17
|
fg: string;
|
|
@@ -13,5 +21,5 @@ interface EnvTone {
|
|
|
13
21
|
darkBg?: string;
|
|
14
22
|
}
|
|
15
23
|
export declare function envTone(env?: TenantEnv): EnvTone;
|
|
16
|
-
export declare function TenantChip({ tenant, tenants, onSwitch, busy }: TenantChipProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export declare function TenantChip({ tenant, tenants, onSwitch, busy, locked, lockedReason }: TenantChipProps): import("react/jsx-runtime").JSX.Element;
|
|
17
25
|
export {};
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type Action, type
|
|
2
|
+
import { type Action, type Entity, type EntityField, type Screen } from "../../types/schema";
|
|
3
3
|
import type { RefsMap } from "../../components/fields";
|
|
4
|
-
export interface
|
|
4
|
+
export interface QuickPeekRegularFieldSpec {
|
|
5
5
|
key: string;
|
|
6
6
|
label: string;
|
|
7
|
-
|
|
7
|
+
kind: "field";
|
|
8
8
|
field: EntityField;
|
|
9
|
-
/** Read-only when the schema forbids edits, RBAC denies write, or the
|
|
10
|
-
* field is primary/auto/computed/ref. */
|
|
11
9
|
readOnly?: boolean;
|
|
12
10
|
}
|
|
11
|
+
export interface QuickPeekFileFieldSpec {
|
|
12
|
+
key: string;
|
|
13
|
+
label: string;
|
|
14
|
+
kind: "file";
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export type QuickPeekFieldSpec = QuickPeekRegularFieldSpec | QuickPeekFileFieldSpec;
|
|
13
18
|
export interface QuickPeekProps<R extends Record<string, unknown> = Record<string, unknown>> {
|
|
19
|
+
entity: Pick<Entity, "fields" | "files">;
|
|
20
|
+
entityCode?: string;
|
|
14
21
|
record: R;
|
|
15
22
|
title: string;
|
|
16
23
|
recordId?: string;
|
|
@@ -35,7 +42,7 @@ export interface QuickPeekProps<R extends Record<string, unknown> = Record<strin
|
|
|
35
42
|
/** Fires once per explicit user commit — never on keystroke. */
|
|
36
43
|
onSave?: (next: R) => void;
|
|
37
44
|
}
|
|
38
|
-
export declare function QuickPeek<R extends Record<string, unknown>>({ record, title, recordId, avatarName, fields, refs, screen, actions, executeAction, onClose, onOpen, onSave, }: QuickPeekProps<R>): import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
export declare function QuickPeek<R extends Record<string, unknown>>({ entity, entityCode, record, title, recordId, avatarName, fields, refs, screen, actions, executeAction, onClose, onOpen, onSave, }: QuickPeekProps<R>): import("react/jsx-runtime").JSX.Element;
|
|
39
46
|
export interface QEFieldProps {
|
|
40
47
|
label: string;
|
|
41
48
|
children: ReactNode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface SmartDetailPageProps {
|
|
2
2
|
screenCode: string;
|
|
3
3
|
}
|
|
4
|
-
export declare function SmartDetailPage({ screenCode }: SmartDetailPageProps): import("react/jsx-runtime").JSX.Element
|
|
4
|
+
export declare function SmartDetailPage({ screenCode }: SmartDetailPageProps): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
export {};
|
|
@@ -25,5 +25,5 @@ export declare function resolveDetailHref(list: {
|
|
|
25
25
|
} | undefined, allScreens: Record<string, {
|
|
26
26
|
route?: string;
|
|
27
27
|
}> | undefined): string | null;
|
|
28
|
-
export declare function SmartListPage({ screenCode }: SmartListPageProps): import("react/jsx-runtime").JSX.Element
|
|
28
|
+
export declare function SmartListPage({ screenCode }: SmartListPageProps): import("react/jsx-runtime").JSX.Element;
|
|
29
29
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CSSProperties, InputHTMLAttributes, ReactNode } from "react";
|
|
1
|
+
import type { ButtonHTMLAttributes, CSSProperties, InputHTMLAttributes, ReactNode } from "react";
|
|
2
2
|
export type FieldLabelProps = {
|
|
3
3
|
children?: ReactNode;
|
|
4
4
|
required?: boolean;
|
|
@@ -25,13 +25,13 @@ export type SelectOption = {
|
|
|
25
25
|
value: string;
|
|
26
26
|
label: ReactNode;
|
|
27
27
|
};
|
|
28
|
-
export type SelectProps = {
|
|
28
|
+
export type SelectProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "value" | "onChange"> & {
|
|
29
29
|
value?: string;
|
|
30
30
|
options: SelectOption[];
|
|
31
31
|
onChange: (v: string) => void;
|
|
32
32
|
placeholder?: string;
|
|
33
33
|
};
|
|
34
|
-
export declare function Select({ value, options, onChange, placeholder }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
export declare function Select({ value, options, onChange, placeholder, ...rest }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
35
35
|
export type DateInputProps = {
|
|
36
36
|
value?: string | null;
|
|
37
37
|
onChange?: (v: string) => void;
|
|
@@ -7,5 +7,6 @@ export type MenuItemProps = {
|
|
|
7
7
|
kbd?: ReactNode;
|
|
8
8
|
active?: boolean;
|
|
9
9
|
divider?: boolean;
|
|
10
|
+
"data-testid"?: string;
|
|
10
11
|
};
|
|
11
|
-
export declare function MenuItem({ icon, children, onClick, danger, kbd, active, divider }: MenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function MenuItem({ icon, children, onClick, danger, kbd, active, divider, "data-testid": dataTestId }: MenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -14,5 +14,6 @@ export interface CommandPaletteProps {
|
|
|
14
14
|
navigation?: NavNode[];
|
|
15
15
|
screens?: Record<string, Screen>;
|
|
16
16
|
includeAI?: boolean;
|
|
17
|
+
aiName?: string;
|
|
17
18
|
}
|
|
18
|
-
export declare function CommandPalette({ open, onClose, onAction, mode, navigation, screens, includeAI, }: CommandPaletteProps): import("react/jsx-runtime").JSX.Element | null;
|
|
19
|
+
export declare function CommandPalette({ open, onClose, onAction, mode, navigation, screens, includeAI, aiName, }: CommandPaletteProps): import("react/jsx-runtime").JSX.Element | null;
|