@fayz-ai/app-builder 1.0.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 +21 -0
- package/README.md +50 -0
- package/dist/admin/AdminAppBuilder.d.ts +11 -0
- package/dist/admin/AdminAppBuilder.d.ts.map +1 -0
- package/dist/admin/agent-surface.d.ts +18 -0
- package/dist/admin/agent-surface.d.ts.map +1 -0
- package/dist/admin/options.d.ts +22 -0
- package/dist/admin/options.d.ts.map +1 -0
- package/dist/admin/target.d.ts +20 -0
- package/dist/admin/target.d.ts.map +1 -0
- package/dist/chunk-45SFS6FV.js +721 -0
- package/dist/chunk-45SFS6FV.js.map +1 -0
- package/dist/components/AppBuilder.d.ts +12 -0
- package/dist/components/AppBuilder.d.ts.map +1 -0
- package/dist/components/BuilderCanvas.d.ts +9 -0
- package/dist/components/BuilderCanvas.d.ts.map +1 -0
- package/dist/components/BuilderChrome.d.ts +25 -0
- package/dist/components/BuilderChrome.d.ts.map +1 -0
- package/dist/components/BuilderEmpty.d.ts +7 -0
- package/dist/components/BuilderEmpty.d.ts.map +1 -0
- package/dist/components/PagePicker.d.ts +20 -0
- package/dist/components/PagePicker.d.ts.map +1 -0
- package/dist/components/PublishButton.d.ts +9 -0
- package/dist/components/PublishButton.d.ts.map +1 -0
- package/dist/hooks/useBuilderFrame.d.ts +22 -0
- package/dist/hooks/useBuilderFrame.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/bridge.d.ts +38 -0
- package/dist/lib/bridge.d.ts.map +1 -0
- package/dist/lib/cn.d.ts +4 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/pages.d.ts +29 -0
- package/dist/lib/pages.d.ts.map +1 -0
- package/dist/lib/url.d.ts +9 -0
- package/dist/lib/url.d.ts.map +1 -0
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +277 -0
- package/dist/plugin.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +71 -0
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { useBuilderFrame, AppBuilder, BuilderEmpty } from './chunk-45SFS6FV.js';
|
|
2
|
+
import * as React3 from 'react';
|
|
3
|
+
import React3__default from 'react';
|
|
4
|
+
import { registerPluginFactory } from '@fayz-ai/core';
|
|
5
|
+
import { useRightRailStore, useAssistantInviteStore, openAssistantAs, adminNavigateTo, askAI, useOrganizationStore, useFayzProjectsStore, defaultProjectOf, useAgentSurface } from '@fayz-ai/admin';
|
|
6
|
+
import { useLayoutStore } from '@fayz-ai/ui';
|
|
7
|
+
import { jsx } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
function env(key) {
|
|
10
|
+
const bag = import.meta.env;
|
|
11
|
+
return bag?.[key];
|
|
12
|
+
}
|
|
13
|
+
function useAdminBuilderTarget(options) {
|
|
14
|
+
const currentOrgId = useOrganizationStore((s) => s.currentOrg?.id);
|
|
15
|
+
const projects = useFayzProjectsStore((s) => s.projects);
|
|
16
|
+
const load = useFayzProjectsStore((s) => s.load);
|
|
17
|
+
const loadedFor = useFayzProjectsStore((s) => s.loadedFor);
|
|
18
|
+
React3.useEffect(() => {
|
|
19
|
+
if (loadedFor !== (currentOrgId ?? null)) void load(currentOrgId);
|
|
20
|
+
}, [currentOrgId, loadedFor, load]);
|
|
21
|
+
const settled = loadedFor === (currentOrgId ?? null);
|
|
22
|
+
const active = defaultProjectOf(projects);
|
|
23
|
+
if (!active) return { target: null, loading: !settled };
|
|
24
|
+
const metadata = active.metadata ?? {};
|
|
25
|
+
const perTenant = typeof metadata.sandbox_url === "string" ? metadata.sandbox_url : null;
|
|
26
|
+
const sandboxUrl = perTenant ?? options?.sandboxUrl ?? env("VITE_APP_BUILDER_SANDBOX_URL") ?? null;
|
|
27
|
+
return {
|
|
28
|
+
target: {
|
|
29
|
+
projectId: active.fayzProjectId,
|
|
30
|
+
name: active.name,
|
|
31
|
+
url: sandboxUrl ?? active.previewUrl,
|
|
32
|
+
liveUrl: active.previewUrl
|
|
33
|
+
},
|
|
34
|
+
loading: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
var INSTRUCTIONS = [
|
|
38
|
+
"The user is looking at their own app inside the admin. The canvas on the left is the running",
|
|
39
|
+
"app; you are the column on the right.",
|
|
40
|
+
"Use builder_navigate/builder_reload/builder_set_viewport to move the canvas so you are both",
|
|
41
|
+
"looking at the same thing before discussing a change.",
|
|
42
|
+
"To change the app itself, call builder_request_change with the user's intent in one sentence.",
|
|
43
|
+
"Never claim a change was applied unless that tool says so."
|
|
44
|
+
].join(" ");
|
|
45
|
+
function tool(id, name, description, properties, required, mode = "read") {
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
name,
|
|
49
|
+
description,
|
|
50
|
+
mode,
|
|
51
|
+
parameters: { type: "object", properties, required },
|
|
52
|
+
execution: { plane: "client" },
|
|
53
|
+
// Moving the canvas is what looking together costs; a confirmation card per
|
|
54
|
+
// scroll would make the colleague useless at the one thing it is for.
|
|
55
|
+
requiresConfirmation: false,
|
|
56
|
+
role: "setup",
|
|
57
|
+
permission: { feature: "site", action: "edit" }
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function useBuilderAgentSurface(deps) {
|
|
61
|
+
const ref = React3.useRef(deps);
|
|
62
|
+
ref.current = deps;
|
|
63
|
+
const contribution = React3.useMemo(
|
|
64
|
+
() => ({
|
|
65
|
+
id: "app_builder.canvas",
|
|
66
|
+
pluginId: "app_builder",
|
|
67
|
+
role: "setup",
|
|
68
|
+
instructions: INSTRUCTIONS,
|
|
69
|
+
describe: () => {
|
|
70
|
+
const d = ref.current;
|
|
71
|
+
return {
|
|
72
|
+
title: `Site \u2014 ${d.target.name}`,
|
|
73
|
+
state: {
|
|
74
|
+
projectId: d.target.projectId ?? null,
|
|
75
|
+
projectName: d.target.name,
|
|
76
|
+
currentPath: d.currentPath,
|
|
77
|
+
viewport: d.viewport,
|
|
78
|
+
liveUrl: d.target.liveUrl ?? null,
|
|
79
|
+
canApplyChanges: !!d.onRequestChange
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
tools: [
|
|
84
|
+
{
|
|
85
|
+
tool: tool(
|
|
86
|
+
"app_builder.navigate",
|
|
87
|
+
"builder_navigate",
|
|
88
|
+
'Point the canvas at a path of the app (e.g. "/", "/produtos").',
|
|
89
|
+
{ path: { type: "string", description: "Path starting with /" } },
|
|
90
|
+
["path"]
|
|
91
|
+
),
|
|
92
|
+
execute: (args) => {
|
|
93
|
+
const path = String(args.path ?? "/");
|
|
94
|
+
ref.current.actions.navigate(path);
|
|
95
|
+
return { ok: true, path };
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
tool: tool("app_builder.reload", "builder_reload", "Reload the canvas.", {}, []),
|
|
100
|
+
execute: () => {
|
|
101
|
+
ref.current.actions.reload();
|
|
102
|
+
return { ok: true };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
tool: tool(
|
|
107
|
+
"app_builder.set_viewport",
|
|
108
|
+
"builder_set_viewport",
|
|
109
|
+
"Show the app at a device width.",
|
|
110
|
+
{ viewport: { type: "string", enum: ["desktop", "mobile"] } },
|
|
111
|
+
["viewport"]
|
|
112
|
+
),
|
|
113
|
+
execute: (args) => {
|
|
114
|
+
const viewport = String(args.viewport ?? "desktop");
|
|
115
|
+
ref.current.actions.setViewport(viewport);
|
|
116
|
+
return { ok: true, viewport };
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
tool: tool(
|
|
121
|
+
"app_builder.request_change",
|
|
122
|
+
"builder_request_change",
|
|
123
|
+
"Ask the builder to change the app the user is looking at.",
|
|
124
|
+
{ instruction: { type: "string", description: "One sentence describing the change" } },
|
|
125
|
+
["instruction"],
|
|
126
|
+
"persist"
|
|
127
|
+
),
|
|
128
|
+
execute: async (args) => {
|
|
129
|
+
const d = ref.current;
|
|
130
|
+
const instruction = String(args.instruction ?? "").trim();
|
|
131
|
+
if (!instruction) return { error: "empty_instruction" };
|
|
132
|
+
if (!d.onRequestChange || !d.target.projectId) {
|
|
133
|
+
return {
|
|
134
|
+
error: "not_connected",
|
|
135
|
+
message: "The build path is not wired up yet \u2014 nothing was changed."
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const result = await d.onRequestChange({
|
|
139
|
+
projectId: d.target.projectId,
|
|
140
|
+
path: d.currentPath,
|
|
141
|
+
instruction
|
|
142
|
+
});
|
|
143
|
+
return { ok: true, message: result };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
}),
|
|
148
|
+
[]
|
|
149
|
+
);
|
|
150
|
+
useAgentSurface(contribution);
|
|
151
|
+
}
|
|
152
|
+
var EMPTY_TARGET = { projectId: null, name: "", url: null, liveUrl: null };
|
|
153
|
+
var INVITE_ID = "app_builder.site";
|
|
154
|
+
function AdminAppBuilder({ options }) {
|
|
155
|
+
const { target: resolved, loading } = useAdminBuilderTarget(options);
|
|
156
|
+
const target = resolved ?? EMPTY_TARGET;
|
|
157
|
+
const [viewport, setViewport] = React3.useState("desktop");
|
|
158
|
+
const frame = useBuilderFrame(target.url);
|
|
159
|
+
const openPanel = useRightRailStore((s) => s.openPanel);
|
|
160
|
+
const closeRail = useRightRailStore((s) => s.close);
|
|
161
|
+
const railOpen = useRightRailStore((s) => s.open);
|
|
162
|
+
const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
|
|
163
|
+
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
164
|
+
const setInvite = useAssistantInviteStore((s) => s.setInvite);
|
|
165
|
+
React3.useEffect(() => {
|
|
166
|
+
openPanel("chat");
|
|
167
|
+
openAssistantAs("setup");
|
|
168
|
+
setInvite(
|
|
169
|
+
{
|
|
170
|
+
id: INVITE_ID,
|
|
171
|
+
role: "setup",
|
|
172
|
+
persona: options?.labels?.assistantPersona ?? "Site da loja",
|
|
173
|
+
headline: options?.labels?.assistantHeadline ?? "O que voc\xEA quer mudar no site?",
|
|
174
|
+
description: options?.labels?.assistantDescription ?? "Pe\xE7a a mudan\xE7a em portugu\xEAs \u2014 texto, cor, imagem, se\xE7\xE3o \u2014 e ela aparece aqui do lado.",
|
|
175
|
+
placeholder: options?.labels?.assistantPlaceholder ?? "Ex.: troque o banner da home",
|
|
176
|
+
suggestions: options?.labels?.assistantSuggestions ?? [
|
|
177
|
+
{ label: "Trocar a imagem principal da home" },
|
|
178
|
+
{ label: "Mudar as cores da loja" },
|
|
179
|
+
{ label: "Criar uma p\xE1gina de contato" }
|
|
180
|
+
],
|
|
181
|
+
// Docked, never a hero: the hero would cover the canvas the invite is
|
|
182
|
+
// about.
|
|
183
|
+
auto: false
|
|
184
|
+
},
|
|
185
|
+
INVITE_ID
|
|
186
|
+
);
|
|
187
|
+
return () => setInvite(null, INVITE_ID);
|
|
188
|
+
}, [openPanel, setInvite, options?.labels]);
|
|
189
|
+
const collapsedByUs = React3.useRef(false);
|
|
190
|
+
React3.useEffect(() => {
|
|
191
|
+
if (!useLayoutStore.getState().sidebarCollapsed) {
|
|
192
|
+
collapsedByUs.current = true;
|
|
193
|
+
setSidebarCollapsed(true);
|
|
194
|
+
}
|
|
195
|
+
return () => {
|
|
196
|
+
if (collapsedByUs.current) setSidebarCollapsed(false);
|
|
197
|
+
};
|
|
198
|
+
}, [setSidebarCollapsed]);
|
|
199
|
+
useBuilderAgentSurface({
|
|
200
|
+
target,
|
|
201
|
+
currentPath: frame.currentPath,
|
|
202
|
+
viewport,
|
|
203
|
+
actions: { navigate: frame.navigate, reload: frame.reload, setViewport },
|
|
204
|
+
onRequestChange: options?.onRequestChange
|
|
205
|
+
});
|
|
206
|
+
const [publishing, setPublishing] = React3.useState(false);
|
|
207
|
+
const publish = options?.onPublish;
|
|
208
|
+
const publishContract = React3.useMemo(() => {
|
|
209
|
+
if (!publish || !target.projectId) return void 0;
|
|
210
|
+
const projectId = target.projectId;
|
|
211
|
+
return {
|
|
212
|
+
state: publishing ? "publishing" : "idle",
|
|
213
|
+
publish: async () => {
|
|
214
|
+
setPublishing(true);
|
|
215
|
+
try {
|
|
216
|
+
await publish({ projectId });
|
|
217
|
+
} finally {
|
|
218
|
+
setPublishing(false);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}, [publish, target.projectId, publishing]);
|
|
223
|
+
return /* @__PURE__ */ jsx(
|
|
224
|
+
AppBuilder,
|
|
225
|
+
{
|
|
226
|
+
target,
|
|
227
|
+
frame,
|
|
228
|
+
viewport,
|
|
229
|
+
onViewportChange: setViewport,
|
|
230
|
+
publish: publishContract,
|
|
231
|
+
chatContract: { send: (message, context) => askAI({ prompt: message, hint: context?.selector }) },
|
|
232
|
+
onClose: () => adminNavigateTo("/"),
|
|
233
|
+
onToggleChat: () => railOpen ? closeRail() : openPanel("chat"),
|
|
234
|
+
onToggleWide: () => setSidebarCollapsed(!sidebarCollapsed),
|
|
235
|
+
empty: loading ? /* @__PURE__ */ jsx("div", { className: "h-full" }) : /* @__PURE__ */ jsx(BuilderEmpty, { labels: options?.labels, onAction: () => adminNavigateTo("/settings/sites") }),
|
|
236
|
+
labels: options?.labels
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/plugin.ts
|
|
242
|
+
var EDIT = { feature: "site", action: "edit" };
|
|
243
|
+
function createAppBuilderPlugin(options) {
|
|
244
|
+
const route = options?.route ?? "/site";
|
|
245
|
+
const PageComponent = () => React3__default.createElement(AdminAppBuilder, { options });
|
|
246
|
+
PageComponent.displayName = "AdminAppBuilder";
|
|
247
|
+
return {
|
|
248
|
+
id: "app_builder",
|
|
249
|
+
name: options?.navLabel ?? "Site",
|
|
250
|
+
icon: "PenSquare",
|
|
251
|
+
version: "1.0.0",
|
|
252
|
+
scope: options?.scope ?? "universal",
|
|
253
|
+
verticalId: options?.verticalId,
|
|
254
|
+
defaultEnabled: true,
|
|
255
|
+
dependencies: [],
|
|
256
|
+
defaultAgentRole: "setup",
|
|
257
|
+
declaredFeatures: [{ id: "site", label: "Site", group: "Convert" }],
|
|
258
|
+
// No nav entry on purpose: the door is the "Editar" action on the site
|
|
259
|
+
// widget in the sidebar footer (the app points site.editRoute here).
|
|
260
|
+
navigation: [],
|
|
261
|
+
routes: [
|
|
262
|
+
{
|
|
263
|
+
path: route,
|
|
264
|
+
component: PageComponent,
|
|
265
|
+
immersive: true,
|
|
266
|
+
title: options?.navLabel ?? "Site",
|
|
267
|
+
permission: EDIT
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
widgets: []
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
registerPluginFactory("app_builder", createAppBuilderPlugin);
|
|
274
|
+
|
|
275
|
+
export { AdminAppBuilder, createAppBuilderPlugin, useAdminBuilderTarget };
|
|
276
|
+
//# sourceMappingURL=plugin.js.map
|
|
277
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/admin/target.ts","../src/admin/agent-surface.ts","../src/admin/AdminAppBuilder.tsx","../src/plugin.ts"],"names":["React","React2"],"mappings":"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","file":"plugin.js","sourcesContent":["import * as React from 'react'\nimport { useFayzProjectsStore, defaultProjectOf, useOrganizationStore } from '@fayz-ai/admin'\nimport type { BuilderTarget } from '../types'\n\nexport interface AdminTargetOptions {\n /** Address of the running preview container, when the host knows it. */\n sandboxUrl?: string\n}\n\nfunction env(key: string): string | undefined {\n const bag = (import.meta as unknown as { env?: Record<string, string | undefined> }).env\n return bag?.[key]\n}\n\n/**\n * The tenant's default site row, as a builder target.\n *\n * The canvas is THE APP — the running preview container — never the platform's\n * editor: embedding the editor would put its chat inside ours and hand the\n * merchant two conversations for one question. With no sandbox address known it\n * falls back to the published site, which is still the app, just yesterday's.\n */\nexport interface AdminTargetState {\n target: BuilderTarget | null\n /** The tenant's projects have not answered yet — \"none\" is not the answer. */\n loading: boolean\n}\n\nexport function useAdminBuilderTarget(options?: AdminTargetOptions): AdminTargetState {\n const currentOrgId = useOrganizationStore((s) => s.currentOrg?.id)\n const projects = useFayzProjectsStore((s) => s.projects)\n const load = useFayzProjectsStore((s) => s.load)\n const loadedFor = useFayzProjectsStore((s) => s.loadedFor)\n\n // The page can be entered by URL, before the sidebar widget loaded anything.\n React.useEffect(() => {\n if (loadedFor !== (currentOrgId ?? null)) void load(currentOrgId)\n }, [currentOrgId, loadedFor, load])\n\n // Rendering \"no site linked\" before the first read comes back flashed a wrong\n // answer at everyone who has one.\n const settled = loadedFor === (currentOrgId ?? null)\n const active = defaultProjectOf(projects)\n if (!active) return { target: null, loading: !settled }\n\n const metadata = (active.metadata ?? {}) as Record<string, unknown>\n const perTenant = typeof metadata.sandbox_url === 'string' ? metadata.sandbox_url : null\n const sandboxUrl = perTenant ?? options?.sandboxUrl ?? env('VITE_APP_BUILDER_SANDBOX_URL') ?? null\n\n return {\n target: {\n projectId: active.fayzProjectId,\n name: active.name,\n url: sandboxUrl ?? active.previewUrl,\n liveUrl: active.previewUrl,\n },\n loading: false,\n }\n}\n","import * as React from 'react'\nimport { useAgentSurface } from '@fayz-ai/admin'\nimport type { PluginAITool } from '@fayz-ai/core'\nimport type { BuilderTarget, BuilderViewport } from '../types'\n\nconst INSTRUCTIONS = [\n 'The user is looking at their own app inside the admin. The canvas on the left is the running',\n 'app; you are the column on the right.',\n 'Use builder_navigate/builder_reload/builder_set_viewport to move the canvas so you are both',\n 'looking at the same thing before discussing a change.',\n \"To change the app itself, call builder_request_change with the user's intent in one sentence.\",\n 'Never claim a change was applied unless that tool says so.',\n].join(' ')\n\nfunction tool(\n id: string,\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n mode: 'read' | 'persist' = 'read',\n): PluginAITool {\n return {\n id,\n name,\n description,\n mode,\n parameters: { type: 'object', properties, required } as PluginAITool['parameters'],\n execution: { plane: 'client' },\n // Moving the canvas is what looking together costs; a confirmation card per\n // scroll would make the colleague useless at the one thing it is for.\n requiresConfirmation: false,\n role: 'setup',\n permission: { feature: 'site', action: 'edit' },\n }\n}\n\nexport interface BuilderAgentSurfaceDeps {\n target: BuilderTarget\n currentPath: string\n viewport: BuilderViewport\n actions: {\n navigate: (path: string) => void\n reload: () => void\n setViewport: (viewport: BuilderViewport) => void\n }\n onRequestChange?: (input: { projectId: string; path: string; instruction: string }) => Promise<string> | string\n}\n\nexport function useBuilderAgentSurface(deps: BuilderAgentSurfaceDeps): void {\n const ref = React.useRef(deps)\n ref.current = deps\n\n const contribution = React.useMemo(\n () => ({\n id: 'app_builder.canvas',\n pluginId: 'app_builder',\n role: 'setup' as const,\n instructions: INSTRUCTIONS,\n describe: () => {\n const d = ref.current\n return {\n title: `Site — ${d.target.name}`,\n state: {\n projectId: d.target.projectId ?? null,\n projectName: d.target.name,\n currentPath: d.currentPath,\n viewport: d.viewport,\n liveUrl: d.target.liveUrl ?? null,\n canApplyChanges: !!d.onRequestChange,\n },\n }\n },\n tools: [\n {\n tool: tool(\n 'app_builder.navigate',\n 'builder_navigate',\n 'Point the canvas at a path of the app (e.g. \"/\", \"/produtos\").',\n { path: { type: 'string', description: 'Path starting with /' } },\n ['path'],\n ),\n execute: (args: Record<string, unknown>) => {\n const path = String(args.path ?? '/')\n ref.current.actions.navigate(path)\n return { ok: true, path }\n },\n },\n {\n tool: tool('app_builder.reload', 'builder_reload', 'Reload the canvas.', {}, []),\n execute: () => {\n ref.current.actions.reload()\n return { ok: true }\n },\n },\n {\n tool: tool(\n 'app_builder.set_viewport',\n 'builder_set_viewport',\n 'Show the app at a device width.',\n { viewport: { type: 'string', enum: ['desktop', 'mobile'] } },\n ['viewport'],\n ),\n execute: (args: Record<string, unknown>) => {\n const viewport = String(args.viewport ?? 'desktop') as BuilderViewport\n ref.current.actions.setViewport(viewport)\n return { ok: true, viewport }\n },\n },\n {\n tool: tool(\n 'app_builder.request_change',\n 'builder_request_change',\n 'Ask the builder to change the app the user is looking at.',\n { instruction: { type: 'string', description: 'One sentence describing the change' } },\n ['instruction'],\n 'persist',\n ),\n execute: async (args: Record<string, unknown>) => {\n const d = ref.current\n const instruction = String(args.instruction ?? '').trim()\n if (!instruction) return { error: 'empty_instruction' }\n if (!d.onRequestChange || !d.target.projectId) {\n return {\n error: 'not_connected',\n message: 'The build path is not wired up yet — nothing was changed.',\n }\n }\n const result = await d.onRequestChange({\n projectId: d.target.projectId,\n path: d.currentPath,\n instruction,\n })\n return { ok: true, message: result }\n },\n },\n ],\n }),\n [],\n )\n\n useAgentSurface(contribution)\n}\n","import * as React from 'react'\nimport {\n adminNavigateTo,\n askAI,\n openAssistantAs,\n useAssistantInviteStore,\n useRightRailStore,\n} from '@fayz-ai/admin'\nimport { useLayoutStore } from '@fayz-ai/ui'\nimport { AppBuilder } from '../components/AppBuilder'\nimport { BuilderEmpty } from '../components/BuilderEmpty'\nimport { useBuilderFrame } from '../hooks/useBuilderFrame'\nimport { useAdminBuilderTarget } from './target'\nimport { useBuilderAgentSurface } from './agent-surface'\nimport type { AppBuilderPluginOptions } from './options'\nimport type { BuilderTarget, BuilderViewport } from '../types'\n\nconst EMPTY_TARGET: BuilderTarget = { projectId: null, name: '', url: null, liveUrl: null }\nconst INVITE_ID = 'app_builder.site'\n\n/**\n * The admin's wiring of the surface: where the target comes from, what the\n * window buttons do here, and which colleague is on duty. Everything in this\n * file is host-specific — the surface itself knows none of it.\n */\nexport function AdminAppBuilder({ options }: { options?: AppBuilderPluginOptions }) {\n const { target: resolved, loading } = useAdminBuilderTarget(options)\n const target = resolved ?? EMPTY_TARGET\n const [viewport, setViewport] = React.useState<BuilderViewport>('desktop')\n\n // Owned here, not by the surface, because the agent tools drive it.\n const frame = useBuilderFrame(target.url)\n\n const openPanel = useRightRailStore((s) => s.openPanel)\n const closeRail = useRightRailStore((s) => s.close)\n const railOpen = useRightRailStore((s) => s.open)\n const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed)\n const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed)\n\n const setInvite = useAssistantInviteStore((s) => s.setInvite)\n\n // The screen IS the conversation: the canvas is what you point at while you\n // ask. What the colleague opens with has to be about the site — the generic\n // \"vamos deixar a loja pronta pra vender\" is the right pitch on the dashboard\n // and the wrong one in front of the site's own home page.\n React.useEffect(() => {\n openPanel('chat')\n openAssistantAs('setup')\n setInvite(\n {\n id: INVITE_ID,\n role: 'setup',\n persona: options?.labels?.assistantPersona ?? 'Site da loja',\n headline: options?.labels?.assistantHeadline ?? 'O que você quer mudar no site?',\n description:\n options?.labels?.assistantDescription\n ?? 'Peça a mudança em português — texto, cor, imagem, seção — e ela aparece aqui do lado.',\n placeholder: options?.labels?.assistantPlaceholder ?? 'Ex.: troque o banner da home',\n suggestions: options?.labels?.assistantSuggestions ?? [\n { label: 'Trocar a imagem principal da home' },\n { label: 'Mudar as cores da loja' },\n { label: 'Criar uma página de contato' },\n ],\n // Docked, never a hero: the hero would cover the canvas the invite is\n // about.\n auto: false,\n },\n INVITE_ID,\n )\n return () => setInvite(null, INVITE_ID)\n }, [openPanel, setInvite, options?.labels])\n\n // Entering the builder gives the canvas the desk. Only the collapse WE caused\n // is undone on the way out, so a menu the user closed by hand stays closed.\n const collapsedByUs = React.useRef(false)\n React.useEffect(() => {\n if (!useLayoutStore.getState().sidebarCollapsed) {\n collapsedByUs.current = true\n setSidebarCollapsed(true)\n }\n return () => {\n if (collapsedByUs.current) setSidebarCollapsed(false)\n }\n }, [setSidebarCollapsed])\n\n useBuilderAgentSurface({\n target,\n currentPath: frame.currentPath,\n viewport,\n actions: { navigate: frame.navigate, reload: frame.reload, setViewport },\n onRequestChange: options?.onRequestChange,\n })\n\n const [publishing, setPublishing] = React.useState(false)\n const publish = options?.onPublish\n const publishContract = React.useMemo(() => {\n if (!publish || !target.projectId) return undefined\n const projectId = target.projectId\n return {\n state: publishing ? ('publishing' as const) : ('idle' as const),\n publish: async () => {\n setPublishing(true)\n try {\n await publish({ projectId })\n } finally {\n setPublishing(false)\n }\n },\n }\n }, [publish, target.projectId, publishing])\n\n return (\n <AppBuilder\n target={target}\n frame={frame}\n viewport={viewport}\n onViewportChange={setViewport}\n publish={publishContract}\n chatContract={{ send: (message, context) => askAI({ prompt: message, hint: context?.selector }) }}\n onClose={() => adminNavigateTo('/')}\n onToggleChat={() => (railOpen ? closeRail() : openPanel('chat'))}\n onToggleWide={() => setSidebarCollapsed(!sidebarCollapsed)}\n empty={\n loading ? (\n <div className=\"h-full\" />\n ) : (\n <BuilderEmpty labels={options?.labels} onAction={() => adminNavigateTo('/settings/sites')} />\n )\n }\n labels={options?.labels}\n />\n )\n}\n","import React from 'react'\nimport type { PluginManifest } from '@fayz-ai/core'\nimport { registerPluginFactory } from '@fayz-ai/core'\nimport { AdminAppBuilder } from './admin/AdminAppBuilder'\nimport type { AppBuilderPluginOptions } from './admin/options'\n\nexport type { AppBuilderPluginOptions } from './admin/options'\nexport { AdminAppBuilder } from './admin/AdminAppBuilder'\nexport { useAdminBuilderTarget } from './admin/target'\n\nconst EDIT = { feature: 'site', action: 'edit' as const }\n\n/**\n * The admin's mount of the builder surface. Everything host-specific — the\n * target, the rail, the agent — lives behind this door, so the surface itself\n * stays importable by an app that has none of it.\n */\nexport function createAppBuilderPlugin(options?: AppBuilderPluginOptions): PluginManifest {\n const route = options?.route ?? '/site'\n\n const PageComponent: React.ComponentType<unknown> = () =>\n React.createElement(AdminAppBuilder, { options })\n PageComponent.displayName = 'AdminAppBuilder'\n\n return {\n id: 'app_builder',\n name: options?.navLabel ?? 'Site',\n icon: 'PenSquare',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n defaultAgentRole: 'setup',\n declaredFeatures: [{ id: 'site', label: 'Site', group: 'Convert' }],\n // No nav entry on purpose: the door is the \"Editar\" action on the site\n // widget in the sidebar footer (the app points site.editRoute here).\n navigation: [],\n routes: [\n {\n path: route,\n component: PageComponent,\n immersive: true,\n title: options?.navLabel ?? 'Site',\n permission: EDIT,\n },\n ],\n widgets: [],\n }\n}\n\nregisterPluginFactory('app_builder', createAppBuilderPlugin as (config?: Record<string, unknown>) => PluginManifest)\n"]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
import type { BuilderFrameController } from './hooks/useBuilderFrame';
|
|
3
|
+
export type BuilderViewport = 'desktop' | 'mobile';
|
|
4
|
+
/** Which state of seeing the app is on screen. Code is the same surface, not a second one. */
|
|
5
|
+
export type BuilderMode = 'preview' | 'code';
|
|
6
|
+
/** What the surface is pointed at. One shape, whatever the host resolved it from. */
|
|
7
|
+
export interface BuilderTarget {
|
|
8
|
+
/** Platform project id, when the host has one. */
|
|
9
|
+
projectId?: string | null;
|
|
10
|
+
name: string;
|
|
11
|
+
/** What the canvas loads — the running preview container. */
|
|
12
|
+
url: string | null;
|
|
13
|
+
/** The published address, for the door on the address pill. */
|
|
14
|
+
liveUrl?: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** An element the user pointed at, in the shape the platform already formats. */
|
|
17
|
+
export interface BuilderElementContext {
|
|
18
|
+
tagName?: string;
|
|
19
|
+
selector?: string;
|
|
20
|
+
className?: string;
|
|
21
|
+
/** "file:line:col" — what "go to code" resolves against. */
|
|
22
|
+
sourceId?: string;
|
|
23
|
+
text?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* How the surface talks to whatever conversation the host mounted. It never
|
|
27
|
+
* draws a chat — "change this block" starts on the canvas and has to end up in
|
|
28
|
+
* a message, and this is the whole of what that needs.
|
|
29
|
+
*/
|
|
30
|
+
export interface BuilderChatContract {
|
|
31
|
+
send(message: string, context?: BuilderElementContext): void;
|
|
32
|
+
busy?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** Same button in both hosts, each with its own transport. */
|
|
35
|
+
export interface BuilderPublishContract {
|
|
36
|
+
state: 'idle' | 'publishing' | 'published' | 'error';
|
|
37
|
+
publish(): void | Promise<void>;
|
|
38
|
+
lastPublishedAt?: string;
|
|
39
|
+
error?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface BuilderLabels {
|
|
42
|
+
viewLive?: string;
|
|
43
|
+
publish?: string;
|
|
44
|
+
publishUnavailable?: string;
|
|
45
|
+
reload?: string;
|
|
46
|
+
preview?: string;
|
|
47
|
+
code?: string;
|
|
48
|
+
emptyTitle?: string;
|
|
49
|
+
emptyBody?: string;
|
|
50
|
+
emptyAction?: string;
|
|
51
|
+
loadFailed?: string;
|
|
52
|
+
loadFailedHint?: string;
|
|
53
|
+
openInTab?: string;
|
|
54
|
+
searchPage?: string;
|
|
55
|
+
noPage?: string;
|
|
56
|
+
assistantPersona?: string;
|
|
57
|
+
assistantHeadline?: string;
|
|
58
|
+
assistantDescription?: string;
|
|
59
|
+
assistantPlaceholder?: string;
|
|
60
|
+
assistantSuggestions?: Array<{
|
|
61
|
+
label: string;
|
|
62
|
+
prompt?: string;
|
|
63
|
+
}>;
|
|
64
|
+
close?: string;
|
|
65
|
+
toggleChat?: string;
|
|
66
|
+
toggleWide?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface AppBuilderProps {
|
|
69
|
+
target: BuilderTarget;
|
|
70
|
+
/** Defaults to 'preview'. */
|
|
71
|
+
mode?: BuilderMode;
|
|
72
|
+
/** Absent means the surface has one state and draws no switch. */
|
|
73
|
+
onModeChange?: (mode: BuilderMode) => void;
|
|
74
|
+
/** The code surface, when the host has one. Absent means 'code' does not exist. */
|
|
75
|
+
code?: React.ReactNode;
|
|
76
|
+
/** The conversation, laid out as a column beside the canvas. Absent = canvas only. */
|
|
77
|
+
chat?: React.ReactNode;
|
|
78
|
+
chatContract?: BuilderChatContract;
|
|
79
|
+
publish?: BuilderPublishContract;
|
|
80
|
+
/** The window buttons. Each is omitted rather than shown dead. */
|
|
81
|
+
onClose?: () => void;
|
|
82
|
+
onToggleChat?: () => void;
|
|
83
|
+
onToggleWide?: () => void;
|
|
84
|
+
/** The canvas reported a route change. */
|
|
85
|
+
onNavigate?: (path: string) => void;
|
|
86
|
+
onPick?: (context: BuilderElementContext) => void;
|
|
87
|
+
viewport?: BuilderViewport;
|
|
88
|
+
onViewportChange?: (viewport: BuilderViewport) => void;
|
|
89
|
+
/** What to render when the host has no target yet. */
|
|
90
|
+
empty?: React.ReactNode;
|
|
91
|
+
labels?: BuilderLabels;
|
|
92
|
+
/**
|
|
93
|
+
* A controller the host already owns, so it can drive the canvas from
|
|
94
|
+
* outside (an agent tool, a keyboard shortcut). Omit and the surface owns one.
|
|
95
|
+
* A host that passes it also owns the `onNavigate`/`onPick` wiring, since it
|
|
96
|
+
* built the controller.
|
|
97
|
+
*/
|
|
98
|
+
frame?: BuilderFrameController;
|
|
99
|
+
slots?: {
|
|
100
|
+
/** Host-only controls at the end of the window bar (the editor's tabs). */
|
|
101
|
+
chromeEnd?: React.ReactNode;
|
|
102
|
+
/** Under the canvas — a terminal, a status dock. */
|
|
103
|
+
belowCanvas?: React.ReactNode;
|
|
104
|
+
};
|
|
105
|
+
className?: string;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fayz-ai/app-builder",
|
|
3
|
+
"fayz": {
|
|
4
|
+
"status": "preview"
|
|
5
|
+
},
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"description": "[experimental] Fayz SDK — the app-builder surface: window chrome, live canvas, publish and chat seams",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./plugin": {
|
|
19
|
+
"types": "./dist/plugin.d.ts",
|
|
20
|
+
"import": "./dist/plugin.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"lucide-react": ">=0.400.0 <1.0.0",
|
|
28
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
29
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
30
|
+
"@fayz-ai/admin": "^0.19.0",
|
|
31
|
+
"@fayz-ai/core": "^0.19.0",
|
|
32
|
+
"@fayz-ai/ui": "^0.19.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"@fayz-ai/admin": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"@fayz-ai/core": {
|
|
39
|
+
"optional": true
|
|
40
|
+
},
|
|
41
|
+
"@fayz-ai/ui": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/react": "^18.3.0",
|
|
47
|
+
"@types/react-dom": "^18.3.0",
|
|
48
|
+
"lucide-react": "^0.400.0",
|
|
49
|
+
"react": "^18.3.0",
|
|
50
|
+
"tsup": "^8.2.0",
|
|
51
|
+
"typescript": "^5.5.0",
|
|
52
|
+
"@fayz-ai/admin": "^0.19.0",
|
|
53
|
+
"@fayz-ai/core": "^0.19.0",
|
|
54
|
+
"@fayz-ai/ui": "^0.19.0"
|
|
55
|
+
},
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"keywords": [
|
|
58
|
+
"fayz",
|
|
59
|
+
"fayz-sdk",
|
|
60
|
+
"app-builder"
|
|
61
|
+
],
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsup && tsc -b --force",
|
|
67
|
+
"dev": "tsup --watch",
|
|
68
|
+
"typecheck": "tsc -b",
|
|
69
|
+
"clean": "rm -rf dist .tsbuildinfo"
|
|
70
|
+
}
|
|
71
|
+
}
|