cortena-ui 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +149 -1
- package/dist/a2ui/views.js +2 -2
- package/dist/agent-chat/a2ui-block.d.ts +60 -0
- package/dist/agent-chat/a2ui-block.js +69 -0
- package/dist/agent-chat/a2ui-block.js.map +1 -0
- package/dist/agent-chat/agui-client.d.ts +40 -0
- package/dist/agent-chat/agui-client.js +251 -0
- package/dist/agent-chat/agui-client.js.map +1 -0
- package/dist/agent-chat/bridge.d.ts +109 -0
- package/dist/agent-chat/bridge.js +349 -0
- package/dist/agent-chat/bridge.js.map +1 -0
- package/dist/agent-chat/session.d.ts +58 -0
- package/dist/agent-chat/session.js +249 -0
- package/dist/agent-chat/session.js.map +1 -0
- package/dist/agent-chat/store.d.ts +67 -0
- package/dist/agent-chat/store.js +548 -0
- package/dist/agent-chat/store.js.map +1 -0
- package/dist/agent-chat/types.d.ts +187 -0
- package/dist/agent-chat/types.js +17 -0
- package/dist/agent-chat/types.js.map +1 -0
- package/dist/agent-chat.d.ts +10 -0
- package/dist/agent-chat.js +10 -0
- package/dist/components/admin-permissions/admin-permissions.d.ts +66 -0
- package/dist/components/admin-permissions/admin-permissions.js +101 -0
- package/dist/components/admin-permissions/admin-permissions.js.map +1 -0
- package/dist/components/admin-permissions/context.d.ts +70 -0
- package/dist/components/admin-permissions/context.js +258 -0
- package/dist/components/admin-permissions/context.js.map +1 -0
- package/dist/components/admin-permissions/index.d.ts +10 -0
- package/dist/components/admin-permissions/licence.d.ts +15 -0
- package/dist/components/admin-permissions/licence.js +78 -0
- package/dist/components/admin-permissions/licence.js.map +1 -0
- package/dist/components/admin-permissions/matrix.d.ts +20 -0
- package/dist/components/admin-permissions/matrix.js +191 -0
- package/dist/components/admin-permissions/matrix.js.map +1 -0
- package/dist/components/admin-permissions/members.d.ts +18 -0
- package/dist/components/admin-permissions/members.js +185 -0
- package/dist/components/admin-permissions/members.js.map +1 -0
- package/dist/components/admin-permissions/role-assignment.d.ts +35 -0
- package/dist/components/admin-permissions/role-assignment.js +174 -0
- package/dist/components/admin-permissions/role-assignment.js.map +1 -0
- package/dist/components/admin-permissions/roles.d.ts +25 -0
- package/dist/components/admin-permissions/roles.js +168 -0
- package/dist/components/admin-permissions/roles.js.map +1 -0
- package/dist/components/admin-permissions/types.d.ts +152 -0
- package/dist/components/admin-permissions/types.js +63 -0
- package/dist/components/admin-permissions/types.js.map +1 -0
- package/dist/components/agent-chat-popup.d.ts +29 -0
- package/dist/components/agent-chat-popup.js +188 -0
- package/dist/components/agent-chat-popup.js.map +1 -0
- package/dist/components/agent-chat.d.ts +143 -0
- package/dist/components/agent-chat.js +578 -0
- package/dist/components/agent-chat.js.map +1 -0
- package/dist/components/app-shell.d.ts +126 -0
- package/dist/components/app-shell.js +297 -0
- package/dist/components/app-shell.js.map +1 -0
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button-link.js +1 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/checkbox.d.ts +1 -1
- package/dist/components/combobox.d.ts +1 -1
- package/dist/components/combobox.js +1 -1
- package/dist/components/consent-screen.d.ts +65 -0
- package/dist/components/consent-screen.js +123 -0
- package/dist/components/consent-screen.js.map +1 -0
- package/dist/components/data-table/data-table.d.ts +15 -1
- package/dist/components/data-table/data-table.js +18 -4
- package/dist/components/data-table/data-table.js.map +1 -1
- package/dist/components/data-table/index.d.ts +4 -4
- package/dist/components/data-table/parts.d.ts +27 -3
- package/dist/components/data-table/parts.js +175 -55
- package/dist/components/data-table/parts.js.map +1 -1
- package/dist/components/data-table/types.d.ts +61 -0
- package/dist/components/data-table/use-data-table.js +91 -6
- package/dist/components/data-table/use-data-table.js.map +1 -1
- package/dist/components/data-table/use-server-source.js +119 -28
- package/dist/components/data-table/use-server-source.js.map +1 -1
- package/dist/components/help-panel.d.ts +131 -0
- package/dist/components/help-panel.js +545 -0
- package/dist/components/help-panel.js.map +1 -0
- package/dist/components/login-screen.d.ts +127 -0
- package/dist/components/login-screen.js +339 -0
- package/dist/components/login-screen.js.map +1 -0
- package/dist/components/session-guard.d.ts +268 -0
- package/dist/components/session-guard.js +632 -0
- package/dist/components/session-guard.js.map +1 -0
- package/dist/components/toast.d.ts +1 -1
- package/dist/core.d.ts +5 -1
- package/dist/core.js +11 -7
- package/dist/data-table.d.ts +13 -4
- package/dist/data-table.js +10 -2
- package/dist/hooks/use-cortena-theme.js +49 -3
- package/dist/hooks/use-cortena-theme.js.map +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.js +21 -8
- package/dist/markdown.d.ts +2 -1
- package/dist/markdown.js +2 -1
- package/package.json +16 -4
- package/src/agent-chat/a2ui-block.ts +118 -0
- package/src/agent-chat/agui-client.ts +405 -0
- package/src/agent-chat/bridge.ts +433 -0
- package/src/agent-chat/session.ts +392 -0
- package/src/agent-chat/store.ts +738 -0
- package/src/agent-chat/types.ts +213 -0
- package/src/components/admin-permissions/admin-permissions.tsx +130 -0
- package/src/components/admin-permissions/context.tsx +376 -0
- package/src/components/admin-permissions/index.tsx +32 -0
- package/src/components/admin-permissions/licence.tsx +84 -0
- package/src/components/admin-permissions/matrix.tsx +257 -0
- package/src/components/admin-permissions/members.tsx +204 -0
- package/src/components/admin-permissions/role-assignment.tsx +239 -0
- package/src/components/admin-permissions/roles.tsx +169 -0
- package/src/components/admin-permissions/types.ts +231 -0
- package/src/components/agent-chat-popup.tsx +289 -0
- package/src/components/agent-chat.tsx +843 -0
- package/src/components/app-shell.tsx +502 -0
- package/src/components/consent-screen.tsx +239 -0
- package/src/components/data-table/data-table.tsx +36 -0
- package/src/components/data-table/index.tsx +6 -1
- package/src/components/data-table/parts.tsx +223 -47
- package/src/components/data-table/types.ts +68 -0
- package/src/components/data-table/use-data-table.ts +152 -4
- package/src/components/data-table/use-server-source.ts +150 -12
- package/src/components/help-panel.tsx +765 -0
- package/src/components/login-screen.tsx +479 -0
- package/src/components/session-guard.tsx +1071 -0
- package/src/entries/agent-chat.ts +113 -0
- package/src/entries/core.ts +8 -0
- package/src/entries/data-table.ts +41 -0
- package/src/entries/markdown.ts +25 -0
- package/src/hooks/use-cortena-theme.ts +63 -4
- package/src/index.ts +6 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { A2UIChatBlock } from "./a2ui-block.js";
|
|
3
|
+
//#region src/agent-chat/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A row of `sessions.list`.
|
|
6
|
+
*
|
|
7
|
+
* Titles fall back in a fixed order (§17.2 of the extension how-to):
|
|
8
|
+
* `label`, `autoTitle`, `derivedTitle`, `displayName`, `lastMessagePreview`,
|
|
9
|
+
* then "New conversation". `sessionTitle` below is that order, in one place.
|
|
10
|
+
*/
|
|
11
|
+
export interface AgentSession {
|
|
12
|
+
/** The session key, verbatim — this is also the AG-UI `threadId`. */
|
|
13
|
+
key: string;
|
|
14
|
+
label?: string | null;
|
|
15
|
+
autoTitle?: string | null;
|
|
16
|
+
derivedTitle?: string | null;
|
|
17
|
+
displayName?: string | null;
|
|
18
|
+
lastMessagePreview?: string | null;
|
|
19
|
+
updatedAt?: number | null;
|
|
20
|
+
}
|
|
21
|
+
/** The title to show for a session row. */
|
|
22
|
+
export declare function sessionTitle(session: AgentSession): string;
|
|
23
|
+
export interface AgentChatMessage {
|
|
24
|
+
id: string;
|
|
25
|
+
role: "user" | "assistant";
|
|
26
|
+
text: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
attachments?: string[];
|
|
29
|
+
/** The chain of thought behind this turn, collapsed by default. */
|
|
30
|
+
thinkingText?: string;
|
|
31
|
+
isError?: boolean;
|
|
32
|
+
/** True when the wire record was a tool call or a tool result; hidden. */
|
|
33
|
+
isToolMessage?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The run that produced this message. Absent on anything loaded from
|
|
36
|
+
* history. A `final` folds into the previous bubble only when the two share
|
|
37
|
+
* this value, so one run's answer cannot be rewritten by the next one's.
|
|
38
|
+
*/
|
|
39
|
+
runId?: string;
|
|
40
|
+
/** UI the agent drew, carried outside the prose. */
|
|
41
|
+
a2ui?: A2UIChatBlock[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One chat event. The same `{ state: "delta" | "final" | "error" }` shape
|
|
45
|
+
* cortenacore broadcasts, so a client for a different transport has a
|
|
46
|
+
* documented target to hit.
|
|
47
|
+
*
|
|
48
|
+
* `delta` carries a SNAPSHOT, not an increment: every delta repeats all the
|
|
49
|
+
* text, all the thinking and every A2UI block seen so far. Two transports that
|
|
50
|
+
* disagree about this leave the store in different states for the same run.
|
|
51
|
+
*/
|
|
52
|
+
export interface AgentChatEvent {
|
|
53
|
+
runId: string;
|
|
54
|
+
sessionKey: string;
|
|
55
|
+
seq: number;
|
|
56
|
+
state: "delta" | "final" | "error";
|
|
57
|
+
message?: {
|
|
58
|
+
role?: string;
|
|
59
|
+
content?: Array<{
|
|
60
|
+
type: string;
|
|
61
|
+
text?: string;
|
|
62
|
+
thinking?: string;
|
|
63
|
+
a2ui?: unknown;
|
|
64
|
+
}>;
|
|
65
|
+
timestamp?: number;
|
|
66
|
+
};
|
|
67
|
+
errorMessage?: string;
|
|
68
|
+
}
|
|
69
|
+
/** One step of the tool strip. */
|
|
70
|
+
export interface AgentToolEvent {
|
|
71
|
+
event: "tool.start" | "tool.args" | "tool.progress" | "tool.complete" | "tool.error";
|
|
72
|
+
data: {
|
|
73
|
+
id?: string;
|
|
74
|
+
name?: string;
|
|
75
|
+
/** `tool.args`: the call's input, parsed when it was valid JSON. */
|
|
76
|
+
input?: unknown;
|
|
77
|
+
output?: unknown;
|
|
78
|
+
startedAt?: number;
|
|
79
|
+
completedAt?: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Decisions cortenacore accepts on `POST /agui/approval`. */
|
|
83
|
+
export type AgentApprovalDecision = "allow-once" | "allow-always" | "deny";
|
|
84
|
+
export interface AgentApprovalRequest {
|
|
85
|
+
id: string;
|
|
86
|
+
/** The command, or the tool, the approval is about. */
|
|
87
|
+
toolName: string;
|
|
88
|
+
args: Record<string, unknown>;
|
|
89
|
+
timestamp: number;
|
|
90
|
+
/**
|
|
91
|
+
* The run and the session this request arrived on, captured when it arrived.
|
|
92
|
+
*
|
|
93
|
+
* A decision is bound to them rather than resolved against whatever run
|
|
94
|
+
* happens to be in flight when the user presses a button. The two come
|
|
95
|
+
* apart routinely: an approval pauses a run, the user opens another session
|
|
96
|
+
* in the drawer or starts a second turn, and the client's idea of "the
|
|
97
|
+
* current thread" has moved on. Answering "allow" against the wrong thread
|
|
98
|
+
* is a command approved in a conversation nobody was asked about.
|
|
99
|
+
*/
|
|
100
|
+
runId: string;
|
|
101
|
+
sessionKey: string;
|
|
102
|
+
/** Set when a decision was made and the server would not take it. */
|
|
103
|
+
error?: string;
|
|
104
|
+
}
|
|
105
|
+
/** Where an approval came from, and therefore where the decision must go. */
|
|
106
|
+
export interface AgentApprovalContext {
|
|
107
|
+
runId: string;
|
|
108
|
+
sessionKey: string;
|
|
109
|
+
}
|
|
110
|
+
export type AgentApprovalEvent = {
|
|
111
|
+
type: "request";
|
|
112
|
+
request: AgentApprovalRequest;
|
|
113
|
+
} |
|
|
114
|
+
/** `decision: null` is an expiry. It is treated as a denial. */
|
|
115
|
+
{
|
|
116
|
+
type: "resolved";
|
|
117
|
+
id: string;
|
|
118
|
+
decision: AgentApprovalDecision | null;
|
|
119
|
+
};
|
|
120
|
+
export interface AgentChatSendInput {
|
|
121
|
+
/** The session key to run in. `threadId` is this value verbatim. */
|
|
122
|
+
sessionKey: string;
|
|
123
|
+
/** What the user typed. Empty for an action sent by rendered UI. */
|
|
124
|
+
message: string;
|
|
125
|
+
/** The run id; also the idempotency key. */
|
|
126
|
+
runId: string;
|
|
127
|
+
attachments?: unknown[];
|
|
128
|
+
thinking?: "low" | "medium" | "high";
|
|
129
|
+
model?: {
|
|
130
|
+
provider: string;
|
|
131
|
+
model: string;
|
|
132
|
+
};
|
|
133
|
+
/** A click inside an A2UI surface. Never a sentence the user appears to type. */
|
|
134
|
+
a2uiAction?: {
|
|
135
|
+
surfaceId: string;
|
|
136
|
+
componentId: string;
|
|
137
|
+
name: string;
|
|
138
|
+
payload: Record<string, unknown>;
|
|
139
|
+
};
|
|
140
|
+
/** The same, from inside a rendered MCP App. */
|
|
141
|
+
mcpAppAction?: Record<string, unknown>;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Everything `AgentChat` needs from a server.
|
|
145
|
+
*
|
|
146
|
+
* `request` is deliberately a method name plus params rather than a URL: the
|
|
147
|
+
* two calls the chat makes — `sessions.list` and `chat.history` — are control
|
|
148
|
+
* plane methods, and a host that proxies them through its own gateway chooses
|
|
149
|
+
* its own paths.
|
|
150
|
+
*/
|
|
151
|
+
export interface AgentChatClient {
|
|
152
|
+
/** The agent this client talks to. Session keys are `agent:<agentId>:…`. */
|
|
153
|
+
readonly agentId: string;
|
|
154
|
+
/** Start a run. Resolves when the run has been accepted, not when it ends. */
|
|
155
|
+
send(input: AgentChatSendInput): Promise<void>;
|
|
156
|
+
/** Stop the run in flight. Best-effort; local state resets either way. */
|
|
157
|
+
abort(params?: {
|
|
158
|
+
runId?: string;
|
|
159
|
+
sessionKey?: string;
|
|
160
|
+
}): Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* Answer an approval. `context` is the `{ runId, sessionKey }` the request
|
|
163
|
+
* carried, not the run in flight — see `AgentApprovalRequest`. Rejects when
|
|
164
|
+
* the server refuses the decision, so the caller can put the card back.
|
|
165
|
+
*/
|
|
166
|
+
resolveApproval(id: string, decision: AgentApprovalDecision, context: AgentApprovalContext): Promise<void>;
|
|
167
|
+
/** `sessions.list` and `chat.history`. */
|
|
168
|
+
request<T = unknown>(method: string, params: Record<string, unknown>): Promise<T>;
|
|
169
|
+
/** Chat deltas, finals and errors. Returns an unsubscribe. */
|
|
170
|
+
onChat(sink: (event: AgentChatEvent) => void): () => void;
|
|
171
|
+
/** Tool starts, arguments, progress and results. Returns an unsubscribe. */
|
|
172
|
+
onTool(sink: (event: AgentToolEvent) => void): () => void;
|
|
173
|
+
/** Approval requests and resolutions. Returns an unsubscribe. */
|
|
174
|
+
onApproval(sink: (event: AgentApprovalEvent) => void): () => void;
|
|
175
|
+
}
|
|
176
|
+
/** What `sessions.list` answers. */
|
|
177
|
+
export interface AgentSessionsListResponse {
|
|
178
|
+
sessions?: AgentSession[];
|
|
179
|
+
}
|
|
180
|
+
/** What `chat.history` answers. `messages` are raw transcript records. */
|
|
181
|
+
export interface AgentChatHistoryResponse {
|
|
182
|
+
messages?: unknown[];
|
|
183
|
+
hasMore?: boolean;
|
|
184
|
+
total?: number;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/agent-chat/types.ts
|
|
3
|
+
/** The title to show for a session row. */
|
|
4
|
+
function sessionTitle(session) {
|
|
5
|
+
for (const candidate of [
|
|
6
|
+
session.label,
|
|
7
|
+
session.autoTitle,
|
|
8
|
+
session.derivedTitle,
|
|
9
|
+
session.displayName,
|
|
10
|
+
session.lastMessagePreview
|
|
11
|
+
]) if (typeof candidate === "string" && candidate.trim()) return candidate.trim();
|
|
12
|
+
return "New conversation";
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { sessionTitle };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../src/agent-chat/types.ts"],"sourcesContent":["/**\n * The transport contract `AgentChat` is written against.\n *\n * Everything the chat surface needs from a server is behind `AgentChatClient`,\n * and nothing in the interface names AG-UI, HTTP or React. That is what makes\n * the surface testable — `test/agent-chat.test.tsx` drives it from a fake that\n * replays scripted event sequences — and it is what keeps `@ag-ui/client` out\n * of every module except `./agui-client.ts`.\n *\n * The three sinks are subscriptions rather than constructor callbacks so one\n * client can outlive several mounts of the component, which is what a pop-up\n * that collapses and reopens mid-run does.\n */\n\nimport type { A2UIChatBlock } from \"./a2ui-block\";\n\n/* ── what a session is ───────────────────────────────────────────────────── */\n\n/**\n * A row of `sessions.list`.\n *\n * Titles fall back in a fixed order (§17.2 of the extension how-to):\n * `label`, `autoTitle`, `derivedTitle`, `displayName`, `lastMessagePreview`,\n * then \"New conversation\". `sessionTitle` below is that order, in one place.\n */\nexport interface AgentSession {\n /** The session key, verbatim — this is also the AG-UI `threadId`. */\n key: string;\n label?: string | null;\n autoTitle?: string | null;\n derivedTitle?: string | null;\n displayName?: string | null;\n lastMessagePreview?: string | null;\n updatedAt?: number | null;\n}\n\n/** The title to show for a session row. */\nexport function sessionTitle(session: AgentSession): string {\n for (const candidate of [\n session.label,\n session.autoTitle,\n session.derivedTitle,\n session.displayName,\n session.lastMessagePreview,\n ]) {\n if (typeof candidate === \"string\" && candidate.trim()) {\n return candidate.trim();\n }\n }\n return \"New conversation\";\n}\n\n/* ── what a message is ───────────────────────────────────────────────────── */\n\nexport interface AgentChatMessage {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n timestamp: number;\n attachments?: string[];\n /** The chain of thought behind this turn, collapsed by default. */\n thinkingText?: string;\n isError?: boolean;\n /** True when the wire record was a tool call or a tool result; hidden. */\n isToolMessage?: boolean;\n /**\n * The run that produced this message. Absent on anything loaded from\n * history. A `final` folds into the previous bubble only when the two share\n * this value, so one run's answer cannot be rewritten by the next one's.\n */\n runId?: string;\n /** UI the agent drew, carried outside the prose. */\n a2ui?: A2UIChatBlock[];\n}\n\n/* ── the three sinks ─────────────────────────────────────────────────────── */\n\n/**\n * One chat event. The same `{ state: \"delta\" | \"final\" | \"error\" }` shape\n * cortenacore broadcasts, so a client for a different transport has a\n * documented target to hit.\n *\n * `delta` carries a SNAPSHOT, not an increment: every delta repeats all the\n * text, all the thinking and every A2UI block seen so far. Two transports that\n * disagree about this leave the store in different states for the same run.\n */\nexport interface AgentChatEvent {\n runId: string;\n sessionKey: string;\n seq: number;\n state: \"delta\" | \"final\" | \"error\";\n message?: {\n role?: string;\n content?: Array<{ type: string; text?: string; thinking?: string; a2ui?: unknown }>;\n timestamp?: number;\n };\n errorMessage?: string;\n}\n\n/** One step of the tool strip. */\nexport interface AgentToolEvent {\n event: \"tool.start\" | \"tool.args\" | \"tool.progress\" | \"tool.complete\" | \"tool.error\";\n data: {\n id?: string;\n name?: string;\n /** `tool.args`: the call's input, parsed when it was valid JSON. */\n input?: unknown;\n output?: unknown;\n startedAt?: number;\n completedAt?: number;\n };\n}\n\n/** Decisions cortenacore accepts on `POST /agui/approval`. */\nexport type AgentApprovalDecision = \"allow-once\" | \"allow-always\" | \"deny\";\n\nexport interface AgentApprovalRequest {\n id: string;\n /** The command, or the tool, the approval is about. */\n toolName: string;\n args: Record<string, unknown>;\n timestamp: number;\n /**\n * The run and the session this request arrived on, captured when it arrived.\n *\n * A decision is bound to them rather than resolved against whatever run\n * happens to be in flight when the user presses a button. The two come\n * apart routinely: an approval pauses a run, the user opens another session\n * in the drawer or starts a second turn, and the client's idea of \"the\n * current thread\" has moved on. Answering \"allow\" against the wrong thread\n * is a command approved in a conversation nobody was asked about.\n */\n runId: string;\n sessionKey: string;\n /** Set when a decision was made and the server would not take it. */\n error?: string;\n}\n\n/** Where an approval came from, and therefore where the decision must go. */\nexport interface AgentApprovalContext {\n runId: string;\n sessionKey: string;\n}\n\nexport type AgentApprovalEvent =\n | { type: \"request\"; request: AgentApprovalRequest }\n /** `decision: null` is an expiry. It is treated as a denial. */\n | { type: \"resolved\"; id: string; decision: AgentApprovalDecision | null };\n\n/* ── the client ──────────────────────────────────────────────────────────── */\n\nexport interface AgentChatSendInput {\n /** The session key to run in. `threadId` is this value verbatim. */\n sessionKey: string;\n /** What the user typed. Empty for an action sent by rendered UI. */\n message: string;\n /** The run id; also the idempotency key. */\n runId: string;\n attachments?: unknown[];\n thinking?: \"low\" | \"medium\" | \"high\";\n model?: { provider: string; model: string };\n /** A click inside an A2UI surface. Never a sentence the user appears to type. */\n a2uiAction?: { surfaceId: string; componentId: string; name: string; payload: Record<string, unknown> };\n /** The same, from inside a rendered MCP App. */\n mcpAppAction?: Record<string, unknown>;\n}\n\n/**\n * Everything `AgentChat` needs from a server.\n *\n * `request` is deliberately a method name plus params rather than a URL: the\n * two calls the chat makes — `sessions.list` and `chat.history` — are control\n * plane methods, and a host that proxies them through its own gateway chooses\n * its own paths.\n */\nexport interface AgentChatClient {\n /** The agent this client talks to. Session keys are `agent:<agentId>:…`. */\n readonly agentId: string;\n /** Start a run. Resolves when the run has been accepted, not when it ends. */\n send(input: AgentChatSendInput): Promise<void>;\n /** Stop the run in flight. Best-effort; local state resets either way. */\n abort(params?: { runId?: string; sessionKey?: string }): Promise<void>;\n /**\n * Answer an approval. `context` is the `{ runId, sessionKey }` the request\n * carried, not the run in flight — see `AgentApprovalRequest`. Rejects when\n * the server refuses the decision, so the caller can put the card back.\n */\n resolveApproval(\n id: string,\n decision: AgentApprovalDecision,\n context: AgentApprovalContext,\n ): Promise<void>;\n /** `sessions.list` and `chat.history`. */\n request<T = unknown>(method: string, params: Record<string, unknown>): Promise<T>;\n /** Chat deltas, finals and errors. Returns an unsubscribe. */\n onChat(sink: (event: AgentChatEvent) => void): () => void;\n /** Tool starts, arguments, progress and results. Returns an unsubscribe. */\n onTool(sink: (event: AgentToolEvent) => void): () => void;\n /** Approval requests and resolutions. Returns an unsubscribe. */\n onApproval(sink: (event: AgentApprovalEvent) => void): () => void;\n}\n\n/** What `sessions.list` answers. */\nexport interface AgentSessionsListResponse {\n sessions?: AgentSession[];\n}\n\n/** What `chat.history` answers. `messages` are raw transcript records. */\nexport interface AgentChatHistoryResponse {\n messages?: unknown[];\n hasMore?: boolean;\n total?: number;\n}\n"],"mappings":";;;AAqCA,SAAgB,aAAa,SAA+B;CAC1D,KAAK,MAAM,aAAa;EACtB,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;CACV,GACE,IAAI,OAAO,cAAc,YAAY,UAAU,KAAK,GAClD,OAAO,UAAU,KAAK;CAG1B,OAAO;AACT"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { A2UIChatBlock, extractA2UIBlocks, foldA2UIBlocks, readA2UIBlock } from "./agent-chat/a2ui-block.js";
|
|
3
|
+
import { AgentApprovalDecision, AgentApprovalEvent, AgentApprovalRequest, AgentChatClient, AgentChatEvent, AgentChatHistoryResponse, AgentChatMessage, AgentChatSendInput, AgentSession, AgentSessionsListResponse, AgentToolEvent, sessionTitle } from "./agent-chat/types.js";
|
|
4
|
+
import { AGUI_CUSTOM_NAMES, AgentChatBridge, AgentChatBridgeOptions, AguiIncomingEvent } from "./agent-chat/bridge.js";
|
|
5
|
+
import { CreateAguiAgentChatClientOptions, createAguiAgentChatClient } from "./agent-chat/agui-client.js";
|
|
6
|
+
import { isHiddenMessage, isProvisionalSessionKey, isSessionOfAgent, mintSessionKey, parseHistoryMessages, readStoredSessionKey, sessionStorageKey, viewStorageKey, wouldShrinkHistory, writeStoredSessionKey } from "./agent-chat/session.js";
|
|
7
|
+
import { AgentChatState, AgentToolEntry, UseAgentChatOptions, UseAgentChatResult, useAgentChat } from "./agent-chat/store.js";
|
|
8
|
+
import { AgentA2UIBlocks, AgentA2UIBlocksProps, AgentApprovalCard, AgentApprovalCardProps, AgentChat, AgentChatProps, AgentMessageBubble, AgentMessageBubbleProps, AgentSessionDrawer, AgentSessionDrawerProps, AgentStreamingBubble, AgentStreamingBubbleProps, AgentThinking, AgentThinkingProps, AgentToolCard, AgentToolCardProps, AgentToolRenderContext, readMcpAppUri, shouldStickToBottom } from "./components/agent-chat.js";
|
|
9
|
+
import { AgentChatPopup, AgentChatPopupExtension, AgentChatPopupProps, AgentChatPopupState } from "./components/agent-chat-popup.js";
|
|
10
|
+
export { type A2UIChatBlock, AGUI_CUSTOM_NAMES, AgentA2UIBlocks, type AgentA2UIBlocksProps, AgentApprovalCard, type AgentApprovalCardProps, type AgentApprovalDecision, type AgentApprovalEvent, type AgentApprovalRequest, AgentChat, AgentChatBridge, type AgentChatBridgeOptions, type AgentChatClient, type AgentChatEvent, type AgentChatHistoryResponse, type AgentChatMessage, AgentChatPopup, type AgentChatPopupExtension, type AgentChatPopupProps, type AgentChatPopupState, type AgentChatProps, type AgentChatSendInput, type AgentChatState, AgentMessageBubble, type AgentMessageBubbleProps, type AgentSession, AgentSessionDrawer, type AgentSessionDrawerProps, type AgentSessionsListResponse, AgentStreamingBubble, type AgentStreamingBubbleProps, AgentThinking, type AgentThinkingProps, AgentToolCard, type AgentToolCardProps, type AgentToolEntry, type AgentToolEvent, type AgentToolRenderContext, type AguiIncomingEvent, type CreateAguiAgentChatClientOptions, type UseAgentChatOptions, type UseAgentChatResult, createAguiAgentChatClient, extractA2UIBlocks, foldA2UIBlocks, isHiddenMessage, isProvisionalSessionKey, isSessionOfAgent, mintSessionKey, parseHistoryMessages, readA2UIBlock, readMcpAppUri, readStoredSessionKey, sessionStorageKey, sessionTitle, shouldStickToBottom, useAgentChat, viewStorageKey, wouldShrinkHistory, writeStoredSessionKey };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { extractA2UIBlocks, foldA2UIBlocks, readA2UIBlock } from "./agent-chat/a2ui-block.js";
|
|
3
|
+
import { AGUI_CUSTOM_NAMES, AgentChatBridge } from "./agent-chat/bridge.js";
|
|
4
|
+
import { createAguiAgentChatClient } from "./agent-chat/agui-client.js";
|
|
5
|
+
import { isHiddenMessage, isProvisionalSessionKey, isSessionOfAgent, mintSessionKey, parseHistoryMessages, readStoredSessionKey, sessionStorageKey, viewStorageKey, wouldShrinkHistory, writeStoredSessionKey } from "./agent-chat/session.js";
|
|
6
|
+
import { useAgentChat } from "./agent-chat/store.js";
|
|
7
|
+
import { sessionTitle } from "./agent-chat/types.js";
|
|
8
|
+
import { AgentA2UIBlocks, AgentApprovalCard, AgentChat, AgentMessageBubble, AgentSessionDrawer, AgentStreamingBubble, AgentThinking, AgentToolCard, readMcpAppUri, shouldStickToBottom } from "./components/agent-chat.js";
|
|
9
|
+
import { AgentChatPopup } from "./components/agent-chat-popup.js";
|
|
10
|
+
export { AGUI_CUSTOM_NAMES, AgentA2UIBlocks, AgentApprovalCard, AgentChat, AgentChatBridge, AgentChatPopup, AgentMessageBubble, AgentSessionDrawer, AgentStreamingBubble, AgentThinking, AgentToolCard, createAguiAgentChatClient, extractA2UIBlocks, foldA2UIBlocks, isHiddenMessage, isProvisionalSessionKey, isSessionOfAgent, mintSessionKey, parseHistoryMessages, readA2UIBlock, readMcpAppUri, readStoredSessionKey, sessionStorageKey, sessionTitle, shouldStickToBottom, useAgentChat, viewStorageKey, wouldShrinkHistory, writeStoredSessionKey };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { AdminPermissionsApi, AdminRoute } from "./types.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
//#region src/components/admin-permissions/admin-permissions.d.ts
|
|
5
|
+
export interface AdminPermissionsProps extends Omit<React.ComponentProps<"div">, "children" | "title"> {
|
|
6
|
+
/** Everything the screen reads and writes. The extension's, entirely. */
|
|
7
|
+
api: AdminPermissionsApi;
|
|
8
|
+
/**
|
|
9
|
+
* The screen to show. Pass it and the host's router owns the URL; omit it
|
|
10
|
+
* and the composition keeps the current screen in its own state.
|
|
11
|
+
*/
|
|
12
|
+
route?: AdminRoute;
|
|
13
|
+
/** The screen to open with when `route` is not passed. @default "members" */
|
|
14
|
+
defaultRoute?: AdminRoute;
|
|
15
|
+
/**
|
|
16
|
+
* Fired when the administrator picks another screen. Always fired, whether
|
|
17
|
+
* or not `route` is controlled, so a host can push the URL without also
|
|
18
|
+
* having to hold the state.
|
|
19
|
+
*/
|
|
20
|
+
onNavigate?: (route: AdminRoute) => void;
|
|
21
|
+
/** `false` hides the title block, for a host that renders its own. */
|
|
22
|
+
header?: boolean;
|
|
23
|
+
title?: React.ReactNode;
|
|
24
|
+
description?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* AdminPermissions — the consistent admin screen for every Cortena extension
|
|
28
|
+
* (§13, DESIGN-D11 as amended by DESIGN-D22; audit rules P-14, P-42, P-43).
|
|
29
|
+
*
|
|
30
|
+
* Four screens behind `admin/*`: `/admin/members`, `/admin/roles`,
|
|
31
|
+
* `/admin/licence`, `/admin/permissions`. It renders whatever roles and
|
|
32
|
+
* actions the extension declares and **defines none of its own** — there is
|
|
33
|
+
* not one role name, action name or grant in this folder. That is exactly what
|
|
34
|
+
* lets a verification workflow and a work tracker have completely different
|
|
35
|
+
* role models and still look like one product.
|
|
36
|
+
*
|
|
37
|
+
* ```tsx
|
|
38
|
+
* <Route path="admin/*" element={
|
|
39
|
+
* <AdminPermissions
|
|
40
|
+
* api={adminApi}
|
|
41
|
+
* route={screenFromPath(location.pathname)}
|
|
42
|
+
* onNavigate={(next) => navigate(`/admin/${next}`)}
|
|
43
|
+
* />
|
|
44
|
+
* } />
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* **Router-agnostic, by a `route` prop plus `onNavigate`.** The component
|
|
48
|
+
* imports no router and never touches `history` or `location`. Those four
|
|
49
|
+
* paths are real URLs — deep-linkable, bookmarkable, and answerable to the
|
|
50
|
+
* back button — and only the host's router can own a URL; a component that
|
|
51
|
+
* pushed history itself would fight whichever router the extension chose.
|
|
52
|
+
* `onNavigate` reports the intent, the host turns it into a navigation, and
|
|
53
|
+
* `route` comes back as the answer.
|
|
54
|
+
*
|
|
55
|
+
* An internal fallback exists for the case with no router in it — the guide,
|
|
56
|
+
* a test, a small app: omit `route` and the composition keeps the screen in
|
|
57
|
+
* its own state. `onNavigate` still fires, so a host can start uncontrolled
|
|
58
|
+
* and add a router later without changing anything else.
|
|
59
|
+
*
|
|
60
|
+
* The navigation is a real tablist rather than a row of links, because the
|
|
61
|
+
* choice is between panels of one screen; the host is free to hide it with
|
|
62
|
+
* `header={false}` and drive `route` from its own chrome.
|
|
63
|
+
*/
|
|
64
|
+
export declare function AdminPermissions({ api, route, defaultRoute, onNavigate, header, title, description, className, ...props }: AdminPermissionsProps): React.JSX.Element;
|
|
65
|
+
//#endregion
|
|
66
|
+
//# sourceMappingURL=admin-permissions.d.ts.map
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../../lib/cn.js";
|
|
4
|
+
import { PageHeader } from "../page-header.js";
|
|
5
|
+
import { Tabs, TabsList, TabsTrigger } from "../tabs.js";
|
|
6
|
+
import { ADMIN_ROUTES, ADMIN_ROUTE_LABELS } from "./types.js";
|
|
7
|
+
import { AdminPermissionsProvider } from "./context.js";
|
|
8
|
+
import { AdminLicenceScreen } from "./licence.js";
|
|
9
|
+
import { AdminMatrix } from "./matrix.js";
|
|
10
|
+
import { AdminMembers } from "./members.js";
|
|
11
|
+
import { AdminRoles } from "./roles.js";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import * as React from "react";
|
|
14
|
+
//#region src/components/admin-permissions/admin-permissions.tsx
|
|
15
|
+
/**
|
|
16
|
+
* AdminPermissions — the consistent admin screen for every Cortena extension
|
|
17
|
+
* (§13, DESIGN-D11 as amended by DESIGN-D22; audit rules P-14, P-42, P-43).
|
|
18
|
+
*
|
|
19
|
+
* Four screens behind `admin/*`: `/admin/members`, `/admin/roles`,
|
|
20
|
+
* `/admin/licence`, `/admin/permissions`. It renders whatever roles and
|
|
21
|
+
* actions the extension declares and **defines none of its own** — there is
|
|
22
|
+
* not one role name, action name or grant in this folder. That is exactly what
|
|
23
|
+
* lets a verification workflow and a work tracker have completely different
|
|
24
|
+
* role models and still look like one product.
|
|
25
|
+
*
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <Route path="admin/*" element={
|
|
28
|
+
* <AdminPermissions
|
|
29
|
+
* api={adminApi}
|
|
30
|
+
* route={screenFromPath(location.pathname)}
|
|
31
|
+
* onNavigate={(next) => navigate(`/admin/${next}`)}
|
|
32
|
+
* />
|
|
33
|
+
* } />
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* **Router-agnostic, by a `route` prop plus `onNavigate`.** The component
|
|
37
|
+
* imports no router and never touches `history` or `location`. Those four
|
|
38
|
+
* paths are real URLs — deep-linkable, bookmarkable, and answerable to the
|
|
39
|
+
* back button — and only the host's router can own a URL; a component that
|
|
40
|
+
* pushed history itself would fight whichever router the extension chose.
|
|
41
|
+
* `onNavigate` reports the intent, the host turns it into a navigation, and
|
|
42
|
+
* `route` comes back as the answer.
|
|
43
|
+
*
|
|
44
|
+
* An internal fallback exists for the case with no router in it — the guide,
|
|
45
|
+
* a test, a small app: omit `route` and the composition keeps the screen in
|
|
46
|
+
* its own state. `onNavigate` still fires, so a host can start uncontrolled
|
|
47
|
+
* and add a router later without changing anything else.
|
|
48
|
+
*
|
|
49
|
+
* The navigation is a real tablist rather than a row of links, because the
|
|
50
|
+
* choice is between panels of one screen; the host is free to hide it with
|
|
51
|
+
* `header={false}` and drive `route` from its own chrome.
|
|
52
|
+
*/
|
|
53
|
+
function AdminPermissions({ api, route, defaultRoute = "members", onNavigate, header = true, title = "Administration", description = "Who is in this organisation, what your roles let them do, and the licence behind it.", className, ...props }) {
|
|
54
|
+
const [internal, setInternal] = React.useState(defaultRoute);
|
|
55
|
+
const active = route ?? internal;
|
|
56
|
+
const go = (next) => {
|
|
57
|
+
if (route === void 0) setInternal(next);
|
|
58
|
+
onNavigate?.(next);
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ jsx(AdminPermissionsProvider, {
|
|
61
|
+
api,
|
|
62
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
63
|
+
"data-slot": "admin-permissions",
|
|
64
|
+
"data-route": active,
|
|
65
|
+
className: cn("flex flex-col gap-5", className),
|
|
66
|
+
...props,
|
|
67
|
+
children: [
|
|
68
|
+
header ? /* @__PURE__ */ jsx(PageHeader, {
|
|
69
|
+
title,
|
|
70
|
+
description
|
|
71
|
+
}) : null,
|
|
72
|
+
/* @__PURE__ */ jsx(Tabs, {
|
|
73
|
+
value: active,
|
|
74
|
+
onValueChange: (value) => go(value),
|
|
75
|
+
"data-slot": "admin-permissions-nav",
|
|
76
|
+
children: /* @__PURE__ */ jsx(TabsList, {
|
|
77
|
+
"aria-label": "Administration screens",
|
|
78
|
+
children: ADMIN_ROUTES.map((item) => /* @__PURE__ */ jsx(TabsTrigger, {
|
|
79
|
+
value: item,
|
|
80
|
+
"data-route": item,
|
|
81
|
+
children: ADMIN_ROUTE_LABELS[item]
|
|
82
|
+
}, item))
|
|
83
|
+
})
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ jsxs("div", {
|
|
86
|
+
"data-slot": "admin-permissions-screen",
|
|
87
|
+
children: [
|
|
88
|
+
active === "members" ? /* @__PURE__ */ jsx(AdminMembers, {}) : null,
|
|
89
|
+
active === "roles" ? /* @__PURE__ */ jsx(AdminRoles, {}) : null,
|
|
90
|
+
active === "licence" ? /* @__PURE__ */ jsx(AdminLicenceScreen, {}) : null,
|
|
91
|
+
active === "permissions" ? /* @__PURE__ */ jsx(AdminMatrix, {}) : null
|
|
92
|
+
]
|
|
93
|
+
})
|
|
94
|
+
]
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { AdminPermissions };
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=admin-permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-permissions.js","names":[],"sources":["../../../src/components/admin-permissions/admin-permissions.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { PageHeader } from \"@/components/page-header\";\nimport { Tabs, TabsList, TabsTrigger } from \"@/components/tabs\";\nimport { cn } from \"@/lib/cn\";\nimport { AdminPermissionsProvider } from \"./context\";\nimport { AdminLicenceScreen } from \"./licence\";\nimport { AdminMatrix } from \"./matrix\";\nimport { AdminMembers } from \"./members\";\nimport { AdminRoles } from \"./roles\";\nimport {\n ADMIN_ROUTES,\n ADMIN_ROUTE_LABELS,\n type AdminPermissionsApi,\n type AdminRoute,\n} from \"./types\";\n\nexport interface AdminPermissionsProps\n extends Omit<React.ComponentProps<\"div\">, \"children\" | \"title\"> {\n /** Everything the screen reads and writes. The extension's, entirely. */\n api: AdminPermissionsApi;\n /**\n * The screen to show. Pass it and the host's router owns the URL; omit it\n * and the composition keeps the current screen in its own state.\n */\n route?: AdminRoute;\n /** The screen to open with when `route` is not passed. @default \"members\" */\n defaultRoute?: AdminRoute;\n /**\n * Fired when the administrator picks another screen. Always fired, whether\n * or not `route` is controlled, so a host can push the URL without also\n * having to hold the state.\n */\n onNavigate?: (route: AdminRoute) => void;\n /** `false` hides the title block, for a host that renders its own. */\n header?: boolean;\n title?: React.ReactNode;\n description?: React.ReactNode;\n}\n\n/**\n * AdminPermissions — the consistent admin screen for every Cortena extension\n * (§13, DESIGN-D11 as amended by DESIGN-D22; audit rules P-14, P-42, P-43).\n *\n * Four screens behind `admin/*`: `/admin/members`, `/admin/roles`,\n * `/admin/licence`, `/admin/permissions`. It renders whatever roles and\n * actions the extension declares and **defines none of its own** — there is\n * not one role name, action name or grant in this folder. That is exactly what\n * lets a verification workflow and a work tracker have completely different\n * role models and still look like one product.\n *\n * ```tsx\n * <Route path=\"admin/*\" element={\n * <AdminPermissions\n * api={adminApi}\n * route={screenFromPath(location.pathname)}\n * onNavigate={(next) => navigate(`/admin/${next}`)}\n * />\n * } />\n * ```\n *\n * **Router-agnostic, by a `route` prop plus `onNavigate`.** The component\n * imports no router and never touches `history` or `location`. Those four\n * paths are real URLs — deep-linkable, bookmarkable, and answerable to the\n * back button — and only the host's router can own a URL; a component that\n * pushed history itself would fight whichever router the extension chose.\n * `onNavigate` reports the intent, the host turns it into a navigation, and\n * `route` comes back as the answer.\n *\n * An internal fallback exists for the case with no router in it — the guide,\n * a test, a small app: omit `route` and the composition keeps the screen in\n * its own state. `onNavigate` still fires, so a host can start uncontrolled\n * and add a router later without changing anything else.\n *\n * The navigation is a real tablist rather than a row of links, because the\n * choice is between panels of one screen; the host is free to hide it with\n * `header={false}` and drive `route` from its own chrome.\n */\nexport function AdminPermissions({\n api,\n route,\n defaultRoute = \"members\",\n onNavigate,\n header = true,\n title = \"Administration\",\n description = \"Who is in this organisation, what your roles let them do, and the licence behind it.\",\n className,\n ...props\n}: AdminPermissionsProps) {\n const [internal, setInternal] = React.useState<AdminRoute>(defaultRoute);\n const active = route ?? internal;\n\n const go = (next: AdminRoute) => {\n if (route === undefined) setInternal(next);\n onNavigate?.(next);\n };\n\n return (\n <AdminPermissionsProvider api={api}>\n <div\n data-slot=\"admin-permissions\"\n data-route={active}\n className={cn(\"flex flex-col gap-5\", className)}\n {...props}\n >\n {header ? <PageHeader title={title} description={description} /> : null}\n <Tabs\n value={active}\n onValueChange={(value) => go(value as AdminRoute)}\n data-slot=\"admin-permissions-nav\"\n >\n <TabsList aria-label=\"Administration screens\">\n {ADMIN_ROUTES.map((item) => (\n <TabsTrigger key={item} value={item} data-route={item}>\n {ADMIN_ROUTE_LABELS[item]}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n <div data-slot=\"admin-permissions-screen\">\n {active === \"members\" ? <AdminMembers /> : null}\n {active === \"roles\" ? <AdminRoles /> : null}\n {active === \"licence\" ? <AdminLicenceScreen /> : null}\n {active === \"permissions\" ? <AdminMatrix /> : null}\n </div>\n </div>\n </AdminPermissionsProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+EA,SAAgB,iBAAiB,EAC/B,KACA,OACA,eAAe,WACf,YACA,SAAS,MACT,QAAQ,kBACR,cAAc,wFACd,WACA,GAAG,SACqB;CACxB,MAAM,CAAC,UAAU,eAAe,MAAM,SAAqB,YAAY;CACvE,MAAM,SAAS,SAAS;CAExB,MAAM,MAAM,SAAqB;EAC/B,IAAI,UAAU,KAAA,GAAW,YAAY,IAAI;EACzC,aAAa,IAAI;CACnB;CAEA,OACE,oBAAC,0BAAD;EAA+B;EAC7B,UAAA,qBAAC,OAAD;GACE,aAAU;GACV,cAAY;GACZ,WAAW,GAAG,uBAAuB,SAAS;GAC9C,GAAI;GAJN,UAAA;IAMG,SAAS,oBAAC,YAAD;KAAmB;KAAoB;IAAc,CAAA,IAAI;IACnE,oBAAC,MAAD;KACE,OAAO;KACP,gBAAgB,UAAU,GAAG,KAAmB;KAChD,aAAU;KAEV,UAAA,oBAAC,UAAD;MAAU,cAAW;MAClB,UAAA,aAAa,KAAK,SACjB,oBAAC,aAAD;OAAwB,OAAO;OAAM,cAAY;OAC9C,UAAA,mBAAmB;MACT,GAFK,IAEL,CACd;KACO,CAAA;IACN,CAAA;IACN,qBAAC,OAAD;KAAK,aAAU;KAAf,UAAA;MACG,WAAW,YAAY,oBAAC,cAAD,CAAe,CAAA,IAAI;MAC1C,WAAW,UAAU,oBAAC,YAAD,CAAa,CAAA,IAAI;MACtC,WAAW,YAAY,oBAAC,oBAAD,CAAqB,CAAA,IAAI;MAChD,WAAW,gBAAgB,oBAAC,aAAD,CAAc,CAAA,IAAI;KAC3C;;GACF;;CACmB,CAAA;AAE9B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { AdminGroupRow, AdminMemberRow, AdminPermissionMatrix, AdminPermissionsApi, AdminPrincipal } from "./types.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
//#region src/components/admin-permissions/context.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* One async read, with the abort a screen needs when it unmounts mid-flight.
|
|
7
|
+
* Small on purpose: the composition fetches three documents of its own (the
|
|
8
|
+
* matrix, the licence, the groups) and the members list is a DataTable on the
|
|
9
|
+
* server source, which owns its own fetching.
|
|
10
|
+
*/
|
|
11
|
+
export interface Resource<T> {
|
|
12
|
+
data: T | null;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
loading: boolean;
|
|
15
|
+
reload: () => void;
|
|
16
|
+
}
|
|
17
|
+
export interface AdminPermissionsContextValue {
|
|
18
|
+
api: AdminPermissionsApi;
|
|
19
|
+
/** The declared vocabulary and matrix. `null` until it loads, or when it failed. */
|
|
20
|
+
matrix: AdminPermissionMatrix | null;
|
|
21
|
+
matrixState: Resource<AdminPermissionMatrix>;
|
|
22
|
+
/** Write the matrix. Optimistic: the grid shows the new grants before the PUT lands. */
|
|
23
|
+
saveMatrix: (next: AdminPermissionMatrix) => Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* True when the API offers groups *and* the org actually has some. Every
|
|
26
|
+
* group affordance is hidden otherwise.
|
|
27
|
+
*/
|
|
28
|
+
hasGroups: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* True while the answer to `hasGroups` is not known yet — the list is in
|
|
31
|
+
* flight, or it failed.
|
|
32
|
+
*
|
|
33
|
+
* `hasGroups === false` used to mean three different things at once: this
|
|
34
|
+
* API has no groups, this org has none, and `groups.list()` blew up. The
|
|
35
|
+
* third renders identically to the second, so a member who inherits
|
|
36
|
+
* "reviewer" from Legal is shown with no roles at all and an administrator
|
|
37
|
+
* reads that as a fact about the member. A failure has to look like a
|
|
38
|
+
* failure.
|
|
39
|
+
*/
|
|
40
|
+
groupsUnknown: boolean;
|
|
41
|
+
groups: AdminGroupRow[];
|
|
42
|
+
groupsState: Resource<{
|
|
43
|
+
rows: AdminGroupRow[];
|
|
44
|
+
roles: Record<string, string[]>;
|
|
45
|
+
}>;
|
|
46
|
+
/** group id → the roles it carries, including any not yet confirmed by the server. */
|
|
47
|
+
groupRoles: Readonly<Record<string, string[]>>;
|
|
48
|
+
/** Bumped after a successful assignment, so the members source refetches. */
|
|
49
|
+
revision: number;
|
|
50
|
+
/** Direct member roles written since the last refetch, by user id. */
|
|
51
|
+
memberRoles: ReadonlyMap<string, string[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Assign a principal's direct roles. Optimistic: the screen shows the new
|
|
54
|
+
* roles immediately and puts the old ones back if the call rejects, then
|
|
55
|
+
* rethrows so the caller can show the envelope's message.
|
|
56
|
+
*/
|
|
57
|
+
assignRoles: (principal: AdminPrincipal, roles: string[]) => Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Hand back rows the server has just answered with, so any optimistic entry
|
|
60
|
+
* the server now agrees with can be dropped. See `memberRoles`.
|
|
61
|
+
*/
|
|
62
|
+
reconcileMembers: (rows: readonly AdminMemberRow[]) => void;
|
|
63
|
+
}
|
|
64
|
+
export declare function useAdminPermissions(): AdminPermissionsContextValue;
|
|
65
|
+
export declare function AdminPermissionsProvider({ api, children }: {
|
|
66
|
+
api: AdminPermissionsApi;
|
|
67
|
+
children: React.ReactNode;
|
|
68
|
+
}): React.JSX.Element;
|
|
69
|
+
//#endregion
|
|
70
|
+
//# sourceMappingURL=context.d.ts.map
|