@powerhousedao/codegen 6.0.0-dev.135 → 6.0.0-dev.137
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/dist/{file-builders-DyZN9JYf.mjs → file-builders-DRzXbZTI.mjs} +183 -183
- package/dist/file-builders-DRzXbZTI.mjs.map +1 -0
- package/dist/{index-CPl7k91J.d.mts → index-CHAnPBj2.d.mts} +26 -26
- package/dist/index-CHAnPBj2.d.mts.map +1 -0
- package/dist/index.d.mts +5 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +10 -10
- package/dist/index.mjs.map +1 -1
- package/dist/src/file-builders/index.d.mts +2 -2
- package/dist/src/file-builders/index.mjs +3 -3
- package/dist/src/templates/index.d.mts +48 -48
- package/dist/src/templates/index.d.mts.map +1 -1
- package/dist/src/templates/index.mjs +2 -2
- package/dist/src/utils/index.d.mts +1 -1
- package/dist/src/utils/index.mjs +1 -1
- package/dist/src/utils/index.mjs.map +1 -1
- package/dist/{templates-CKdxigVj.mjs → templates-DBvz_qPL.mjs} +522 -522
- package/dist/templates-DBvz_qPL.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/file-builders-DyZN9JYf.mjs.map +0 -1
- package/dist/index-CPl7k91J.d.mts.map +0 -1
- package/dist/templates-CKdxigVj.mjs.map +0 -1
|
@@ -1,72 +1,416 @@
|
|
|
1
1
|
import { getActionInputName, getActionInputTypeNames, getActionType, getActionTypeName } from "./src/name-builders/index.mjs";
|
|
2
2
|
import { camelCase, capitalCase, constantCase, kebabCase, pascalCase } from "change-case";
|
|
3
3
|
import { css, html, js, json, md, ts, tsx, yaml } from "@tmpl/core";
|
|
4
|
-
//#region src/templates/
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
//#region src/templates/app/components/CreateDocument.ts
|
|
5
|
+
const createDocumentFileTemplate = tsx`
|
|
6
|
+
import type { VetraDocumentModelModule } from "@powerhousedao/reactor-browser";
|
|
7
|
+
import {
|
|
8
|
+
showCreateDocumentModal,
|
|
9
|
+
useAllowedDocumentModelModules,
|
|
10
|
+
} from "@powerhousedao/reactor-browser";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Document creation UI component.
|
|
14
|
+
* Displays available document types as clickable buttons.
|
|
15
|
+
*/
|
|
16
|
+
export function CreateDocument() {
|
|
17
|
+
const allowedDocumentModelModules = useAllowedDocumentModelModules();
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
{/* Customize section title here */}
|
|
22
|
+
<h3 className="mb-3 mt-4 text-sm font-bold text-gray-600">
|
|
23
|
+
Create document
|
|
24
|
+
</h3>
|
|
25
|
+
{/* Customize layout by changing flex-wrap, gap, or grid layout */}
|
|
26
|
+
<div className="flex w-full flex-wrap gap-4">
|
|
27
|
+
{allowedDocumentModelModules?.map((documentModelModule) => {
|
|
28
|
+
return (
|
|
29
|
+
<CreateDocumentButton
|
|
30
|
+
key={documentModelModule.documentModel.global.id}
|
|
31
|
+
documentModelModule={documentModelModule}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
type Props = {
|
|
41
|
+
documentModelModule: VetraDocumentModelModule;
|
|
42
|
+
};
|
|
43
|
+
function CreateDocumentButton({ documentModelModule }: Props) {
|
|
44
|
+
const documentType = documentModelModule.documentModel.global.id;
|
|
45
|
+
const documentModelName =
|
|
46
|
+
documentModelModule.documentModel.global.name || documentType;
|
|
47
|
+
const documentModelDescription =
|
|
48
|
+
documentModelModule.documentModel.global.description;
|
|
49
|
+
return (
|
|
50
|
+
<button
|
|
51
|
+
className="cursor-pointer rounded-md bg-gray-200 py-2 px-3 hover:bg-gray-300"
|
|
52
|
+
title={documentModelName}
|
|
53
|
+
aria-description={documentModelDescription}
|
|
54
|
+
onClick={() => showCreateDocumentModal(documentType)}
|
|
55
|
+
>
|
|
56
|
+
{documentModelName}
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
`.raw;
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/templates/app/components/DriveContents.ts
|
|
63
|
+
const appDriveContentsFileTemplate = () => tsx`
|
|
64
|
+
import { CreateDocument } from "./CreateDocument.js";
|
|
65
|
+
import { EmptyState } from "./EmptyState.js";
|
|
66
|
+
import { Files } from "./Files.js";
|
|
67
|
+
import { Folders } from "./Folders.js";
|
|
68
|
+
import { NavigationBreadcrumbs } from "./NavigationBreadcrumbs.js";
|
|
25
69
|
|
|
26
|
-
|
|
70
|
+
/** Shows the documents and folders in the selected drive */
|
|
71
|
+
export function DriveContents() {
|
|
72
|
+
return (
|
|
73
|
+
<div className="space-y-6 px-6">
|
|
74
|
+
<NavigationBreadcrumbs />
|
|
75
|
+
<Folders />
|
|
76
|
+
<Files />
|
|
77
|
+
<EmptyState />
|
|
78
|
+
<CreateDocument />
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
27
82
|
|
|
28
|
-
**MANDATORY**: The \`reactor-mcp\` MUST BE USED when handling documents or document-models for the Powerhouse/Vetra ecosystem.
|
|
29
|
-
If the \`reactor-mcp\` server is unavailable, ask the user to run \`ph vetra\` on a separate terminal to start the server and try to reconnect to the MCP server, DO NOT run it yourself.
|
|
30
83
|
|
|
31
|
-
|
|
84
|
+
`.raw;
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/templates/app/components/DriveExplorer.ts
|
|
87
|
+
const driveExplorerFileTemplate = tsx`
|
|
88
|
+
import type { EditorProps } from "document-model";
|
|
89
|
+
import { FolderTree } from "./FolderTree.js";
|
|
90
|
+
import { DriveContents } from "./DriveContents.js";
|
|
32
91
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Main drive explorer component with sidebar navigation and content area.
|
|
94
|
+
* Layout: Left sidebar (folder tree) + Right content area (files/folders + document editor)
|
|
95
|
+
*/
|
|
96
|
+
export function DriveExplorer({ children }: EditorProps) {
|
|
97
|
+
// if a document is selected then it's editor will be passed as children
|
|
98
|
+
const showDocumentEditor = !!children;
|
|
38
99
|
|
|
39
|
-
|
|
100
|
+
return (
|
|
101
|
+
<div className="flex h-full">
|
|
102
|
+
<FolderTree />
|
|
103
|
+
<div className="flex-1 overflow-y-auto p-4">
|
|
104
|
+
{/* Conditional rendering: Document editor or folder contents */}
|
|
105
|
+
{showDocumentEditor ? (
|
|
106
|
+
/* Document editor view */
|
|
107
|
+
children
|
|
108
|
+
) : (
|
|
109
|
+
/* Folder contents view */
|
|
110
|
+
<DriveContents />
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
`.raw;
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/templates/app/components/EmptyState.ts
|
|
119
|
+
const emptyStateFileTemplate = tsx`
|
|
120
|
+
import { useNodesInSelectedDriveOrFolder } from "@powerhousedao/reactor-browser";
|
|
40
121
|
|
|
41
|
-
|
|
122
|
+
/** Shows a message when the selected drive or folder is empty */
|
|
123
|
+
export function EmptyState() {
|
|
124
|
+
const nodes = useNodesInSelectedDriveOrFolder();
|
|
125
|
+
const hasNodes = nodes.length > 0;
|
|
126
|
+
if (hasNodes) return null;
|
|
42
127
|
|
|
43
|
-
|
|
128
|
+
return (
|
|
129
|
+
<div className="py-12 text-center text-gray-500">
|
|
130
|
+
<p className="text-lg">This folder is empty</p>
|
|
131
|
+
<p className="mt-2 text-sm">Create your first document or folder below</p>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
`.raw;
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/templates/app/components/Files.ts
|
|
138
|
+
const appFilesFileTemplate = () => tsx`
|
|
139
|
+
import { FileItem } from "@powerhousedao/design-system/connect";
|
|
140
|
+
import {
|
|
141
|
+
useNodesInSelectedDriveOrFolder,
|
|
142
|
+
isFileNodeKind,
|
|
143
|
+
} from "@powerhousedao/reactor-browser";
|
|
44
144
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
145
|
+
/** Shows the files in the selected drive or folder */
|
|
146
|
+
export function Files() {
|
|
147
|
+
const nodes = useNodesInSelectedDriveOrFolder();
|
|
148
|
+
const fileNodes = nodes.filter((n) => isFileNodeKind(n));
|
|
149
|
+
const hasFiles = fileNodes.length > 0;
|
|
49
150
|
|
|
50
|
-
|
|
151
|
+
if (!hasFiles) return null;
|
|
51
152
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
153
|
+
return (
|
|
154
|
+
<div>
|
|
155
|
+
<h3 className="mb-2 text-sm font-semibold text-gray-600">Documents</h3>
|
|
156
|
+
<div className="flex flex-wrap gap-4">
|
|
157
|
+
{fileNodes.map((fileNode) => (
|
|
158
|
+
<FileItem key={fileNode.id} fileNode={fileNode} />
|
|
159
|
+
))}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
55
164
|
|
|
56
|
-
|
|
165
|
+
`.raw;
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/templates/app/components/Folders.ts
|
|
168
|
+
const appFoldersFileTemplate = () => tsx`
|
|
169
|
+
import { FolderItem } from "@powerhousedao/design-system/connect";
|
|
170
|
+
import {
|
|
171
|
+
useNodesInSelectedDriveOrFolder,
|
|
172
|
+
isFolderNodeKind,
|
|
173
|
+
} from "@powerhousedao/reactor-browser";
|
|
57
174
|
|
|
58
|
-
|
|
175
|
+
/** Shows the folders in the selected drive or folder */
|
|
176
|
+
export function Folders() {
|
|
177
|
+
const nodes = useNodesInSelectedDriveOrFolder();
|
|
178
|
+
const folderNodes = nodes.filter((n) => isFolderNodeKind(n));
|
|
179
|
+
const hasFolders = folderNodes.length > 0;
|
|
180
|
+
if (!hasFolders) return null;
|
|
59
181
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
<h3 className="mb-2 text-sm font-bold text-gray-600">Folders</h3>
|
|
185
|
+
<div className="flex flex-wrap gap-4">
|
|
186
|
+
{folderNodes.map((folderNode) => (
|
|
187
|
+
<FolderItem key={folderNode.id} folderNode={folderNode} />
|
|
188
|
+
))}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
`.raw;
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/templates/app/components/FolderTree.ts
|
|
196
|
+
const folderTreeFileTemplate = tsx`
|
|
197
|
+
import {
|
|
198
|
+
Sidebar,
|
|
199
|
+
SidebarProvider,
|
|
200
|
+
type SidebarNode,
|
|
201
|
+
} from "@powerhousedao/document-engineering";
|
|
202
|
+
import {
|
|
203
|
+
setSelectedNode,
|
|
204
|
+
useNodesInSelectedDrive,
|
|
205
|
+
useSelectedDrive,
|
|
206
|
+
useSelectedNode,
|
|
207
|
+
} from "@powerhousedao/reactor-browser";
|
|
208
|
+
import type { Node } from "@powerhousedao/shared/document-drive";
|
|
209
|
+
import { useMemo } from "react";
|
|
64
210
|
|
|
65
|
-
|
|
211
|
+
function buildSidebarNodes(
|
|
212
|
+
nodes: Node[],
|
|
213
|
+
parentId: string | null | undefined,
|
|
214
|
+
): SidebarNode[] {
|
|
215
|
+
return nodes
|
|
216
|
+
.filter((n) => {
|
|
217
|
+
if (parentId == null) {
|
|
218
|
+
return n.parentFolder == null;
|
|
219
|
+
}
|
|
220
|
+
return n.parentFolder === parentId;
|
|
221
|
+
})
|
|
222
|
+
.map((node): SidebarNode => {
|
|
223
|
+
if (node.kind === "folder") {
|
|
224
|
+
return {
|
|
225
|
+
id: node.id,
|
|
226
|
+
title: node.name,
|
|
227
|
+
icon: "FolderClose" as const,
|
|
228
|
+
expandedIcon: "FolderOpen" as const,
|
|
229
|
+
children: buildSidebarNodes(nodes, node.id),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
id: node.id,
|
|
234
|
+
title: node.name,
|
|
235
|
+
icon: "File" as const,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
}
|
|
66
239
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
240
|
+
function transformNodesToSidebarNodes(
|
|
241
|
+
nodes: Node[],
|
|
242
|
+
driveName: string,
|
|
243
|
+
): SidebarNode[] {
|
|
244
|
+
return [
|
|
245
|
+
{
|
|
246
|
+
id: "root",
|
|
247
|
+
title: driveName,
|
|
248
|
+
icon: "Drive" as const,
|
|
249
|
+
children: buildSidebarNodes(nodes, null),
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Hierarchical folder tree navigation component using Sidebar from document-engineering.
|
|
256
|
+
* Displays folders and files in a tree structure with expand/collapse functionality, search, and resize support.
|
|
257
|
+
*/
|
|
258
|
+
export function FolderTree() {
|
|
259
|
+
const [selectedDrive] = useSelectedDrive();
|
|
260
|
+
const nodes = useNodesInSelectedDrive();
|
|
261
|
+
const selectedNode = useSelectedNode();
|
|
262
|
+
const driveName = selectedDrive.header.name;
|
|
263
|
+
// Transform Node[] to hierarchical SidebarNode structure
|
|
264
|
+
const sidebarNodes = useMemo(
|
|
265
|
+
() => transformNodesToSidebarNodes(nodes || [], driveName),
|
|
266
|
+
[nodes, driveName],
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const handleActiveNodeChange = (node: SidebarNode) => {
|
|
270
|
+
// If root node is selected, pass undefined to match existing behavior
|
|
271
|
+
if (node.id === "root") {
|
|
272
|
+
setSelectedNode(undefined);
|
|
273
|
+
} else {
|
|
274
|
+
setSelectedNode(node.id);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
// Map selectedNodeId to activeNodeId (use "root" when undefined)
|
|
278
|
+
const activeNodeId =
|
|
279
|
+
!selectedNode || selectedNode.id === selectedDrive.header.id
|
|
280
|
+
? "root"
|
|
281
|
+
: selectedNode.id;
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<SidebarProvider nodes={sidebarNodes}>
|
|
285
|
+
<Sidebar
|
|
286
|
+
className="pt-1"
|
|
287
|
+
nodes={sidebarNodes}
|
|
288
|
+
activeNodeId={activeNodeId}
|
|
289
|
+
onActiveNodeChange={handleActiveNodeChange}
|
|
290
|
+
sidebarTitle="Drive Explorer"
|
|
291
|
+
showSearchBar={true}
|
|
292
|
+
resizable={true}
|
|
293
|
+
allowPinning={false}
|
|
294
|
+
showStatusFilter={false}
|
|
295
|
+
initialWidth={256}
|
|
296
|
+
defaultLevel={2}
|
|
297
|
+
/>
|
|
298
|
+
</SidebarProvider>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
`.raw;
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/templates/app/components/NavigationBreadcrumbs.ts
|
|
304
|
+
const driveExplorerNavigationBreadcrumbsFileTemplate = () => tsx`
|
|
305
|
+
import { Breadcrumbs } from "@powerhousedao/design-system/connect";
|
|
306
|
+
|
|
307
|
+
/** Shows the navigation breadcrumbs for the selected drive or folder */
|
|
308
|
+
export function NavigationBreadcrumbs() {
|
|
309
|
+
return (
|
|
310
|
+
<div className="border-b border-gray-200 pb-3 space-y-3">
|
|
311
|
+
<Breadcrumbs />
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
`.raw;
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/templates/app/config.ts
|
|
318
|
+
const appConfigFileTemplate = (v) => ts`
|
|
319
|
+
import type { PHAppConfig } from "@powerhousedao/reactor-browser";
|
|
320
|
+
|
|
321
|
+
/** Editor config for the <%= pascalCaseAppName %> */
|
|
322
|
+
export const editorConfig: PHAppConfig = {
|
|
323
|
+
isDragAndDropEnabled: ${v.isDragAndDropEnabledString},
|
|
324
|
+
allowedDocumentTypes: ${v.allowedDocumentTypesString}
|
|
325
|
+
};
|
|
326
|
+
`.raw;
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/templates/app/editor.ts
|
|
329
|
+
const appEditorFileTemplate = () => tsx`
|
|
330
|
+
import { useSetPHAppConfig } from "@powerhousedao/reactor-browser";
|
|
331
|
+
import type { EditorProps } from "document-model";
|
|
332
|
+
import { DriveExplorer } from "./components/DriveExplorer.js";
|
|
333
|
+
import { editorConfig } from "./config.js";
|
|
334
|
+
|
|
335
|
+
/** Editor component for the app */
|
|
336
|
+
export default function Editor(props: EditorProps) {
|
|
337
|
+
// set the config for this app
|
|
338
|
+
// you can update these configs in \`./config.ts\`
|
|
339
|
+
useSetPHAppConfig(editorConfig);
|
|
340
|
+
return (
|
|
341
|
+
<div className="bg-gray-50 p-6">
|
|
342
|
+
<DriveExplorer {...props} />
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
`.raw;
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/templates/boilerplate/AGENTS.md.ts
|
|
349
|
+
const agentsTemplate = md`
|
|
350
|
+
# Powerhouse Document Models Assistant
|
|
351
|
+
|
|
352
|
+
This project creates document models, editors, processors and subgraphs for the Powerhouse ecosystem. Your role is to help users create these modules based on their needs.
|
|
353
|
+
|
|
354
|
+
## Core Concepts
|
|
355
|
+
|
|
356
|
+
- **Document Model**: A template for creating documents. Defines schema and allowed operations for a document type.
|
|
357
|
+
- **Document**: An instance of a document model containing actual data that follows the model's structure and can be modified using operations.
|
|
358
|
+
- **Drive**: A document of type "powerhouse/document-drive" representing a collection of documents and folders. Add documents using "addActions" with "ADD_FILE" action.
|
|
359
|
+
- **Action**: A proposed change to a document (JSON object with action name and input). Dispatch using "addActions" tool.
|
|
360
|
+
- **Operation**: A completed change to a document containing the action plus metadata (index, timestamp, hash, errors). Actions become operations after dispatch.
|
|
361
|
+
|
|
362
|
+
## Technology Primer
|
|
363
|
+
|
|
364
|
+
- **Reactor**: The core Powerhouse engine. It is modular and storage-agnostic, loads document models at runtime, and synchronizes documents across nodes via drives.
|
|
365
|
+
- **Reactor Package**: A deployable bundle that extends the Reactor. It contains one or more document models, editors, processors, and subgraphs. A Vetra project generates a Reactor Package.
|
|
366
|
+
- **Connect**: The Powerhouse web application for document management. End users open Connect to browse drives, create documents, and interact with editors.
|
|
367
|
+
- **Switchboard**: The Powerhouse API service. It exposes GraphQL and MCP endpoints so external tools can read/write documents programmatically.
|
|
368
|
+
- **Vetra**: The local development environment for building Reactor Packages. It includes Vetra Studio (a local Connect instance) and Vetra Switchboard (a local Switchboard with reactor-mcp). Start it with \`ph vetra\`.
|
|
369
|
+
|
|
370
|
+
## CRITICAL: MCP Tool Usage Rules
|
|
371
|
+
|
|
372
|
+
**MANDATORY**: The \`reactor-mcp\` MUST BE USED when handling documents or document-models for the Powerhouse/Vetra ecosystem.
|
|
373
|
+
If the \`reactor-mcp\` server is unavailable, ask the user to run \`ph vetra\` on a separate terminal to start the server and try to reconnect to the MCP server, DO NOT run it yourself.
|
|
374
|
+
|
|
375
|
+
### Key Requirements:
|
|
376
|
+
|
|
377
|
+
- Never set document IDs manually - they're auto-generated by 'createDocument'
|
|
378
|
+
- Minimize "addActions" calls by batching multiple actions together
|
|
379
|
+
- Add new document model documents to "vetra-{hash}" drive unless specified otherwise
|
|
380
|
+
- Always check document model schema before calling addActions
|
|
381
|
+
- Use MCP tools for ALL document and document-model operations
|
|
382
|
+
|
|
383
|
+
## Document Model Creation Workflow
|
|
384
|
+
|
|
385
|
+
### 1. Planning Phase
|
|
386
|
+
|
|
387
|
+
**MANDATORY**: Present your proposal to the user and ask for confirmation before implementing ANY document model.
|
|
388
|
+
|
|
389
|
+
- **ALWAYS** describe the proposed document model structure (state schema, operations, modules) before creating
|
|
390
|
+
- **NEVER** proceed with implementation without explicit user approval of your proposal
|
|
391
|
+
- When in doubt, ask for clarification
|
|
392
|
+
- Break complex models into logical modules and operations
|
|
393
|
+
|
|
394
|
+
#### Document Type ID Format
|
|
395
|
+
|
|
396
|
+
- **Type ID**: \`{organization}/{document-type-name}\` (e.g., \`pizza-plaza/order\`, \`acme/invoice\`)
|
|
397
|
+
- **File extension**: 2-4 characters with leading dot (e.g., \`.ordr\`, \`.inv\`)
|
|
398
|
+
- **Name**: Must match \`/[a-zA-Z][a-zA-Z0-9 ]*/\` — human-readable, capitalized (e.g., \`"Order"\`, \`"Invoice"\`)
|
|
399
|
+
|
|
400
|
+
### 2. Pre-Implementation Requirements
|
|
401
|
+
|
|
402
|
+
**MANDATORY**: Check document model schema before making any MCP tool calls.
|
|
403
|
+
|
|
404
|
+
- **ALWAYS** use \`mcp__reactor-mcp__getDocumentModelSchema\` with \`type: "powerhouse/document-model"\` first
|
|
405
|
+
- Review input schema requirements for operations like \`ADD_MODULE\`, \`ADD_OPERATION\`, etc.
|
|
406
|
+
- Ensure all required parameters (like \`id\` or \`scope\` fields) are included in action inputs
|
|
407
|
+
- This prevents failed tool calls and reduces iteration
|
|
408
|
+
|
|
409
|
+
### 3. Implementation Requirements
|
|
410
|
+
|
|
411
|
+
- Document model reducers must be **pure synchronous functions**
|
|
412
|
+
- Reducers receive current state and operation, always returning the same result
|
|
413
|
+
- Values like dates/IDs must come from operation input, not generated in reducer
|
|
70
414
|
- Reducer code goes into SET_OPERATION_REDUCER action (no function header needed)
|
|
71
415
|
- Reducers are wrapped with Mutative - you can mutate the state object directly
|
|
72
416
|
- External imports go at the beginning of the actual reducer file in \`src/\`
|
|
@@ -1405,14 +1749,6 @@ const indexHtmlTemplate = html(_templateObject$1 || (_templateObject$1 = _tagged
|
|
|
1405
1749
|
var _templateObject;
|
|
1406
1750
|
const legacyIndexHtmlTemplate = html(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta\n name=\"description\"\n content=\"Connect is a hub for your most important documents and processes, translated into software. Easily capture data in a structured way with dedicated business process packages. Collaborate on shared documents with ease while using your preferred storage solution (decentralized, centralized, or local).\"\n />\n <title>Powerhouse Connect</title>\n <link rel=\"icon\" href=\"/icon.ico\" />\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link\n href=\"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap\"\n rel=\"stylesheet\"\n />\n <style>\n @import \"tailwindcss\";\n @import \"@powerhousedao/design-system/style.css\";\n @import \"@powerhousedao/document-engineering/style.css\";\n @source \"./node_modules/@powerhousedao/connect\";\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\">\n // initializes Connect on '<div id=\"root\"></div>'\n import \"@powerhousedao/connect/main.js\";\n <\/script>\n </body>\n </html>\n"]))).raw;
|
|
1407
1751
|
//#endregion
|
|
1408
|
-
//#region src/templates/boilerplate/main.tsx.ts
|
|
1409
|
-
const mainTsxTemplate = tsx`
|
|
1410
|
-
import { startConnect } from "@powerhousedao/connect";
|
|
1411
|
-
import * as localPackage from "./index.js";
|
|
1412
|
-
|
|
1413
|
-
startConnect(localPackage);
|
|
1414
|
-
`.raw;
|
|
1415
|
-
//#endregion
|
|
1416
1752
|
//#region src/templates/boilerplate/index.ts
|
|
1417
1753
|
const indexTsTemplate = ts`
|
|
1418
1754
|
import type { Manifest } from "document-model";
|
|
@@ -2089,6 +2425,14 @@ For more information on this, and how to apply and follow the GNU AGPL, see
|
|
|
2089
2425
|
<https://www.gnu.org/licenses/>.
|
|
2090
2426
|
`;
|
|
2091
2427
|
//#endregion
|
|
2428
|
+
//#region src/templates/boilerplate/main.tsx.ts
|
|
2429
|
+
const mainTsxTemplate = tsx`
|
|
2430
|
+
import { startConnect } from "@powerhousedao/connect";
|
|
2431
|
+
import * as localPackage from "./index.js";
|
|
2432
|
+
|
|
2433
|
+
startConnect(localPackage);
|
|
2434
|
+
`.raw;
|
|
2435
|
+
//#endregion
|
|
2092
2436
|
//#region src/templates/boilerplate/mcp.json.ts
|
|
2093
2437
|
const mcpTemplate = json`
|
|
2094
2438
|
{
|
|
@@ -3536,6 +3880,11 @@ const documentModelSrcIndexFileTemplate = ts`
|
|
|
3536
3880
|
export * from "./utils.js";
|
|
3537
3881
|
`.raw;
|
|
3538
3882
|
//#endregion
|
|
3883
|
+
//#region src/templates/document-model/src/utils.ts
|
|
3884
|
+
const documentModelSrcUtilsTemplate = ts`
|
|
3885
|
+
export {};
|
|
3886
|
+
`.raw;
|
|
3887
|
+
//#endregion
|
|
3539
3888
|
//#region src/templates/document-model/tests/document-model.test.ts
|
|
3540
3889
|
const documentModelTestFileTemplate = (v) => ts`
|
|
3541
3890
|
/**
|
|
@@ -3685,452 +4034,103 @@ function makeTestCaseForAction(action, isPhDocumentOfTypeFunctionName) {
|
|
|
3685
4034
|
${camelCaseActionName}(input),
|
|
3686
4035
|
);
|
|
3687
4036
|
|
|
3688
|
-
expect(${isPhDocumentOfTypeFunctionName}(updatedDocument)).toBe(true);
|
|
3689
|
-
expect(updatedDocument.operations.${scope}).toHaveLength(1);
|
|
3690
|
-
expect(updatedDocument.operations.${scope}[0].action.type).toBe(
|
|
3691
|
-
"${constantCaseActionName}",
|
|
3692
|
-
);
|
|
3693
|
-
expect(updatedDocument.operations.${scope}[0].action.input).toStrictEqual(input);
|
|
3694
|
-
expect(updatedDocument.operations.${scope}[0].index).toEqual(0);
|
|
3695
|
-
});
|
|
3696
|
-
`.raw;
|
|
3697
|
-
}
|
|
3698
|
-
function makeActionImportNames(v) {
|
|
3699
|
-
const actionNames = makeCamelCaseActionNamesForImport(v.actions);
|
|
3700
|
-
const inputSchemaNames = makeActionInputSchemasForImport(v.actions);
|
|
3701
|
-
return [
|
|
3702
|
-
"reducer",
|
|
3703
|
-
"utils",
|
|
3704
|
-
v.isPhDocumentOfTypeFunctionName,
|
|
3705
|
-
...actionNames,
|
|
3706
|
-
...inputSchemaNames
|
|
3707
|
-
];
|
|
3708
|
-
}
|
|
3709
|
-
function makeActionsImports(v) {
|
|
3710
|
-
return ts`
|
|
3711
|
-
import {
|
|
3712
|
-
${makeActionImportNames(v).join("\n")}
|
|
3713
|
-
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
3714
|
-
`.raw;
|
|
3715
|
-
}
|
|
3716
|
-
function makeTestCasesForActions(actions, isPhDocumentOfTypeFunctionName) {
|
|
3717
|
-
return actions.map((action) => makeTestCaseForAction(action, isPhDocumentOfTypeFunctionName)).join("\n\n");
|
|
3718
|
-
}
|
|
3719
|
-
const documentModelOperationsModuleTestFileTemplate = (v) => ts`
|
|
3720
|
-
/**
|
|
3721
|
-
* This is a scaffold file meant for customization:
|
|
3722
|
-
* - change it by adding new tests or modifying the existing ones
|
|
3723
|
-
*/
|
|
3724
|
-
|
|
3725
|
-
import { describe, it, expect } from 'vitest';
|
|
3726
|
-
import { generateMock } from '@powerhousedao/common';
|
|
3727
|
-
import {
|
|
3728
|
-
reducer,
|
|
3729
|
-
utils,
|
|
3730
|
-
${v.isPhDocumentOfTypeFunctionName},
|
|
3731
|
-
${makeCamelCaseActionNamesForImport(v.actions)},
|
|
3732
|
-
${makeActionInputSchemasForImport(v.actions)},
|
|
3733
|
-
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
3734
|
-
|
|
3735
|
-
describe("${makeModuleOperationsTypeName(v.module)}", () => {
|
|
3736
|
-
${makeTestCasesForActions(v.actions, v.isPhDocumentOfTypeFunctionName)}
|
|
3737
|
-
});
|
|
3738
|
-
|
|
3739
|
-
`.raw;
|
|
3740
|
-
//#endregion
|
|
3741
|
-
//#region src/templates/document-model/src/utils.ts
|
|
3742
|
-
const documentModelSrcUtilsTemplate = ts`
|
|
3743
|
-
export {};
|
|
3744
|
-
`.raw;
|
|
3745
|
-
//#endregion
|
|
3746
|
-
//#region src/templates/document-model/upgrades/upgrade-manifest.ts
|
|
3747
|
-
const upgradeManifestTemplate = (v) => ts`
|
|
3748
|
-
import type { UpgradeManifest } from "document-model";
|
|
3749
|
-
import { latestVersion, supportedVersions } from "./versions.js";
|
|
3750
|
-
|
|
3751
|
-
export const ${v.upgradeManifestName}: UpgradeManifest<typeof supportedVersions> = {
|
|
3752
|
-
documentType: "${v.documentModelId}",
|
|
3753
|
-
latestVersion,
|
|
3754
|
-
supportedVersions,
|
|
3755
|
-
upgrades: {},
|
|
3756
|
-
};
|
|
3757
|
-
`.raw;
|
|
3758
|
-
//#endregion
|
|
3759
|
-
//#region src/templates/document-model/upgrades/upgrade-transition.ts
|
|
3760
|
-
const upgradeTransitionTemplate = (v) => ts`
|
|
3761
|
-
import type { Action, PHDocument, UpgradeTransition } from "document-model";
|
|
3762
|
-
import type { ${v.phStateName} as StateV${v.previousVersion} } from "${v.documentModelPackageImportPath}/v${v.previousVersion}";
|
|
3763
|
-
import type { ${v.phStateName} as StateV${v.version} } from "${v.documentModelPackageImportPath}/v${v.version}";
|
|
3764
|
-
|
|
3765
|
-
function upgradeReducer(
|
|
3766
|
-
document: PHDocument<StateV${v.previousVersion}>,
|
|
3767
|
-
action: Action,
|
|
3768
|
-
): PHDocument<StateV${v.version}> {
|
|
3769
|
-
return {
|
|
3770
|
-
...document,
|
|
3771
|
-
};
|
|
3772
|
-
}
|
|
3773
|
-
|
|
3774
|
-
export const v${v.version}: UpgradeTransition = {
|
|
3775
|
-
toVersion: ${v.version},
|
|
3776
|
-
upgradeReducer,
|
|
3777
|
-
description: "",
|
|
3778
|
-
};
|
|
3779
|
-
`.raw;
|
|
3780
|
-
//#endregion
|
|
3781
|
-
//#region src/templates/document-model/utils.ts
|
|
3782
|
-
const documentModelUtilsTemplate = ({ phStateName, pascalCaseDocumentType }) => ts`
|
|
3783
|
-
import type { DocumentModelUtils } from "document-model";
|
|
3784
|
-
import type { ${phStateName} } from "./gen/types.js";
|
|
3785
|
-
import { utils as genUtils } from "./gen/utils.js";
|
|
3786
|
-
import * as customUtils from "./src/utils.js";
|
|
3787
|
-
|
|
3788
|
-
/** Utils for the ${pascalCaseDocumentType} document model */
|
|
3789
|
-
export const utils: DocumentModelUtils<${phStateName}> = { ...genUtils, ...customUtils };
|
|
3790
|
-
`.raw;
|
|
3791
|
-
//#endregion
|
|
3792
|
-
//#region src/templates/drive-editor/components/CreateDocument.ts
|
|
3793
|
-
const createDocumentFileTemplate = tsx`
|
|
3794
|
-
import type { VetraDocumentModelModule } from "@powerhousedao/reactor-browser";
|
|
3795
|
-
import {
|
|
3796
|
-
showCreateDocumentModal,
|
|
3797
|
-
useAllowedDocumentModelModules,
|
|
3798
|
-
} from "@powerhousedao/reactor-browser";
|
|
3799
|
-
|
|
3800
|
-
/**
|
|
3801
|
-
* Document creation UI component.
|
|
3802
|
-
* Displays available document types as clickable buttons.
|
|
3803
|
-
*/
|
|
3804
|
-
export function CreateDocument() {
|
|
3805
|
-
const allowedDocumentModelModules = useAllowedDocumentModelModules();
|
|
3806
|
-
|
|
3807
|
-
return (
|
|
3808
|
-
<div>
|
|
3809
|
-
{/* Customize section title here */}
|
|
3810
|
-
<h3 className="mb-3 mt-4 text-sm font-bold text-gray-600">
|
|
3811
|
-
Create document
|
|
3812
|
-
</h3>
|
|
3813
|
-
{/* Customize layout by changing flex-wrap, gap, or grid layout */}
|
|
3814
|
-
<div className="flex w-full flex-wrap gap-4">
|
|
3815
|
-
{allowedDocumentModelModules?.map((documentModelModule) => {
|
|
3816
|
-
return (
|
|
3817
|
-
<CreateDocumentButton
|
|
3818
|
-
key={documentModelModule.documentModel.global.id}
|
|
3819
|
-
documentModelModule={documentModelModule}
|
|
3820
|
-
/>
|
|
3821
|
-
);
|
|
3822
|
-
})}
|
|
3823
|
-
</div>
|
|
3824
|
-
</div>
|
|
3825
|
-
);
|
|
3826
|
-
}
|
|
3827
|
-
|
|
3828
|
-
type Props = {
|
|
3829
|
-
documentModelModule: VetraDocumentModelModule;
|
|
3830
|
-
};
|
|
3831
|
-
function CreateDocumentButton({ documentModelModule }: Props) {
|
|
3832
|
-
const documentType = documentModelModule.documentModel.global.id;
|
|
3833
|
-
const documentModelName =
|
|
3834
|
-
documentModelModule.documentModel.global.name || documentType;
|
|
3835
|
-
const documentModelDescription =
|
|
3836
|
-
documentModelModule.documentModel.global.description;
|
|
3837
|
-
return (
|
|
3838
|
-
<button
|
|
3839
|
-
className="cursor-pointer rounded-md bg-gray-200 py-2 px-3 hover:bg-gray-300"
|
|
3840
|
-
title={documentModelName}
|
|
3841
|
-
aria-description={documentModelDescription}
|
|
3842
|
-
onClick={() => showCreateDocumentModal(documentType)}
|
|
3843
|
-
>
|
|
3844
|
-
{documentModelName}
|
|
3845
|
-
</button>
|
|
3846
|
-
);
|
|
3847
|
-
}
|
|
3848
|
-
`.raw;
|
|
3849
|
-
//#endregion
|
|
3850
|
-
//#region src/templates/drive-editor/components/DriveContents.ts
|
|
3851
|
-
const driveEditorDriveContentsFileTemplate = () => tsx`
|
|
3852
|
-
import { CreateDocument } from "./CreateDocument.js";
|
|
3853
|
-
import { EmptyState } from "./EmptyState.js";
|
|
3854
|
-
import { Files } from "./Files.js";
|
|
3855
|
-
import { Folders } from "./Folders.js";
|
|
3856
|
-
import { NavigationBreadcrumbs } from "./NavigationBreadcrumbs.js";
|
|
3857
|
-
|
|
3858
|
-
/** Shows the documents and folders in the selected drive */
|
|
3859
|
-
export function DriveContents() {
|
|
3860
|
-
return (
|
|
3861
|
-
<div className="space-y-6 px-6">
|
|
3862
|
-
<NavigationBreadcrumbs />
|
|
3863
|
-
<Folders />
|
|
3864
|
-
<Files />
|
|
3865
|
-
<EmptyState />
|
|
3866
|
-
<CreateDocument />
|
|
3867
|
-
</div>
|
|
3868
|
-
);
|
|
3869
|
-
}
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
`.raw;
|
|
3873
|
-
//#endregion
|
|
3874
|
-
//#region src/templates/drive-editor/components/DriveExplorer.ts
|
|
3875
|
-
const driveExplorerFileTemplate = tsx`
|
|
3876
|
-
import type { EditorProps } from "document-model";
|
|
3877
|
-
import { FolderTree } from "./FolderTree.js";
|
|
3878
|
-
import { DriveContents } from "./DriveContents.js";
|
|
3879
|
-
|
|
3880
|
-
/**
|
|
3881
|
-
* Main drive explorer component with sidebar navigation and content area.
|
|
3882
|
-
* Layout: Left sidebar (folder tree) + Right content area (files/folders + document editor)
|
|
3883
|
-
*/
|
|
3884
|
-
export function DriveExplorer({ children }: EditorProps) {
|
|
3885
|
-
// if a document is selected then it's editor will be passed as children
|
|
3886
|
-
const showDocumentEditor = !!children;
|
|
3887
|
-
|
|
3888
|
-
return (
|
|
3889
|
-
<div className="flex h-full">
|
|
3890
|
-
<FolderTree />
|
|
3891
|
-
<div className="flex-1 overflow-y-auto p-4">
|
|
3892
|
-
{/* Conditional rendering: Document editor or folder contents */}
|
|
3893
|
-
{showDocumentEditor ? (
|
|
3894
|
-
/* Document editor view */
|
|
3895
|
-
children
|
|
3896
|
-
) : (
|
|
3897
|
-
/* Folder contents view */
|
|
3898
|
-
<DriveContents />
|
|
3899
|
-
)}
|
|
3900
|
-
</div>
|
|
3901
|
-
</div>
|
|
3902
|
-
);
|
|
3903
|
-
}
|
|
3904
|
-
`.raw;
|
|
3905
|
-
//#endregion
|
|
3906
|
-
//#region src/templates/drive-editor/components/EmptyState.ts
|
|
3907
|
-
const emptyStateFileTemplate = tsx`
|
|
3908
|
-
import { useNodesInSelectedDriveOrFolder } from "@powerhousedao/reactor-browser";
|
|
3909
|
-
|
|
3910
|
-
/** Shows a message when the selected drive or folder is empty */
|
|
3911
|
-
export function EmptyState() {
|
|
3912
|
-
const nodes = useNodesInSelectedDriveOrFolder();
|
|
3913
|
-
const hasNodes = nodes.length > 0;
|
|
3914
|
-
if (hasNodes) return null;
|
|
3915
|
-
|
|
3916
|
-
return (
|
|
3917
|
-
<div className="py-12 text-center text-gray-500">
|
|
3918
|
-
<p className="text-lg">This folder is empty</p>
|
|
3919
|
-
<p className="mt-2 text-sm">Create your first document or folder below</p>
|
|
3920
|
-
</div>
|
|
3921
|
-
);
|
|
3922
|
-
}
|
|
3923
|
-
`.raw;
|
|
3924
|
-
//#endregion
|
|
3925
|
-
//#region src/templates/drive-editor/components/Files.ts
|
|
3926
|
-
const driveEditorFilesFileTemplate = () => tsx`
|
|
3927
|
-
import { FileItem } from "@powerhousedao/design-system/connect";
|
|
3928
|
-
import {
|
|
3929
|
-
useNodesInSelectedDriveOrFolder,
|
|
3930
|
-
isFileNodeKind,
|
|
3931
|
-
} from "@powerhousedao/reactor-browser";
|
|
3932
|
-
|
|
3933
|
-
/** Shows the files in the selected drive or folder */
|
|
3934
|
-
export function Files() {
|
|
3935
|
-
const nodes = useNodesInSelectedDriveOrFolder();
|
|
3936
|
-
const fileNodes = nodes.filter((n) => isFileNodeKind(n));
|
|
3937
|
-
const hasFiles = fileNodes.length > 0;
|
|
3938
|
-
|
|
3939
|
-
if (!hasFiles) return null;
|
|
3940
|
-
|
|
3941
|
-
return (
|
|
3942
|
-
<div>
|
|
3943
|
-
<h3 className="mb-2 text-sm font-semibold text-gray-600">Documents</h3>
|
|
3944
|
-
<div className="flex flex-wrap gap-4">
|
|
3945
|
-
{fileNodes.map((fileNode) => (
|
|
3946
|
-
<FileItem key={fileNode.id} fileNode={fileNode} />
|
|
3947
|
-
))}
|
|
3948
|
-
</div>
|
|
3949
|
-
</div>
|
|
3950
|
-
);
|
|
3951
|
-
}
|
|
3952
|
-
|
|
3953
|
-
`.raw;
|
|
3954
|
-
//#endregion
|
|
3955
|
-
//#region src/templates/drive-editor/components/Folders.ts
|
|
3956
|
-
const driveEditorFoldersFileTemplate = () => tsx`
|
|
3957
|
-
import { FolderItem } from "@powerhousedao/design-system/connect";
|
|
3958
|
-
import {
|
|
3959
|
-
useNodesInSelectedDriveOrFolder,
|
|
3960
|
-
isFolderNodeKind,
|
|
3961
|
-
} from "@powerhousedao/reactor-browser";
|
|
3962
|
-
|
|
3963
|
-
/** Shows the folders in the selected drive or folder */
|
|
3964
|
-
export function Folders() {
|
|
3965
|
-
const nodes = useNodesInSelectedDriveOrFolder();
|
|
3966
|
-
const folderNodes = nodes.filter((n) => isFolderNodeKind(n));
|
|
3967
|
-
const hasFolders = folderNodes.length > 0;
|
|
3968
|
-
if (!hasFolders) return null;
|
|
3969
|
-
|
|
3970
|
-
return (
|
|
3971
|
-
<div>
|
|
3972
|
-
<h3 className="mb-2 text-sm font-bold text-gray-600">Folders</h3>
|
|
3973
|
-
<div className="flex flex-wrap gap-4">
|
|
3974
|
-
{folderNodes.map((folderNode) => (
|
|
3975
|
-
<FolderItem key={folderNode.id} folderNode={folderNode} />
|
|
3976
|
-
))}
|
|
3977
|
-
</div>
|
|
3978
|
-
</div>
|
|
3979
|
-
);
|
|
3980
|
-
}
|
|
3981
|
-
`.raw;
|
|
3982
|
-
//#endregion
|
|
3983
|
-
//#region src/templates/drive-editor/components/FolderTree.ts
|
|
3984
|
-
const folderTreeFileTemplate = tsx`
|
|
3985
|
-
import {
|
|
3986
|
-
Sidebar,
|
|
3987
|
-
SidebarProvider,
|
|
3988
|
-
type SidebarNode,
|
|
3989
|
-
} from "@powerhousedao/document-engineering";
|
|
3990
|
-
import {
|
|
3991
|
-
setSelectedNode,
|
|
3992
|
-
useNodesInSelectedDrive,
|
|
3993
|
-
useSelectedDrive,
|
|
3994
|
-
useSelectedNode,
|
|
3995
|
-
} from "@powerhousedao/reactor-browser";
|
|
3996
|
-
import type { Node } from "@powerhousedao/shared/document-drive";
|
|
3997
|
-
import { useMemo } from "react";
|
|
3998
|
-
|
|
3999
|
-
function buildSidebarNodes(
|
|
4000
|
-
nodes: Node[],
|
|
4001
|
-
parentId: string | null | undefined,
|
|
4002
|
-
): SidebarNode[] {
|
|
4003
|
-
return nodes
|
|
4004
|
-
.filter((n) => {
|
|
4005
|
-
if (parentId == null) {
|
|
4006
|
-
return n.parentFolder == null;
|
|
4007
|
-
}
|
|
4008
|
-
return n.parentFolder === parentId;
|
|
4009
|
-
})
|
|
4010
|
-
.map((node): SidebarNode => {
|
|
4011
|
-
if (node.kind === "folder") {
|
|
4012
|
-
return {
|
|
4013
|
-
id: node.id,
|
|
4014
|
-
title: node.name,
|
|
4015
|
-
icon: "FolderClose" as const,
|
|
4016
|
-
expandedIcon: "FolderOpen" as const,
|
|
4017
|
-
children: buildSidebarNodes(nodes, node.id),
|
|
4018
|
-
};
|
|
4019
|
-
}
|
|
4020
|
-
return {
|
|
4021
|
-
id: node.id,
|
|
4022
|
-
title: node.name,
|
|
4023
|
-
icon: "File" as const,
|
|
4024
|
-
};
|
|
4037
|
+
expect(${isPhDocumentOfTypeFunctionName}(updatedDocument)).toBe(true);
|
|
4038
|
+
expect(updatedDocument.operations.${scope}).toHaveLength(1);
|
|
4039
|
+
expect(updatedDocument.operations.${scope}[0].action.type).toBe(
|
|
4040
|
+
"${constantCaseActionName}",
|
|
4041
|
+
);
|
|
4042
|
+
expect(updatedDocument.operations.${scope}[0].action.input).toStrictEqual(input);
|
|
4043
|
+
expect(updatedDocument.operations.${scope}[0].index).toEqual(0);
|
|
4025
4044
|
});
|
|
4045
|
+
`.raw;
|
|
4026
4046
|
}
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
children: buildSidebarNodes(nodes, null),
|
|
4038
|
-
},
|
|
4039
|
-
];
|
|
4047
|
+
function makeActionImportNames(v) {
|
|
4048
|
+
const actionNames = makeCamelCaseActionNamesForImport(v.actions);
|
|
4049
|
+
const inputSchemaNames = makeActionInputSchemasForImport(v.actions);
|
|
4050
|
+
return [
|
|
4051
|
+
"reducer",
|
|
4052
|
+
"utils",
|
|
4053
|
+
v.isPhDocumentOfTypeFunctionName,
|
|
4054
|
+
...actionNames,
|
|
4055
|
+
...inputSchemaNames
|
|
4056
|
+
];
|
|
4040
4057
|
}
|
|
4041
|
-
|
|
4058
|
+
function makeActionsImports(v) {
|
|
4059
|
+
return ts`
|
|
4060
|
+
import {
|
|
4061
|
+
${makeActionImportNames(v).join("\n")}
|
|
4062
|
+
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
4063
|
+
`.raw;
|
|
4064
|
+
}
|
|
4065
|
+
function makeTestCasesForActions(actions, isPhDocumentOfTypeFunctionName) {
|
|
4066
|
+
return actions.map((action) => makeTestCaseForAction(action, isPhDocumentOfTypeFunctionName)).join("\n\n");
|
|
4067
|
+
}
|
|
4068
|
+
const documentModelOperationsModuleTestFileTemplate = (v) => ts`
|
|
4042
4069
|
/**
|
|
4043
|
-
*
|
|
4044
|
-
*
|
|
4070
|
+
* This is a scaffold file meant for customization:
|
|
4071
|
+
* - change it by adding new tests or modifying the existing ones
|
|
4045
4072
|
*/
|
|
4046
|
-
export function FolderTree() {
|
|
4047
|
-
const [selectedDrive] = useSelectedDrive();
|
|
4048
|
-
const nodes = useNodesInSelectedDrive();
|
|
4049
|
-
const selectedNode = useSelectedNode();
|
|
4050
|
-
const driveName = selectedDrive.header.name;
|
|
4051
|
-
// Transform Node[] to hierarchical SidebarNode structure
|
|
4052
|
-
const sidebarNodes = useMemo(
|
|
4053
|
-
() => transformNodesToSidebarNodes(nodes || [], driveName),
|
|
4054
|
-
[nodes, driveName],
|
|
4055
|
-
);
|
|
4056
4073
|
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
}
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4074
|
+
import { describe, it, expect } from 'vitest';
|
|
4075
|
+
import { generateMock } from '@powerhousedao/common';
|
|
4076
|
+
import {
|
|
4077
|
+
reducer,
|
|
4078
|
+
utils,
|
|
4079
|
+
${v.isPhDocumentOfTypeFunctionName},
|
|
4080
|
+
${makeCamelCaseActionNamesForImport(v.actions)},
|
|
4081
|
+
${makeActionInputSchemasForImport(v.actions)},
|
|
4082
|
+
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
4083
|
+
|
|
4084
|
+
describe("${makeModuleOperationsTypeName(v.module)}", () => {
|
|
4085
|
+
${makeTestCasesForActions(v.actions, v.isPhDocumentOfTypeFunctionName)}
|
|
4086
|
+
});
|
|
4070
4087
|
|
|
4071
|
-
return (
|
|
4072
|
-
<SidebarProvider nodes={sidebarNodes}>
|
|
4073
|
-
<Sidebar
|
|
4074
|
-
className="pt-1"
|
|
4075
|
-
nodes={sidebarNodes}
|
|
4076
|
-
activeNodeId={activeNodeId}
|
|
4077
|
-
onActiveNodeChange={handleActiveNodeChange}
|
|
4078
|
-
sidebarTitle="Drive Explorer"
|
|
4079
|
-
showSearchBar={true}
|
|
4080
|
-
resizable={true}
|
|
4081
|
-
allowPinning={false}
|
|
4082
|
-
showStatusFilter={false}
|
|
4083
|
-
initialWidth={256}
|
|
4084
|
-
defaultLevel={2}
|
|
4085
|
-
/>
|
|
4086
|
-
</SidebarProvider>
|
|
4087
|
-
);
|
|
4088
|
-
}
|
|
4089
4088
|
`.raw;
|
|
4090
4089
|
//#endregion
|
|
4091
|
-
//#region src/templates/
|
|
4092
|
-
const
|
|
4093
|
-
import {
|
|
4090
|
+
//#region src/templates/document-model/upgrades/upgrade-manifest.ts
|
|
4091
|
+
const upgradeManifestTemplate = (v) => ts`
|
|
4092
|
+
import type { UpgradeManifest } from "document-model";
|
|
4093
|
+
import { latestVersion, supportedVersions } from "./versions.js";
|
|
4094
4094
|
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
}
|
|
4103
|
-
`.raw;
|
|
4095
|
+
export const ${v.upgradeManifestName}: UpgradeManifest<typeof supportedVersions> = {
|
|
4096
|
+
documentType: "${v.documentModelId}",
|
|
4097
|
+
latestVersion,
|
|
4098
|
+
supportedVersions,
|
|
4099
|
+
upgrades: {},
|
|
4100
|
+
};
|
|
4101
|
+
`.raw;
|
|
4104
4102
|
//#endregion
|
|
4105
|
-
//#region src/templates/
|
|
4106
|
-
const
|
|
4107
|
-
import type {
|
|
4103
|
+
//#region src/templates/document-model/upgrades/upgrade-transition.ts
|
|
4104
|
+
const upgradeTransitionTemplate = (v) => ts`
|
|
4105
|
+
import type { Action, PHDocument, UpgradeTransition } from "document-model";
|
|
4106
|
+
import type { ${v.phStateName} as StateV${v.previousVersion} } from "${v.documentModelPackageImportPath}/v${v.previousVersion}";
|
|
4107
|
+
import type { ${v.phStateName} as StateV${v.version} } from "${v.documentModelPackageImportPath}/v${v.version}";
|
|
4108
4108
|
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4109
|
+
function upgradeReducer(
|
|
4110
|
+
document: PHDocument<StateV${v.previousVersion}>,
|
|
4111
|
+
action: Action,
|
|
4112
|
+
): PHDocument<StateV${v.version}> {
|
|
4113
|
+
return {
|
|
4114
|
+
...document,
|
|
4115
|
+
};
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
export const v${v.version}: UpgradeTransition = {
|
|
4119
|
+
toVersion: ${v.version},
|
|
4120
|
+
upgradeReducer,
|
|
4121
|
+
description: "",
|
|
4113
4122
|
};
|
|
4114
4123
|
`.raw;
|
|
4115
4124
|
//#endregion
|
|
4116
|
-
//#region src/templates/
|
|
4117
|
-
const
|
|
4118
|
-
import {
|
|
4119
|
-
import type {
|
|
4120
|
-
import {
|
|
4121
|
-
import
|
|
4125
|
+
//#region src/templates/document-model/utils.ts
|
|
4126
|
+
const documentModelUtilsTemplate = ({ phStateName, pascalCaseDocumentType }) => ts`
|
|
4127
|
+
import type { DocumentModelUtils } from "document-model";
|
|
4128
|
+
import type { ${phStateName} } from "./gen/types.js";
|
|
4129
|
+
import { utils as genUtils } from "./gen/utils.js";
|
|
4130
|
+
import * as customUtils from "./src/utils.js";
|
|
4122
4131
|
|
|
4123
|
-
/**
|
|
4124
|
-
export
|
|
4125
|
-
// set the config for this drive editor
|
|
4126
|
-
// you can update these configs in \`./config.ts\`
|
|
4127
|
-
useSetPHDriveEditorConfig(editorConfig);
|
|
4128
|
-
return (
|
|
4129
|
-
<div className="bg-gray-50 p-6">
|
|
4130
|
-
<DriveExplorer {...props} />
|
|
4131
|
-
</div>
|
|
4132
|
-
);
|
|
4133
|
-
}
|
|
4132
|
+
/** Utils for the ${pascalCaseDocumentType} document model */
|
|
4133
|
+
export const utils: DocumentModelUtils<${phStateName}> = { ...genUtils, ...customUtils };
|
|
4134
4134
|
`.raw;
|
|
4135
4135
|
//#endregion
|
|
4136
4136
|
//#region src/templates/processors/utils.ts
|
|
@@ -4165,6 +4165,12 @@ export const ${v.camelCaseName}FactoryBuilder: ProcessorFactoryBuilder = (module
|
|
|
4165
4165
|
}
|
|
4166
4166
|
`.raw;
|
|
4167
4167
|
//#endregion
|
|
4168
|
+
//#region src/templates/processors/analytics/index.ts
|
|
4169
|
+
const analyticsIndexTemplate = ts`
|
|
4170
|
+
export * from "./factory.js";
|
|
4171
|
+
export * from "./processor.js";
|
|
4172
|
+
`.raw;
|
|
4173
|
+
//#endregion
|
|
4168
4174
|
//#region src/templates/processors/analytics/processor.ts
|
|
4169
4175
|
const analyticsProcessorTemplate = (v) => ts`
|
|
4170
4176
|
import type { AnalyticsSeriesInput, AnalyticsPath, IAnalyticsStore } from "@powerhousedao/analytics-engine-core";
|
|
@@ -4197,10 +4203,11 @@ export class ${v.pascalCaseName} implements IProcessor {
|
|
|
4197
4203
|
}
|
|
4198
4204
|
`.raw;
|
|
4199
4205
|
//#endregion
|
|
4200
|
-
//#region src/templates/processors/
|
|
4201
|
-
const
|
|
4202
|
-
|
|
4203
|
-
|
|
4206
|
+
//#region src/templates/processors/factory-builders.ts
|
|
4207
|
+
const factoryBuildersTemplate = ts`
|
|
4208
|
+
import type { ProcessorFactoryBuilder } from "@powerhousedao/reactor";
|
|
4209
|
+
|
|
4210
|
+
export const processorFactoryBuilders: ProcessorFactoryBuilder[] = [];
|
|
4204
4211
|
`.raw;
|
|
4205
4212
|
//#endregion
|
|
4206
4213
|
//#region src/templates/processors/factory.ts
|
|
@@ -4243,13 +4250,6 @@ export const processorFactory = async (module: IProcessorHostModule) => {
|
|
|
4243
4250
|
};
|
|
4244
4251
|
`.raw;
|
|
4245
4252
|
//#endregion
|
|
4246
|
-
//#region src/templates/processors/factory-builders.ts
|
|
4247
|
-
const factoryBuildersTemplate = ts`
|
|
4248
|
-
import type { ProcessorFactoryBuilder } from "@powerhousedao/reactor";
|
|
4249
|
-
|
|
4250
|
-
export const processorFactoryBuilders: ProcessorFactoryBuilder[] = [];
|
|
4251
|
-
`.raw;
|
|
4252
|
-
//#endregion
|
|
4253
4253
|
//#region src/templates/processors/index.ts
|
|
4254
4254
|
const processorsIndexTemplate = ts`
|
|
4255
4255
|
/**
|
|
@@ -4306,6 +4306,29 @@ export * from "./factory.js";
|
|
|
4306
4306
|
export * from "./processor.js";
|
|
4307
4307
|
`.raw;
|
|
4308
4308
|
//#endregion
|
|
4309
|
+
//#region src/templates/processors/relational-db/migrations.ts
|
|
4310
|
+
const relationalDbMigrationsTemplate = () => ts`
|
|
4311
|
+
import type { IRelationalDb } from "@powerhousedao/reactor-browser"
|
|
4312
|
+
|
|
4313
|
+
export async function up(db: IRelationalDb<any>): Promise<void> {
|
|
4314
|
+
// Create table
|
|
4315
|
+
await db.schema
|
|
4316
|
+
.createTable("todo")
|
|
4317
|
+
.addColumn("task", "varchar(255)")
|
|
4318
|
+
.addColumn("status", "boolean")
|
|
4319
|
+
.addPrimaryKeyConstraint("todo_pkey", [
|
|
4320
|
+
"task"
|
|
4321
|
+
])
|
|
4322
|
+
.ifNotExists()
|
|
4323
|
+
.execute();
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
export async function down(db: IRelationalDb<any>): Promise<void> {
|
|
4327
|
+
// drop table
|
|
4328
|
+
await db.schema.dropTable("todo").execute();
|
|
4329
|
+
}
|
|
4330
|
+
`.raw;
|
|
4331
|
+
//#endregion
|
|
4309
4332
|
//#region src/templates/processors/relational-db/processor.ts
|
|
4310
4333
|
const defaultNamespaceComment = "// Default namespace: `${this.name}_${driveId.replaceAll(\"-\", \"_\")}`";
|
|
4311
4334
|
const relationalDbProcessorTemplate = (v) => ts`
|
|
@@ -4334,29 +4357,6 @@ export class ${v.pascalCaseName} extends RelationalDbProcessor<DB> {
|
|
|
4334
4357
|
}
|
|
4335
4358
|
`.raw;
|
|
4336
4359
|
//#endregion
|
|
4337
|
-
//#region src/templates/processors/relational-db/migrations.ts
|
|
4338
|
-
const relationalDbMigrationsTemplate = () => ts`
|
|
4339
|
-
import type { IRelationalDb } from "@powerhousedao/reactor-browser"
|
|
4340
|
-
|
|
4341
|
-
export async function up(db: IRelationalDb<any>): Promise<void> {
|
|
4342
|
-
// Create table
|
|
4343
|
-
await db.schema
|
|
4344
|
-
.createTable("todo")
|
|
4345
|
-
.addColumn("task", "varchar(255)")
|
|
4346
|
-
.addColumn("status", "boolean")
|
|
4347
|
-
.addPrimaryKeyConstraint("todo_pkey", [
|
|
4348
|
-
"task"
|
|
4349
|
-
])
|
|
4350
|
-
.ifNotExists()
|
|
4351
|
-
.execute();
|
|
4352
|
-
}
|
|
4353
|
-
|
|
4354
|
-
export async function down(db: IRelationalDb<any>): Promise<void> {
|
|
4355
|
-
// drop table
|
|
4356
|
-
await db.schema.dropTable("todo").execute();
|
|
4357
|
-
}
|
|
4358
|
-
`.raw;
|
|
4359
|
-
//#endregion
|
|
4360
4360
|
//#region src/templates/processors/relational-db/schema.ts
|
|
4361
4361
|
const relationalDbSchemaTemplate = () => ts`
|
|
4362
4362
|
export interface Todo {
|
|
@@ -4606,6 +4606,6 @@ function camel(name) {
|
|
|
4606
4606
|
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
4607
4607
|
}
|
|
4608
4608
|
//#endregion
|
|
4609
|
-
export {
|
|
4609
|
+
export { tsConfigTemplate as $, documentModelHooksFileTemplate as A, claudeSettingsLocalTemplate as At, getModuleExportType as B, appDriveContentsFileTemplate as Bt, makeActionsImports as C, documentModelsIndexTemplate as Ct, documentModelSrcIndexFileTemplate as D, dockerfileTemplate as Dt, documentModelSrcUtilsTemplate as E, nginxConfTemplate as Et, documentModelPhFactoriesFileTemplate as F, folderTreeFileTemplate as Ft, documentModelGenControllerFileTemplate as G, documentModelDocumentTypeTemplate as H, documentModelOperationsModuleOperationsFileTemplate as I, appFoldersFileTemplate as It, documentEditorModuleFileTemplate as J, documentModelGenActionsFileTemplate as K, documentModelOperationsModuleErrorFileTemplate as L, appFilesFileTemplate as Lt, documentModelGenTypesTemplate as M, appEditorFileTemplate as Mt, documentModelSchemaIndexTemplate as N, appConfigFileTemplate as Nt, documentModelModuleFileTemplate as O, connectEntrypointTemplate as Ot, documentModelGenReducerFileTemplate as P, driveExplorerNavigationBreadcrumbsFileTemplate as Pt, viteConfigTemplate as Q, documentModelOperationsModuleCreatorsFileTemplate as R, emptyStateFileTemplate as Rt, makeActionImportNames as S, editorsTemplate as St, documentModelTestFileTemplate as T, switchboardEntrypointTemplate as Tt, documentModelDocumentSchemaFileTemplate as U, documentModelGenIndexFileTemplate as V, createDocumentFileTemplate as Vt, documentModelGenCreatorsFileTemplate as W, docsFromCliHelpTemplate as X, documentEditorEditorFileTemplate as Y, vitestConfigTemplate as Z, analyticsFactoryTemplate as _, gitIgnoreTemplate as _t, subgraphLibFileTemplate as a, buildPowerhouseConfigTemplate as at, upgradeManifestTemplate as b, eslintConfigTemplate as bt, relationalDbProcessorTemplate as c, exportsTemplate as ct, relationalDbFactoryTemplate as d, mcpTemplate as dt, tsconfigPathsTemplate as et, processorsIndexTemplate as f, mainTsxTemplate as ft, analyticsIndexTemplate as g, indexHtmlTemplate as gt, analyticsProcessorTemplate as h, legacyIndexHtmlTemplate as ht, customSubgraphSchemaTemplate as i, powerhouseManifestTemplate as it, documentModelGenUtilsTemplate as j, agentsTemplate as jt, documentModelIndexTemplate as k, cursorMcpTemplate as kt, relationalDbMigrationsTemplate as l, packageJsonTemplate as lt, factoryBuildersTemplate as m, indexTsTemplate as mt, documentModelSubgraphSchemaTemplate as n, styleTemplate as nt, subgraphIndexFileTemplate as o, packageJsonExportsTemplate as ot, processorsFactoryTemplate as p, licenseTemplate as pt, documentModelRootActionsFileTemplate as q, customSubgraphResolversTemplate as r, readmeTemplate as rt, relationalDbSchemaTemplate as s, packageJsonScriptsTemplate as st, documentModelSubgraphResolversTemplate as t, subgraphsIndexTemplate as tt, relationalDbIndexTemplate as u, npmrcTemplate as ut, documentModelUtilsTemplate as v, syncAndPublishWorkflowTemplate as vt, makeTestCaseForAction as w, documentModelsTemplate as wt, documentModelOperationsModuleTestFileTemplate as x, editorsIndexTemplate as xt, upgradeTransitionTemplate as y, geminiSettingsTemplate as yt, documentModelOperationModuleActionsFileTemplate as z, driveExplorerFileTemplate as zt };
|
|
4610
4610
|
|
|
4611
|
-
//# sourceMappingURL=templates-
|
|
4611
|
+
//# sourceMappingURL=templates-DBvz_qPL.mjs.map
|