@inspectorhub/field-ui 0.1.0 → 0.3.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,79 @@ 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 }`) |
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
+ What the host has to provide:
50
+
51
+ - A `FieldDataSource` (next section). `editorData` is what its `load()` returns.
52
+ It must follow the host's revalidation: units, publish state and the rest are
53
+ read from it on every render, so pass the fresh value after each reload.
54
+ - `slots` (`FieldHostSlots`). Publishing is guarded by a human check that fails
55
+ closed: a host that provides neither `slots.ensureHuman` nor
56
+ `slots.humanCheck: 'not-required'` gets a check that refuses every send. A
57
+ host that has no such check by design must write `humanCheck: 'not-required'`
58
+ down explicitly; leaving the slot out does not turn the gate off.
59
+ - `slots.renderUnsavedGuard`, if leaving the editor with unsaved work should be
60
+ intercepted. Absent means leaving is never intercepted.
61
+ - The theme (see Theme) and the peer dependencies (see below).
62
+
63
+ A member of `urls`, `resources` or `slots` that the host leaves out hides the
64
+ entry point that needs it. In particular `urls.costExport` is optional: a host
65
+ without a cost download omits it and the Cost Items panel shows no export
66
+ links.
67
+
68
+ ### Back key and open dialogs
69
+
70
+ The editor opens dialogs and drawers on an overlay stack that lives inside this
71
+ package (it is bundled, so a host cannot read it from anywhere else). A host
72
+ that handles a hardware back key or a back gesture asks this copy first:
73
+
74
+ ```ts
75
+ import { escapeWillBeHandled, hasOpenLayer } from '@inspectorhub/field-ui';
76
+
77
+ if (hasOpenLayer()) {
78
+ // an editor dialog is open: let it consume the key instead of leaving the page
79
+ }
80
+ ```
81
+
82
+ `hasOpenLayer()` is true while any layer is open (dialog, drawer, popover or
83
+ menu). `escapeWillBeHandled()` is true when the next Escape will be taken by an
84
+ overlay.
85
+
86
+ ### Server rendering
87
+
88
+ The editor is built for a client that renders it. A host that renders on a
89
+ server must give `react-konva` a server stub of its own: the photo annotator
90
+ only renders in the browser.
91
+
25
92
  ## The data source contract
26
93
 
27
94
  The editor never talks to a server itself. A host implements `FieldDataSource`
@@ -50,6 +117,18 @@ signatures):
50
117
  - `collab`: a `CollabConnector` that opens the results Yjs document for an
51
118
  inspection (local persistence first, then network).
52
119
  - `media`: a `PendingMediaStore` and a `uploader(inspectionId, doc)` factory.
120
+ Optional `decodeImage(file, opts)` is the host's decoder for a file the browser cannot decode (HEIC/HEIF):
121
+ - It returns `{ blob }`, an upright JPEG, or `null` to decline (the file is then handled as before).
122
+ - For a file it recognises but cannot decode it rejects with `DecodeImageError(code, message)`. The `message` is
123
+ shown to the person and only that file is skipped; the rest of the batch continues. Test for it with
124
+ `isDecodeImageError(err)` (by `err.name`), not `instanceof`, if the host may hold a second copy of the package.
125
+ - With keep original on, a HEIC is still converted: `opts.keepOriginal.maxBytes` (10 MiB) asks for full size at the
126
+ best JPEG quality that fits. Otherwise `opts.maxLongEdge` bounds the size.
127
+ - Absent, photos behave exactly as in 0.2.1.
128
+
129
+ A failed `MediaUploader` result may carry `kept: true`: the host stored the bytes on the device and wrote the
130
+ pending entry itself with the exported `appendPendingPhoto(doc, findingKey, pendingId)`. The editor then shows a
131
+ neutral notice and counts the batch as saved, not failed.
53
132
  - `urls`: `FieldUrls`, the address builders for photos, reports, presence and
54
133
  in-app paths.
55
134
  - `resources`: `FieldResources`, optional capabilities (preferences, canned
@@ -58,6 +137,9 @@ signatures):
58
137
  host leaves out hides its entry point.
59
138
  - `platform`: `'web' | 'ios' | 'android'`.
60
139
 
140
+ The `compliance` signoff command's `personId` and `name` are optional (sent together). Absent means the signed-in
141
+ user; the panel shows the two inputs only to an owner or manager, empty.
142
+
61
143
  `FieldHostSlots` carries host-owned UI (theme switch, PDF export, human
62
144
  verification). An absent slot hides its entry point.
63
145
 
@@ -91,24 +173,28 @@ output. In your global stylesheet:
91
173
 
92
174
  The editor's photo gallery and lightbox use `react-photo-album` and
93
175
  `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
- ```
176
+ copied into `dist`. Instead `dist/index.js` keeps two bare side-effect imports,
177
+ `import "react-photo-album/rows.css"` and
178
+ `import "yet-another-react-lightbox/styles.css"`, which your bundler resolves
179
+ from its own copy of each library. The bundler therefore has to handle `.css`
180
+ imports from `node_modules` (Vite and webpack with a CSS loader do). If yours
181
+ does not, import the two files yourself from your global stylesheet entry.
103
182
 
104
183
  ## Peer dependencies
105
184
 
106
185
  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.
186
+ each: `react` and `react-dom` ^19, `yjs`, `lib0`, `y-protocols`, `konva`,
187
+ `react-konva`, `react-easy-crop`, `react-photo-album`, `sortablejs`,
188
+ `yet-another-react-lightbox`. `y-indexeddb` is also declared, but as an
189
+ optional peer: this package never imports it, and only a host that persists the
190
+ results document in the browser through its own `CollabConnector` needs it.
191
+
192
+ The built root entry imports `react`, `react-dom`, `yjs`, `react-konva` (which
193
+ needs `konva`), `react-easy-crop`, `react-photo-album`, `sortablejs` and
194
+ `yet-another-react-lightbox`, so a host that mounts `FieldEditor` installs all
195
+ of those. `lib0` and `y-protocols` are declared as peers but the built root
196
+ entry does not import them today; they matter to a host that writes its own
197
+ `CollabConnector` on the Yjs sync protocol.
112
198
 
113
199
  ## Licence
114
200
 
@@ -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,89 @@ interface FieldCommandRunner {
126
126
  */
127
127
  declare function useFieldCommand(inspectionId: string): FieldCommandRunner;
128
128
 
129
+ /**
130
+ * The inspection editor page. It reads everything from its `data` prop, the field data source and the host
131
+ * slots, so any host (web route, native shell) mounts it the same way. `data` must follow the host's
132
+ * revalidation: units, publish state and the rest are read from it on every render.
133
+ */
134
+ declare function FieldEditor({ data }: {
135
+ data: EditorData;
136
+ }): React.JSX.Element;
137
+
138
+ declare function findingKey(unitId: string | null | undefined, sectionId: string, itemId: string): string;
139
+ declare function findingsForUnit(data: Record<string, unknown>, unitId: string): Record<string, unknown>;
140
+ /**
141
+ * A meta key of a results blob (`_inspector_signature`): underscore-prefixed with
142
+ * fewer than three colon-separated parts. It is not a finding, and the results
143
+ * document never holds it: importing a blob skips it, and projecting a document
144
+ * skips one an older document may still carry as an empty item map (either would
145
+ * otherwise replace the real value in the blob with `{}`). Composite finding keys
146
+ * start with `_default:` too, which is why the prefix alone cannot decide.
147
+ */
148
+ declare function isMetaKey(key: string): boolean;
149
+
150
+ /**
151
+ * Bridges a live Y.Doc (from `use-results-doc.ts`) to the editor's in-memory
152
+ * ResultMap, and provides write helpers that mirror the editor's existing hook
153
+ * signatures so Task 9 can swap them in 1:1.
154
+ *
155
+ * NO React import. NO import of packages/field-ui/src/hooks/useInspection.ts (React-heavy).
156
+ */
157
+
158
+ /**
159
+ * #181 PR-G — append a brand-new offline photo as a PENDING doc entry.
160
+ *
161
+ * The binary is only in the local media-pending store (not yet on R2), so the
162
+ * entry has an EMPTY `key` + `pendingUpload: true` + the `pendingId` that
163
+ * resolves to the local blob. The report SKIPS such entries (it filters
164
+ * `pendingUpload`); the editor renders them from the local objectURL. On drain
165
+ * the entry is swapped to its real R2 key via `resolvePendingPhoto` below.
166
+ */
167
+ declare function appendPendingPhoto(doc: Y.Doc, findingKey: string, pendingId: string): void;
168
+ /**
169
+ * #181 PR-G — resolve a pending photo after its offline blob has uploaded.
170
+ *
171
+ * Swaps the doc entry pending→real key and CLEARS the pending markers
172
+ * (`pendingUpload` / `pendingId` / `pendingKind`) by replace-in-place (field
173
+ * delete is unsupported). The match key differs by kind:
174
+ * - `photo`: the entry's `key` is empty; match by the entry whose
175
+ * `pendingId === pendingId`, then set the real `key` = result.key.
176
+ * - `crop`: match by base `key`, set `croppedKey` = result.croppedKey.
177
+ * - `annotate`: match by base `key`, set `annotatedKey` = result.annotatedKey.
178
+ *
179
+ * No-op if no matching pending entry is found (idempotent — a duplicate drain
180
+ * after the swap simply finds nothing).
181
+ */
182
+ declare function resolvePendingPhoto(doc: Y.Doc, findingKey: string, pendingId: string, kind: 'photo' | 'crop' | 'annotate', photoKey: string | undefined, result: {
183
+ key?: string;
184
+ croppedKey?: string;
185
+ annotatedKey?: string;
186
+ }): void;
187
+
188
+ /**
189
+ * Commercial PCA Phase M — seeded reliance / point-in-time / site-specific
190
+ * disclaimer text (ASTM §4.2.1–4.2.4). Defaults the Phase S §2.5 (User Reliance)
191
+ * and Limitations regions so the legal boilerplate is never missing; the
192
+ * inspector may edit it. Plain text (textarea, no RTE — project decision).
193
+ */
194
+ declare const RELIANCE_TEMPLATES: {
195
+ readonly userReliance: string;
196
+ readonly pointInTime: string;
197
+ readonly siteSpecific: string;
198
+ };
199
+
200
+ /** What the editor behaves like until the tenant's own preferences arrive (or when the host has no preference store). */
201
+ declare const DEFAULT_INSPECTION_PREFS: InspectionPrefs;
202
+
203
+ /** True while at least one layer is open. */
204
+ declare function hasOpenLayer(): boolean;
205
+ /**
206
+ * True when the next Escape will be taken by an overlay. For window-level hotkeys that are
207
+ * not layers: they must skip Escape exactly when a layer will answer it, and not when a
208
+ * layer is open that does not answer Escape.
209
+ */
210
+ declare function escapeWillBeHandled(): boolean;
211
+
129
212
  /**
130
213
  * Pin the package to one locale, explicitly.
131
214
  *
@@ -151,5 +234,5 @@ declare function setFieldLocale(locale: string): void;
151
234
  */
152
235
  declare function bindFieldLocaleSource(read: (() => string) | null): void;
153
236
 
154
- export { CommandData, CommandResult, FieldCommand, FieldDataSource, FieldDataSourceProvider, bindFieldLocaleSource, isIgnored, setFieldLocale, useFieldCommand, useFieldDataSource, useFieldEnsureHuman, useFieldSlots };
237
+ 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
238
  export type { FieldCommandRunner, FieldDataSourceProviderProps, FieldHostSlots, FieldRunResult, IgnoredRun };