@inspectorhub/field-ui 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,12 +4,10 @@ The OpenInspection field inspection editor as an embeddable React package, so
4
4
  the web app and other hosts (for example the offline-first native client) can
5
5
  share one editor.
6
6
 
7
- **Status: pre-1.0 and incomplete.** The package is being extracted from the
8
- OpenInspection web app in steps. What the published build exports today is the
9
- host contract (the data source, its types, the provider and hooks, locale
10
- binding) and an in-memory fake for tests. The editor components themselves are
11
- not exported yet; this README says so again where it matters, and will change
12
- when they land.
7
+ **Status: pre-1.0.** The editor page component (`FieldEditor`) is exported,
8
+ together with the host contract it runs on. While the version is below 1.0 a
9
+ minor release can change the exported surface; pin a minor range such as
10
+ `~0.3.0` until 1.0.
13
11
 
14
12
  ## What the build exports
15
13
 
@@ -18,10 +16,86 @@ external):
18
16
 
19
17
  | Entry | Contents |
20
18
  |---|---|
21
- | `@inspectorhub/field-ui` | `FieldDataSourceProvider`, `useFieldDataSource`, `useFieldCommand`, `useFieldEnsureHuman`, `useFieldSlots`, `isIgnored`, `setFieldLocale`, `bindFieldLocaleSource`, and the TypeScript types of the contract |
19
+ | `@inspectorhub/field-ui` | The editor and the host contract, listed below |
22
20
  | `@inspectorhub/field-ui/testing` | `createFakeDataSource` and fakes for each optional resource, for host test suites |
23
21
  | `@inspectorhub/field-ui/theme.css` | The design-token and base-layer CSS (see Theme) |
24
22
 
23
+ The root entry exports:
24
+
25
+ | Group | Exports |
26
+ |---|---|
27
+ | The editor | `FieldEditor` (props: `{ data: EditorData; readOnly?: boolean }`) |
28
+ | Host contract | `FieldDataSourceProvider`, `useFieldDataSource`, `useFieldCommand`, `useFieldEnsureHuman`, `useFieldSlots`, `isIgnored`, `setFieldLocale`, `bindFieldLocaleSource`, and the TypeScript types of the contract (`FieldDataSource`, `FieldHostSlots`, `FieldUrls`, `FieldResources`, `EditorData`, `SessionInfo`, and so on) |
29
+ | Finding keys | `findingKey`, `findingsForUnit`, `isMetaKey` (the same functions the server uses to address a result) |
30
+ | Photo decoding | `DecodeImageError`, `isDecodeImageError` (matches by error name, so it works across two copies of the package), and the types `DecodeImage`, `DecodeImageOptions`, `DecodedImage` (see `media` below) |
31
+ | Results document | `appendPendingPhoto` (adds a pending photo entry for a file the host stored on the device; needs the finding key and a pending id; adding the same pending id twice leaves one entry), `resolvePendingPhoto` (swaps a pending offline photo entry in the results document for its uploaded key; needs the finding key) |
32
+ | Commercial report text | `resolvePcaNarrative`, `RELIANCE_TEMPLATES`, `METADATA_PRESETS` |
33
+ | Preferences | `DEFAULT_INSPECTION_PREFS` |
34
+ | Overlay stack | `hasOpenLayer`, `escapeWillBeHandled` (see "Back key and open dialogs") |
35
+ | Types | `GalleryPhoto`, `CoverPhotoOption`, `ReportProfileOption`, `VideoUploadRef`, `PublishReadiness`, `PublishBlockingDefect` |
36
+
37
+ ## Mounting the editor
38
+
39
+ ```tsx
40
+ import { FieldDataSourceProvider, FieldEditor, setFieldLocale } from '@inspectorhub/field-ui';
41
+
42
+ setFieldLocale(session.locale); // and keep it in step with the host's own locale
43
+
44
+ <FieldDataSourceProvider dataSource={myDataSource} slots={mySlots}>
45
+ <FieldEditor data={editorData} />
46
+ </FieldDataSourceProvider>
47
+ ```
48
+
49
+ Pass `readOnly` to show an inspection for reading only. Nothing in a read-only editor can write: the results document is
50
+ never handed to a write helper, every write on the data source (commands, uploads, the media store, the resource writes)
51
+ is refused with "This inspection is read-only.", and the controls that write (rating row, notes input, add-photo tile,
52
+ publish, sign, finish, structure and template editing, settings, units, cost items, batch and Speed mode) are not
53
+ rendered. Navigation, the section and item lists, the rating as an indicator, notes as text, photos and their viewer, the
54
+ preview and updates from other people keep working. Without `readOnly` nothing changes.
55
+
56
+ What the host has to provide:
57
+
58
+ - A `FieldDataSource` (next section). `editorData` is what its `load()` returns.
59
+ It must follow the host's revalidation: units, publish state and the rest are
60
+ read from it on every render, so pass the fresh value after each reload.
61
+ - `slots` (`FieldHostSlots`). Publishing is guarded by a human check that fails
62
+ closed: a host that provides neither `slots.ensureHuman` nor
63
+ `slots.humanCheck: 'not-required'` gets a check that refuses every send. A
64
+ host that has no such check by design must write `humanCheck: 'not-required'`
65
+ down explicitly; leaving the slot out does not turn the gate off.
66
+ - `slots.renderUnsavedGuard`, if leaving the editor with unsaved work should be
67
+ intercepted. Absent means leaving is never intercepted.
68
+ - The theme (see Theme) and the peer dependencies (see below).
69
+
70
+ A member of `urls`, `resources` or `slots` that the host leaves out hides the
71
+ entry point that needs it. In particular `urls.costExport` is optional: a host
72
+ without a cost download omits it and the Cost Items panel shows no export
73
+ links.
74
+
75
+ ### Back key and open dialogs
76
+
77
+ The editor opens dialogs and drawers on an overlay stack that lives inside this
78
+ package (it is bundled, so a host cannot read it from anywhere else). A host
79
+ that handles a hardware back key or a back gesture asks this copy first:
80
+
81
+ ```ts
82
+ import { escapeWillBeHandled, hasOpenLayer } from '@inspectorhub/field-ui';
83
+
84
+ if (hasOpenLayer()) {
85
+ // an editor dialog is open: let it consume the key instead of leaving the page
86
+ }
87
+ ```
88
+
89
+ `hasOpenLayer()` is true while any layer is open (dialog, drawer, popover or
90
+ menu). `escapeWillBeHandled()` is true when the next Escape will be taken by an
91
+ overlay.
92
+
93
+ ### Server rendering
94
+
95
+ The editor is built for a client that renders it. A host that renders on a
96
+ server must give `react-konva` a server stub of its own: the photo annotator
97
+ only renders in the browser.
98
+
25
99
  ## The data source contract
26
100
 
27
101
  The editor never talks to a server itself. A host implements `FieldDataSource`
@@ -50,6 +124,18 @@ signatures):
50
124
  - `collab`: a `CollabConnector` that opens the results Yjs document for an
51
125
  inspection (local persistence first, then network).
52
126
  - `media`: a `PendingMediaStore` and a `uploader(inspectionId, doc)` factory.
127
+ Optional `decodeImage(file, opts)` is the host's decoder for a file the browser cannot decode (HEIC/HEIF):
128
+ - It returns `{ blob }`, an upright JPEG, or `null` to decline (the file is then handled as before).
129
+ - For a file it recognises but cannot decode it rejects with `DecodeImageError(code, message)`. The `message` is
130
+ shown to the person and only that file is skipped; the rest of the batch continues. Test for it with
131
+ `isDecodeImageError(err)` (by `err.name`), not `instanceof`, if the host may hold a second copy of the package.
132
+ - With keep original on, a HEIC is still converted: `opts.keepOriginal.maxBytes` (10 MiB) asks for full size at the
133
+ best JPEG quality that fits. Otherwise `opts.maxLongEdge` bounds the size.
134
+ - Absent, photos behave exactly as in 0.2.1.
135
+
136
+ A failed `MediaUploader` result may carry `kept: true`: the host stored the bytes on the device and wrote the
137
+ pending entry itself with the exported `appendPendingPhoto(doc, findingKey, pendingId)`. The editor then shows a
138
+ neutral notice and counts the batch as saved, not failed.
53
139
  - `urls`: `FieldUrls`, the address builders for photos, reports, presence and
54
140
  in-app paths.
55
141
  - `resources`: `FieldResources`, optional capabilities (preferences, canned
@@ -58,6 +144,9 @@ signatures):
58
144
  host leaves out hides its entry point.
59
145
  - `platform`: `'web' | 'ios' | 'android'`.
60
146
 
147
+ The `compliance` signoff command's `personId` and `name` are optional (sent together). Absent means the signed-in
148
+ user; the panel shows the two inputs only to an owner or manager, empty.
149
+
61
150
  `FieldHostSlots` carries host-owned UI (theme switch, PDF export, human
62
151
  verification). An absent slot hides its entry point.
63
152
 
@@ -91,24 +180,28 @@ output. In your global stylesheet:
91
180
 
92
181
  The editor's photo gallery and lightbox use `react-photo-album` and
93
182
  `yet-another-react-lightbox`. Those libraries are peers and their CSS is not
94
- copied into `dist`. The current build does not import any of them yet (the
95
- editor components are not exported), so nothing is required today. Once the
96
- editor components ship, the host must import, from its own copy of each
97
- library:
98
-
99
- ```ts
100
- import 'yet-another-react-lightbox/styles.css';
101
- import 'react-photo-album/rows.css';
102
- ```
183
+ copied into `dist`. Instead `dist/index.js` keeps two bare side-effect imports,
184
+ `import "react-photo-album/rows.css"` and
185
+ `import "yet-another-react-lightbox/styles.css"`, which your bundler resolves
186
+ from its own copy of each library. The bundler therefore has to handle `.css`
187
+ imports from `node_modules` (Vite and webpack with a CSS loader do). If yours
188
+ does not, import the two files yourself from your global stylesheet entry.
103
189
 
104
190
  ## Peer dependencies
105
191
 
106
192
  Declared in `package.json` and resolved by the host so there is one copy of
107
- each: `react` and `react-dom` ^19, `yjs`, `lib0`, `y-indexeddb`, `y-protocols`,
108
- `konva`, `react-konva`, `react-easy-crop`, `react-photo-album`, `sortablejs`,
109
- `yet-another-react-lightbox`. Today only `react` and `yjs` (in
110
- `/testing`) are imported by the build; the rest are declared ahead of the
111
- editor components.
193
+ each: `react` and `react-dom` ^19, `yjs`, `lib0`, `y-protocols`, `konva`,
194
+ `react-konva`, `react-easy-crop`, `react-photo-album`, `sortablejs`,
195
+ `yet-another-react-lightbox`. `y-indexeddb` is also declared, but as an
196
+ optional peer: this package never imports it, and only a host that persists the
197
+ results document in the browser through its own `CollabConnector` needs it.
198
+
199
+ The built root entry imports `react`, `react-dom`, `yjs`, `react-konva` (which
200
+ needs `konva`), `react-easy-crop`, `react-photo-album`, `sortablejs` and
201
+ `yet-another-react-lightbox`, so a host that mounts `FieldEditor` installs all
202
+ of those. `lib0` and `y-protocols` are declared as peers but the built root
203
+ entry does not import them today; they matter to a host that writes its own
204
+ `CollabConnector` on the Yjs sync protocol.
112
205
 
113
206
  ## Licence
114
207
 
@@ -87,6 +87,7 @@ interface PropertyMetaField {
87
87
  unit?: string;
88
88
  options?: string[];
89
89
  }
90
+ declare const METADATA_PRESETS: Record<string, PropertyMetaField[]>;
90
91
 
91
92
  /**
92
93
  * Commercial PCA Phase S — editable narrative blocks for the report.
@@ -111,6 +112,11 @@ interface PcaNarrative {
111
112
  reconnaissance: string;
112
113
  additionalConsiderations: string;
113
114
  }
115
+ /**
116
+ * Overlay stored non-empty blocks onto the seed. Empty/whitespace/missing keys
117
+ * fall back to seed; unknown (old-shape) keys are ignored. Never throws.
118
+ */
119
+ declare function resolvePcaNarrative(raw: unknown): PcaNarrative;
114
120
 
115
121
  type AutoAdvanceMode = 'always' | 'keyboard' | 'off';
116
122
 
@@ -969,7 +975,7 @@ type UnitCommand = {
969
975
  };
970
976
  /**
971
977
  * Compliance panel mutations (action intents `compliance-*`). Keys per op:
972
- * - signoff: `role`, `personId`, `name` (all required), `license` (empty becomes null), `dualRole`
978
+ * - signoff: `role`; `personId` and `name` are optional and sent only together, when an owner or manager records another person (omitted means the signed-in user; the server refuses them from any other role). `license` (empty becomes null), `dualRole`
973
979
  * - remove-signoff: `role`
974
980
  * - doc-review-seed: none (populates an empty checklist)
975
981
  * - doc-review: `documentKey`, the four flags, `notes` (empty becomes null)
@@ -980,8 +986,8 @@ type ComplianceCommand = {
980
986
  type: 'compliance';
981
987
  op: 'signoff';
982
988
  role: ReportSignoffView['role'];
983
- personId: string;
984
- name: string;
989
+ personId?: string;
990
+ name?: string;
985
991
  license: string | null;
986
992
  dualRole: boolean;
987
993
  } | {
@@ -1168,6 +1174,8 @@ interface MediaFileResult {
1168
1174
  key?: string;
1169
1175
  /** `NO_KEY`, `HTTP_<status>` or `EXCEPTION`. */
1170
1176
  error?: string;
1177
+ /** The upload failed but the host kept the bytes on the device (see `MediaUploader`): not a lost photo. */
1178
+ kept?: boolean;
1171
1179
  }
1172
1180
  /**
1173
1181
  * Online media upload. It is NOT a `FieldCommand`: the editor never routes photo
@@ -1185,7 +1193,9 @@ interface MediaFileResult {
1185
1193
  * - `kind: 'crop'`: `key` is the derived `croppedKey`.
1186
1194
  * - `kind: 'annotate'`: `key` is the derived `annotatedKey`.
1187
1195
  *
1188
- * Failure is `{ ok: false, error }` and never a throw.
1196
+ * Failure is `{ ok: false, error }` and never a throw. A host that stored the bytes on the device and wrote the
1197
+ * pending entry itself (see `appendPendingPhoto`) adds `kept: true`: the photo is not lost and uploads later, so the
1198
+ * editor counts it as saved on the device instead of as a failure.
1189
1199
  */
1190
1200
  interface MediaUploader {
1191
1201
  upload(rec: PendingMediaRecord): Promise<{
@@ -1195,9 +1205,35 @@ interface MediaUploader {
1195
1205
  } | {
1196
1206
  ok: false;
1197
1207
  error: string;
1208
+ kept?: true;
1198
1209
  }>;
1199
1210
  }
1200
1211
 
1212
+ interface DecodeImageOptions {
1213
+ /** Longest edge the caller will keep; the decoder SHOULD NOT return more pixels than this. Omitted: full size. */
1214
+ maxLongEdge?: number;
1215
+ /**
1216
+ * Present when the person keeps original quality. Return the image at full size and the highest JPEG quality that
1217
+ * keeps the result within `maxBytes`.
1218
+ */
1219
+ keepOriginal?: {
1220
+ maxBytes: number;
1221
+ };
1222
+ }
1223
+ interface DecodedImage {
1224
+ /** A browser-decodable image (image/jpeg). Orientation already applied; no metadata is required. */
1225
+ blob: Blob;
1226
+ }
1227
+ /** A file the host recognised but could not decode. `message` is shown to the person, so the host words it. */
1228
+ declare class DecodeImageError extends Error {
1229
+ readonly code: 'UNSUPPORTED' | 'TOO_LARGE_PIXELS' | 'CORRUPT' | 'TIMEOUT';
1230
+ constructor(code: DecodeImageError['code'], message: string);
1231
+ }
1232
+ /** Matched by name rather than by class, so an error from a second copy of this package in the host still counts. */
1233
+ declare function isDecodeImageError(e: unknown): e is DecodeImageError;
1234
+ /** The optional `media.decodeImage` capability of a data source. */
1235
+ type DecodeImage = (file: Blob, opts: DecodeImageOptions) => Promise<DecodedImage | null>;
1236
+
1201
1237
  /** Everything the editor reads on open. Mirrors the inspection-edit loader's return value. */
1202
1238
  interface EditorData {
1203
1239
  inspection: EditorInspection;
@@ -1330,8 +1366,11 @@ interface FieldUrls {
1330
1366
  photo(inspectionId: string, key: string): string;
1331
1367
  reportPreview(tenantSlug: string, inspectionId: string): string | null;
1332
1368
  reportPdf(inspectionId: string): string | null;
1333
- /** The cost tables' download (`csv` or `xlsx`); a plain link the browser follows. */
1334
- costExport(inspectionId: string, format: 'csv' | 'xlsx'): string;
1369
+ /**
1370
+ * The cost tables' download (`csv` or `xlsx`); a plain link the browser follows. Optional: a host with no
1371
+ * such download (a native shell) leaves it out and the Cost Items panel shows no export links.
1372
+ */
1373
+ costExport?(inspectionId: string, format: 'csv' | 'xlsx'): string;
1335
1374
  statutoryPreviewPdf(inspectionId: string): string | null;
1336
1375
  videoObject(inspectionId: string, ref: string): string | null;
1337
1376
  /**
@@ -1508,6 +1547,12 @@ interface FieldDataSource {
1508
1547
  * `doc` at upload time and answers `{ ok: false }` when `doc` is null or the photo is gone.
1509
1548
  */
1510
1549
  uploader(inspectionId: string, doc: Y.Doc | null): MediaUploader;
1550
+ /**
1551
+ * Optional. Turns a file the browser cannot decode (HEIC/HEIF) into a JPEG the browser can. Return null when this
1552
+ * host cannot decode that file (the caller keeps its own behaviour); reject with `DecodeImageError` for a file
1553
+ * that is recognised but cannot be decoded. Absent: photos behave exactly as before.
1554
+ */
1555
+ decodeImage?: DecodeImage;
1511
1556
  };
1512
1557
  urls: FieldUrls;
1513
1558
  resources: FieldResources;
@@ -1516,4 +1561,5 @@ interface FieldDataSource {
1516
1561
  platform: 'web' | 'ios' | 'android';
1517
1562
  }
1518
1563
 
1519
- export type { ResultsDocHandle as $, AiAssistInput as A, BulkCreateUnitsBody as B, CommandResult as C, DateFormat as D, EditorData as E, FieldCommand as F, NotifyVariant as G, PcaNarrativeData as H, InspectionPrefs as I, PendingMediaRecord as J, PendingMediaStore as K, PhotoAnnotateMeta as L, MediaFileResult as M, NotifyOptions as N, OutboundCoolingWindow as O, PcaNarrative as P, PhotoCropMeta as Q, PhotoCropTransform as R, PhotoUploadMeta as S, PropertyMetaField as T, PsqView as U, RatingLevel as V, RelianceText as W, RepairItemOption as X, ReportLinkTtl as Y, ReportSignoffView as Z, ResultMap as _, CommandData as a, RevisionStatus as a0, SaveStructureTemplateCommand as a1, SchemaItem as a2, SchemaSection as a3, SessionInfo as a4, SettingsSheetData as a5, StatutoryCoverage as a6, StatutoryDetails as a7, TagLibraryEntry as a8, TemplateItemBody as a9, TemplateSchema as aa, TemplateSearchResult as ab, TemplateSnapshot as ac, TemplateSummary as ad, TimeFormat as ae, UnitCommand as af, UnitProgress as ag, VersionSnapshotContent as ah, VersionSnapshotEntry as ai, VideoFinalizeInput as aj, VideoUploadRef as ak, VideoUploadTicket as al, FieldDataSource as b, AiAssistResult as c, AutoAdvanceMode as d, AutofillReason as e, CollabConnector as f, CommandDataMap as g, CommandKey as h, CommentsLibraryEntry as i, CommentsLibraryQuery as j, CommentsLibraryRow as k, CommercialPresets as l, ComplianceCommand as m, CompliancePanelData as n, CostItemView as o, CostItemWrite as p, CostItemsListing as q, DefectCategory as r, DocumentReviewItemView as s, EditorInspection as t, ExecuteOptions as u, FieldResources as v, FieldUrls as w, InspectionUnit as x, MediaPoolListing as y, MediaUploader as z };
1564
+ export { METADATA_PRESETS as M, isDecodeImageError as aw, resolvePcaNarrative as ax, DecodeImageError as t };
1565
+ export type { PsqView as $, AiAssistInput as A, BulkCreateUnitsBody as B, CommandResult as C, DateFormat as D, EditorData as E, FieldCommand as F, FieldResources as G, FieldUrls as H, InspectionPrefs as I, GalleryPhoto as J, InspectionUnit as K, MediaFileResult as L, MediaPoolListing as N, MediaUploader as O, NotifyOptions as P, NotifyVariant as Q, OutboundCoolingWindow as R, PcaNarrative as S, PcaNarrativeData as T, PendingMediaRecord as U, PendingMediaStore as V, PhotoAnnotateMeta as W, PhotoCropMeta as X, PhotoCropTransform as Y, PhotoUploadMeta as Z, PropertyMetaField as _, CommandData as a, PublishBlockingDefect as a0, PublishReadiness as a1, RatingLevel as a2, RelianceText as a3, RepairItemOption as a4, ReportLinkTtl as a5, ReportProfileOption as a6, ReportSignoffView as a7, ResultMap as a8, ResultsDocHandle as a9, RevisionStatus as aa, SaveStructureTemplateCommand as ab, SchemaItem as ac, SchemaSection as ad, SessionInfo as ae, SettingsSheetData as af, StatutoryCoverage as ag, StatutoryDetails as ah, TagLibraryEntry as ai, TemplateItemBody as aj, TemplateSchema as ak, TemplateSearchResult as al, TemplateSnapshot as am, TemplateSummary as an, TimeFormat as ao, UnitCommand as ap, UnitProgress as aq, VersionSnapshotContent as ar, VersionSnapshotEntry as as, VideoFinalizeInput as at, VideoUploadRef as au, VideoUploadTicket as av, FieldDataSource as b, AiAssistResult as c, AutoAdvanceMode as d, AutofillReason as e, CollabConnector as f, CommandDataMap as g, CommandKey as h, CommentsLibraryEntry as i, CommentsLibraryQuery as j, CommentsLibraryRow as k, CommercialPresets as l, ComplianceCommand as m, CompliancePanelData as n, CostItemView as o, CostItemWrite as p, CostItemsListing as q, CoverPhotoOption as r, DecodeImage as s, DecodeImageOptions as u, DecodedImage as v, DefectCategory as w, DocumentReviewItemView as x, EditorInspection as y, ExecuteOptions as z };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { F as FieldCommand, C as CommandResult, a as CommandData, b as FieldDataSource } from './data-source-B_ogNIa3.js';
2
- export { A as AiAssistInput, c as AiAssistResult, d as AutoAdvanceMode, e as AutofillReason, B as BulkCreateUnitsBody, f as CollabConnector, g as CommandDataMap, h as CommandKey, i as CommentsLibraryEntry, j as CommentsLibraryQuery, k as CommentsLibraryRow, l as CommercialPresets, m as ComplianceCommand, n as CompliancePanelData, o as CostItemView, p as CostItemWrite, q as CostItemsListing, D as DateFormat, r as DefectCategory, s as DocumentReviewItemView, E as EditorData, t as EditorInspection, u as ExecuteOptions, v as FieldResources, w as FieldUrls, I as InspectionPrefs, x as InspectionUnit, M as MediaFileResult, y as MediaPoolListing, z as MediaUploader, N as NotifyOptions, G as NotifyVariant, O as OutboundCoolingWindow, P as PcaNarrative, H as PcaNarrativeData, J as PendingMediaRecord, K as PendingMediaStore, L as PhotoAnnotateMeta, Q as PhotoCropMeta, R as PhotoCropTransform, S as PhotoUploadMeta, T as PropertyMetaField, U as PsqView, V as RatingLevel, W as RelianceText, X as RepairItemOption, Y as ReportLinkTtl, Z as ReportSignoffView, _ as ResultMap, $ as ResultsDocHandle, a0 as RevisionStatus, a1 as SaveStructureTemplateCommand, a2 as SchemaItem, a3 as SchemaSection, a4 as SessionInfo, a5 as SettingsSheetData, a6 as StatutoryCoverage, a7 as StatutoryDetails, a8 as TagLibraryEntry, a9 as TemplateItemBody, aa as TemplateSchema, ab as TemplateSearchResult, ac as TemplateSnapshot, ad as TemplateSummary, ae as TimeFormat, af as UnitCommand, ag as UnitProgress, ah as VersionSnapshotContent, ai as VersionSnapshotEntry, aj as VideoFinalizeInput, ak as VideoUploadRef, al as VideoUploadTicket } from './data-source-B_ogNIa3.js';
3
- import * as react from 'react';
1
+ import { F as FieldCommand, C as CommandResult, a as CommandData, b as FieldDataSource, E as EditorData, I as InspectionPrefs } from './data-source-IaWUdrIc.js';
2
+ export { A as AiAssistInput, c as AiAssistResult, d as AutoAdvanceMode, e as AutofillReason, B as BulkCreateUnitsBody, f as CollabConnector, g as CommandDataMap, h as CommandKey, i as CommentsLibraryEntry, j as CommentsLibraryQuery, k as CommentsLibraryRow, l as CommercialPresets, m as ComplianceCommand, n as CompliancePanelData, o as CostItemView, p as CostItemWrite, q as CostItemsListing, r as CoverPhotoOption, D as DateFormat, s as DecodeImage, t as DecodeImageError, u as DecodeImageOptions, v as DecodedImage, w as DefectCategory, x as DocumentReviewItemView, y as EditorInspection, z as ExecuteOptions, G as FieldResources, H as FieldUrls, J as GalleryPhoto, K as InspectionUnit, M as METADATA_PRESETS, L as MediaFileResult, N as MediaPoolListing, O as MediaUploader, P as NotifyOptions, Q as NotifyVariant, R as OutboundCoolingWindow, S as PcaNarrative, T as PcaNarrativeData, U as PendingMediaRecord, V as PendingMediaStore, W as PhotoAnnotateMeta, X as PhotoCropMeta, Y as PhotoCropTransform, Z as PhotoUploadMeta, _ as PropertyMetaField, $ as PsqView, a0 as PublishBlockingDefect, a1 as PublishReadiness, a2 as RatingLevel, a3 as RelianceText, a4 as RepairItemOption, a5 as ReportLinkTtl, a6 as ReportProfileOption, a7 as ReportSignoffView, a8 as ResultMap, a9 as ResultsDocHandle, aa as RevisionStatus, ab as SaveStructureTemplateCommand, ac as SchemaItem, ad as SchemaSection, ae as SessionInfo, af as SettingsSheetData, ag as StatutoryCoverage, ah as StatutoryDetails, ai as TagLibraryEntry, aj as TemplateItemBody, ak as TemplateSchema, al as TemplateSearchResult, am as TemplateSnapshot, an as TemplateSummary, ao as TimeFormat, ap as UnitCommand, aq as UnitProgress, ar as VersionSnapshotContent, as as VersionSnapshotEntry, at as VideoFinalizeInput, au as VideoUploadRef, av as VideoUploadTicket, aw as isDecodeImageError, ax as resolvePcaNarrative } from './data-source-IaWUdrIc.js';
3
+ import * as React from 'react';
4
4
  import { ReactNode } from 'react';
5
- import 'yjs';
5
+ import * as Y from 'yjs';
6
6
 
7
7
  /**
8
8
  * Host-supplied UI, injected through `FieldDataSourceProvider` rather than read
@@ -60,7 +60,7 @@ interface FieldDataSourceProviderProps {
60
60
  slots?: FieldHostSlots;
61
61
  children?: ReactNode;
62
62
  }
63
- declare function FieldDataSourceProvider({ dataSource, slots, children }: FieldDataSourceProviderProps): react.JSX.Element;
63
+ declare function FieldDataSourceProvider({ dataSource, slots, children }: FieldDataSourceProviderProps): React.JSX.Element;
64
64
  /** The data source of the nearest provider. Throws outside one: a silent null would fail much later. */
65
65
  declare function useFieldDataSource(): FieldDataSource;
66
66
  /** The host-supplied UI slots; an empty object when the provider was given none. */
@@ -126,6 +126,86 @@ interface FieldCommandRunner {
126
126
  */
127
127
  declare function useFieldCommand(inspectionId: string): FieldCommandRunner;
128
128
 
129
+ /** The inspection editor page. `readOnly` shows it for reading only: nothing in it can write, and the write controls are not offered. */
130
+ declare function FieldEditor({ data, readOnly }: {
131
+ data: EditorData;
132
+ readOnly?: boolean;
133
+ }): React.JSX.Element;
134
+
135
+ declare function findingKey(unitId: string | null | undefined, sectionId: string, itemId: string): string;
136
+ declare function findingsForUnit(data: Record<string, unknown>, unitId: string): Record<string, unknown>;
137
+ /**
138
+ * A meta key of a results blob (`_inspector_signature`): underscore-prefixed with
139
+ * fewer than three colon-separated parts. It is not a finding, and the results
140
+ * document never holds it: importing a blob skips it, and projecting a document
141
+ * skips one an older document may still carry as an empty item map (either would
142
+ * otherwise replace the real value in the blob with `{}`). Composite finding keys
143
+ * start with `_default:` too, which is why the prefix alone cannot decide.
144
+ */
145
+ declare function isMetaKey(key: string): boolean;
146
+
147
+ /**
148
+ * Bridges a live Y.Doc (from `use-results-doc.ts`) to the editor's in-memory
149
+ * ResultMap, and provides write helpers that mirror the editor's existing hook
150
+ * signatures so Task 9 can swap them in 1:1.
151
+ *
152
+ * NO React import. NO import of packages/field-ui/src/hooks/useInspection.ts (React-heavy).
153
+ */
154
+
155
+ /**
156
+ * #181 PR-G — append a brand-new offline photo as a PENDING doc entry.
157
+ *
158
+ * The binary is only in the local media-pending store (not yet on R2), so the
159
+ * entry has an EMPTY `key` + `pendingUpload: true` + the `pendingId` that
160
+ * resolves to the local blob. The report SKIPS such entries (it filters
161
+ * `pendingUpload`); the editor renders them from the local objectURL. On drain
162
+ * the entry is swapped to its real R2 key via `resolvePendingPhoto` below.
163
+ */
164
+ declare function appendPendingPhoto(doc: Y.Doc, findingKey: string, pendingId: string): void;
165
+ /**
166
+ * #181 PR-G — resolve a pending photo after its offline blob has uploaded.
167
+ *
168
+ * Swaps the doc entry pending→real key and CLEARS the pending markers
169
+ * (`pendingUpload` / `pendingId` / `pendingKind`) by replace-in-place (field
170
+ * delete is unsupported). The match key differs by kind:
171
+ * - `photo`: the entry's `key` is empty; match by the entry whose
172
+ * `pendingId === pendingId`, then set the real `key` = result.key.
173
+ * - `crop`: match by base `key`, set `croppedKey` = result.croppedKey.
174
+ * - `annotate`: match by base `key`, set `annotatedKey` = result.annotatedKey.
175
+ *
176
+ * No-op if no matching pending entry is found (idempotent — a duplicate drain
177
+ * after the swap simply finds nothing).
178
+ */
179
+ declare function resolvePendingPhoto(doc: Y.Doc, findingKey: string, pendingId: string, kind: 'photo' | 'crop' | 'annotate', photoKey: string | undefined, result: {
180
+ key?: string;
181
+ croppedKey?: string;
182
+ annotatedKey?: string;
183
+ }): void;
184
+
185
+ /**
186
+ * Commercial PCA Phase M — seeded reliance / point-in-time / site-specific
187
+ * disclaimer text (ASTM §4.2.1–4.2.4). Defaults the Phase S §2.5 (User Reliance)
188
+ * and Limitations regions so the legal boilerplate is never missing; the
189
+ * inspector may edit it. Plain text (textarea, no RTE — project decision).
190
+ */
191
+ declare const RELIANCE_TEMPLATES: {
192
+ readonly userReliance: string;
193
+ readonly pointInTime: string;
194
+ readonly siteSpecific: string;
195
+ };
196
+
197
+ /** What the editor behaves like until the tenant's own preferences arrive (or when the host has no preference store). */
198
+ declare const DEFAULT_INSPECTION_PREFS: InspectionPrefs;
199
+
200
+ /** True while at least one layer is open. */
201
+ declare function hasOpenLayer(): boolean;
202
+ /**
203
+ * True when the next Escape will be taken by an overlay. For window-level hotkeys that are
204
+ * not layers: they must skip Escape exactly when a layer will answer it, and not when a
205
+ * layer is open that does not answer Escape.
206
+ */
207
+ declare function escapeWillBeHandled(): boolean;
208
+
129
209
  /**
130
210
  * Pin the package to one locale, explicitly.
131
211
  *
@@ -151,5 +231,5 @@ declare function setFieldLocale(locale: string): void;
151
231
  */
152
232
  declare function bindFieldLocaleSource(read: (() => string) | null): void;
153
233
 
154
- export { CommandData, CommandResult, FieldCommand, FieldDataSource, FieldDataSourceProvider, bindFieldLocaleSource, isIgnored, setFieldLocale, useFieldCommand, useFieldDataSource, useFieldEnsureHuman, useFieldSlots };
234
+ export { CommandData, CommandResult, DEFAULT_INSPECTION_PREFS, EditorData, FieldCommand, FieldDataSource, FieldDataSourceProvider, FieldEditor, InspectionPrefs, RELIANCE_TEMPLATES, appendPendingPhoto, bindFieldLocaleSource, escapeWillBeHandled, findingKey, findingsForUnit, hasOpenLayer, isIgnored, isMetaKey, resolvePendingPhoto, setFieldLocale, useFieldCommand, useFieldDataSource, useFieldEnsureHuman, useFieldSlots };
155
235
  export type { FieldCommandRunner, FieldDataSourceProviderProps, FieldHostSlots, FieldRunResult, IgnoredRun };