@open-mercato/ui 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3045.b4b3320cc2
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +2 -1
- package/__integration__/TC-AI-UI-003-aichat-registry.spec.tsx +204 -0
- package/dist/ai/AiAssistantLauncher.js +596 -0
- package/dist/ai/AiAssistantLauncher.js.map +7 -0
- package/dist/ai/AiChat.js +1092 -0
- package/dist/ai/AiChat.js.map +7 -0
- package/dist/ai/AiChatSessions.js +297 -0
- package/dist/ai/AiChatSessions.js.map +7 -0
- package/dist/ai/AiDock.js +347 -0
- package/dist/ai/AiDock.js.map +7 -0
- package/dist/ai/AiMessageContent.js +369 -0
- package/dist/ai/AiMessageContent.js.map +7 -0
- package/dist/ai/ChatPaneTabs.js +251 -0
- package/dist/ai/ChatPaneTabs.js.map +7 -0
- package/dist/ai/index.js +115 -0
- package/dist/ai/index.js.map +7 -0
- package/dist/ai/parts/ConfirmationCard.js +211 -0
- package/dist/ai/parts/ConfirmationCard.js.map +7 -0
- package/dist/ai/parts/FieldDiffCard.js +119 -0
- package/dist/ai/parts/FieldDiffCard.js.map +7 -0
- package/dist/ai/parts/MutationPreviewCard.js +224 -0
- package/dist/ai/parts/MutationPreviewCard.js.map +7 -0
- package/dist/ai/parts/MutationResultCard.js +240 -0
- package/dist/ai/parts/MutationResultCard.js.map +7 -0
- package/dist/ai/parts/approval-cards-map.js +15 -0
- package/dist/ai/parts/approval-cards-map.js.map +7 -0
- package/dist/ai/parts/index.js +24 -0
- package/dist/ai/parts/index.js.map +7 -0
- package/dist/ai/parts/pending-action-api.js +60 -0
- package/dist/ai/parts/pending-action-api.js.map +7 -0
- package/dist/ai/parts/types.js +1 -0
- package/dist/ai/parts/types.js.map +7 -0
- package/dist/ai/parts/useAiPendingActionPolling.js +126 -0
- package/dist/ai/parts/useAiPendingActionPolling.js.map +7 -0
- package/dist/ai/records/ActivityCard.js +83 -0
- package/dist/ai/records/ActivityCard.js.map +7 -0
- package/dist/ai/records/CompanyCard.js +81 -0
- package/dist/ai/records/CompanyCard.js.map +7 -0
- package/dist/ai/records/DealCard.js +76 -0
- package/dist/ai/records/DealCard.js.map +7 -0
- package/dist/ai/records/PersonCard.js +68 -0
- package/dist/ai/records/PersonCard.js.map +7 -0
- package/dist/ai/records/ProductCard.js +68 -0
- package/dist/ai/records/ProductCard.js.map +7 -0
- package/dist/ai/records/RecordCard.js +29 -0
- package/dist/ai/records/RecordCard.js.map +7 -0
- package/dist/ai/records/RecordCardShell.js +103 -0
- package/dist/ai/records/RecordCardShell.js.map +7 -0
- package/dist/ai/records/index.js +31 -0
- package/dist/ai/records/index.js.map +7 -0
- package/dist/ai/records/registry.js +51 -0
- package/dist/ai/records/registry.js.map +7 -0
- package/dist/ai/records/types.js +1 -0
- package/dist/ai/records/types.js.map +7 -0
- package/dist/ai/ui-part-registry.js +112 -0
- package/dist/ai/ui-part-registry.js.map +7 -0
- package/dist/ai/ui-part-slots.js +14 -0
- package/dist/ai/ui-part-slots.js.map +7 -0
- package/dist/ai/ui-parts/pending-phase3-placeholder.js +35 -0
- package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +7 -0
- package/dist/ai/upload-adapter.js +256 -0
- package/dist/ai/upload-adapter.js.map +7 -0
- package/dist/ai/useAiChat.js +549 -0
- package/dist/ai/useAiChat.js.map +7 -0
- package/dist/ai/useAiChatUpload.js +127 -0
- package/dist/ai/useAiChatUpload.js.map +7 -0
- package/dist/ai/useAiShortcuts.js +43 -0
- package/dist/ai/useAiShortcuts.js.map +7 -0
- package/dist/backend/AppShell.js +8 -4
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/BackendChromeProvider.js +2 -0
- package/dist/backend/BackendChromeProvider.js.map +2 -2
- package/dist/backend/DataTable.js +19 -2
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/FilterBar.js +19 -15
- package/dist/backend/FilterBar.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +31 -3
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/injection/spotIds.js +6 -0
- package/dist/backend/injection/spotIds.js.map +2 -2
- package/dist/backend/notifications/useNotificationEffect.js +38 -2
- package/dist/backend/notifications/useNotificationEffect.js.map +2 -2
- package/dist/index.js +1 -0
- package/dist/index.js.map +2 -2
- package/jest.config.cjs +7 -1
- package/jest.markdown-mock.tsx +7 -0
- package/package.json +10 -4
- package/src/ai/AiAssistantLauncher.tsx +805 -0
- package/src/ai/AiChat.tsx +1483 -0
- package/src/ai/AiChatSessions.tsx +429 -0
- package/src/ai/AiDock.tsx +505 -0
- package/src/ai/AiMessageContent.tsx +515 -0
- package/src/ai/ChatPaneTabs.tsx +310 -0
- package/src/ai/__tests__/AiChat.conversation.test.tsx +160 -0
- package/src/ai/__tests__/AiChat.debug.test.tsx +152 -0
- package/src/ai/__tests__/AiChat.registry.test.tsx +213 -0
- package/src/ai/__tests__/AiChat.test.tsx +257 -0
- package/src/ai/__tests__/AiDock.test.tsx +124 -0
- package/src/ai/__tests__/AiMessageContent.test.ts +111 -0
- package/src/ai/__tests__/ui-part-registry.test.ts +199 -0
- package/src/ai/__tests__/ui-part-slots.test.ts +43 -0
- package/src/ai/__tests__/upload-adapter.test.ts +213 -0
- package/src/ai/__tests__/useAiChatUpload.test.tsx +163 -0
- package/src/ai/__tests__/useAiShortcuts.test.tsx +100 -0
- package/src/ai/index.ts +125 -0
- package/src/ai/parts/ConfirmationCard.tsx +310 -0
- package/src/ai/parts/FieldDiffCard.tsx +173 -0
- package/src/ai/parts/MutationPreviewCard.tsx +302 -0
- package/src/ai/parts/MutationResultCard.tsx +360 -0
- package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +169 -0
- package/src/ai/parts/__tests__/FieldDiffCard.test.tsx +74 -0
- package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +177 -0
- package/src/ai/parts/__tests__/MutationResultCard.test.tsx +127 -0
- package/src/ai/parts/__tests__/useAiPendingActionPolling.test.tsx +151 -0
- package/src/ai/parts/approval-cards-map.ts +24 -0
- package/src/ai/parts/index.ts +27 -0
- package/src/ai/parts/pending-action-api.ts +123 -0
- package/src/ai/parts/types.ts +84 -0
- package/src/ai/parts/useAiPendingActionPolling.ts +210 -0
- package/src/ai/records/ActivityCard.tsx +102 -0
- package/src/ai/records/CompanyCard.tsx +89 -0
- package/src/ai/records/DealCard.tsx +85 -0
- package/src/ai/records/PersonCard.tsx +77 -0
- package/src/ai/records/ProductCard.tsx +83 -0
- package/src/ai/records/RecordCard.tsx +37 -0
- package/src/ai/records/RecordCardShell.tsx +169 -0
- package/src/ai/records/index.ts +30 -0
- package/src/ai/records/registry.tsx +80 -0
- package/src/ai/records/types.ts +90 -0
- package/src/ai/ui-part-registry.ts +233 -0
- package/src/ai/ui-part-slots.ts +32 -0
- package/src/ai/ui-parts/pending-phase3-placeholder.tsx +50 -0
- package/src/ai/upload-adapter.ts +421 -0
- package/src/ai/useAiChat.ts +865 -0
- package/src/ai/useAiChatUpload.ts +180 -0
- package/src/ai/useAiShortcuts.ts +79 -0
- package/src/backend/AppShell.tsx +12 -5
- package/src/backend/BackendChromeProvider.tsx +2 -0
- package/src/backend/DataTable.tsx +20 -1
- package/src/backend/FilterBar.tsx +26 -13
- package/src/backend/__tests__/BackendChromeProvider.test.tsx +45 -0
- package/src/backend/dashboard/DashboardScreen.tsx +38 -3
- package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +24 -1
- package/src/backend/injection/spotIds.ts +6 -0
- package/src/backend/notifications/__tests__/useNotificationEffect.test.tsx +77 -0
- package/src/backend/notifications/useNotificationEffect.ts +47 -2
- package/src/index.ts +1 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { ComponentType } from 'react'
|
|
4
|
+
import {
|
|
5
|
+
RESERVED_AI_UI_PART_IDS,
|
|
6
|
+
isReservedAiUiPartId,
|
|
7
|
+
type ReservedAiUiPartId,
|
|
8
|
+
} from './ui-part-slots'
|
|
9
|
+
import { PendingPhase3Placeholder } from './ui-parts/pending-phase3-placeholder'
|
|
10
|
+
import { AI_MUTATION_APPROVAL_CARDS } from './parts/approval-cards-map'
|
|
11
|
+
|
|
12
|
+
export { RESERVED_AI_UI_PART_IDS, isReservedAiUiPartId }
|
|
13
|
+
export type { ReservedAiUiPartId }
|
|
14
|
+
|
|
15
|
+
export type AiUiPartComponentId = ReservedAiUiPartId | (string & {})
|
|
16
|
+
|
|
17
|
+
export type AiUiPartProps = {
|
|
18
|
+
/** Stable component id emitted by the server-side UI-part producer. */
|
|
19
|
+
componentId: AiUiPartComponentId
|
|
20
|
+
/** Arbitrary payload the server attached to this UI part. */
|
|
21
|
+
payload?: unknown
|
|
22
|
+
/** Optional pending-action id for mutation-approval cards (Phase 3). */
|
|
23
|
+
pendingActionId?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type AiUiPartComponent<P = AiUiPartProps> = ComponentType<P>
|
|
27
|
+
|
|
28
|
+
export interface AiUiPartRegistryEntry {
|
|
29
|
+
componentId: string
|
|
30
|
+
reserved: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A UI-part registry instance. Consumers typically use
|
|
35
|
+
* {@link defaultAiUiPartRegistry} for global registrations; tests and embedded
|
|
36
|
+
* pages that need isolation can construct a dedicated registry via
|
|
37
|
+
* {@link createAiUiPartRegistry} and pass it to `<AiChat registry={...} />`.
|
|
38
|
+
*/
|
|
39
|
+
export interface AiUiPartRegistry {
|
|
40
|
+
register<P = AiUiPartProps>(
|
|
41
|
+
componentId: AiUiPartComponentId,
|
|
42
|
+
component: AiUiPartComponent<P>,
|
|
43
|
+
): void
|
|
44
|
+
unregister(componentId: AiUiPartComponentId): void
|
|
45
|
+
resolve<P = AiUiPartProps>(
|
|
46
|
+
componentId: AiUiPartComponentId,
|
|
47
|
+
): AiUiPartComponent<P> | null
|
|
48
|
+
has(componentId: AiUiPartComponentId): boolean
|
|
49
|
+
list(): AiUiPartRegistryEntry[]
|
|
50
|
+
clear(): void
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type RegistryStore = Map<string, AiUiPartComponent<AiUiPartProps>>
|
|
54
|
+
|
|
55
|
+
export interface CreateAiUiPartRegistryOptions {
|
|
56
|
+
/**
|
|
57
|
+
* When true (default) the registry is pre-seeded with the shared
|
|
58
|
+
* `PendingPhase3Placeholder` for every id in {@link RESERVED_AI_UI_PART_IDS}.
|
|
59
|
+
* Pass `false` to get an empty registry — useful for tests that want to
|
|
60
|
+
* assert the registry is genuinely empty.
|
|
61
|
+
*/
|
|
62
|
+
seedReservedPlaceholders?: boolean
|
|
63
|
+
/**
|
|
64
|
+
* When true, the registry is pre-seeded with the live Phase 3 mutation
|
|
65
|
+
* approval cards from `@open-mercato/ui/ai/parts` instead of the humane
|
|
66
|
+
* pending placeholder. The app-wide {@link defaultAiUiPartRegistry}
|
|
67
|
+
* opts in, so end users see the real cards without any bootstrap wiring;
|
|
68
|
+
* scoped registries created via {@link createAiUiPartRegistry} keep the
|
|
69
|
+
* placeholder by default so unit tests and embedded playground mounts
|
|
70
|
+
* stay deterministic and isolated.
|
|
71
|
+
*/
|
|
72
|
+
seedLiveApprovalCards?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function seedReservedSlots(
|
|
76
|
+
store: RegistryStore,
|
|
77
|
+
options: { seedLive: boolean } = { seedLive: false },
|
|
78
|
+
): void {
|
|
79
|
+
for (const reservedId of RESERVED_AI_UI_PART_IDS) {
|
|
80
|
+
if (store.has(reservedId)) continue
|
|
81
|
+
if (options.seedLive && AI_MUTATION_APPROVAL_CARDS[reservedId]) {
|
|
82
|
+
store.set(
|
|
83
|
+
reservedId,
|
|
84
|
+
AI_MUTATION_APPROVAL_CARDS[reservedId] as AiUiPartComponent<AiUiPartProps>,
|
|
85
|
+
)
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
store.set(
|
|
89
|
+
reservedId,
|
|
90
|
+
PendingPhase3Placeholder as AiUiPartComponent<AiUiPartProps>,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create a fresh UI-part registry. By default the four Phase 3 reserved
|
|
97
|
+
* slot ids are pre-seeded with {@link PendingPhase3Placeholder} so consumers
|
|
98
|
+
* that forget to register the real cards see a humane "Phase 3 pending"
|
|
99
|
+
* state instead of the neutral debug chip.
|
|
100
|
+
*/
|
|
101
|
+
export function createAiUiPartRegistry(
|
|
102
|
+
options?: CreateAiUiPartRegistryOptions,
|
|
103
|
+
): AiUiPartRegistry {
|
|
104
|
+
const seedReserved = options?.seedReservedPlaceholders !== false
|
|
105
|
+
const seedLive = options?.seedLiveApprovalCards === true
|
|
106
|
+
const store: RegistryStore = new Map()
|
|
107
|
+
if (seedReserved) {
|
|
108
|
+
seedReservedSlots(store, { seedLive })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const registry: AiUiPartRegistry = {
|
|
112
|
+
register(componentId, component) {
|
|
113
|
+
if (!componentId) {
|
|
114
|
+
throw new Error('registerAiUiPart requires a non-empty componentId')
|
|
115
|
+
}
|
|
116
|
+
store.set(componentId, component as AiUiPartComponent<AiUiPartProps>)
|
|
117
|
+
},
|
|
118
|
+
unregister(componentId) {
|
|
119
|
+
store.delete(componentId)
|
|
120
|
+
},
|
|
121
|
+
resolve<P = AiUiPartProps>(componentId: AiUiPartComponentId) {
|
|
122
|
+
const found = store.get(componentId)
|
|
123
|
+
if (!found) return null
|
|
124
|
+
return found as unknown as AiUiPartComponent<P>
|
|
125
|
+
},
|
|
126
|
+
has(componentId) {
|
|
127
|
+
return store.has(componentId)
|
|
128
|
+
},
|
|
129
|
+
list() {
|
|
130
|
+
const entries: AiUiPartRegistryEntry[] = []
|
|
131
|
+
for (const componentId of store.keys()) {
|
|
132
|
+
entries.push({
|
|
133
|
+
componentId,
|
|
134
|
+
reserved: isReservedAiUiPartId(componentId),
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
return entries
|
|
138
|
+
},
|
|
139
|
+
clear() {
|
|
140
|
+
store.clear()
|
|
141
|
+
if (seedReserved) {
|
|
142
|
+
seedReservedSlots(store, { seedLive })
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
return registry
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const REGISTRY_GLOBAL_KEY = '__openMercatoAiUiPartRegistry'
|
|
150
|
+
|
|
151
|
+
function getDefaultRegistry(): AiUiPartRegistry {
|
|
152
|
+
const scope = globalThis as typeof globalThis & {
|
|
153
|
+
[REGISTRY_GLOBAL_KEY]?: AiUiPartRegistry
|
|
154
|
+
}
|
|
155
|
+
if (!scope[REGISTRY_GLOBAL_KEY]) {
|
|
156
|
+
// Default registry seeds the LIVE mutation-approval cards (Step 5.10).
|
|
157
|
+
// Scoped registries created via `createAiUiPartRegistry()` still default
|
|
158
|
+
// to the humane placeholder so playground embeds + unit tests stay
|
|
159
|
+
// deterministic and isolated.
|
|
160
|
+
scope[REGISTRY_GLOBAL_KEY] = createAiUiPartRegistry({ seedLiveApprovalCards: true })
|
|
161
|
+
}
|
|
162
|
+
return scope[REGISTRY_GLOBAL_KEY]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The app-wide default UI-part registry. Seeded with Phase 3 placeholders on
|
|
167
|
+
* first access. Tests that need a clean registry should instantiate their own
|
|
168
|
+
* via {@link createAiUiPartRegistry} and pass it to `<AiChat registry={...}>`.
|
|
169
|
+
*/
|
|
170
|
+
export const defaultAiUiPartRegistry: AiUiPartRegistry = new Proxy(
|
|
171
|
+
{} as AiUiPartRegistry,
|
|
172
|
+
{
|
|
173
|
+
get(_target, prop: keyof AiUiPartRegistry) {
|
|
174
|
+
const impl = getDefaultRegistry()
|
|
175
|
+
const value = impl[prop]
|
|
176
|
+
return typeof value === 'function' ? value.bind(impl) : value
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Register a UI-part component on the module-global default registry. Legacy
|
|
183
|
+
* Step 4.1 API — preserved verbatim so existing callers keep working. Prefer
|
|
184
|
+
* {@link defaultAiUiPartRegistry} directly (or a scoped registry) in new code.
|
|
185
|
+
*
|
|
186
|
+
* Idempotent: re-registering overwrites the previous entry so hot reload and
|
|
187
|
+
* Phase 3 card replacement (which overwrites the seeded placeholder) stay
|
|
188
|
+
* deterministic.
|
|
189
|
+
*/
|
|
190
|
+
export function registerAiUiPart<P = AiUiPartProps>(
|
|
191
|
+
componentId: AiUiPartComponentId,
|
|
192
|
+
component: AiUiPartComponent<P>,
|
|
193
|
+
): void {
|
|
194
|
+
defaultAiUiPartRegistry.register(componentId, component)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolve a registered UI-part component on the module-global default
|
|
199
|
+
* registry. Returns `null` when no component has been registered — callers
|
|
200
|
+
* MUST handle the null case gracefully (the canonical consumer, `<AiChat>`,
|
|
201
|
+
* renders a neutral placeholder chip + logs a `console.warn`).
|
|
202
|
+
*/
|
|
203
|
+
export function resolveAiUiPart<P = AiUiPartProps>(
|
|
204
|
+
componentId: AiUiPartComponentId,
|
|
205
|
+
): AiUiPartComponent<P> | null {
|
|
206
|
+
return defaultAiUiPartRegistry.resolve<P>(componentId)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Remove a UI-part component registration from the module-global default
|
|
211
|
+
* registry. Primarily useful for tests to keep a clean slate between runs.
|
|
212
|
+
*/
|
|
213
|
+
export function unregisterAiUiPart(componentId: AiUiPartComponentId): void {
|
|
214
|
+
defaultAiUiPartRegistry.unregister(componentId)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Clear every registration on the module-global default registry and re-seed
|
|
219
|
+
* the Phase 3 reserved placeholders. Test-only helper; production code must
|
|
220
|
+
* never invoke this.
|
|
221
|
+
*/
|
|
222
|
+
export function resetAiUiPartRegistryForTests(): void {
|
|
223
|
+
defaultAiUiPartRegistry.clear()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Snapshot every registration on the module-global default registry. Used by
|
|
228
|
+
* debugging UIs (Step 4.6) to enumerate what's registered without mutating
|
|
229
|
+
* state.
|
|
230
|
+
*/
|
|
231
|
+
export function listAiUiParts(): AiUiPartRegistryEntry[] {
|
|
232
|
+
return defaultAiUiPartRegistry.list()
|
|
233
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserved UI-part component ids for Phase 3 approval cards.
|
|
3
|
+
*
|
|
4
|
+
* These ids match the server-emitted UI parts the mutation-approval runtime
|
|
5
|
+
* will produce in Phase 3 Steps 5.6 and 5.10. They are listed here so the
|
|
6
|
+
* registry contract can be validated at compile time and the default registry
|
|
7
|
+
* ships with the same slot names the runtime will later emit.
|
|
8
|
+
*
|
|
9
|
+
* Hard rule: this tuple is FROZEN. Adding new reserved ids is additive, but
|
|
10
|
+
* renaming or removing any entry is a breaking change per the backward-
|
|
11
|
+
* compatibility contract (see `BACKWARD_COMPATIBILITY.md` §6 Widget injection
|
|
12
|
+
* spot IDs).
|
|
13
|
+
*/
|
|
14
|
+
export const RESERVED_AI_UI_PART_IDS = Object.freeze([
|
|
15
|
+
'mutation-preview-card',
|
|
16
|
+
'field-diff-card',
|
|
17
|
+
'confirmation-card',
|
|
18
|
+
'mutation-result-card',
|
|
19
|
+
] as const)
|
|
20
|
+
|
|
21
|
+
export type ReservedAiUiPartId = (typeof RESERVED_AI_UI_PART_IDS)[number]
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns true when the given componentId is one of the Phase 3 reserved
|
|
25
|
+
* slot identifiers. Used by the registry to flag seeded placeholders and by
|
|
26
|
+
* debugging UIs (Step 4.6) to render reserved slots distinctly.
|
|
27
|
+
*/
|
|
28
|
+
export function isReservedAiUiPartId(
|
|
29
|
+
componentId: string,
|
|
30
|
+
): componentId is ReservedAiUiPartId {
|
|
31
|
+
return (RESERVED_AI_UI_PART_IDS as readonly string[]).includes(componentId)
|
|
32
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Info } from 'lucide-react'
|
|
5
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
|
+
import { Alert, AlertDescription, AlertTitle } from '../../primitives/alert'
|
|
7
|
+
import type { AiUiPartProps } from '../ui-part-registry'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default placeholder for the four Phase 3 reserved slot ids.
|
|
11
|
+
*
|
|
12
|
+
* Consumers that forget to register the real mutation-approval cards still
|
|
13
|
+
* see a humane "Phase 3 pending" state instead of the neutral debug chip the
|
|
14
|
+
* `<AiChat>` fallback uses for genuinely unknown component ids. Uses the
|
|
15
|
+
* shared DS `Alert` primitive with `variant="info"` — no hardcoded colors.
|
|
16
|
+
*
|
|
17
|
+
* When Step 5.10 lands the real cards, app bootstrappers will call
|
|
18
|
+
* `registerAiUiPart('mutation-preview-card', MutationPreviewCard)` (etc.)
|
|
19
|
+
* which overwrites this placeholder. The unit tests in
|
|
20
|
+
* `__tests__/ui-part-registry.test.ts` pin that replacement behavior so the
|
|
21
|
+
* Phase 3 hand-off stays deterministic.
|
|
22
|
+
*/
|
|
23
|
+
export function PendingPhase3Placeholder({ componentId }: AiUiPartProps) {
|
|
24
|
+
const t = useT()
|
|
25
|
+
return (
|
|
26
|
+
<Alert
|
|
27
|
+
variant="info"
|
|
28
|
+
data-ai-ui-part-pending-phase3={componentId}
|
|
29
|
+
>
|
|
30
|
+
<Info className="size-4" aria-hidden />
|
|
31
|
+
<AlertTitle>
|
|
32
|
+
{t(
|
|
33
|
+
'ai_assistant.chat.pending_phase3.title',
|
|
34
|
+
'Mutation approval card pending',
|
|
35
|
+
)}
|
|
36
|
+
</AlertTitle>
|
|
37
|
+
<AlertDescription>
|
|
38
|
+
<span>
|
|
39
|
+
{t(
|
|
40
|
+
'ai_assistant.chat.pending_phase3.body',
|
|
41
|
+
'This interactive card will land in Phase 3 of the unified AI framework.',
|
|
42
|
+
)}
|
|
43
|
+
</span>
|
|
44
|
+
<span className="ml-1 font-mono text-xs">{componentId}</span>
|
|
45
|
+
</AlertDescription>
|
|
46
|
+
</Alert>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default PendingPhase3Placeholder
|