@thatopen/services 0.6.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 (71) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +485 -100
  3. package/dist/cli.js +12 -23
  4. package/dist/index.cjs.js +2 -2
  5. package/dist/index.es.js +41 -50
  6. package/dist/vite-env.d.ts +1 -1
  7. package/docs/ai-quickstart.md +18 -3
  8. package/docs/cli/create.md +29 -29
  9. package/docs/cli/local-server.md +38 -38
  10. package/docs/cli/login.md +32 -32
  11. package/docs/cli/publish.md +31 -31
  12. package/docs/cli/run.md +26 -26
  13. package/docs/cli/serve.md +43 -43
  14. package/docs/cli/swap.md +30 -30
  15. package/docs/flow-architecture.md +1 -1
  16. package/docs/flow-plugin-guide.md +1 -1
  17. package/docs/platform-token.md +81 -0
  18. package/docs/revit-collab-quickstart.md +89 -18
  19. package/docs/rhino-interop-quickstart.md +391 -0
  20. package/package.json +1 -1
  21. package/resources/AGENTS.md +40 -40
  22. package/src/built-in/CDEManager/example.ts +214 -214
  23. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  24. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  25. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  26. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  27. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  28. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  29. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  30. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  31. package/src/built-in/UIManager/src/app/example.ts +97 -97
  32. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  33. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  34. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  35. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  36. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  37. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  38. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  39. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
  40. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  41. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
  42. package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -70
  43. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  44. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
  45. package/src/built-in/index.ts +485 -100
  46. package/src/cli/templates/app/index.html +35 -35
  47. package/src/cli/templates/app/package.json +32 -32
  48. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  49. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  50. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  51. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  52. package/src/cli/templates/app/src/globals.ts +1 -1
  53. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  54. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  55. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  56. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  57. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  58. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  59. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  60. package/src/cli/templates/app/tsconfig.json +16 -16
  61. package/src/cli/templates/app/vite.config.js +57 -57
  62. package/src/cli/templates/cloud-component/README.md +205 -205
  63. package/src/cli/templates/cloud-component/_thatopen +5 -5
  64. package/src/cli/templates/cloud-component/declarations.json +4 -4
  65. package/src/cli/templates/cloud-component/package.json +21 -21
  66. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  67. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  68. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  69. package/src/cli/templates/shared/AGENTS.md +8 -8
  70. package/src/cli/templates/shared/CLAUDE.md +1 -1
  71. package/src/cli/templates/shared/_gitignore +5 -5
@@ -957,170 +957,555 @@ export interface RevitFlowCounts {
957
957
  /**
958
958
  * A revit-flow commit — one push of changes from the Revit add-in. `id` is a
959
959
  * monotonic integer; `parent` points at the previous commit (null for the
960
- * root). The commit whose scan was full (earliest with a .frag) carries the
961
- * WHOLE model in `revitflow_frag_<id>.frag`; later frags are incremental.
960
+ * root). The first commit is the baseline: its geometry IS the visible model
961
+ * file. Later commits carry only their delta geometry, in a HIDDEN .frag whose
962
+ * id is `deltaId`.
962
963
  */
963
964
  export interface RevitFlowCommit {
964
- id: number;
965
- parent: number | null;
965
+ /**
966
+ * WHAT this commit is. Identity, minted by whoever wrote it, meaningful outside the model it
967
+ * belongs to — which the old integer was not: that was the platform's storage version, a number
968
+ * the CDE handed out. An application that does not push through a Revit central has no such
969
+ * number, and an integer sequence asserts one line of history, which Revit's central enforces and
970
+ * nothing else has to.
971
+ */
972
+ guid: string;
973
+ /**
974
+ * The commits this one was built on. Absent on a root.
975
+ *
976
+ * A LIST, because a history that can only ever be a line is a different thing from a graph. A
977
+ * fork was already expressible with a single parent — two commits naming the same one — but a
978
+ * MERGE means naming both lines, and that needs this. The first entry is the line the commit
979
+ * continues, which is what lets the panel lay out lanes without guessing which side is the trunk.
980
+ *
981
+ * Revit never writes more than one: its central IS a single line and it merges inside itself
982
+ * rather than in this graph. The list is for the applications that are not Revit.
983
+ */
984
+ parents?: string[] | null;
985
+ /**
986
+ * WHERE this landed in the CDE, when there is such a place. Data, never identity, and never shown
987
+ * as the commit's name. Absent for any application with no version counter.
988
+ */
989
+ version?: number;
990
+ /**
991
+ * Position in this model's history, computed from the parent chain when the commits are read.
992
+ *
993
+ * Order questions — "is this commit before the one on screen?" — used to be asked of the integer
994
+ * id, which meant every application had to produce a sequence to be understood. They are asked of
995
+ * this instead: derived from the graph, so an application that has no numbers still answers them.
996
+ */
997
+ ordinal: number;
998
+ /**
999
+ * What the author said this commit was for, in their own words. Absent when nobody said
1000
+ * anything, which is most commits.
1001
+ *
1002
+ * In Revit it comes from the Comment box in its own Synchronize with Central dialog, which
1003
+ * people have been typing into for years while it went nowhere. Free text: never parsed, never
1004
+ * relied on for structure.
1005
+ */
1006
+ message?: string;
966
1007
  author: string;
967
1008
  source: string;
968
1009
  timestamp: string;
969
1010
  changes: RevitFlowChange[];
970
1011
  counts: RevitFlowCounts;
1012
+ /**
1013
+ * Hidden-file id of this commit's delta .frag (created+modified geometry).
1014
+ * Absent on the baseline/first commit and on geometry-less commits. The
1015
+ * platform's hidden-file API has no queryable name, so the delta is loaded by
1016
+ * this id, never by filename.
1017
+ */
1018
+ deltaId?: string;
1019
+ /**
1020
+ * Platform user id of the committer, recorded by the add-in from its API key. Lets the
1021
+ * UI resolve the author to a real project member (avatar + name) via getProjectData,
1022
+ * instead of deriving initials from the raw Revit username. Absent on history written
1023
+ * before this was recorded, or when the key isn't a readable JWT.
1024
+ */
1025
+ userId?: string;
1026
+ /**
1027
+ * Which loaded model this commit belongs to (the model file's id == the viewer's
1028
+ * modelId). Tagged in by the manager when it reads the history: commit ids are
1029
+ * per-model version numbers, so they repeat across models and can't identify a commit
1030
+ * on their own.
1031
+ */
1032
+ modelId?: string;
1033
+ /** The model's name AS SHOWN IN PROJECT FILES, for the cards and the model filter. */
1034
+ modelName?: string;
1035
+ /**
1036
+ * This commit re-exported the WHOLE model, so the project's visible .frag shows the
1037
+ * model as of here — what anyone opening it from Project Files gets, without the
1038
+ * history panel. True for the initial capture and for the periodic full exports the
1039
+ * team publishes; ordinary commits ship only their delta.
1040
+ */
1041
+ full?: boolean;
1042
+ /**
1043
+ * Interoperability operations this commit materialised. Present only when somebody in
1044
+ * Revit pulled a deferred edit: the commit is the native half of it, and this is the
1045
+ * link back to the operation that asked for it. Ordinary hand-authored commits, which
1046
+ * are nearly all of them, carry no such key at all.
1047
+ */
1048
+ appliedOps?: string[];
1049
+ /**
1050
+ * Set when this entry is NOT a commit but a deferred edit still waiting to be applied.
1051
+ *
1052
+ * Pending operations are surfaced commit-SHAPED rather than as a parallel kind of thing, so
1053
+ * they inherit search, the user and model filters, the change-kind chips and the
1054
+ * filter-by-selection behaviour instead of needing all of it reimplemented. It also means
1055
+ * one of them can be clicked and previewed: a `modify` or `delete` names elements that
1056
+ * already exist, which is exactly what the outline machinery consumes, so you can see what
1057
+ * a change will touch before anybody applies it. A `create` has nothing to show yet, which
1058
+ * is honest rather than missing.
1059
+ *
1060
+ * Everything that treats an entry as history must check this first: it has not happened.
1061
+ */
1062
+ pendingOp?: RevitFlowOp;
971
1063
  }
972
1064
  /** Shape of `revitflow_history.json`. */
973
1065
  export interface RevitFlowHistory {
974
1066
  model: string;
975
1067
  commits: RevitFlowCommit[];
976
1068
  }
1069
+ /**
1070
+ * One deferred edit waiting to be applied to a model.
1071
+ *
1072
+ * Operations are NOT commits and do not live in the graph: a commit is immutable past, an
1073
+ * operation is optional future. They sit above the model's HEAD until somebody opens the
1074
+ * model in Revit and applies one, at which point they become a commit and stop being
1075
+ * pending. Mirrors the add-in's `revitflow_ops.json`.
1076
+ */
1077
+ export interface RevitFlowOp {
1078
+ opId: string;
1079
+ /** Groups the operations one authored action produced across several models. */
1080
+ batchId?: string;
1081
+ /** Hidden-file id of the operation's .frag payload. */
1082
+ fragId?: string;
1083
+ title?: string;
1084
+ /** add | set | remove | mixed. Older queues say modify | create | delete. */
1085
+ verb?: string;
1086
+ author?: string;
1087
+ /**
1088
+ * Platform user id of whoever authored it. Resolved to a real project member exactly as a
1089
+ * commit's is, so the same person does not appear as "Antonio That Open" on their commits
1090
+ * and as their raw OS username on their operations.
1091
+ */
1092
+ userId?: string;
1093
+ createdAt?: string;
1094
+ /** The commit this operation was authored against. */
1095
+ /** The commit this was authored against, by guid. What a proposal actually refers to. */
1096
+ baseCommitGuid?: string;
1097
+ /** The platform version that commit landed in. Data, for reconciling with the CDE. */
1098
+ baseCommit?: number;
1099
+ /** How many elements it targets. */
1100
+ targets?: number;
1101
+ state?: "pending" | "applied" | "discarded" | string;
1102
+ /** The native commit that materialised it, once applied. */
1103
+ appliedInCommit?: number | null;
1104
+ /**
1105
+ * Native ids the operation actually touched or created, written by the add-in when it applied.
1106
+ * A FLAT list: it does not say which source object produced which result, because nothing in
1107
+ * this system ties an element to an element in another application.
1108
+ *
1109
+ * It is what lets an APPLIED proposal be selected and previewed like a commit, with no payload
1110
+ * fetch at all. Note which halves that leaves blind, because they are opposite ends:
1111
+ *
1112
+ * add pending → nothing exists yet · applied → these are what it made
1113
+ * set pending → its targets · applied → the same targets
1114
+ * remove pending → its targets · applied → nothing, they are gone
1115
+ *
1116
+ * So the only case still needing the payload is a PENDING set or remove, whose target ids live
1117
+ * in the operation's .frag rather than in this index.
1118
+ */
1119
+ affectedIds?: string[];
1120
+ /** Tagged in by the manager, exactly as commits are: which model this queue belongs to. */
1121
+ modelId?: string;
1122
+ modelName?: string;
1123
+ }
1124
+ /** Shape of `revitflow_ops.json`. */
1125
+ export interface RevitFlowOpsIndex {
1126
+ schema?: string;
1127
+ ops: RevitFlowOp[];
1128
+ }
977
1129
  /** Dominant change type of a commit, used to color its timeline node. */
978
1130
  export type DominantChange = "create" | "update" | "delete";
979
- /** Dominant change type of a commit: delete → create → update. */
1131
+ /** What to do with elements outside the selected commit. */
1132
+ export type ContextMode = "default" | "hidden";
1133
+ /** The three kinds of change a commit can carry. */
1134
+ export type ChangeKind = "create" | "update" | "delete";
1135
+ /**
1136
+ * Stable id for a commit across models — which is now just its guid.
1137
+ *
1138
+ * It used to have to be `${model}:${id}`, because the id was a per-model version number and every
1139
+ * model had a commit 3. A guid needs no help being unique, and that is exactly what it was for.
1140
+ */
1141
+ export declare const commitKey: (c: {
1142
+ guid?: string;
1143
+ pendingOp?: {
1144
+ opId: string;
1145
+ };
1146
+ }) => string;
1147
+ /** How the commit is shown in 3D: review colours, or the model as it ended up. */
1148
+ export type HighlightMode = "colored" | "plain";
1149
+ /** Dominant change type of a commit: delete → create → update. */
980
1150
  export declare function dominantChange(commit: RevitFlowCommit): DominantChange;
981
1151
  /**
982
- * GitHistoryManager — headless engine for the revit-flow git-history built-in.
1152
+ * GitHistoryManager — headless engine for the revit-flow git-history built-in.
983
1153
  *
984
- * Owns ALL viewer mutation: it fetches the commit history + fragment artifacts
985
- * from project storage, loads the baseline (full-model) .frag into the shared
986
- * viewer, and colors a commit's changed elements (create → green, update →
987
- * blue, delete → red) via named Highlighter styles. The Lit panel
988
- * (`top-git-history`) is intent-only: it calls these methods and subscribes to
989
- * the events below, and never touches the Highlighter / FragmentsManager
990
- * directly.
1154
+ * Owns ALL viewer mutation for the change outlines. It does NOT load the model —
1155
+ * the user opens a model from the Project Files panel, and this manager reacts to
1156
+ * that load: it reads the model's hidden `revitflow_history.json` (if any) and
1157
+ * publishes the commit list. Showing a commit outlines its changed elements
1158
+ * (create → green, update → blue, delete → red) via three independently-colored
1159
+ * `OBF.Outliner` groups — a cheap post-process outline that never recolors the
1160
+ * fragment geometry. The Lit panel (`top-git-history`) is intent-only: it calls
1161
+ * these methods and subscribes to the events below.
991
1162
  *
992
- * Data model (per model, in `bimterop/revit-<docId>/`):
993
- * - revitflow_history.json — { model, commits: [{ id, parent, author,
994
- * source, timestamp, changes:[{type,uniqueId}], counts }] }
995
- * - revitflow_frag_<N>.frag — fragments file for commit N. The earliest
996
- * commit with a frag is the FULL model (baseline); later frags are
997
- * incremental (only that commit's created+modified geometry).
1163
+ * Data model, per loaded model (fileId == modelId), all HIDDEN children of it:
1164
+ * - revitflow_history.json — { model, commits: [{ id, parent, author, source,
1165
+ * timestamp, changes:[{type,uniqueId}], counts }] }. The first commit is the
1166
+ * INITIAL full-model capture (its geometry == the visible baseline file).
1167
+ * - revitflow_frag_<N>.frag — delta fragments for commit N (its created+modified
1168
+ * geometry), loaded on demand so created elements can be colored.
998
1169
  */
999
1170
  declare class _GitHistoryManager extends OBC.Component {
1000
1171
  static readonly uuid: "3f9c1a7e-6b2d-4e18-9a5c-7d0e2f4b6c81";
1001
1172
  enabled: boolean;
1002
1173
  readonly onCommitsChanged: OBC.Event<RevitFlowCommit[]>;
1003
- readonly onCommitChanged: OBC.Event<number | null>;
1174
+ /** {@link commitKey} of the highlighted commit, or null when cleared. */
1175
+ readonly onCommitChanged: OBC.Event<string | null>;
1004
1176
  readonly onLoadingChanged: OBC.Event<boolean>;
1005
1177
  readonly onError: OBC.Event<string>;
1178
+ /** The GUID currently selected in the 3D view (null when the selection is cleared). */
1179
+ readonly onSelectedGuidChanged: OBC.Event<string | null>;
1006
1180
  world?: OBC.World;
1007
- /** When true, showCommit() ghosts (dims) every non-changed element. */
1008
- isolateChanges: boolean;
1009
- /** Green — created elements. */
1010
- readonly STYLE_CREATED = "#22c55e";
1011
- /** Blue — modified elements. */
1012
- readonly STYLE_MODIFIED = "#3b82f6";
1013
- /** Red — deleted elements. */
1014
- readonly STYLE_DELETED = "#ef4444";
1181
+ /**
1182
+ * What happens to elements that are NOT part of the selected commit:
1183
+ * "default" — left as they are (the change outlines sit on the full model)
1184
+ * "hidden" — hidden entirely, isolating the commit's elements
1185
+ *
1186
+ * A dimmed ("ghost") middle ground was dropped: the fragments x-ray writes depth
1187
+ * from the pixels its screen door keeps, so dimmed geometry still occluded the very
1188
+ * elements under review — worse the denser the model in front. Fixing that properly
1189
+ * belongs in the renderer, not here, so this offers the two states that are exact.
1190
+ */
1191
+ contextMode: ContextMode;
1192
+ /**
1193
+ * Which change kinds to outline. All three by default; the panel's filter buttons
1194
+ * narrow it (e.g. only deletions) without reloading anything.
1195
+ */
1196
+ typeFilter: Set<ChangeKind>;
1197
+ /**
1198
+ * How many commits either side of the selected one to keep pre-loaded, so stepping
1199
+ * through the timeline is instant instead of fetching on every click. The window
1200
+ * slides with the selection and everything outside it is disposed, which bounds
1201
+ * memory no matter how long the history gets.
1202
+ */
1203
+ prefetchRadius: number;
1204
+ /**
1205
+ * How a commit is presented in 3D:
1206
+ * "colored" — outline created/edited/deleted in their colours (the review lens)
1207
+ * "plain" — no outlines: the model simply as it stood AFTER the commit, so the
1208
+ * deleted elements are hidden rather than painted red
1209
+ */
1210
+ highlightMode: HighlightMode;
1211
+ /** @deprecated kept so older callers still compile — maps onto contextMode. */
1212
+ get isolateChanges(): boolean;
1213
+ /** Green — created elements. */
1214
+ readonly COLOR_CREATED = "#22c55e";
1215
+ /** Golden yellow — modified elements. */
1216
+ readonly COLOR_MODIFIED = "#e3b341";
1217
+ /** Red — deleted elements. */
1218
+ readonly COLOR_DELETED = "#ef4444";
1219
+ readonly GROUP_CREATED = "revitflow-created";
1220
+ readonly GROUP_MODIFIED = "revitflow-modified";
1221
+ readonly GROUP_DELETED = "revitflow-deleted";
1015
1222
  private _client?;
1016
1223
  private _projectId?;
1017
- private _docFolderId?;
1018
- private _modelName;
1019
- private _commits;
1020
- private _currentCommitId;
1021
- /** filename(lowercase) → fileId, for the doc folder's current listing. */
1022
- private _fileIndex;
1023
- /** modelIds (== frag fileIds) already loaded into the viewer. */
1024
- private _loadedFrags;
1224
+ /**
1225
+ * One entry per loaded revit-flow model. Commit ids are per-model version numbers, so
1226
+ * they collide across models — every commit is tagged with its modelId and a commit is
1227
+ * addressed by {@link commitKey}, never by id alone.
1228
+ */
1229
+ private _models;
1230
+ /** Which models the timeline shows. Empty = all of them. */
1231
+ private _selectedModels;
1232
+ /** `${modelId}:${id}` of the commit currently highlighted, or null. */
1233
+ private _currentKey;
1234
+ /** modelIds already probed for a sibling history (avoids re-probing). */
1235
+ private _probed;
1236
+ /** hiddenIds of delta frags this manager has loaded into the viewer. */
1237
+ private _loadedDeltas;
1025
1238
  /** Ghosted localIds per modelId, so isolate can be cleared/re-applied. */
1026
- private _ghosted;
1027
- private _baselineCommitId?;
1028
- private _stylesRegistered;
1239
+ private _contextApplied;
1240
+ /** localIds hidden because the shown commit deleted them (plain mode). */
1241
+ private _hiddenDeleted;
1242
+ /** localIds hidden because a newer visible source supplies the same element. */
1243
+ private _hiddenSuperseded;
1244
+ /** True once the three named Outliner groups have been created. */
1245
+ private _groupsReady;
1029
1246
  private _loading;
1247
+ private _wired;
1030
1248
  constructor(components: OBC.Components);
1031
1249
  get ready(): boolean;
1032
1250
  get loading(): boolean;
1251
+ /**
1252
+ * Commits of the selected models, newest last, interleaved by time so a multi-model
1253
+ * timeline reads chronologically rather than model-by-model.
1254
+ */
1033
1255
  get commits(): RevitFlowCommit[];
1034
- get currentCommitId(): number | null;
1256
+ /** True when this entry is a proposal rather than something that happened. */
1257
+ isProposal(commit: RevitFlowCommit | null | undefined): boolean;
1258
+ /**
1259
+ * The entry a proposal/commit links to, in whichever direction applies: a commit points at
1260
+ * the proposal it materialised, a proposal at the commit that did it. Returns null when
1261
+ * there is no counterpart, which is the normal case for hand-authored work.
1262
+ */
1263
+ counterpart(commit: RevitFlowCommit): RevitFlowCommit | null;
1264
+ /**
1265
+ * Deferred edits still waiting to be applied, across the selected models, oldest first.
1266
+ *
1267
+ * Kept separate from {@link commits} on purpose. A commit is something that happened; a
1268
+ * pending operation is something that might. Merging them into one list would force every
1269
+ * consumer to keep asking which kind it was holding.
1270
+ */
1271
+ get pendingOps(): RevitFlowOp[];
1272
+ /** The loaded revit-flow models, for the panel's model filter. */
1273
+ get models(): Array<{
1274
+ modelId: string;
1275
+ name: string;
1276
+ commits: number;
1277
+ }>;
1278
+ get selectedModels(): Set<string>;
1279
+ get currentKey(): string | null;
1280
+ /** Name of the single loaded model, or "" when there are none/several. */
1035
1281
  get modelName(): string;
1036
- get baselineCommitId(): number | undefined;
1037
- /** True if commit `id` has a fragments file available in storage. */
1038
- hasFrag(commitId: number): boolean;
1282
+ /** The commit behind {@link currentKey}, or undefined when nothing is highlighted. */
1283
+ private _currentCommit;
1284
+ /** Restrict the timeline to these models (empty set = show all of them). */
1285
+ setSelectedModels(modelIds: Iterable<string>): Promise<void>;
1286
+ /** True when `commit` is its model's baseline (the full-model capture). */
1287
+ isBaseline(commit: RevitFlowCommit): boolean;
1288
+ /** True if `commit` has a hidden delta .frag to load. */
1289
+ hasFrag(commit: RevitFlowCommit): boolean;
1039
1290
  /**
1040
- * Wire the platform client and load the commit history. Resolves the
1041
- * viewer world in the background (it may not exist until <top-viewer>
1042
- * mounts). Consumer panels call this once on connect.
1291
+ * Wire the platform client and subscribe to model loads. Does NOT load any
1292
+ * geometry: the manager only attaches to models the user opens from the Files
1293
+ * panel. Consumer panels call this once on connect.
1043
1294
  */
1044
1295
  init(client: PlatformClient): Promise<void>;
1045
1296
  /**
1046
- * (Re)fetch `revitflow_history.json` from the project's
1047
- * `bimterop/revit-*` folder and publish the commit list. Also indexes the
1048
- * folder's files so frag lookups are a local map hit.
1297
+ * Follow the viewer's selection so the element view tracks whatever the user clicks in
1298
+ * 3D — no "trace this" button to press. The Highlighter's events appear asynchronously
1299
+ * (they're registered when the viewer sets it up), so retry briefly until they exist.
1300
+ */
1301
+ private _watchSelection;
1302
+ private _onModelSet;
1303
+ private _onModelDeleted;
1304
+ /**
1305
+ * Probe a loaded model for a sibling visible `revitflow_history.json` in its
1306
+ * folder. If present, adopt it as the active model and publish its commits.
1307
+ * Silent for non-revit models and for our own delta frags — only genuine
1308
+ * fetch/parse failures raise onError.
1309
+ */
1310
+ private _probeModel;
1311
+ /**
1312
+ * Read the model's deferred-edit queue, if it has one.
1313
+ *
1314
+ * Best effort by design: a model that has never been the target of an operation simply has
1315
+ * no queue, and that is the normal case rather than an error. A failure here must never
1316
+ * cost the user their history, which is why it cannot throw out of the probe.
1317
+ */
1318
+ private _readOps;
1319
+ /**
1320
+ * Re-scan every currently-loaded model for a hidden revit-flow history (the
1321
+ * panel's Refresh button). Clears the probe cache so models load-order edge
1322
+ * cases re-resolve.
1049
1323
  */
1050
1324
  loadHistory(): Promise<void>;
1051
1325
  /**
1052
- * Show commit `commitId`: ensure the baseline (full-model) .frag is loaded,
1053
- * load the commit's own incremental frag (so created elements are present),
1054
- * then color its changed elements. Deleted elements that no longer exist in
1055
- * any loaded model are skipped. Fires onCommitChanged.
1326
+ * Show `commit`. The baseline geometry is the visible model the user already loaded, so
1327
+ * the baseline view just clears outlines. Any other commit pulls its hidden delta frag
1328
+ * in (so created elements exist) and outlines its changed elements. Fires
1329
+ * onCommitChanged. Takes the commit itself, not an id: ids repeat across models.
1330
+ */
1331
+ showCommit(commit: RevitFlowCommit): Promise<void>;
1332
+ /**
1333
+ * Keep the deltas of the commits around `centerId` loaded and drop the rest, so
1334
+ * stepping through the timeline never stalls and memory stays bounded: moving from
1335
+ * commit 10 to 11 fetches one delta and disposes one, instead of reloading a window.
1336
+ */
1337
+ private _slidePrefetchWindow;
1338
+ /**
1339
+ * Show the model as it stood AT `commit`: the visible baseline is the model at its
1340
+ * INITIAL commit, and everything created later lives in per-commit delta .frags loaded
1341
+ * as separate models. Those deltas stay loaded (the prefetch window keeps neighbours
1342
+ * warm), so stepping BACK has to hide the ones from later commits — otherwise a wall
1343
+ * added in commit 3 was still sitting there while you were looking at commit 2.
1344
+ * Only touches deltas of the commit's own model; other models keep their own state.
1056
1345
  */
1057
- showCommit(commitId: number): Promise<void>;
1058
- /** Remove all revit-flow coloring + ghosting and reset the selection. */
1346
+ private _applyFutureVisibility;
1347
+ /** Which commit a loaded delta belongs to (deltas are keyed by their hidden id). */
1348
+ private _commitOfDelta;
1349
+ /** Make every loaded delta visible again (the model as of its latest commit). */
1350
+ private _showAllDeltas;
1351
+ /** Remove all revit-flow outlines + ghosting and reset the selection. */
1059
1352
  clear(): Promise<void>;
1060
- /** Toggle isolate mode; re-applies to the current commit if one is shown. */
1353
+ /** Set what happens to non-commit elements; re-applies to the current commit. */
1354
+ setContextMode(mode: ContextMode): Promise<void>;
1355
+ /**
1356
+ * Every commit that touched `uniqueId`, oldest first, with what happened to it there.
1357
+ * The element's life story: created here, edited there, finally deleted.
1358
+ */
1359
+ elementHistory(uniqueId: string): Array<{
1360
+ commit: RevitFlowCommit;
1361
+ type: string;
1362
+ }>;
1363
+ /** Per-author totals across the (optionally date-filtered) history. */
1364
+ userActivity(since?: Date): Array<{
1365
+ author: string;
1366
+ userId?: string;
1367
+ commits: number;
1368
+ added: number;
1369
+ modified: number;
1370
+ removed: number;
1371
+ last: string;
1372
+ }>;
1373
+ /**
1374
+ * The GUID of whatever is currently selected in the viewer, so the element view can
1375
+ * follow the user's selection. Returns null when nothing (or something unresolvable)
1376
+ * is selected.
1377
+ */
1378
+ /** GUIDs of everything currently selected in the viewer (empty when nothing is). */
1379
+ selectedGuids(): Promise<string[]>;
1380
+ selectedGuid(): Promise<string | null>;
1381
+ /**
1382
+ * Select every element the commit touched, in the viewer's own selection — so the
1383
+ * usual tools (properties, isolate, zoom) work on it like any hand-made selection.
1384
+ * Honours the current type filter, so "only deletions" selects only those.
1385
+ * @returns how many elements ended up selected.
1386
+ */
1387
+ selectCommitElements(commit: RevitFlowCommit, zoom?: boolean): Promise<number>;
1388
+ /** Show/hide one kind of change; re-applies to the current commit. */
1389
+ setTypeVisible(kind: ChangeKind, on: boolean): Promise<void>;
1390
+ /** @deprecated use setContextMode. */
1061
1391
  setIsolateChanges(on: boolean): Promise<void>;
1062
- private _colorStyle;
1063
- private _registerStyles;
1064
- private _clearHighlights;
1392
+ /** The shared Outliner component (created on first access by the viewer). */
1393
+ private _outliner;
1394
+ /**
1395
+ * Create the three independently-colored outline groups once. The viewer
1396
+ * owns the Outliner's DEFAULT group (selection) — we only add our own named
1397
+ * groups and never touch "default".
1398
+ */
1399
+ private _ensureGroups;
1400
+ /** Clear our three outline groups only — leaves the viewer's default group. */
1401
+ private _clearOutlines;
1065
1402
  /**
1066
1403
  * Resolve each change bucket's GUIDs to localIds across every loaded model
1067
- * and paint them with the matching named style.
1404
+ * and route them to the matching outline group.
1068
1405
  */
1069
- private _colorCommit;
1070
- /** GUIDs → { modelId: Set<localId> } across the given models. */
1406
+ private _outlineCommit;
1407
+ /**
1408
+ * Remove every localId that `_applySupersededVisibility` has hidden from a change map.
1409
+ * Returns a copy: the input maps come from `_buildMap` and are used elsewhere.
1410
+ */
1411
+ private _dropSuperseded;
1412
+ /**
1413
+ * Hide (or restore) the elements a commit deleted. Tracked separately from the context
1414
+ * modes so switching Colored↔Plain doesn't disturb ghosting or the rest of the model.
1415
+ */
1416
+ private _applyDeletedVisibility;
1417
+ /**
1418
+ * Show each element ONCE: from the newest source that supplies it.
1419
+ *
1420
+ * THE BUG THIS EXISTS FOR. The baseline .frag holds the whole model as of the first
1421
+ * commit, and every later commit ships a delta .frag with the geometry it created AND
1422
+ * the geometry it modified. Both stay in the scene. For a CREATED element that is
1423
+ * right — it exists in exactly one place. For a MODIFIED one it is not: the element is
1424
+ * in the baseline at its old shape and in the delta at its new one, and the viewer drew
1425
+ * both. Measured 2026-08-05 on a wall whose height Quim raised: the new wall stood
1426
+ * proud of the old one, with the coincident faces z-fighting into a dotted band.
1427
+ *
1428
+ * The rule is the obvious one once stated: for any element, only the newest VISIBLE
1429
+ * source may draw it. Deltas are walked newest-first, each guid is claimed by the first
1430
+ * source that has it, and every older copy — in an older delta or in the baseline — is
1431
+ * hidden. Deltas newer than the commit being shown are already hidden wholesale by
1432
+ * `_applyFutureVisibility`, so they never get to claim anything.
1433
+ *
1434
+ * Restores before it hides, like `_applyDeletedVisibility`, so stepping backwards
1435
+ * through the timeline brings the old geometry back rather than accumulating hidden
1436
+ * elements until the model is empty.
1437
+ */
1438
+ private _applySupersededVisibility;
1439
+ /** Switch between review colours and the plain post-commit model. */
1440
+ setHighlightMode(mode: HighlightMode): Promise<void>;
1441
+ /**
1442
+ * Make sure the geometry of `deleted` GUIDs is loaded. Anything already resolvable in
1443
+ * a loaded model (the usual case: the element shipped in the baseline) needs nothing;
1444
+ * for the rest, walk back through the history to the commit that CREATED each one and
1445
+ * load that commit's delta, which is the last place its geometry existed.
1446
+ */
1447
+ private _ensureDeletedGeometry;
1448
+ /** GUIDs → { modelId: Set<localId> } across the given models. */
1071
1449
  private _buildMap;
1072
1450
  private _mergeMaps;
1073
- private _ghostAllExcept;
1074
- private _clearGhost;
1075
- private _ensureBaselineLoaded;
1076
- /** Load commit `commitId`'s .frag into the viewer under modelId == fileId. */
1077
- private _ensureFragLoaded;
1078
1451
  /**
1079
- * Resolve (and cache) the `bimterop/revit-*` doc subfolder id. Filters the
1080
- * project-wide folder list locally by parentId + name prefix, mirroring the
1081
- * SnapshotList prototype (the server-side parentFolderId filter is
1082
- * unreliable). Returns null if the project hasn't been pushed to yet.
1452
+ * Dim ("ghost") or hide every element that is NOT part of the shown commit, per the
1453
+ * chosen context mode. Best-effort per model: a model that doesn't support the call is
1454
+ * simply left alone — the outlines still convey the change.
1455
+ */
1456
+ private _applyContext;
1457
+ /** Undo whatever the context mode did: drop the ghost overlay and restore visibility. */
1458
+ private _clearContext;
1459
+ /**
1460
+ * Kept for the panel's connect hook. Prefetching is now driven by the SELECTION (see
1461
+ * _slidePrefetchWindow): nothing is downloaded until a commit is opened, and only its
1462
+ * neighbourhood is kept. Warming the newest N up front, as this used to do, spent
1463
+ * bandwidth on commits nobody had asked for and grew without bound.
1083
1464
  */
1084
- private _resolveDocFolderId;
1465
+ warmDeltas(): void;
1466
+ /** Load a commit's hidden delta .frag (by its hidden id) into the viewer (modelId == hiddenId). */
1467
+ private _ensureDeltaLoaded;
1085
1468
  private _setLoading;
1086
1469
  private _firstWorld;
1087
1470
  }
1088
1471
  /**
1089
- * GitHistoryManager — headless engine for the revit-flow git-history built-in.
1472
+ * GitHistoryManager — headless engine for the revit-flow git-history built-in.
1090
1473
  *
1091
- * Owns ALL viewer mutation: it fetches the commit history + fragment artifacts
1092
- * from project storage, loads the baseline (full-model) .frag into the shared
1093
- * viewer, and colors a commit's changed elements (create → green, update →
1094
- * blue, delete → red) via named Highlighter styles. The Lit panel
1095
- * (`top-git-history`) is intent-only: it calls these methods and subscribes to
1096
- * the events below, and never touches the Highlighter / FragmentsManager
1097
- * directly.
1474
+ * Owns ALL viewer mutation for the change outlines. It does NOT load the model —
1475
+ * the user opens a model from the Project Files panel, and this manager reacts to
1476
+ * that load: it reads the model's hidden `revitflow_history.json` (if any) and
1477
+ * publishes the commit list. Showing a commit outlines its changed elements
1478
+ * (create → green, update → blue, delete → red) via three independently-colored
1479
+ * `OBF.Outliner` groups — a cheap post-process outline that never recolors the
1480
+ * fragment geometry. The Lit panel (`top-git-history`) is intent-only: it calls
1481
+ * these methods and subscribes to the events below.
1098
1482
  *
1099
- * Data model (per model, in `bimterop/revit-<docId>/`):
1100
- * - revitflow_history.json — { model, commits: [{ id, parent, author,
1101
- * source, timestamp, changes:[{type,uniqueId}], counts }] }
1102
- * - revitflow_frag_<N>.frag — fragments file for commit N. The earliest
1103
- * commit with a frag is the FULL model (baseline); later frags are
1104
- * incremental (only that commit's created+modified geometry).
1483
+ * Data model, per loaded model (fileId == modelId), all HIDDEN children of it:
1484
+ * - revitflow_history.json — { model, commits: [{ id, parent, author, source,
1485
+ * timestamp, changes:[{type,uniqueId}], counts }] }. The first commit is the
1486
+ * INITIAL full-model capture (its geometry == the visible baseline file).
1487
+ * - revitflow_frag_<N>.frag — delta fragments for commit N (its created+modified
1488
+ * geometry), loaded on demand so created elements can be colored.
1105
1489
  */
1106
1490
  export type GitHistoryManager = InstanceType<typeof _GitHistoryManager>;
1107
1491
  /**
1108
- * GitHistoryManager — headless engine for the revit-flow git-history built-in.
1492
+ * GitHistoryManager — headless engine for the revit-flow git-history built-in.
1109
1493
  *
1110
- * Owns ALL viewer mutation: it fetches the commit history + fragment artifacts
1111
- * from project storage, loads the baseline (full-model) .frag into the shared
1112
- * viewer, and colors a commit's changed elements (create → green, update →
1113
- * blue, delete → red) via named Highlighter styles. The Lit panel
1114
- * (`top-git-history`) is intent-only: it calls these methods and subscribes to
1115
- * the events below, and never touches the Highlighter / FragmentsManager
1116
- * directly.
1494
+ * Owns ALL viewer mutation for the change outlines. It does NOT load the model —
1495
+ * the user opens a model from the Project Files panel, and this manager reacts to
1496
+ * that load: it reads the model's hidden `revitflow_history.json` (if any) and
1497
+ * publishes the commit list. Showing a commit outlines its changed elements
1498
+ * (create → green, update → blue, delete → red) via three independently-colored
1499
+ * `OBF.Outliner` groups — a cheap post-process outline that never recolors the
1500
+ * fragment geometry. The Lit panel (`top-git-history`) is intent-only: it calls
1501
+ * these methods and subscribes to the events below.
1117
1502
  *
1118
- * Data model (per model, in `bimterop/revit-<docId>/`):
1119
- * - revitflow_history.json — { model, commits: [{ id, parent, author,
1120
- * source, timestamp, changes:[{type,uniqueId}], counts }] }
1121
- * - revitflow_frag_<N>.frag — fragments file for commit N. The earliest
1122
- * commit with a frag is the FULL model (baseline); later frags are
1123
- * incremental (only that commit's created+modified geometry).
1503
+ * Data model, per loaded model (fileId == modelId), all HIDDEN children of it:
1504
+ * - revitflow_history.json — { model, commits: [{ id, parent, author, source,
1505
+ * timestamp, changes:[{type,uniqueId}], counts }] }. The first commit is the
1506
+ * INITIAL full-model capture (its geometry == the visible baseline file).
1507
+ * - revitflow_frag_<N>.frag — delta fragments for commit N (its created+modified
1508
+ * geometry), loaded on demand so created elements can be colored.
1124
1509
  */
1125
1510
  export declare const GitHistoryManager: typeof _GitHistoryManager & {
1126
1511
  uuid: "3f9c1a7e-6b2d-4e18-9a5c-7d0e2f4b6c81";