@thatopen/services 0.5.0 → 0.7.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.
Files changed (72) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +569 -0
  3. package/dist/cli.js +12 -23
  4. package/dist/index.cjs.js +2 -2
  5. package/dist/index.es.js +41 -49
  6. package/dist/vite-env.d.ts +1 -1
  7. package/docs/ai-quickstart.md +37 -0
  8. package/docs/builtin/paths.json +4 -0
  9. package/docs/cli/create.md +29 -29
  10. package/docs/cli/local-server.md +38 -38
  11. package/docs/cli/login.md +32 -32
  12. package/docs/cli/publish.md +31 -31
  13. package/docs/cli/run.md +26 -26
  14. package/docs/cli/serve.md +43 -43
  15. package/docs/cli/swap.md +30 -30
  16. package/docs/flow-architecture.md +389 -0
  17. package/docs/flow-plugin-guide.md +337 -0
  18. package/docs/platform-token.md +81 -0
  19. package/docs/revit-collab-quickstart.md +363 -173
  20. package/docs/rhino-interop-quickstart.md +391 -0
  21. package/package.json +1 -1
  22. package/resources/AGENTS.md +40 -40
  23. package/src/built-in/CDEManager/example.ts +214 -214
  24. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  25. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  26. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  27. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  28. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  29. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  30. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  31. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  32. package/src/built-in/UIManager/src/app/example.ts +97 -97
  33. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  34. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  35. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  36. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  37. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  38. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  39. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  40. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
  41. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  42. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
  43. package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -0
  44. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  45. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
  46. package/src/built-in/index.ts +570 -0
  47. package/src/cli/templates/app/index.html +35 -35
  48. package/src/cli/templates/app/package.json +32 -32
  49. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  50. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  51. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  52. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  53. package/src/cli/templates/app/src/globals.ts +1 -1
  54. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  55. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  56. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  57. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  58. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  59. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  60. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  61. package/src/cli/templates/app/tsconfig.json +16 -16
  62. package/src/cli/templates/app/vite.config.js +57 -57
  63. package/src/cli/templates/cloud-component/README.md +205 -205
  64. package/src/cli/templates/cloud-component/_thatopen +5 -5
  65. package/src/cli/templates/cloud-component/declarations.json +4 -4
  66. package/src/cli/templates/cloud-component/package.json +21 -21
  67. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  68. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  69. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  70. package/src/cli/templates/shared/AGENTS.md +8 -8
  71. package/src/cli/templates/shared/CLAUDE.md +1 -1
  72. package/src/cli/templates/shared/_gitignore +5 -5
@@ -0,0 +1,391 @@
1
+ # Rhino → Revit, start to finish
2
+
3
+ You are an assistant. Somebody is modelling in Rhino for a project whose model lives in Revit, and
4
+ they want what they draw to become real Revit elements. This is the whole path, in the order it
5
+ happens, with the numbers from a run that actually worked.
6
+
7
+ It assumes the collaboration side is already set up — a central published, a project on the
8
+ platform. If it is not, that is
9
+ **[the Revit collaboration quick start](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/revit-collab-quickstart.md)**, and it comes first.
10
+
11
+ ## What is actually happening
12
+
13
+ Five steps, and it is worth saying them plainly before touching anything, because the shape is what
14
+ people get wrong:
15
+
16
+ 1. Rhino **publishes** its model to the platform as a `.frag`. That is all the plug-in does. It
17
+ mentions Revit nowhere.
18
+ 2. A **converter** — code in the user's own app, not in either plug-in — reads that `.frag` and
19
+ decides what it means in Revit's vocabulary.
20
+ 3. The converter **queues a proposal**. It changes nothing.
21
+ 4. Somebody **in Revit accepts it**. Applying is a decision, and the decision belongs to the person
22
+ whose model it is.
23
+ 5. They **sync**, and it becomes a commit like any other.
24
+
25
+ **No element is ever tied to an element.** The only thing linking a Rhino object to a Revit column
26
+ is the converter's own note of what it asked for. Anybody proposing to store a link between the two
27
+ models is proposing the thing this architecture exists to avoid.
28
+
29
+ ## Before anything
30
+
31
+ Log in: **[Logging in to That Open Platform](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/platform-token.md)**. The Rhino plug-in reads
32
+ `~/.thatopen/config.json`, the same file the CLI writes. It deliberately does not read the Revit
33
+ add-in's session — same machine, but one plug-in reading another's private state is coupling nobody
34
+ can undo later.
35
+
36
+ Install the plug-in. **Rhino has to be closed**; the installer holds its files.
37
+
38
+ ```bash
39
+ thatopen rhino install
40
+ ```
41
+
42
+ ## Driving Rhino without a keyboard
43
+
44
+ The plug-in runs a local HTTP server, exactly like the Revit add-in. This is what lets you do the
45
+ whole job while the user watches, instead of dictating commands for them to type.
46
+
47
+ The port and the token are written to `%APPDATA%\ThatOpen\rhino-addin.json`:
48
+
49
+ ```json
50
+ { "port": 5620, "token": "…", "pid": 5436 }
51
+ ```
52
+
53
+ Every request is a POST with a JSON body and the header `X-RhinoFlow-Token`. **Never print the
54
+ token**, and never write it into a script you are about to show.
55
+
56
+ There is also a `script` endpoint that runs Rhino's own command line. Use it for drawing, which is
57
+ what it is good at. **Never use it for a command that asks a question** — the import command wants
58
+ the project id the first time it runs, and a scripted command has nobody to answer, so Rhino sits
59
+ there with no error and no progress. That is why `import-model` and `publish` take their answers as
60
+ parameters instead of being reachable through `script`.
61
+
62
+ ## 1. Bring the team's model into Rhino
63
+
64
+ ```
65
+ POST /import-model { "project": "<projectId>", "document": "<doc>" }
66
+ ```
67
+
68
+ ```json
69
+ { "ok": true, "model": "rstadvancedsampleproject.frag",
70
+ "layer": "ThatOpen reference — rstadvancedsampleproject",
71
+ "added": 907, "skipped": 0 }
72
+ ```
73
+
74
+ It lands on a **locked layer**, with no properties, and it cannot be sent back. Say why if the user
75
+ asks, because it looks like a limitation and it is a decision: a `.frag` is a tessellation — what a
76
+ Revit wall LOOKS like, not what it IS — so editing one in Rhino and returning it could only produce
77
+ a worse version of a model Revit already owns. The lock stops somebody editing a mesh that is really
78
+ somebody else's wall and then proposing that as a change.
79
+
80
+ If the folder holds more than one model the call comes back with `available` listing them and no
81
+ import. Ask which; do not pick the first. Somebody spending an afternoon modelling against the wrong
82
+ building is a bad afternoon.
83
+
84
+ ## 2. Find out where things are, before you place anything
85
+
86
+ Do not invent coordinates. Ask the model:
87
+
88
+ ```
89
+ POST /objects { "limit": 3000 }
90
+ ```
91
+
92
+ Each object comes back with `min`, `max`, `center`, its layer, and the user strings the import
93
+ stamped on it — including `ThatOpen:Category`, which is Revit's own category name.
94
+
95
+ **Group before you conclude.** Reading the first few rows of that list is not measuring, and it will
96
+ mislead you. Measured on the sample project: the first four columns in the list were 750×750, based
97
+ at Z −2500, 2500 tall. They were the basement — four out of seventy. The building's actual column is
98
+ **450×450, based at Z 0, 3500 tall**, which is 63 of the 70. Taking the section from the first rows
99
+ produced columns that were too fat and buried, twice.
100
+
101
+ The same query gives you the grid. Sort each row of columns by X, look at the gaps, and the midpoint
102
+ of a wide one is a defensible place for a new column — it sits under the beam spanning that bay.
103
+
104
+ ## 3. Place the columns
105
+
106
+ There is no "make me a column" endpoint, and there should not be. This plug-in moves data and
107
+ decides nothing about what any of it means; the moment it grows a command called `columns` it has an
108
+ opinion about buildings, and that opinion belongs in the converter. Rhino's own commands do the job:
109
+
110
+ ```
111
+ POST /script { "script": "_-Layer _New \"flow:columns\" _Current \"flow:columns\" _Enter" }
112
+ POST /script { "script": "_-Point -5544,-1413,0" }
113
+ POST /script { "script": "_-Box -5769,-1638,0 -5319,-1188,0 3500" }
114
+ ```
115
+
116
+ Verified: that produces a point at the given coordinate and a 450×450×3500 extrusion centred on it.
117
+ `_-Box` takes two opposite corners of the base and then a height, so a 450 square centred on
118
+ `(x, y)` runs from `(x−225, y−225)` to `(x+225, y+225)`.
119
+
120
+ **Place BOTH, and understand why.** The converter reads **points** on a layer and turns each into a
121
+ Revit column. It does not read solids. But a point is invisible at building scale, so a Rhino file of
122
+ "the new columns" looks like an empty file — impossible to work with and impossible to show anybody.
123
+ So the point is the data, and the box is the picture. Put them on sibling layers (`flow:columns` and
124
+ `flow:columns (view)`) so the converter's layer holds nothing but points.
125
+
126
+ Nothing downstream reads the box. If somebody later rewrites the converter to read solids, the point
127
+ layer goes away and everything else still works.
128
+
129
+ **Sizes and coordinates are in the DOCUMENT's units.** `POST /status` reports them. The sample
130
+ project is in millimetres, where a column of `0.45` is an invisible splinter. Converting from metres
131
+ behind the user's back would put everything a thousand times too far away.
132
+
133
+ ### The whole thing, worked
134
+
135
+ Four columns down one bay of the sample project, at the midpoints of a 7315 mm gap that repeats row
136
+ after row. This is exactly what was run:
137
+
138
+ ```
139
+ _-Layer _New "flow:columns" _Enter
140
+ _-Layer _New "flow:columns (view)" _Enter
141
+
142
+ _-Layer _Current "flow:columns" _Enter
143
+ _-Point -5544,-5769,0
144
+ _-Point -5544,-1413,0
145
+ _-Point -5544,530,0
146
+ _-Point -5544,6829,0
147
+
148
+ _-Layer _Current "flow:columns (view)" _Enter
149
+ _-Box -5769,-5994,0 -5319,-5544,0 3500
150
+ _-Box -5769,-1638,0 -5319,-1188,0 3500
151
+ _-Box -5769,305,0 -5319,755,0 3500
152
+ _-Box -5769,6604,0 -5319,7054,0 3500
153
+ ```
154
+
155
+ One `POST /script` per line, or several lines in one call. Check `objectsAfter` in the reply: it is
156
+ the only thing that tells a command that did nothing from one that worked.
157
+
158
+ ### And then anything else
159
+
160
+ Nothing above is about columns. It is: make a layer, put geometry on it, and let the converter say
161
+ what it means. The same shape covers whatever the user actually wants —
162
+
163
+ | want | command |
164
+ | --- | --- |
165
+ | a point | `_-Point x,y,z` |
166
+ | a box | `_-Box x1,y1,z1 x2,y2,z2 <height>` |
167
+ | a line | `_-Line x1,y1,z1 x2,y2,z2` |
168
+ | a circle | `_-Circle x,y,z <radius>` |
169
+ | a layer, made current | `_-Layer _New "name" _Current "name" _Enter` |
170
+
171
+ The leading `-` is what matters: it is Rhino's command-line form, which takes its arguments inline
172
+ instead of opening a dialog. Without it the command puts a window on screen and waits for a person,
173
+ and there is no person.
174
+
175
+ Two rules carry over to anything you draw here. The geometry the converter reads goes on **its own
176
+ layer, holding nothing else**, because the converter selects by layer and cannot tell your helper
177
+ geometry from your data. And the coordinates are always in the **document's units**.
178
+
179
+ ## 4. Publish
180
+
181
+ ```
182
+ POST /publish { "project": "<projectId>", "document": "<doc>", "name": "rhino-columns.frag" }
183
+ ```
184
+
185
+ ```json
186
+ { "ok": true, "file": "rhino-columns.frag", "items": 8, "withGeometry": 4, "bytes": 10529 }
187
+ ```
188
+
189
+ **Check that number.** Eight items is four points and four boxes. The first version of this uploaded
190
+ 915 items and 3.4 MB, because the 907 reference meshes went back up with everything else — a
191
+ tessellated copy of the Revit model, republished under the Rhino model's name. Objects stamped
192
+ `ThatOpen:Source` are excluded now, but if you ever see the item count in the hundreds, that is what
193
+ happened.
194
+
195
+ Points count as items without geometry. That is correct: they are the data the converter reads, not
196
+ something to draw.
197
+
198
+ ## 5. The converter
199
+
200
+ This is the part that is **not** in either plug-in, and should not be. It reads the published
201
+ `.frag`, decides what it means, and queues a proposal.
202
+
203
+ The worked example is `RhinoColumnsConverter`: points on a named layer become Revit columns of a
204
+ given family and type. It is written to be read and then rewritten — the next person wants walls
205
+ from curves, or louvres from blocks — but four steps survive any rewrite:
206
+
207
+ 1. read the source model's own data, in the source's own units
208
+ 2. ask what this converter already made, which comes from the queue and nowhere else
209
+ 3. decide: new ones are added, known ones updated, vanished ones removed
210
+ 4. queue one operation
211
+
212
+ It persists nothing of its own. Run it on another machine, or as a cloud component, and it behaves
213
+ identically, because everything it remembers it reads back from what it wrote to the platform.
214
+
215
+ The byte contract is **[Writing a flow plugin](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-plugin-guide.md)**, and the reasoning
216
+ behind the shape is **[the architecture notes](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-architecture.md)**.
217
+
218
+ Everything below is what a run of this cost to find out. None of it is guessable, and all of it
219
+ fails quietly when it is wrong.
220
+
221
+ ### What Rhino actually publishes
222
+
223
+ One item per Rhino object, category `IFCBUILDINGELEMENTPROXY`, carrying these attributes:
224
+
225
+ | attribute | example | note |
226
+ | --- | --- | --- |
227
+ | `Layer` | `flow:columns` | |
228
+ | `Type` | `Point` | `Point`, `Extrusion`, `Curve`, … |
229
+ | `Name` | | the object's name, usually empty |
230
+ | `Position` | `"-5544.4,-1413,0"` | a **string**, in the document's units |
231
+ | `Rhino:Definition` | | block definition, when there is one |
232
+
233
+ **The layers are published as items too**, category `IFCBUILDINGSTOREY`, geometry-less, carrying a
234
+ single `Name` attribute holding the layer's full path. They have no `Layer` and no `Position`. So a
235
+ converter that matches the layer name loosely, or that assumes every item it sees has a position,
236
+ reads a layer as a column. Test both fields, positively:
237
+
238
+ ```ts
239
+ if (attrs.Layer !== "flow:columns") continue;
240
+ if (attrs.Type !== "Point") continue; // and this is what separates points from everything else on it
241
+ ```
242
+
243
+ `Type` is Rhino's own geometry class name: `Point` for a point, `Extrusion` for a `_-Box`, `Curve`
244
+ for a line. It is the only thing distinguishing the data from the helper geometry if somebody ends up
245
+ putting both on one layer.
246
+
247
+ Reading it in the app, where the client is already authenticated:
248
+
249
+ ```ts
250
+ const res = await client.downloadFile(fileId);
251
+ const bytes = new Uint8Array(await res.arrayBuffer());
252
+ const model = FRAGS.EditUtils.getModelFromBuffer(bytes, false);
253
+ ```
254
+
255
+ `getModelFromBuffer(bytes, raw)`: `raw` is false for anything downloaded, because what the platform
256
+ stores is compressed. Attribute rows come back as JSON triples, `[name, value, type]`, one per call
257
+ to `row.data(j)`.
258
+
259
+ ### Units, and why the envelope has no units field
260
+
261
+ A proposal's numbers are in the terms of **what the target published**. Read Revit's export metadata
262
+ and it says so itself:
263
+
264
+ ```json
265
+ "data": { "units": "ft", "axes": "z-up", "note": "Revit's own terms, untouched. Operations are written in these." }
266
+ ```
267
+
268
+ Rhino publishes its document units in the same place, `model.metadata()`, under `data.units`. On the
269
+ sample project that is `Millimeters`. So the converter divides by **304.8**, and it is the converter's
270
+ job precisely because it is the only piece that knows both sides.
271
+
272
+ Check the conversion against something real instead of trusting the constant. An existing column in
273
+ the Revit export sits at `-30.19028688187479` ft. Times 304.8 that is **-9202 mm**, which is exactly
274
+ the bay edge measured in Rhino. That is a conversion proved, not assumed.
275
+
276
+ **Refuse unknown units.** Guessing a scale factor puts the columns a thousand times too far away, and
277
+ nothing about the result says why.
278
+
279
+ ### Building the payload
280
+
281
+ ```ts
282
+ const model = EU.getModelFromBuffer(EU.newModel({ raw: true }), true);
283
+ return EU.edit(model, requests, { raw: false }).model;
284
+ ```
285
+
286
+ `newModel` hands back uncompressed bytes, so `raw` is true on the way in and false on the way out.
287
+
288
+ **Every `CREATE_ITEM` needs its own `localId`.** Three requests sharing one id collapse into a single
289
+ item, with no error and no warning. The first attempt at this produced 1 item from 3 requests, which
290
+ would have queued a proposal for one column out of four. Count from 1, and leave `localId: 0` for the
291
+ `UPDATE_METADATA` request that carries the envelope.
292
+
293
+ ### Writing it to the platform
294
+
295
+ Three calls, in this order: create the visible index if it does not exist, upload the payload as a
296
+ hidden child of it, then write the entry including the id the upload returned.
297
+
298
+ ```ts
299
+ const created = await client.createFile({
300
+ file, name: "revitflow_ops.json",
301
+ parentFolderId: folderId, // parentFolderId, NOT folderId
302
+ projectId, // required
303
+ versionTag: "1",
304
+ });
305
+
306
+ const { hiddenFileId } = await client.createHiddenFile(
307
+ new File([payload], `${opId}.frag`), opsId,
308
+ );
309
+
310
+ await client.updateFile(opsId, { file, versionTag: `v-${Date.now()}` });
311
+ ```
312
+
313
+ Two failures paid for, both at runtime, both from code that compiled and read fine:
314
+
315
+ - **"A projectId is required to upload files."** The folder field was misnamed at the same time and
316
+ said nothing at all, because an unknown key is dropped in silence. Fix the `projectId` alone and
317
+ the queue gets created at the root of the project, where nothing looks for it.
318
+ - **"Duplicated entry"** on the update. The version tag was numbered by counting the entries: the
319
+ file is created with tag `"1"`, the first entry makes the list one long, and the update asks for
320
+ `"1"` again. Counting is wrong past that too, since removing an op makes the next one reuse a tag.
321
+ Use a timestamp. This is what the Revit add-in settled on.
322
+
323
+ Miss the id `createHiddenFile` returns and the payload is unreachable forever: hidden files have no
324
+ queryable name, so the visible index is the only place it can be written down. If the index write
325
+ fails after the upload, that payload is orphaned. Harmless, invisible, and worth knowing.
326
+
327
+ ### Do not learn the format from files on the machine
328
+
329
+ `PlatformClient.fromPlatformContext()` exists in the app, not in a script, so a Node one-liner cannot
330
+ read back what Rhino just published. The tempting fallback is the `.frag` files lying around under
331
+ `%APPDATA%\ThatOpen` and in `flow-reconcile`. They are somebody's earlier session. One of them held
332
+ four points on a layer called `Columnas`, left over from an older run, and reading it as current
333
+ would have built a converter against a layer name nobody was using any more.
334
+
335
+ The tables above are here so nobody has to do that. If you still want to see real bytes, publish
336
+ something small and read it back inside the app.
337
+
338
+ ## 6. Accept it in Revit
339
+
340
+ The Revit add-in's local API, `%APPDATA%\ThatOpen\revit-addin.json`, header `X-RevitFlow-Token`.
341
+
342
+ **List what is waiting:**
343
+ ```
344
+ POST /ops
345
+ ```
346
+
347
+ **Check one before offering it:**
348
+ ```
349
+ POST /op-check { "opId": "op-…" }
350
+ ```
351
+
352
+ It answers with the version the proposal was authored against and the version the model is on now.
353
+ A proposal does not go stale — it says "move this BY two feet", not "put it at this coordinate" — but
354
+ show the user both numbers and let them decide.
355
+
356
+ **Apply it, once they have said yes:**
357
+ ```
358
+ POST /apply-ops { "opId": "op-…" }
359
+ ```
360
+
361
+ Everything in the proposal happens in **one** Revit transaction: one undo, one entry in the history.
362
+ There is also a **Proposals** button on the ribbon, which shows the queue and lets a person accept or
363
+ refuse. Prefer it when there is somebody at the machine — accepting somebody else's change is a
364
+ decision, and a decision wants the thing being decided in front of you.
365
+
366
+ The change is in their local only. They still sync.
367
+
368
+ ## 7. Sync, and look at it
369
+
370
+ Revit's own Synchronize with Central. The add-in rides it: the work reaches the team and the commit
371
+ lands in the history, with the new columns in it, viewable in the app like any other change.
372
+
373
+ ## Traps, all of them paid for
374
+
375
+ - **Rhino must be closed to install or update the plug-in.** It holds its own files, and a partial
376
+ copy leaves a plug-in that is neither version.
377
+ - **`script` deadlocks on any command that asks a question.** Use the endpoints.
378
+ - **Units are the document's**, everywhere. Ask `POST /status` rather than assuming metres.
379
+ - **The converter reads points, not solids.** Somebody modelling a beautiful steel section will
380
+ convert nothing at all, with no error to explain it.
381
+ - **Do not publish what you imported.** If the item count is in the hundreds, reference geometry is
382
+ going back up.
383
+ - **Group before concluding anything about the model.** The first rows of a listing are a sample,
384
+ and on this project the sample was the exception twice running.
385
+ - **Every `CREATE_ITEM` needs its own `localId`.** Sharing one collapses them into a single item,
386
+ silently. Four columns asked for, one column proposed.
387
+ - **`parentFolderId`, not `folderId`, and `projectId` alongside it.** An unknown key is dropped
388
+ without a word, so only half the mistake reports itself.
389
+ - **Version tags are timestamps, not counts.** Numbering by how many entries there are collides on
390
+ the very first update.
391
+ - **Stray `.frag` files on the machine are not the current format.** They are an earlier session.
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "access": "public"
16
16
  },
17
17
  "private": false,
18
- "version": "0.5.0",
18
+ "version": "0.7.0",
19
19
  "main": "dist/index.cjs.js",
20
20
  "module": "dist/index.es.js",
21
21
  "types": "dist/index.d.ts",
@@ -1,41 +1,41 @@
1
- # Building on the That Open Platform — Agent Guide
2
-
3
- You are an AI assistant helping a user build a **BIM app** or **cloud component** on the
4
- That Open Platform using **`@thatopen/services`** (the `thatopen` CLI + client library).
5
-
6
- **Read this file first, then immediately load all indexes listed below before doing anything else.**
7
-
8
- > This guide is tool-agnostic: it works with any AI (Claude, Codex, your own). All local docs it
9
- > references ship inside the `@thatopen/services` package under `docs/`.
10
-
11
- ---
12
-
13
- ## Session start — load all indexes in parallel
14
-
15
- Do this before answering any question or writing any code. These are compact description indexes:
16
-
17
- | What | Where |
18
- |---|---|
19
- | Platform built-ins | `node_modules/@thatopen/services/docs/builtin/paths.json` |
20
- | Platform client API | `node_modules/@thatopen/services/docs/client/paths.json` |
21
- | CLI commands | `node_modules/@thatopen/services/docs/cli/paths.json` |
22
- | Engine components (`OBC`, `OBF`) | `https://raw.githubusercontent.com/ThatOpen/engine_components/refs/heads/main/examples/paths.json` |
23
- | Fragments (`FRAGS`) | `https://raw.githubusercontent.com/ThatOpen/engine_fragment/refs/heads/main/examples/paths.json` |
24
- | UI components (`BUI`) | `https://raw.githubusercontent.com/ThatOpen/engine_ui-components/refs/heads/main/examples/paths.json` — **skip** entries whose path contains `packages/obc` or `bim-grid` |
25
-
26
- Once you have these, you know everything available on the platform. Only then fetch a specific example file when you need implementation details.
27
-
28
- > **Path resolution:** all `path` entries inside the local indexes (`docs/`, `src/`) are relative to the `@thatopen/services` package root — prepend `node_modules/@thatopen/services/` when reading any of those files.
29
-
30
- ---
31
-
32
- ## How to work
33
-
34
- - **The scaffold is already a complete, working viewer** (model loading, spatial tree, properties).
35
- **Run it first** (`npm run dev`) to see it work, *then* extend it — don't rebuild a viewer from scratch.
36
- - **Propose a short plan and get the user's OK** before changing files. If scope is unclear, ask.
37
- - If something already exists in the indexes, use it — don't reimplement it.
38
-
39
- ## Hard rules (always apply)
40
-
1
+ # Building on the That Open Platform — Agent Guide
2
+
3
+ You are an AI assistant helping a user build a **BIM app** or **cloud component** on the
4
+ That Open Platform using **`@thatopen/services`** (the `thatopen` CLI + client library).
5
+
6
+ **Read this file first, then immediately load all indexes listed below before doing anything else.**
7
+
8
+ > This guide is tool-agnostic: it works with any AI (Claude, Codex, your own). All local docs it
9
+ > references ship inside the `@thatopen/services` package under `docs/`.
10
+
11
+ ---
12
+
13
+ ## Session start — load all indexes in parallel
14
+
15
+ Do this before answering any question or writing any code. These are compact description indexes:
16
+
17
+ | What | Where |
18
+ |---|---|
19
+ | Platform built-ins | `node_modules/@thatopen/services/docs/builtin/paths.json` |
20
+ | Platform client API | `node_modules/@thatopen/services/docs/client/paths.json` |
21
+ | CLI commands | `node_modules/@thatopen/services/docs/cli/paths.json` |
22
+ | Engine components (`OBC`, `OBF`) | `https://raw.githubusercontent.com/ThatOpen/engine_components/refs/heads/main/examples/paths.json` |
23
+ | Fragments (`FRAGS`) | `https://raw.githubusercontent.com/ThatOpen/engine_fragment/refs/heads/main/examples/paths.json` |
24
+ | UI components (`BUI`) | `https://raw.githubusercontent.com/ThatOpen/engine_ui-components/refs/heads/main/examples/paths.json` — **skip** entries whose path contains `packages/obc` or `bim-grid` |
25
+
26
+ Once you have these, you know everything available on the platform. Only then fetch a specific example file when you need implementation details.
27
+
28
+ > **Path resolution:** all `path` entries inside the local indexes (`docs/`, `src/`) are relative to the `@thatopen/services` package root — prepend `node_modules/@thatopen/services/` when reading any of those files.
29
+
30
+ ---
31
+
32
+ ## How to work
33
+
34
+ - **The scaffold is already a complete, working viewer** (model loading, spatial tree, properties).
35
+ **Run it first** (`npm run dev`) to see it work, *then* extend it — don't rebuild a viewer from scratch.
36
+ - **Propose a short plan and get the user's OK** before changing files. If scope is unclear, ask.
37
+ - If something already exists in the indexes, use it — don't reimplement it.
38
+
39
+ ## Hard rules (always apply)
40
+
41
41
  1. **All UI must be built with Lit**, using the web components from `@thatopen/ui` (`BUI`) — `bim-button`, `bim-panel`, `bim-panel-section`, `bim-toolbar`, `bim-dropdown`, `bim-input`, and the rest of `packages/core`. Always consult the design system before writing any UI: `https://raw.githubusercontent.com/ThatOpen/engine_ui-components/refs/heads/main/DESIGN.md`.