@inspectorhub/field-ui 0.4.0 → 0.5.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/dist/index.js CHANGED
@@ -16,7 +16,7 @@ function isDecodeImageError(e) {
16
16
  }
17
17
 
18
18
  // src/context.tsx
19
- import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
19
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
20
20
 
21
21
  // src/i18n/generated/messages/_index.js
22
22
  var index_exports = {};
@@ -456,6 +456,9 @@ __export(index_exports, {
456
456
  editor_dock_photo_studio: () => editor_dock_photo_studio3,
457
457
  editor_dock_speed_mode: () => editor_dock_speed_mode3,
458
458
  editor_finish_fieldwork: () => editor_finish_fieldwork3,
459
+ editor_finish_fieldwork_confirm_body: () => editor_finish_fieldwork_confirm_body3,
460
+ editor_finish_fieldwork_confirm_title: () => editor_finish_fieldwork_confirm_title3,
461
+ editor_finish_fieldwork_done: () => editor_finish_fieldwork_done3,
459
462
  editor_finish_fieldwork_error: () => editor_finish_fieldwork_error3,
460
463
  editor_finish_fieldwork_pending: () => editor_finish_fieldwork_pending3,
461
464
  editor_followup_notes_placeholder: () => editor_followup_notes_placeholder3,
@@ -688,6 +691,8 @@ __export(index_exports, {
688
691
  editor_route_exit_batch_mode: () => editor_route_exit_batch_mode3,
689
692
  editor_route_expand_photo_rail: () => editor_route_expand_photo_rail3,
690
693
  editor_route_filter_all: () => editor_route_filter_all3,
694
+ editor_route_filter_all_in_section: () => editor_route_filter_all_in_section3,
695
+ editor_route_filter_count_in_section: () => editor_route_filter_count_in_section3,
691
696
  editor_route_filter_flagged: () => editor_route_filter_flagged3,
692
697
  editor_route_filter_issues: () => editor_route_filter_issues3,
693
698
  editor_route_filter_unrated: () => editor_route_filter_unrated3,
@@ -835,6 +840,7 @@ __export(index_exports, {
835
840
  editor_structuredelete_title: () => editor_structuredelete_title3,
836
841
  editor_tagchiprow_more: () => editor_tagchiprow_more3,
837
842
  editor_tagchiprow_open_library_aria: () => editor_tagchiprow_open_library_aria3,
843
+ editor_tagpicker_empty: () => editor_tagpicker_empty3,
838
844
  editor_tagpicker_title: () => editor_tagpicker_title3,
839
845
  editor_template_applied_reopen: () => editor_template_applied_reopen3,
840
846
  editor_typeahead_kind_defect: () => editor_typeahead_kind_defect3,
@@ -8187,6 +8193,33 @@ var editor_finish_fieldwork_error = (
8187
8193
  );
8188
8194
  }
8189
8195
  );
8196
+ var editor_finish_fieldwork_confirm_title = (
8197
+ /** @type {(inputs: Editor_Finish_Fieldwork_Confirm_TitleInputs) => LocalizedString} */
8198
+ () => {
8199
+ return (
8200
+ /** @type {LocalizedString} */
8201
+ `Finish fieldwork?`
8202
+ );
8203
+ }
8204
+ );
8205
+ var editor_finish_fieldwork_confirm_body = (
8206
+ /** @type {(inputs: Editor_Finish_Fieldwork_Confirm_BodyInputs) => LocalizedString} */
8207
+ () => {
8208
+ return (
8209
+ /** @type {LocalizedString} */
8210
+ `The on-site work will be marked as finished. Publishing the report does not depend on it.`
8211
+ );
8212
+ }
8213
+ );
8214
+ var editor_finish_fieldwork_done = (
8215
+ /** @type {(inputs: Editor_Finish_Fieldwork_DoneInputs) => LocalizedString} */
8216
+ () => {
8217
+ return (
8218
+ /** @type {LocalizedString} */
8219
+ `Fieldwork marked as finished.`
8220
+ );
8221
+ }
8222
+ );
8190
8223
  var editor_publish_not_completed_prompt = (
8191
8224
  /** @type {(inputs: Editor_Publish_Not_Completed_PromptInputs) => LocalizedString} */
8192
8225
  () => {
@@ -8772,6 +8805,15 @@ var editor_tagpicker_title = (
8772
8805
  );
8773
8806
  }
8774
8807
  );
8808
+ var editor_tagpicker_empty = (
8809
+ /** @type {(inputs: Editor_Tagpicker_EmptyInputs) => LocalizedString} */
8810
+ () => {
8811
+ return (
8812
+ /** @type {LocalizedString} */
8813
+ `There are no tags yet. Add tags in the Library, then choose them here.`
8814
+ );
8815
+ }
8816
+ );
8775
8817
  var editor_unitprogress_summary_aria = (
8776
8818
  /** @type {(inputs: Editor_Unitprogress_Summary_AriaInputs) => LocalizedString} */
8777
8819
  (i) => {
@@ -9792,6 +9834,24 @@ var editor_route_filter_flagged = (
9792
9834
  );
9793
9835
  }
9794
9836
  );
9837
+ var editor_route_filter_all_in_section = (
9838
+ /** @type {(inputs: Editor_Route_Filter_All_In_SectionInputs) => LocalizedString} */
9839
+ () => {
9840
+ return (
9841
+ /** @type {LocalizedString} */
9842
+ `All items in this section`
9843
+ );
9844
+ }
9845
+ );
9846
+ var editor_route_filter_count_in_section = (
9847
+ /** @type {(inputs: Editor_Route_Filter_Count_In_SectionInputs) => LocalizedString} */
9848
+ (i) => {
9849
+ return (
9850
+ /** @type {LocalizedString} */
9851
+ `${i?.label} ${i?.count} in this section`
9852
+ );
9853
+ }
9854
+ );
9795
9855
  var editor_route_exit_batch_mode = (
9796
9856
  /** @type {(inputs: Editor_Route_Exit_Batch_ModeInputs) => LocalizedString} */
9797
9857
  () => {
@@ -22460,7 +22520,7 @@ var report_publish_body_delivery = (
22460
22520
  () => {
22461
22521
  return (
22462
22522
  /** @type {LocalizedString} */
22463
- `It does not send the report itself. Report notices are queued to your automations and go out on the next delivery pass \u2014 to put the report in someone's inbox now, use Send report on the inspection page.`
22523
+ `It does not send the report itself. Report notices are queued to your automations and go out on the next delivery pass \u2014 to put the report in someone's inbox now, send it from the inspection.`
22464
22524
  );
22465
22525
  }
22466
22526
  );
@@ -28748,6 +28808,33 @@ var editor_finish_fieldwork_error2 = (
28748
28808
  );
28749
28809
  }
28750
28810
  );
28811
+ var editor_finish_fieldwork_confirm_title2 = (
28812
+ /** @type {(inputs: Editor_Finish_Fieldwork_Confirm_TitleInputs) => LocalizedString} */
28813
+ () => {
28814
+ return (
28815
+ /** @type {LocalizedString} */
28816
+ `\xBFFinalizar el trabajo de campo?`
28817
+ );
28818
+ }
28819
+ );
28820
+ var editor_finish_fieldwork_confirm_body2 = (
28821
+ /** @type {(inputs: Editor_Finish_Fieldwork_Confirm_BodyInputs) => LocalizedString} */
28822
+ () => {
28823
+ return (
28824
+ /** @type {LocalizedString} */
28825
+ `El trabajo en sitio se marcar\xE1 como finalizado. Publicar el informe no depende de ello.`
28826
+ );
28827
+ }
28828
+ );
28829
+ var editor_finish_fieldwork_done2 = (
28830
+ /** @type {(inputs: Editor_Finish_Fieldwork_DoneInputs) => LocalizedString} */
28831
+ () => {
28832
+ return (
28833
+ /** @type {LocalizedString} */
28834
+ `Trabajo de campo marcado como finalizado.`
28835
+ );
28836
+ }
28837
+ );
28751
28838
  var editor_publish_not_completed_prompt2 = (
28752
28839
  /** @type {(inputs: Editor_Publish_Not_Completed_PromptInputs) => LocalizedString} */
28753
28840
  () => {
@@ -29333,6 +29420,15 @@ var editor_tagpicker_title2 = (
29333
29420
  );
29334
29421
  }
29335
29422
  );
29423
+ var editor_tagpicker_empty2 = (
29424
+ /** @type {(inputs: Editor_Tagpicker_EmptyInputs) => LocalizedString} */
29425
+ () => {
29426
+ return (
29427
+ /** @type {LocalizedString} */
29428
+ `Todav\xEDa no hay etiquetas. Agrega etiquetas en la Biblioteca y luego el\xEDgelas aqu\xED.`
29429
+ );
29430
+ }
29431
+ );
29336
29432
  var editor_unitprogress_summary_aria2 = (
29337
29433
  /** @type {(inputs: Editor_Unitprogress_Summary_AriaInputs) => LocalizedString} */
29338
29434
  (i) => {
@@ -30353,6 +30449,24 @@ var editor_route_filter_flagged2 = (
30353
30449
  );
30354
30450
  }
30355
30451
  );
30452
+ var editor_route_filter_all_in_section2 = (
30453
+ /** @type {(inputs: Editor_Route_Filter_All_In_SectionInputs) => LocalizedString} */
30454
+ () => {
30455
+ return (
30456
+ /** @type {LocalizedString} */
30457
+ `Todos los elementos de esta secci\xF3n`
30458
+ );
30459
+ }
30460
+ );
30461
+ var editor_route_filter_count_in_section2 = (
30462
+ /** @type {(inputs: Editor_Route_Filter_Count_In_SectionInputs) => LocalizedString} */
30463
+ (i) => {
30464
+ return (
30465
+ /** @type {LocalizedString} */
30466
+ `${i?.label} ${i?.count} en esta secci\xF3n`
30467
+ );
30468
+ }
30469
+ );
30356
30470
  var editor_route_exit_batch_mode2 = (
30357
30471
  /** @type {(inputs: Editor_Route_Exit_Batch_ModeInputs) => LocalizedString} */
30358
30472
  () => {
@@ -43021,7 +43135,7 @@ var report_publish_body_delivery2 = (
43021
43135
  () => {
43022
43136
  return (
43023
43137
  /** @type {LocalizedString} */
43024
- `No env\xEDa el informe por s\xED mismo. Los avisos del informe se ponen en cola seg\xFAn sus automatizaciones y salen en la pr\xF3xima tanda de env\xEDos \u2014 para que el informe llegue ahora a la bandeja de alguien, use Enviar informe en la p\xE1gina de la inspecci\xF3n.`
43138
+ `No env\xEDa el informe por s\xED mismo. Los avisos del informe se ponen en cola seg\xFAn sus automatizaciones y salen en la pr\xF3xima tanda de env\xEDos \u2014 para que el informe llegue ahora a la bandeja de alguien, env\xEDelo desde la inspecci\xF3n.`
43025
43139
  );
43026
43140
  }
43027
43141
  );
@@ -48705,6 +48819,30 @@ var editor_finish_fieldwork_error3 = (
48705
48819
  return editor_finish_fieldwork_error2(inputs);
48706
48820
  })
48707
48821
  );
48822
+ var editor_finish_fieldwork_confirm_title3 = (
48823
+ /** @type {((inputs?: Editor_Finish_Fieldwork_Confirm_TitleInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Finish_Fieldwork_Confirm_TitleInputs, { locale?: "en" | "es-419" }, {}>} */
48824
+ ((inputs = {}, options = {}) => {
48825
+ const locale = experimentalStaticLocale ?? options.locale ?? getLocale();
48826
+ if (locale === "en") return editor_finish_fieldwork_confirm_title(inputs);
48827
+ return editor_finish_fieldwork_confirm_title2(inputs);
48828
+ })
48829
+ );
48830
+ var editor_finish_fieldwork_confirm_body3 = (
48831
+ /** @type {((inputs?: Editor_Finish_Fieldwork_Confirm_BodyInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Finish_Fieldwork_Confirm_BodyInputs, { locale?: "en" | "es-419" }, {}>} */
48832
+ ((inputs = {}, options = {}) => {
48833
+ const locale = experimentalStaticLocale ?? options.locale ?? getLocale();
48834
+ if (locale === "en") return editor_finish_fieldwork_confirm_body(inputs);
48835
+ return editor_finish_fieldwork_confirm_body2(inputs);
48836
+ })
48837
+ );
48838
+ var editor_finish_fieldwork_done3 = (
48839
+ /** @type {((inputs?: Editor_Finish_Fieldwork_DoneInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Finish_Fieldwork_DoneInputs, { locale?: "en" | "es-419" }, {}>} */
48840
+ ((inputs = {}, options = {}) => {
48841
+ const locale = experimentalStaticLocale ?? options.locale ?? getLocale();
48842
+ if (locale === "en") return editor_finish_fieldwork_done(inputs);
48843
+ return editor_finish_fieldwork_done2(inputs);
48844
+ })
48845
+ );
48708
48846
  var editor_publish_not_completed_prompt3 = (
48709
48847
  /** @type {((inputs?: Editor_Publish_Not_Completed_PromptInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Publish_Not_Completed_PromptInputs, { locale?: "en" | "es-419" }, {}>} */
48710
48848
  ((inputs = {}, options = {}) => {
@@ -49225,6 +49363,14 @@ var editor_tagpicker_title3 = (
49225
49363
  return editor_tagpicker_title2(inputs);
49226
49364
  })
49227
49365
  );
49366
+ var editor_tagpicker_empty3 = (
49367
+ /** @type {((inputs?: Editor_Tagpicker_EmptyInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Tagpicker_EmptyInputs, { locale?: "en" | "es-419" }, {}>} */
49368
+ ((inputs = {}, options = {}) => {
49369
+ const locale = experimentalStaticLocale ?? options.locale ?? getLocale();
49370
+ if (locale === "en") return editor_tagpicker_empty(inputs);
49371
+ return editor_tagpicker_empty2(inputs);
49372
+ })
49373
+ );
49228
49374
  var editor_unitprogress_summary_aria3 = (
49229
49375
  /** @type {((inputs: Editor_Unitprogress_Summary_AriaInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Unitprogress_Summary_AriaInputs, { locale?: "en" | "es-419" }, {}>} */
49230
49376
  ((inputs, options = {}) => {
@@ -50129,6 +50275,22 @@ var editor_route_filter_flagged3 = (
50129
50275
  return editor_route_filter_flagged2(inputs);
50130
50276
  })
50131
50277
  );
50278
+ var editor_route_filter_all_in_section3 = (
50279
+ /** @type {((inputs?: Editor_Route_Filter_All_In_SectionInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Route_Filter_All_In_SectionInputs, { locale?: "en" | "es-419" }, {}>} */
50280
+ ((inputs = {}, options = {}) => {
50281
+ const locale = experimentalStaticLocale ?? options.locale ?? getLocale();
50282
+ if (locale === "en") return editor_route_filter_all_in_section(inputs);
50283
+ return editor_route_filter_all_in_section2(inputs);
50284
+ })
50285
+ );
50286
+ var editor_route_filter_count_in_section3 = (
50287
+ /** @type {((inputs: Editor_Route_Filter_Count_In_SectionInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Route_Filter_Count_In_SectionInputs, { locale?: "en" | "es-419" }, {}>} */
50288
+ ((inputs, options = {}) => {
50289
+ const locale = experimentalStaticLocale ?? options.locale ?? getLocale();
50290
+ if (locale === "en") return editor_route_filter_count_in_section(inputs);
50291
+ return editor_route_filter_count_in_section2(inputs);
50292
+ })
50293
+ );
50132
50294
  var editor_route_exit_batch_mode3 = (
50133
50295
  /** @type {((inputs?: Editor_Route_Exit_Batch_ModeInputs, options?: { locale?: "en" | "es-419" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Editor_Route_Exit_Batch_ModeInputs, { locale?: "en" | "es-419" }, {}>} */
50134
50296
  ((inputs = {}, options = {}) => {
@@ -62178,6 +62340,35 @@ function useFieldDataSource() {
62178
62340
  }
62179
62341
  return dataSource;
62180
62342
  }
62343
+ function usePhotoUrlVersion() {
62344
+ const { urls } = useFieldDataSource();
62345
+ const store = useMemo(() => {
62346
+ let version = 0;
62347
+ return {
62348
+ // A host that fires once per fetched photo is coalesced to one render per tick.
62349
+ subscribe: (onChange) => {
62350
+ let queued = false;
62351
+ let live = true;
62352
+ const off = urls.subscribe?.(() => {
62353
+ if (queued) return;
62354
+ queued = true;
62355
+ queueMicrotask(() => {
62356
+ queued = false;
62357
+ if (!live) return;
62358
+ version += 1;
62359
+ onChange();
62360
+ });
62361
+ });
62362
+ return () => {
62363
+ live = false;
62364
+ off?.();
62365
+ };
62366
+ },
62367
+ read: () => version
62368
+ };
62369
+ }, [urls]);
62370
+ return useSyncExternalStore(store.subscribe, store.read, store.read);
62371
+ }
62181
62372
  function useFieldSlots() {
62182
62373
  return useContext(SlotsContext);
62183
62374
  }
@@ -62264,7 +62455,7 @@ function useFieldCommand(inspectionId) {
62264
62455
  }
62265
62456
 
62266
62457
  // src/editor/FieldEditor.tsx
62267
- import { useState as useState63, useCallback as useCallback44, useMemo as useMemo14, useEffect as useEffect49, useRef as useRef61 } from "react";
62458
+ import { useState as useState64, useCallback as useCallback47, useMemo as useMemo16, useEffect as useEffect51, useRef as useRef62 } from "react";
62268
62459
 
62269
62460
  // src/shared/inspection-status.ts
62270
62461
  var INSPECTION_STATUS_COMPLETED = "completed";
@@ -62330,7 +62521,7 @@ function makeCustomDefect(input, genId = () => crypto.randomUUID()) {
62330
62521
  }
62331
62522
 
62332
62523
  // src/hooks/useInspection.ts
62333
- import { useState as useState2, useCallback as useCallback6, useMemo as useMemo4, useRef as useRef2, useEffect as useEffect2 } from "react";
62524
+ import { useState as useState2, useCallback as useCallback6, useMemo as useMemo5, useRef as useRef2, useEffect as useEffect2 } from "react";
62334
62525
 
62335
62526
  // src/hooks/findings/shared.ts
62336
62527
  var DEFAULT_UNIT = "_default";
@@ -62371,6 +62562,9 @@ function backfillLevelDescriptions(levels) {
62371
62562
  });
62372
62563
  }
62373
62564
 
62565
+ // src/lib/severity.ts
62566
+ var FALLBACK_RATING_COLOR = "var(--ih-fg-5, #d4d4d8)";
62567
+
62374
62568
  // src/shared/batch-range.ts
62375
62569
  function rangeIds(orderedIds, fromId, toId) {
62376
62570
  const i = orderedIds.indexOf(fromId);
@@ -62482,10 +62676,10 @@ function formatCurrency(cents, opts) {
62482
62676
  }
62483
62677
 
62484
62678
  // src/hooks/inspection/useInspectionProgress.ts
62485
- import { useCallback as useCallback2, useMemo } from "react";
62679
+ import { useCallback as useCallback2, useMemo as useMemo2 } from "react";
62486
62680
  function useInspectionProgress(ctx) {
62487
62681
  const { sections, ratingLevels, getResult, severityForRatingId } = ctx;
62488
- const progress = useMemo(() => {
62682
+ const progress = useMemo2(() => {
62489
62683
  let total = 0;
62490
62684
  let rated = 0;
62491
62685
  for (const sec of sections) {
@@ -62549,7 +62743,7 @@ function useInspectionProgress(ctx) {
62549
62743
  },
62550
62744
  [sections, ratingLevels, getResult]
62551
62745
  );
62552
- const reportStats = useMemo(() => {
62746
+ const reportStats = useMemo2(() => {
62553
62747
  let total = 0;
62554
62748
  let rated = 0;
62555
62749
  let satisfactory = 0;
@@ -62709,7 +62903,7 @@ function useInspectionNavigation(ctx) {
62709
62903
  }
62710
62904
 
62711
62905
  // src/hooks/inspection/useInspectionSearch.ts
62712
- import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
62906
+ import { useCallback as useCallback4, useMemo as useMemo3 } from "react";
62713
62907
  function useInspectionSearch(ctx) {
62714
62908
  const {
62715
62909
  searchQuery,
@@ -62720,7 +62914,7 @@ function useInspectionSearch(ctx) {
62720
62914
  currentSection,
62721
62915
  getResult
62722
62916
  } = ctx;
62723
- const searchNeedle = useMemo2(
62917
+ const searchNeedle = useMemo3(
62724
62918
  () => (searchQuery || "").trim().toLowerCase(),
62725
62919
  [searchQuery]
62726
62920
  );
@@ -62766,7 +62960,7 @@ function useInspectionSearch(ctx) {
62766
62960
  },
62767
62961
  [itemFilter, getResult, ratingLevels, tagsByItem]
62768
62962
  );
62769
- const filterCounts = useMemo2(() => {
62963
+ const filterCounts = useMemo3(() => {
62770
62964
  const items = currentSectionItems;
62771
62965
  const counts = { all: items.length, unrated: 0, issues: 0, flagged: 0 };
62772
62966
  for (const item of items) {
@@ -62791,7 +62985,7 @@ function useInspectionSearch(ctx) {
62791
62985
  }
62792
62986
 
62793
62987
  // src/hooks/inspection/useInspectionBatch.ts
62794
- import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
62988
+ import { useCallback as useCallback5, useMemo as useMemo4 } from "react";
62795
62989
  function useInspectionBatch(ctx, selectSection) {
62796
62990
  const {
62797
62991
  sections,
@@ -62816,11 +63010,11 @@ function useInspectionBatch(ctx, selectSection) {
62816
63010
  }
62817
63011
  setBatchSelected(next);
62818
63012
  }, [currentSectionItems]);
62819
- const selectedBatchCount = useMemo3(
63013
+ const selectedBatchCount = useMemo4(
62820
63014
  () => Object.values(batchSelected).filter(Boolean).length,
62821
63015
  [batchSelected]
62822
63016
  );
62823
- const filteredSectionsForPicker = useMemo3(() => {
63017
+ const filteredSectionsForPicker = useMemo4(() => {
62824
63018
  const q = (sectionPickerQuery || "").toLowerCase().trim();
62825
63019
  const src = sections.map((s, idx) => ({
62826
63020
  idx,
@@ -62940,7 +63134,7 @@ function useInspectionState(opts) {
62940
63134
  }, []);
62941
63135
  const currentSection = sections[currentSectionIdx] || null;
62942
63136
  const currentSectionItems = currentSection?.items || [];
62943
- const activeItem = useMemo4(() => {
63137
+ const activeItem = useMemo5(() => {
62944
63138
  if (!activeItemId) return null;
62945
63139
  return currentSectionItems.find((i) => i.id === activeItemId) || null;
62946
63140
  }, [activeItemId, currentSectionItems]);
@@ -62992,7 +63186,7 @@ function useInspectionState(opts) {
62992
63186
  );
62993
63187
  const getRatingColor = useCallback6(
62994
63188
  (ratingId) => {
62995
- if (!ratingId) return "var(--ih-fg-5, #d4d4d8)";
63189
+ if (!ratingId) return FALLBACK_RATING_COLOR;
62996
63190
  const lvl = ratingLevels.find((l) => l.id === ratingId);
62997
63191
  if (lvl?.color) return lvl.color;
62998
63192
  const legacy = {
@@ -63000,7 +63194,7 @@ function useInspectionState(opts) {
63000
63194
  Monitor: "var(--ih-status-watch, #f59e0b)",
63001
63195
  Defect: "var(--ih-status-bad, #ef4444)"
63002
63196
  };
63003
- return legacy[ratingId] || "var(--ih-fg-5, #d4d4d8)";
63197
+ return legacy[ratingId] || FALLBACK_RATING_COLOR;
63004
63198
  },
63005
63199
  [ratingLevels]
63006
63200
  );
@@ -63071,7 +63265,7 @@ function useInspectionState(opts) {
63071
63265
  return next;
63072
63266
  });
63073
63267
  }, [currentSectionItems]);
63074
- const formattedDate = useMemo4(() => {
63268
+ const formattedDate = useMemo5(() => {
63075
63269
  const d = inspection.date || inspection.scheduledDate || inspection.createdAt;
63076
63270
  if (!d) return "";
63077
63271
  return formatDate(d, { locale });
@@ -63522,6 +63716,7 @@ function removeElement(arr, identityField, identityValue) {
63522
63716
  }
63523
63717
  function applyItemPatch(doc, findingKey3, field, value) {
63524
63718
  const results = doc.getMap("results");
63719
+ if (results.get(findingKey3)?.get?.(field) === value) return;
63525
63720
  doc.transact(() => {
63526
63721
  const item = getOrSeedItem(results, findingKey3);
63527
63722
  item.set(field, value);
@@ -64157,7 +64352,7 @@ function useFindings(results, setResults, options) {
64157
64352
  }
64158
64353
 
64159
64354
  // src/hooks/usePhotoOps.ts
64160
- import { useState as useState10, useCallback as useCallback16, useMemo as useMemo5, useEffect as useEffect8, useRef as useRef11 } from "react";
64355
+ import { useState as useState10, useCallback as useCallback16, useMemo as useMemo6, useEffect as useEffect8, useRef as useRef11 } from "react";
64161
64356
 
64162
64357
  // src/components/media-studio/photo-display-key.ts
64163
64358
  function resolvePhotoDisplayKey(entry) {
@@ -67945,7 +68140,7 @@ function usePhotoOps(ctx) {
67945
68140
  setPhotoStudioOpen
67946
68141
  } = ctx;
67947
68142
  const dataSource = useFieldDataSource();
67948
- const { urls } = dataSource;
68143
+ const { urls } = dataSource, photoUrlVersion = usePhotoUrlVersion();
67949
68144
  const mediaSavesInFlight = useRef11(/* @__PURE__ */ new Set());
67950
68145
  const [galleryCropSource, setGalleryCropSource] = useState10(null);
67951
68146
  const [sessionCoverCrop, setSessionCoverCrop] = useState10(null);
@@ -67971,7 +68166,7 @@ function usePhotoOps(ctx) {
67971
68166
  [findings, state.sectionIdForItem]
67972
68167
  );
67973
68168
  const [pendingUrls, setPendingUrls] = useState10({});
67974
- const pendingIds = useMemo5(() => {
68169
+ const pendingIds = useMemo6(() => {
67975
68170
  const ids = /* @__PURE__ */ new Set();
67976
68171
  if (!collabDoc) return ids;
67977
68172
  for (const sec of state.sections) {
@@ -68067,7 +68262,7 @@ function usePhotoOps(ctx) {
68067
68262
  durationSec: p.durationSec
68068
68263
  };
68069
68264
  }),
68070
- [getItemPhotos, state.inspection.id, pendingUrls, urls]
68265
+ [getItemPhotos, state.inspection.id, pendingUrls, urls, photoUrlVersion]
68071
68266
  );
68072
68267
  const onOpenPhoto = useCallback16((itemId, index) => {
68073
68268
  setViewer({ itemId, index });
@@ -68113,14 +68308,14 @@ function usePhotoOps(ctx) {
68113
68308
  (itemId, indices) => {
68114
68309
  const sectionId = state.currentSection?.id;
68115
68310
  const fk = docFindingKey(itemId, sectionId);
68116
- const snapshot = getItemPhotos(itemId);
68117
- const keys = indices.map((i) => snapshot[i]?.key).filter((k) => !!k);
68311
+ const snapshot2 = getItemPhotos(itemId);
68312
+ const keys = indices.map((i) => snapshot2[i]?.key).filter((k) => !!k);
68118
68313
  patchItemPhotos(itemId, (photos) => photos.filter((_, i) => !indices.includes(i)));
68119
68314
  if (collabDoc && fk) for (const key of keys) removePhoto2(collabDoc, fk, key);
68120
68315
  },
68121
68316
  [state.currentSection, collabDoc, docFindingKey, getItemPhotos, patchItemPhotos]
68122
68317
  );
68123
- const moveTargets = useMemo5(
68318
+ const moveTargets = useMemo6(
68124
68319
  () => state.sections.flatMap(
68125
68320
  (sec) => (sec.items || []).map((it) => ({
68126
68321
  itemId: it.id,
@@ -68135,8 +68330,8 @@ function usePhotoOps(ctx) {
68135
68330
  const fromSectionId = state.currentSection?.id;
68136
68331
  const fromFk = docFindingKey(fromItemId, fromSectionId);
68137
68332
  const toFk = docFindingKey(to.itemId, to.sectionId);
68138
- const snapshot = getItemPhotos(fromItemId);
68139
- const keys = indices.map((i) => snapshot[i]?.key).filter((k) => !!k);
68333
+ const snapshot2 = getItemPhotos(fromItemId);
68334
+ const keys = indices.map((i) => snapshot2[i]?.key).filter((k) => !!k);
68140
68335
  patchItemPhotos(fromItemId, (photos) => photos.filter((_, i) => !indices.includes(i)));
68141
68336
  if (collabDoc && fromFk && toFk) for (const key of keys) movePhoto2(collabDoc, fromFk, toFk, key);
68142
68337
  },
@@ -68397,6 +68592,7 @@ function useInspectionPrefs() {
68397
68592
  const [saving, setSaving] = useState11(false);
68398
68593
  const requested = useRef13(false);
68399
68594
  const committed = useRef13(DEFAULT_INSPECTION_PREFS);
68595
+ const latest = useRef13(DEFAULT_INSPECTION_PREFS);
68400
68596
  const mounted = useRef13(true);
68401
68597
  useEffect10(() => {
68402
68598
  mounted.current = true;
@@ -68415,6 +68611,7 @@ function useInspectionPrefs() {
68415
68611
  (loadedPrefs) => {
68416
68612
  committed.current = loadedPrefs;
68417
68613
  if (!mounted.current) return;
68614
+ latest.current = loadedPrefs;
68418
68615
  setPrefs(loadedPrefs);
68419
68616
  setLoaded(true);
68420
68617
  },
@@ -68441,7 +68638,8 @@ function useInspectionPrefs() {
68441
68638
  if (mounted.current) {
68442
68639
  const later = queued.current;
68443
68640
  const base = committed.current;
68444
- setPrefs({ ...base, ...later });
68641
+ latest.current = { ...base, ...later };
68642
+ setPrefs(latest.current);
68445
68643
  }
68446
68644
  } else {
68447
68645
  const back = {};
@@ -68449,7 +68647,8 @@ function useInspectionPrefs() {
68449
68647
  if (!(key in queued.current)) back[key] = committed.current[key];
68450
68648
  }
68451
68649
  if (mounted.current) {
68452
- setPrefs((prev) => ({ ...prev, ...back }));
68650
+ latest.current = { ...latest.current, ...back };
68651
+ setPrefs(latest.current);
68453
68652
  setSaveFailed(true);
68454
68653
  }
68455
68654
  }
@@ -68459,8 +68658,11 @@ function useInspectionPrefs() {
68459
68658
  if (mounted.current) setSaving(false);
68460
68659
  }
68461
68660
  }, []);
68462
- const patch = useCallback18((delta) => {
68463
- setPrefs((prev) => ({ ...prev, ...delta }));
68661
+ const patch = useCallback18((change) => {
68662
+ const delta = typeof change === "function" ? change(latest.current) : change;
68663
+ if (Object.keys(delta).length === 0) return;
68664
+ latest.current = { ...latest.current, ...delta };
68665
+ setPrefs(latest.current);
68464
68666
  setSaveFailed(false);
68465
68667
  if (!prefsStore) return;
68466
68668
  queued.current = { ...queued.current, ...delta };
@@ -68482,8 +68684,59 @@ function useInspectionPrefs() {
68482
68684
  };
68483
68685
  }
68484
68686
 
68687
+ // src/hooks/useItemTags.ts
68688
+ import { useCallback as useCallback19, useEffect as useEffect11, useRef as useRef14 } from "react";
68689
+ function useItemTags(inspectionId, setTagsByItem) {
68690
+ const dataSource = useFieldDataSource();
68691
+ const store = dataSource.resources.itemTags;
68692
+ const notify = dataSource.notify;
68693
+ const inflight = useRef14(/* @__PURE__ */ new Set());
68694
+ const mounted = useRef14(true);
68695
+ useEffect11(() => {
68696
+ mounted.current = true;
68697
+ return () => {
68698
+ mounted.current = false;
68699
+ };
68700
+ }, []);
68701
+ useEffect11(() => {
68702
+ if (!store || !inspectionId) return;
68703
+ let live = true;
68704
+ void store.list(inspectionId).then(
68705
+ (saved) => {
68706
+ if (!live || !saved) return;
68707
+ setTagsByItem((prev) => ({ ...saved, ...prev }));
68708
+ },
68709
+ () => {
68710
+ }
68711
+ );
68712
+ return () => {
68713
+ live = false;
68714
+ };
68715
+ }, [store, inspectionId, setTagsByItem]);
68716
+ const toggle = useCallback19((itemId, tag, current) => {
68717
+ const key = `${itemId}:${tag.id}`;
68718
+ if (inflight.current.has(key)) return;
68719
+ const attach = !current.some((t) => t.id === tag.id);
68720
+ const apply = (adding) => setTagsByItem((prev) => {
68721
+ const rest = (prev[itemId] ?? []).filter((t) => t.id !== tag.id);
68722
+ return { ...prev, [itemId]: adding ? [...rest, tag] : rest };
68723
+ });
68724
+ apply(attach);
68725
+ if (!store || !inspectionId) return;
68726
+ inflight.current.add(key);
68727
+ const write = attach ? store.add(inspectionId, itemId, tag.id) : store.remove(inspectionId, itemId, tag.id);
68728
+ void Promise.resolve(write).then((result) => result.ok, () => false).then((stored) => {
68729
+ inflight.current.delete(key);
68730
+ if (stored || !mounted.current) return;
68731
+ apply(!attach);
68732
+ notify(index_exports.editor_route_save_failed(), "error", { durationMs: 8e3 });
68733
+ });
68734
+ }, [store, inspectionId, setTagsByItem, notify]);
68735
+ return { toggle, canSave: store !== void 0 };
68736
+ }
68737
+
68485
68738
  // src/hooks/useKeyboard.ts
68486
- import { useEffect as useEffect11, useRef as useRef14 } from "react";
68739
+ import { useEffect as useEffect12, useRef as useRef15 } from "react";
68487
68740
 
68488
68741
  // src/lib/shortcut-scope.ts
68489
68742
  function singleKeyShortcutsAllowed(active, isComposing) {
@@ -68498,11 +68751,11 @@ function singleKeyShortcutsAllowed(active, isComposing) {
68498
68751
 
68499
68752
  // src/hooks/useKeyboard.ts
68500
68753
  function useKeyboard(handlers, enabled = true) {
68501
- const handlersRef = useRef14(handlers);
68754
+ const handlersRef = useRef15(handlers);
68502
68755
  handlersRef.current = handlers;
68503
- const gPrefixRef = useRef14(false);
68504
- const gPrefixTimerRef = useRef14(null);
68505
- useEffect11(() => {
68756
+ const gPrefixRef = useRef15(false);
68757
+ const gPrefixTimerRef = useRef15(null);
68758
+ useEffect12(() => {
68506
68759
  if (!enabled) return;
68507
68760
  function isInField() {
68508
68761
  const tag = document.activeElement?.tagName || "";
@@ -68715,7 +68968,7 @@ function useKeyboard(handlers, enabled = true) {
68715
68968
  }
68716
68969
 
68717
68970
  // src/hooks/useCannedComments.ts
68718
- import { useState as useState12, useCallback as useCallback19, useMemo as useMemo6, useEffect as useEffect12, useRef as useRef15 } from "react";
68971
+ import { useState as useState12, useCallback as useCallback20, useMemo as useMemo7, useEffect as useEffect13, useRef as useRef16 } from "react";
68719
68972
  var BUILT_IN_LIBRARY = buildLibrary();
68720
68973
  function buildLibrary() {
68721
68974
  const L = [];
@@ -68786,8 +69039,8 @@ function mapRow(r) {
68786
69039
  function useCannedComments(options) {
68787
69040
  const { inspectionId, severityForRatingId } = options;
68788
69041
  const library = useFieldDataSource().resources.commentsLibrary;
68789
- const saveInFlight = useRef15(/* @__PURE__ */ new Map());
68790
- const touchInFlight = useRef15(/* @__PURE__ */ new Set());
69042
+ const saveInFlight = useRef16(/* @__PURE__ */ new Map());
69043
+ const touchInFlight = useRef16(/* @__PURE__ */ new Set());
68791
69044
  const [userSnippets, setUserSnippets] = useState12([]);
68792
69045
  const [localSnippets, setLocalSnippets] = useState12([]);
68793
69046
  const [sort, setSortInner] = useState12(() => {
@@ -68804,28 +69057,28 @@ function useCannedComments(options) {
68804
69057
  return "auto";
68805
69058
  }
68806
69059
  });
68807
- const setSort = useCallback19((s) => {
69060
+ const setSort = useCallback20((s) => {
68808
69061
  setSortInner(s);
68809
69062
  try {
68810
69063
  localStorage.setItem("oi:library:sort", s);
68811
69064
  } catch {
68812
69065
  }
68813
69066
  }, []);
68814
- const setFilterMode = useCallback19((m) => {
69067
+ const setFilterMode = useCallback20((m) => {
68815
69068
  setFilterModeInner(m);
68816
69069
  try {
68817
69070
  localStorage.setItem("oi:library:filter-mode", m);
68818
69071
  } catch {
68819
69072
  }
68820
69073
  }, []);
68821
- const loadServerRows = useCallback19(async (query) => {
69074
+ const loadServerRows = useCallback20(async (query) => {
68822
69075
  try {
68823
69076
  return await library?.list(query) ?? [];
68824
69077
  } catch {
68825
69078
  return [];
68826
69079
  }
68827
69080
  }, [library]);
68828
- const fetchFiltered = useCallback19(
69081
+ const fetchFiltered = useCallback20(
68829
69082
  async (ctx) => {
68830
69083
  const query = { sort, filterMode };
68831
69084
  if (ctx.search) query.search = ctx.search;
@@ -68838,7 +69091,7 @@ function useCannedComments(options) {
68838
69091
  },
68839
69092
  [sort, filterMode, loadServerRows]
68840
69093
  );
68841
- const searchLibrary = useCallback19(
69094
+ const searchLibrary = useCallback20(
68842
69095
  async (query) => {
68843
69096
  const q = query.trim();
68844
69097
  if (q.length < 2) return [];
@@ -68847,20 +69100,20 @@ function useCannedComments(options) {
68847
69100
  },
68848
69101
  [loadServerRows]
68849
69102
  );
68850
- const touchSnippet = useCallback19((id) => {
69103
+ const touchSnippet = useCallback20((id) => {
68851
69104
  if (!library || touchInFlight.current.has(id)) return;
68852
69105
  touchInFlight.current.add(id);
68853
69106
  void library.touch(id).catch(() => void 0).finally(() => {
68854
69107
  touchInFlight.current.delete(id);
68855
69108
  });
68856
69109
  }, [library]);
68857
- useEffect12(() => {
69110
+ useEffect13(() => {
68858
69111
  (async () => {
68859
69112
  const rows = await loadServerRows();
68860
69113
  if (rows.length > 0) setUserSnippets(rows.map(mapRow));
68861
69114
  })();
68862
69115
  }, [inspectionId]);
68863
- useEffect12(() => {
69116
+ useEffect13(() => {
68864
69117
  try {
68865
69118
  const raw = localStorage.getItem("oi:snippets");
68866
69119
  if (raw) {
@@ -68872,14 +69125,14 @@ function useCannedComments(options) {
68872
69125
  } catch {
68873
69126
  }
68874
69127
  }, []);
68875
- const commentPool = useMemo6(() => {
69128
+ const commentPool = useMemo7(() => {
68876
69129
  const preset = BUILT_IN_LIBRARY;
68877
69130
  const seenTexts = /* @__PURE__ */ new Set();
68878
69131
  for (const s of userSnippets) seenTexts.add(s.text);
68879
69132
  const dedupedLocal = localSnippets.filter((c) => !seenTexts.has(c.text));
68880
69133
  return [...preset, ...userSnippets, ...dedupedLocal];
68881
69134
  }, [userSnippets, localSnippets]);
68882
- const getFilteredComments = useCallback19(
69135
+ const getFilteredComments = useCallback20(
68883
69136
  (filter, search) => {
68884
69137
  let filtered;
68885
69138
  if (filter === "my-snippets") {
@@ -68901,7 +69154,7 @@ function useCannedComments(options) {
68901
69154
  },
68902
69155
  [commentPool]
68903
69156
  );
68904
- const getQuickComments = useCallback19(
69157
+ const getQuickComments = useCallback20(
68905
69158
  (ratingId, itemLabel, sectionTitle) => {
68906
69159
  const severity = severityForRatingId(ratingId);
68907
69160
  const filtered = severity === "all" ? commentPool : commentPool.filter(
@@ -68935,7 +69188,7 @@ function useCannedComments(options) {
68935
69188
  },
68936
69189
  [commentPool, severityForRatingId]
68937
69190
  );
68938
- const saveSnippet = useCallback19(
69191
+ const saveSnippet = useCallback20(
68939
69192
  (text, severity, section, title, itemLabel) => {
68940
69193
  const flightKey = JSON.stringify([text, severity, section, title, itemLabel]);
68941
69194
  const running = saveInFlight.current.get(flightKey);
@@ -68998,14 +69251,14 @@ function useCannedComments(options) {
68998
69251
  }
68999
69252
 
69000
69253
  // src/hooks/useEditorCapabilities.ts
69001
- import { useSyncExternalStore } from "react";
69254
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
69002
69255
  var subscribeNever = () => () => {
69003
69256
  };
69004
69257
  var cameraSnapshot = () => typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
69005
69258
  function useEditorCapabilities() {
69006
69259
  const { resources } = useFieldDataSource();
69007
69260
  const { renderThemeControl } = useFieldSlots();
69008
- const cameraCapture = useSyncExternalStore(subscribeNever, cameraSnapshot, () => false);
69261
+ const cameraCapture = useSyncExternalStore2(subscribeNever, cameraSnapshot, () => false);
69009
69262
  return {
69010
69263
  commentsLibrary: resources.commentsLibrary !== void 0,
69011
69264
  costItems: resources.costItems !== void 0,
@@ -69039,7 +69292,7 @@ function unsavedChangesAtRisk(input) {
69039
69292
  }
69040
69293
 
69041
69294
  // src/hooks/usePhotoBatchUpload.ts
69042
- import { useCallback as useCallback20, useEffect as useEffect13, useRef as useRef16, useState as useState13 } from "react";
69295
+ import { useCallback as useCallback21, useEffect as useEffect14, useRef as useRef17, useState as useState13 } from "react";
69043
69296
  var CONCURRENCY = 4;
69044
69297
  function newPendingId() {
69045
69298
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
@@ -69051,25 +69304,25 @@ function metaOf(batch) {
69051
69304
  }
69052
69305
  function usePhotoBatchUpload(inspectionId, doc) {
69053
69306
  const dataSource = useFieldDataSource();
69054
- const dataSourceRef = useRef16(dataSource);
69307
+ const dataSourceRef = useRef17(dataSource);
69055
69308
  dataSourceRef.current = dataSource;
69056
- const docRef = useRef16(doc);
69309
+ const docRef = useRef17(doc);
69057
69310
  docRef.current = doc;
69058
- const inspectionRef = useRef16(inspectionId);
69311
+ const inspectionRef = useRef17(inspectionId);
69059
69312
  inspectionRef.current = inspectionId;
69060
69313
  const [pending, setPending] = useState13(0);
69061
69314
  const [outcomes, setOutcomes] = useState13([]);
69062
69315
  const [failedSinceIdle, setFailedSinceIdle] = useState13(false);
69063
- const pendingRef = useRef16(0);
69064
- const tail = useRef16(Promise.resolve());
69065
- const mounted = useRef16(true);
69066
- useEffect13(() => {
69316
+ const pendingRef = useRef17(0);
69317
+ const tail = useRef17(Promise.resolve());
69318
+ const mounted = useRef17(true);
69319
+ useEffect14(() => {
69067
69320
  mounted.current = true;
69068
69321
  return () => {
69069
69322
  mounted.current = false;
69070
69323
  };
69071
69324
  }, []);
69072
- const runBatch = useCallback20(async (batch) => {
69325
+ const runBatch = useCallback21(async (batch) => {
69073
69326
  const uploader = dataSourceRef.current.media.uploader(inspectionRef.current, docRef.current);
69074
69327
  const meta = metaOf(batch);
69075
69328
  const results = new Array(batch.files.length);
@@ -69107,7 +69360,7 @@ function usePhotoBatchUpload(inspectionId, doc) {
69107
69360
  defectKind: batch.target?.kind ?? "canned"
69108
69361
  };
69109
69362
  }, []);
69110
- const submit = useCallback20(
69363
+ const submit = useCallback21(
69111
69364
  (batch) => {
69112
69365
  if (pendingRef.current === 0) setFailedSinceIdle(false);
69113
69366
  pendingRef.current += 1;
@@ -69136,41 +69389,41 @@ function usePhotoBatchUpload(inspectionId, doc) {
69136
69389
  },
69137
69390
  [runBatch]
69138
69391
  );
69139
- const consume = useCallback20((count) => {
69392
+ const consume = useCallback21((count) => {
69140
69393
  if (count > 0) setOutcomes((prev) => prev.slice(count));
69141
69394
  }, []);
69142
69395
  return { busy: pending > 0, outcomes, failedSinceIdle, consume, submit };
69143
69396
  }
69144
69397
 
69145
69398
  // src/hooks/useReportCoverWrites.ts
69146
- import { useCallback as useCallback21, useRef as useRef17 } from "react";
69399
+ import { useCallback as useCallback22, useRef as useRef18 } from "react";
69147
69400
  var FAILED_MS = 8e3;
69148
69401
  function useReportCoverWrites(inspectionId) {
69149
69402
  const dataSource = useFieldDataSource();
69150
- const dataSourceRef = useRef17(dataSource);
69403
+ const dataSourceRef = useRef18(dataSource);
69151
69404
  dataSourceRef.current = dataSource;
69152
69405
  const { submit: submitCover } = useLatestWinsCommand(inspectionId);
69153
69406
  const { submit: submitCrop } = useLatestWinsCommand(inspectionId);
69154
69407
  const { submit: submitAnnotate } = useLatestWinsCommand(inspectionId);
69155
- const report = useCallback21((res) => {
69408
+ const report = useCallback22((res) => {
69156
69409
  if (isIgnored(res)) return;
69157
69410
  if (!res.ok) {
69158
69411
  dataSourceRef.current.notify(index_exports.editor_route_save_failed(), "error", { durationMs: FAILED_MS });
69159
69412
  }
69160
69413
  }, []);
69161
- const setCover = useCallback21(
69414
+ const setCover = useCallback22(
69162
69415
  (coverPhotoId) => {
69163
69416
  void submitCover({ type: "set-cover", coverPhotoId }).then(report);
69164
69417
  },
69165
69418
  [submitCover, report]
69166
69419
  );
69167
- const cropCover = useCallback21(
69420
+ const cropCover = useCallback22(
69168
69421
  (input) => {
69169
69422
  void submitCrop({ type: "crop-cover", sourceKey: input.sourceKey, crop: input.crop, image: input.image }).then(report);
69170
69423
  },
69171
69424
  [submitCrop, report]
69172
69425
  );
69173
- const annotate = useCallback21(
69426
+ const annotate = useCallback22(
69174
69427
  (input) => {
69175
69428
  void submitAnnotate({ type: "annotate", ...input }).then(report);
69176
69429
  },
@@ -69180,7 +69433,7 @@ function useReportCoverWrites(inspectionId) {
69180
69433
  }
69181
69434
 
69182
69435
  // src/hooks/usePresence.ts
69183
- import { useState as useState14, useEffect as useEffect14, useCallback as useCallback22, useRef as useRef18 } from "react";
69436
+ import { useState as useState14, useEffect as useEffect15, useCallback as useCallback23, useRef as useRef19 } from "react";
69184
69437
  var HEARTBEAT_MS = 3e4;
69185
69438
  var MAX_BACKOFF = 3e4;
69186
69439
  function usePresence(options) {
@@ -69189,17 +69442,17 @@ function usePresence(options) {
69189
69442
  const [roster, setRoster] = useState14([]);
69190
69443
  const [connected, setConnected] = useState14(false);
69191
69444
  const [status, setStatus] = useState14("connecting");
69192
- const everConnectedRef = useRef18(false);
69193
- const wsRef = useRef18(null);
69194
- const heartbeatRef = useRef18(null);
69195
- const backoffRef = useRef18(400);
69196
- const closedRef = useRef18(false);
69197
- const send = useCallback22((msg) => {
69445
+ const everConnectedRef = useRef19(false);
69446
+ const wsRef = useRef19(null);
69447
+ const heartbeatRef = useRef19(null);
69448
+ const backoffRef = useRef19(400);
69449
+ const closedRef = useRef19(false);
69450
+ const send = useCallback23((msg) => {
69198
69451
  if (wsRef.current?.readyState === WebSocket.OPEN) {
69199
69452
  wsRef.current.send(JSON.stringify(msg));
69200
69453
  }
69201
69454
  }, []);
69202
- const connect = useCallback22(() => {
69455
+ const connect = useCallback23(() => {
69203
69456
  if (closedRef.current || !enabled) return;
69204
69457
  if (!inspectionId) return;
69205
69458
  const wsUrl = dataSource.urls.presenceSocket(inspectionId);
@@ -69265,13 +69518,13 @@ function usePresence(options) {
69265
69518
  }
69266
69519
  }
69267
69520
  }, [dataSource, inspectionId, userName, photoUrl, enabled, send]);
69268
- const setFocus = useCallback22(
69521
+ const setFocus = useCallback23(
69269
69522
  (itemId) => {
69270
69523
  send({ type: "focus", itemId });
69271
69524
  },
69272
69525
  [send]
69273
69526
  );
69274
- const close = useCallback22(() => {
69527
+ const close = useCallback23(() => {
69275
69528
  closedRef.current = true;
69276
69529
  try {
69277
69530
  send({ type: "bye" });
@@ -69285,7 +69538,7 @@ function usePresence(options) {
69285
69538
  heartbeatRef.current = null;
69286
69539
  setConnected(false);
69287
69540
  }, [send]);
69288
- useEffect14(() => {
69541
+ useEffect15(() => {
69289
69542
  if (!enabled) return;
69290
69543
  closedRef.current = false;
69291
69544
  connect();
@@ -69303,13 +69556,13 @@ function usePresence(options) {
69303
69556
  }
69304
69557
 
69305
69558
  // src/collab/use-results-doc.ts
69306
- import { useState as useState15, useEffect as useEffect15, useRef as useRef19 } from "react";
69559
+ import { useState as useState15, useEffect as useEffect16, useRef as useRef20 } from "react";
69307
69560
  function useResultsDoc(inspectionId, onSynced) {
69308
69561
  const { collab } = useFieldDataSource();
69309
69562
  const [handle, setHandle] = useState15(null);
69310
- const onSyncedRef = useRef19(onSynced);
69563
+ const onSyncedRef = useRef20(onSynced);
69311
69564
  onSyncedRef.current = onSynced;
69312
- useEffect15(() => {
69565
+ useEffect16(() => {
69313
69566
  if (!inspectionId) {
69314
69567
  setHandle(null);
69315
69568
  return;
@@ -69317,10 +69570,10 @@ function useResultsDoc(inspectionId, onSynced) {
69317
69570
  let persisted = false;
69318
69571
  let live = null;
69319
69572
  let closed = false;
69320
- const snapshot = () => live === null ? null : { doc: live.doc, persistenceSynced: persisted, synced: live.synced() };
69573
+ const snapshot2 = () => live === null ? null : { doc: live.doc, persistenceSynced: persisted, synced: live.synced() };
69321
69574
  const publish = () => {
69322
69575
  if (closed) return;
69323
- const next = snapshot();
69576
+ const next = snapshot2();
69324
69577
  if (next !== null) setHandle(next);
69325
69578
  };
69326
69579
  live = collab.connect(inspectionId, {
@@ -69346,7 +69599,7 @@ function useResultsDoc(inspectionId, onSynced) {
69346
69599
  }
69347
69600
 
69348
69601
  // src/hooks/useMediaDrain.ts
69349
- import { useCallback as useCallback23, useEffect as useEffect16, useRef as useRef20 } from "react";
69602
+ import { useCallback as useCallback24, useEffect as useEffect17, useRef as useRef21 } from "react";
69350
69603
  function asDrainUploader(uploader) {
69351
69604
  return {
69352
69605
  async upload(rec) {
@@ -69359,11 +69612,11 @@ function asDrainUploader(uploader) {
69359
69612
  }
69360
69613
  function useMediaDrain(inspectionId, doc, revokePendingUrl) {
69361
69614
  const dataSource = useFieldDataSource();
69362
- const docRef = useRef20(doc);
69615
+ const docRef = useRef21(doc);
69363
69616
  docRef.current = doc;
69364
- const revokeRef = useRef20(revokePendingUrl);
69617
+ const revokeRef = useRef21(revokePendingUrl);
69365
69618
  revokeRef.current = revokePendingUrl;
69366
- const drain = useCallback23(() => {
69619
+ const drain = useCallback24(() => {
69367
69620
  const liveDoc = docRef.current;
69368
69621
  if (!liveDoc) return;
69369
69622
  void drainMediaQueue({
@@ -69386,7 +69639,7 @@ function useMediaDrain(inspectionId, doc, revokePendingUrl) {
69386
69639
  }
69387
69640
  });
69388
69641
  }, [dataSource, inspectionId]);
69389
- useEffect16(
69642
+ useEffect17(
69390
69643
  () => dataSource.onOnlineChange((online) => {
69391
69644
  if (online) drain();
69392
69645
  }),
@@ -69421,7 +69674,7 @@ async function addOfflinePhotos(store, doc, target, files) {
69421
69674
  }
69422
69675
 
69423
69676
  // src/hooks/useResultsProjection.ts
69424
- import { useEffect as useEffect17, useRef as useRef21 } from "react";
69677
+ import { useEffect as useEffect18, useRef as useRef22 } from "react";
69425
69678
 
69426
69679
  // src/collab/results-merge.ts
69427
69680
  import * as Y3 from "yjs";
@@ -69460,27 +69713,27 @@ function mergeUnsyncedResults(prev, doc) {
69460
69713
 
69461
69714
  // src/hooks/useResultsProjection.ts
69462
69715
  function useResultsProjection(collab, setResults) {
69463
- const synced = useRef21(false);
69716
+ const synced = useRef22(false);
69464
69717
  synced.current = collab?.synced === true;
69465
69718
  const doc = collab?.doc;
69466
- useEffect17(() => {
69719
+ useEffect18(() => {
69467
69720
  if (!doc) return;
69468
69721
  return bindResultMap(doc, (next) => setResults((prev) => synced.current ? next : mergeUnsyncedResults(prev, doc)));
69469
69722
  }, [doc, setResults]);
69470
69723
  }
69471
69724
 
69472
69725
  // src/hooks/useSpeedRating.ts
69473
- import { useCallback as useCallback24, useEffect as useEffect18, useRef as useRef22, useState as useState16 } from "react";
69726
+ import { useCallback as useCallback25, useEffect as useEffect19, useRef as useRef23, useState as useState16 } from "react";
69474
69727
  var SPEED_DONE_LINGER_MS = 3500;
69475
69728
  function useSpeedRating({ state, commitRating, doc, activeUnitId, onUndoUnavailable }) {
69476
- const queueRef = useRef22(state.speedQueue);
69729
+ const queueRef = useRef23(state.speedQueue);
69477
69730
  queueRef.current = state.speedQueue;
69478
- const unitRef = useRef22(activeUnitId);
69731
+ const unitRef = useRef23(activeUnitId);
69479
69732
  unitRef.current = activeUnitId;
69480
- const ratedUnit = useRef22(/* @__PURE__ */ new Map());
69481
- const lastRated = useRef22(null);
69733
+ const ratedUnit = useRef23(/* @__PURE__ */ new Map());
69734
+ const lastRated = useRef23(null);
69482
69735
  const [pendingUndo, setPendingUndo] = useState16(null);
69483
- const speedRate = useCallback24(
69736
+ const speedRate = useCallback25(
69484
69737
  (levelIdx) => {
69485
69738
  if (!state.speedMode) return;
69486
69739
  const qi = state.speedQueue[state.speedCurrent];
@@ -69498,7 +69751,7 @@ function useSpeedRating({ state, commitRating, doc, activeUnitId, onUndoUnavaila
69498
69751
  },
69499
69752
  [state, commitRating]
69500
69753
  );
69501
- const undoSpeedRating = useCallback24(
69754
+ const undoSpeedRating = useCallback25(
69502
69755
  (itemId, previous) => {
69503
69756
  const sid = state.sectionIdForItem(itemId);
69504
69757
  if (!sid) return;
@@ -69525,19 +69778,19 @@ function useSpeedRating({ state, commitRating, doc, activeUnitId, onUndoUnavaila
69525
69778
  );
69526
69779
  const done = state.speedMode && state.speedQueue.length === 0;
69527
69780
  const setSpeedMode = state.setSpeedMode;
69528
- useEffect18(() => {
69781
+ useEffect19(() => {
69529
69782
  if (!done) return;
69530
69783
  const t = setTimeout(() => setSpeedMode(false), SPEED_DONE_LINGER_MS);
69531
69784
  return () => clearTimeout(t);
69532
69785
  }, [done, setSpeedMode]);
69533
- useEffect18(() => {
69786
+ useEffect19(() => {
69534
69787
  if (!state.speedMode) setPendingUndo(null);
69535
69788
  }, [state.speedMode]);
69536
69789
  return { speedRate, undoSpeedRating, finishedItem: done ? lastRated.current : null, pendingUndo, setPendingUndo };
69537
69790
  }
69538
69791
 
69539
69792
  // src/components/editor-shared/SectionRail.tsx
69540
- import { useRef as useRef25, useState as useState18 } from "react";
69793
+ import { useRef as useRef26, useState as useState18 } from "react";
69541
69794
 
69542
69795
  // src/components/editor/SectionDonut.tsx
69543
69796
  import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
@@ -69659,15 +69912,15 @@ function sectionIconFor(nameOrId) {
69659
69912
  }
69660
69913
 
69661
69914
  // src/components/editor-shared/useSortableReorder.ts
69662
- import { useEffect as useEffect19, useRef as useRef23 } from "react";
69915
+ import { useEffect as useEffect20, useRef as useRef24 } from "react";
69663
69916
  import Sortable from "sortablejs";
69664
69917
  function useSortableReorder({ ids, onReorder, disabled = false }) {
69665
- const containerRef = useRef23(null);
69666
- const idsRef = useRef23(ids);
69918
+ const containerRef = useRef24(null);
69919
+ const idsRef = useRef24(ids);
69667
69920
  idsRef.current = ids;
69668
- const onReorderRef = useRef23(onReorder);
69921
+ const onReorderRef = useRef24(onReorder);
69669
69922
  onReorderRef.current = onReorder;
69670
- useEffect19(() => {
69923
+ useEffect20(() => {
69671
69924
  const el = containerRef.current;
69672
69925
  if (!el || disabled) return;
69673
69926
  const s = Sortable.create(el, {
@@ -69695,13 +69948,13 @@ function useSortableReorder({ ids, onReorder, disabled = false }) {
69695
69948
  }
69696
69949
 
69697
69950
  // src/components/editor-shared/InlineRename.tsx
69698
- import { useEffect as useEffect20, useRef as useRef24, useState as useState17 } from "react";
69951
+ import { useEffect as useEffect21, useRef as useRef25, useState as useState17 } from "react";
69699
69952
  import { jsx as jsx31 } from "react/jsx-runtime";
69700
69953
  function InlineRename({ value, onCommit, onCancel, className, ariaLabel }) {
69701
69954
  const [draft, setDraft] = useState17(value);
69702
- const ref = useRef24(null);
69703
- const doneRef = useRef24(false);
69704
- useEffect20(() => {
69955
+ const ref = useRef25(null);
69956
+ const doneRef = useRef25(false);
69957
+ useEffect21(() => {
69705
69958
  ref.current?.focus();
69706
69959
  ref.current?.select();
69707
69960
  }, []);
@@ -69794,7 +70047,7 @@ function SectionRail({
69794
70047
  }) {
69795
70048
  const [openMenuId, setOpenMenuId] = useState18(null);
69796
70049
  const [editingId, setEditingId] = useState18(null);
69797
- const menuAnchorRef = useRef25(null);
70050
+ const menuAnchorRef = useRef26(null);
69798
70051
  const openSectionMenu = (sectionId, el) => {
69799
70052
  if (openMenuId === sectionId) {
69800
70053
  setOpenMenuId(null);
@@ -70003,13 +70256,88 @@ function SectionRail({
70003
70256
  // src/components/editor/EditorHeader.tsx
70004
70257
  import { useState as useState22 } from "react";
70005
70258
 
70259
+ // src/lib/readable-text.ts
70260
+ var DARK_INK = "#0f172a";
70261
+ var WHITE = "#ffffff";
70262
+ function isFallbackRatingColor(color) {
70263
+ return color === FALLBACK_RATING_COLOR;
70264
+ }
70265
+ function parseHex(v) {
70266
+ const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(v)?.[1];
70267
+ if (!hex) return null;
70268
+ const full = hex.length === 3 ? [...hex].map((c) => c + c).join("") : hex;
70269
+ return [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16));
70270
+ }
70271
+ function parseRgb(v) {
70272
+ const m = /^rgba?\(\s*(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)(?:[\s,/]+[\d.]+%?)?\s*\)$/i.exec(v);
70273
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
70274
+ }
70275
+ var HEX = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
70276
+ var COLOR_FN = /^(?:rgba?|hsla?)\(\s*[\d.]+(?:deg|%)?(?:\s*[,/]\s*[\d.]+(?:deg|%)?|\s+[\d.]+(?:deg|%)?)*\s*\)$/i;
70277
+ var TOKEN = /^var\(\s*--ih-[\w-]+\s*(?:,\s*([^;{}"'()]+(?:\([^;{}"'()]*\))?))?\s*\)$/;
70278
+ function safeCssColor(color) {
70279
+ if (typeof color !== "string") return null;
70280
+ const v = color.trim();
70281
+ if (v === "" || v.length > 120) return null;
70282
+ if (HEX.test(v) || COLOR_FN.test(v) || namedHex(v) !== null) return v;
70283
+ const palette = paletteHex(v);
70284
+ if (palette !== null) return palette;
70285
+ const token = TOKEN.exec(v);
70286
+ if (token) return token[1] === void 0 || safeCssColor(token[1]) !== null ? v : null;
70287
+ return null;
70288
+ }
70289
+ var NAMED = {
70290
+ red: "#ff0000",
70291
+ orange: "#ffa500",
70292
+ yellow: "#ffff00",
70293
+ blue: "#0000ff",
70294
+ green: "#008000",
70295
+ teal: "#008080",
70296
+ purple: "#800080",
70297
+ indigo: "#4b0082",
70298
+ pink: "#ffc0cb",
70299
+ brown: "#a52a2a",
70300
+ gray: "#808080",
70301
+ grey: "#808080",
70302
+ white: "#ffffff",
70303
+ black: "#000000"
70304
+ };
70305
+ function namedHex(name) {
70306
+ const key = name.toLowerCase();
70307
+ return Object.hasOwn(NAMED, key) ? NAMED[key] : null;
70308
+ }
70309
+ var PALETTE = { amber: "#f59e0b", slate: "#64748b", rose: "#f43f5e", emerald: "#10b981" };
70310
+ function paletteHex(name) {
70311
+ const key = name.toLowerCase();
70312
+ return Object.hasOwn(PALETTE, key) ? PALETTE[key] : null;
70313
+ }
70314
+ function luminance([r, g, b]) {
70315
+ const lin = (c) => {
70316
+ const s = c / 255;
70317
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
70318
+ };
70319
+ return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
70320
+ }
70321
+ var WHITE_FLOOR = 3;
70322
+ function readableTextOn(background) {
70323
+ if (!background) return null;
70324
+ const v = background.trim();
70325
+ const rgb = parseHex(v) ?? parseRgb(v) ?? parseHex(namedHex(v) ?? paletteHex(v) ?? "");
70326
+ if (!rgb) return null;
70327
+ const bg = luminance(rgb);
70328
+ const onWhite = 1.05 / (bg + 0.05);
70329
+ if (onWhite >= WHITE_FLOOR) return WHITE;
70330
+ const onInk = (bg + 0.05) / (luminance(parseHex(DARK_INK)) + 0.05);
70331
+ return onWhite >= onInk ? WHITE : DARK_INK;
70332
+ }
70333
+
70006
70334
  // src/components/editor/ProgressStripText.tsx
70007
70335
  import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
70008
70336
  var Dot = ({ color }) => /* @__PURE__ */ jsx33(
70009
70337
  "span",
70010
70338
  {
70011
70339
  className: "inline-block w-1.5 h-1.5 rounded-full mr-1 align-middle",
70012
- style: { backgroundColor: color }
70340
+ style: { backgroundColor: safeCssColor(color) ?? void 0 }
70013
70341
  }
70014
70342
  );
70015
70343
  function ProgressStripText({ rated, total, defects, monitor, etaMinutes }) {
@@ -70032,7 +70360,7 @@ function ProgressStripText({ rated, total, defects, monitor, etaMinutes }) {
70032
70360
  }
70033
70361
 
70034
70362
  // src/components/editor/TemplateMenu.tsx
70035
- import { useRef as useRef26, useState as useState19 } from "react";
70363
+ import { useRef as useRef27, useState as useState19 } from "react";
70036
70364
  import { Fragment as Fragment2, jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
70037
70365
  function TemplateMenu({
70038
70366
  onChangeTemplate,
@@ -70041,7 +70369,7 @@ function TemplateMenu({
70041
70369
  canUpdateSource
70042
70370
  }) {
70043
70371
  const [open, setOpen] = useState19(false);
70044
- const anchorRef = useRef26(null);
70372
+ const anchorRef = useRef27(null);
70045
70373
  const run = (fn) => {
70046
70374
  setOpen(false);
70047
70375
  fn();
@@ -70099,7 +70427,7 @@ function TemplateMenu({
70099
70427
  }
70100
70428
 
70101
70429
  // src/components/editor/PreviewMenu.tsx
70102
- import { useRef as useRef27, useState as useState20 } from "react";
70430
+ import { useRef as useRef28, useState as useState20 } from "react";
70103
70431
  import { Fragment as Fragment3, jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
70104
70432
  function PreviewMenu({
70105
70433
  onPreviewReport,
@@ -70109,7 +70437,7 @@ function PreviewMenu({
70109
70437
  pdfLabel
70110
70438
  }) {
70111
70439
  const [open, setOpen] = useState20(false);
70112
- const anchorRef = useRef27(null);
70440
+ const anchorRef = useRef28(null);
70113
70441
  const run = (fn) => {
70114
70442
  setOpen(false);
70115
70443
  fn();
@@ -70246,10 +70574,10 @@ function runWithPending(handler, setPending) {
70246
70574
  }
70247
70575
 
70248
70576
  // src/components/editor/HeaderOverflowMenu.tsx
70249
- import { useRef as useRef28, useState as useState21 } from "react";
70577
+ import { useRef as useRef29, useState as useState21 } from "react";
70250
70578
 
70251
70579
  // src/read-only.tsx
70252
- import { createContext as createContext2, useContext as useContext2, useMemo as useMemo7 } from "react";
70580
+ import { createContext as createContext2, useContext as useContext2, useMemo as useMemo8 } from "react";
70253
70581
 
70254
70582
  // src/read-only-view.ts
70255
70583
  var refused = () => ({ ok: false, error: index_exports.editor_read_only() });
@@ -70304,7 +70632,7 @@ function useReadOnly() {
70304
70632
  function ReadOnlyScope({ readOnly, children }) {
70305
70633
  const dataSource = useFieldDataSource();
70306
70634
  const slots = useFieldSlots();
70307
- const view = useMemo7(() => readOnly ? readOnlyView(dataSource) : dataSource, [readOnly, dataSource]);
70635
+ const view = useMemo8(() => readOnly ? readOnlyView(dataSource) : dataSource, [readOnly, dataSource]);
70308
70636
  return /* @__PURE__ */ jsx37(FieldDataSourceProvider, { dataSource: view, slots, children: /* @__PURE__ */ jsx37(ReadOnlyContext.Provider, { value: readOnly, children }) });
70309
70637
  }
70310
70638
 
@@ -70318,7 +70646,7 @@ function HeaderOverflowMenu({
70318
70646
  themeControl
70319
70647
  }) {
70320
70648
  const [open, setOpen] = useState21(false);
70321
- const anchorRef = useRef28(null);
70649
+ const anchorRef = useRef29(null);
70322
70650
  const readOnly = useReadOnly();
70323
70651
  const run = (fn) => {
70324
70652
  setOpen(false);
@@ -70666,7 +70994,7 @@ function FullscreenToggle({ active, onToggle }) {
70666
70994
  }
70667
70995
 
70668
70996
  // src/components/editor-shared/ItemList.tsx
70669
- import { useState as useState24, useRef as useRef29 } from "react";
70997
+ import { useState as useState24, useRef as useRef30 } from "react";
70670
70998
 
70671
70999
  // src/shared/template-hierarchy.ts
70672
71000
  var MAX_ITEM_DEPTH = 3;
@@ -70844,6 +71172,19 @@ function ItemRowIndent({ depth, outline }) {
70844
71172
  ] });
70845
71173
  }
70846
71174
 
71175
+ // src/components/editor-shared/rating-dot.ts
71176
+ function ratingDotClass(rating) {
71177
+ if (rating === "Satisfactory" || rating === "SAT") return "bg-ih-ok";
71178
+ if (rating === "Monitor" || rating === "MON") return "bg-ih-watch";
71179
+ if (rating === "Defect" || rating === "DEF") return "bg-ih-bad";
71180
+ return "bg-ih-border-strong";
71181
+ }
71182
+ function ratingDot(rating, getRatingColor) {
71183
+ const color = safeCssColor(getRatingColor?.(rating));
71184
+ if (color && !isFallbackRatingColor(color)) return { className: "", style: { background: color } };
71185
+ return { className: ratingDotClass(rating) };
71186
+ }
71187
+
70847
71188
  // src/components/editor-shared/ItemSubtreeDeleteModal.tsx
70848
71189
  import { Fragment as Fragment7, jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
70849
71190
  function ItemSubtreeDeleteModal({ pending, onCancel, onConfirm }) {
@@ -70888,12 +71229,6 @@ function slotHeadings(groups, items) {
70888
71229
  return { heading, closes };
70889
71230
  }
70890
71231
  var PANE_SELECTED = "bg-ih-primary-tint border-l-[3px] border-ih-primary font-bold text-ih-primary-text";
70891
- function ratingDotClass(rating) {
70892
- if (rating === "Satisfactory" || rating === "SAT") return "bg-ih-ok";
70893
- if (rating === "Monitor" || rating === "MON") return "bg-ih-watch";
70894
- if (rating === "Defect" || rating === "DEF") return "bg-ih-bad";
70895
- return "bg-ih-border-strong";
70896
- }
70897
71232
  function ItemList({
70898
71233
  mode,
70899
71234
  items,
@@ -70916,7 +71251,9 @@ function ItemList({
70916
71251
  activeUnitId = null,
70917
71252
  groups,
70918
71253
  onAddGroupInstance,
70919
- pane = false
71254
+ pane = false,
71255
+ fullWidth = false,
71256
+ getRatingColor
70920
71257
  }) {
70921
71258
  const { heading: slotHeading, closes: groupClosedBy } = slotHeadings(groups, items);
70922
71259
  const grouped = Boolean(groups?.length);
@@ -70931,10 +71268,10 @@ function ItemList({
70931
71268
  }
70932
71269
  setPendingDelete({ id, label, count });
70933
71270
  };
70934
- const lastClickedRef = useRef29(null);
71271
+ const lastClickedRef = useRef30(null);
70935
71272
  const [editingId, setEditingId] = useState24(null);
70936
71273
  const { menuItemId, openItemMenu, closeItemMenu } = useItemRowMenu();
70937
- const menuAnchorRef = useRef29(null);
71274
+ const menuAnchorRef = useRef30(null);
70938
71275
  const structuralEditing = Boolean(onDuplicateItem || onDeleteItem || onMoveItem || onRenameItem);
70939
71276
  const resultsMap = results ?? {};
70940
71277
  const scopedResult = (itemId) => resultsMap[findingKey(activeUnitId, sectionId, itemId)] || (activeUnitId == null ? resultsMap[itemId] : void 0) || {};
@@ -70944,7 +71281,7 @@ function ItemList({
70944
71281
  }),
70945
71282
  disabled: !onReorderItem || Boolean(batchMode) || editingId !== null
70946
71283
  });
70947
- return /* @__PURE__ */ jsxs34("div", { "data-shortcut-scope": true, className: pane ? "w-full min-w-0 flex-1 min-h-0 overflow-y-auto overscroll-contain flex flex-col" : "w-[280px] flex-shrink-0 border-r border-ih-border overflow-y-auto flex flex-col", children: [
71284
+ return /* @__PURE__ */ jsxs34("div", { "data-shortcut-scope": true, className: pane ? "w-full min-w-0 flex-1 min-h-0 overflow-y-auto overscroll-contain flex flex-col" : fullWidth ? "w-full min-w-0 overflow-y-auto flex flex-col" : "w-[280px] flex-shrink-0 border-r border-ih-border overflow-y-auto flex flex-col", children: [
70948
71285
  /* @__PURE__ */ jsx43("div", { ref: containerRef, className: "flex-1 overflow-y-auto p-2 space-y-0.5", children: items.map((item) => {
70949
71286
  const result = scopedResult(item.id);
70950
71287
  const fullIdx = items.findIndex((i) => i.id === item.id);
@@ -71021,12 +71358,10 @@ function ItemList({
71021
71358
  ),
71022
71359
  /* @__PURE__ */ jsx43(ItemRowIndent, { depth: depths.get(item.id) ?? 0, outline: outlines.get(item.id) ?? "" }),
71023
71360
  /* @__PURE__ */ jsx43("span", { className: "flex-1 truncate", children: item.label }),
71024
- mode === "fill" && Boolean(result.rating) && /* @__PURE__ */ jsx43(
71025
- "span",
71026
- {
71027
- className: `w-2 h-2 rounded-full flex-shrink-0 ${ratingDotClass(result.rating)}`
71028
- }
71029
- ),
71361
+ mode === "fill" && Boolean(result.rating) && (() => {
71362
+ const dot = ratingDot(result.rating, getRatingColor);
71363
+ return /* @__PURE__ */ jsx43("span", { className: `w-2 h-2 rounded-full flex-shrink-0 ${dot.className}`, style: dot.style });
71364
+ })(),
71030
71365
  mode === "author" && /* @__PURE__ */ jsx43("span", { className: "text-[10px] font-mono px-1.5 py-0.5 rounded bg-ih-bg-muted text-ih-fg-2 flex-shrink-0", children: item.type })
71031
71366
  ]
71032
71367
  }
@@ -71126,7 +71461,7 @@ function ItemList({
71126
71461
  }
71127
71462
 
71128
71463
  // src/hooks/useStatutoryGroups.ts
71129
- import { useMemo as useMemo8 } from "react";
71464
+ import { useMemo as useMemo9 } from "react";
71130
71465
 
71131
71466
  // src/shared/statutory-groups.ts
71132
71467
  function parseGroupKey(key) {
@@ -71168,8 +71503,8 @@ function formBoundItemIds(declaration) {
71168
71503
  }
71169
71504
  return ids;
71170
71505
  }
71171
- function declaresStatutoryForm(snapshot) {
71172
- return Boolean(snapshot?.statutoryForm);
71506
+ function declaresStatutoryForm(snapshot2) {
71507
+ return Boolean(snapshot2?.statutoryForm);
71173
71508
  }
71174
71509
 
71175
71510
  // src/shared/form-completeness.ts
@@ -71196,17 +71531,17 @@ function formCompleteness(boundItemIds, results) {
71196
71531
 
71197
71532
  // src/hooks/useStatutoryGroups.ts
71198
71533
  function useStatutoryGroups(templateSnapshot) {
71199
- return useMemo8(() => {
71534
+ return useMemo9(() => {
71200
71535
  const declaration = templateSnapshot?.statutoryForm;
71201
71536
  const groups = deriveEditorGroups(declaration);
71202
71537
  return groups.length > 0 ? groups : void 0;
71203
71538
  }, [templateSnapshot]);
71204
71539
  }
71205
71540
  function useStructuralEditingAllowed(templateSnapshot) {
71206
- return useMemo8(() => !declaresStatutoryForm(templateSnapshot), [templateSnapshot]);
71541
+ return useMemo9(() => !declaresStatutoryForm(templateSnapshot), [templateSnapshot]);
71207
71542
  }
71208
71543
  function useStatutoryFormProgress(templateSnapshot, results) {
71209
- return useMemo8(() => {
71544
+ return useMemo9(() => {
71210
71545
  const declaration = templateSnapshot?.statutoryForm;
71211
71546
  if (!declaration) return null;
71212
71547
  const done = formCompleteness(formBoundItemIds(declaration), results);
@@ -71256,7 +71591,7 @@ function RevisionBanner({ status, inspectionDate }) {
71256
71591
  }
71257
71592
 
71258
71593
  // src/components/editor/ItemEditor.tsx
71259
- import { useState as useState32, useMemo as useMemo11, useEffect as useEffect27, useRef as useRef34 } from "react";
71594
+ import { useState as useState32, useMemo as useMemo13, useEffect as useEffect28, useRef as useRef35 } from "react";
71260
71595
 
71261
71596
  // src/components/editor/CommentTypeahead.tsx
71262
71597
  import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
@@ -71307,7 +71642,7 @@ function CommentTypeahead({
71307
71642
  }
71308
71643
 
71309
71644
  // src/hooks/useCommentTypeahead.ts
71310
- import { useMemo as useMemo9, useState as useState25, useEffect as useEffect21, useCallback as useCallback25 } from "react";
71645
+ import { useMemo as useMemo10, useState as useState25, useEffect as useEffect22, useCallback as useCallback26 } from "react";
71311
71646
 
71312
71647
  // src/shared/comment-typeahead.ts
71313
71648
  function isSubsequence(needle, haystack) {
@@ -71370,15 +71705,15 @@ function replaceFragmentBeforeCaret(value, caret, replacement) {
71370
71705
  // src/hooks/useCommentTypeahead.ts
71371
71706
  function useCommentTypeahead(entries, query, opts) {
71372
71707
  const max = opts?.max ?? 8;
71373
- const matches = useMemo9(
71708
+ const matches = useMemo10(
71374
71709
  () => rankTypeaheadMatches(entries, query).slice(0, max),
71375
71710
  [entries, query, max]
71376
71711
  );
71377
71712
  const [selectedIndex, setSelectedIndex] = useState25(0);
71378
- useEffect21(() => {
71713
+ useEffect22(() => {
71379
71714
  setSelectedIndex(0);
71380
71715
  }, [query, matches.length]);
71381
- const move = useCallback25(
71716
+ const move = useCallback26(
71382
71717
  (delta) => {
71383
71718
  setSelectedIndex((i) => {
71384
71719
  const n = matches.length;
@@ -71388,7 +71723,7 @@ function useCommentTypeahead(entries, query, opts) {
71388
71723
  },
71389
71724
  [matches.length]
71390
71725
  );
71391
- const current = useCallback25(
71726
+ const current = useCallback26(
71392
71727
  () => matches[selectedIndex] ?? null,
71393
71728
  [matches, selectedIndex]
71394
71729
  );
@@ -71396,21 +71731,21 @@ function useCommentTypeahead(entries, query, opts) {
71396
71731
  }
71397
71732
 
71398
71733
  // src/components/editor/AiAssistPanel.tsx
71399
- import { useEffect as useEffect22, useRef as useRef30, useState as useState26 } from "react";
71734
+ import { useEffect as useEffect23, useRef as useRef31, useState as useState26 } from "react";
71400
71735
  import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
71401
71736
  function AiAssistPanel({ notes, context, resultId, onAccept }) {
71402
71737
  const ai = useFieldDataSource().resources.aiAssist;
71403
71738
  const [draft, setDraft] = useState26(null);
71404
71739
  const [reviewed, setReviewed] = useState26(false);
71405
71740
  const [error, setError] = useState26(null);
71406
- const inFlight2 = useRef30(false);
71741
+ const inFlight2 = useRef31(false);
71407
71742
  const [busy, setBusy] = useState26(false);
71408
- const mounted = useRef30(true);
71409
- const draftRef = useRef30(draft);
71743
+ const mounted = useRef31(true);
71744
+ const draftRef = useRef31(draft);
71410
71745
  draftRef.current = draft;
71411
- const onAcceptRef = useRef30(onAccept);
71746
+ const onAcceptRef = useRef31(onAccept);
71412
71747
  onAcceptRef.current = onAccept;
71413
- useEffect22(() => {
71748
+ useEffect23(() => {
71414
71749
  mounted.current = true;
71415
71750
  return () => {
71416
71751
  mounted.current = false;
@@ -71556,21 +71891,21 @@ function NotesFieldHeader({
71556
71891
  }
71557
71892
 
71558
71893
  // src/components/editor/CloneLastButton.tsx
71559
- import { useState as useState27, useRef as useRef31, useEffect as useEffect23 } from "react";
71894
+ import { useState as useState27, useRef as useRef32, useEffect as useEffect24 } from "react";
71560
71895
  import { jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
71561
71896
  function CloneLastButton({ defaultScope, onClone, disabled }) {
71562
71897
  const [open, setOpen] = useState27(false);
71563
71898
  const [sessionScope, setSessionScope] = useState27(defaultScope);
71564
- const ref = useRef31(null);
71899
+ const ref = useRef32(null);
71565
71900
  const SCOPE_LABELS = {
71566
71901
  rating: index_exports.editor_clone_scope_rating(),
71567
71902
  rating_notes: index_exports.editor_clone_scope_rating_notes(),
71568
71903
  all: index_exports.editor_clone_scope_all()
71569
71904
  };
71570
- useEffect23(() => {
71905
+ useEffect24(() => {
71571
71906
  setSessionScope(defaultScope);
71572
71907
  }, [defaultScope]);
71573
- useEffect23(() => {
71908
+ useEffect24(() => {
71574
71909
  if (!open) return;
71575
71910
  const fn = (e) => {
71576
71911
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -71812,7 +72147,9 @@ function RatingSegment({
71812
72147
  children: ratings.map((r, i) => {
71813
72148
  const selected = r.value === value;
71814
72149
  const toneClass = selected ? TONE_FILLED[r.tone] : TONE_IDLE[r.tone];
71815
- const style = r.color ? { background: r.color, color: "#fff" } : void 0;
72150
+ const colour = safeCssColor(r.color);
72151
+ const tokenFill = !!colour && isFallbackRatingColor(colour);
72152
+ const style = colour && !tokenFill ? { background: colour, color: readableTextOn(colour) ?? "#fff" } : void 0;
71816
72153
  const text = size === "sm" ? r.shortLabel ?? r.label : r.shortLabel ? /* @__PURE__ */ jsxs40(Fragment8, { children: [
71817
72154
  /* @__PURE__ */ jsx50("span", { className: "@min-[6.5rem]:hidden", children: r.shortLabel }),
71818
72155
  /* @__PURE__ */ jsx50("span", { className: "hidden @min-[6.5rem]:inline", children: r.label })
@@ -71832,7 +72169,7 @@ function RatingSegment({
71832
72169
  tabIndex: i === focusIndex ? 0 : -1,
71833
72170
  onClick: () => onChange(r.value),
71834
72171
  onKeyDown: (e) => onKeyDown(e, i),
71835
- className: `transition-colors focus:outline-none focus:shadow-ih-focus ${sizeClass} ${r.color ? "" : toneClass}`,
72172
+ className: `transition-colors focus:outline-none focus:shadow-ih-focus ${sizeClass} ${tokenFill ? "bg-ih-bg-muted text-ih-fg-1 border border-ih-border" : colour ? "" : toneClass}`,
71836
72173
  style,
71837
72174
  children: [
71838
72175
  /* @__PURE__ */ jsx50("span", { className: "block truncate px-0.5", children: text }),
@@ -71892,12 +72229,13 @@ var CATEGORY_TOKENS = {
71892
72229
  };
71893
72230
  var DEFAULT_TOKENS = "bg-ih-bg-muted text-ih-fg-2";
71894
72231
  function DefectCategoryChip({ category, className, color }) {
71895
- const tokens = color ? "bg-ih-bg-muted" : CATEGORY_TOKENS[category] ?? DEFAULT_TOKENS;
72232
+ const safe = safeCssColor(color);
72233
+ const tokens = safe ? "bg-ih-bg-muted" : CATEGORY_TOKENS[category] ?? DEFAULT_TOKENS;
71896
72234
  return /* @__PURE__ */ jsx52(
71897
72235
  "span",
71898
72236
  {
71899
72237
  className: `text-[9px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded-full ${tokens}${className ? ` ${className}` : ""}`,
71900
- style: color ? { color } : void 0,
72238
+ style: safe ? { color: safe } : void 0,
71901
72239
  children: category
71902
72240
  }
71903
72241
  );
@@ -71937,7 +72275,7 @@ function CannedCommentRow({
71937
72275
  }
71938
72276
 
71939
72277
  // src/components/editor/DefectFieldsRow.tsx
71940
- import { useMemo as useMemo10 } from "react";
72278
+ import { useMemo as useMemo11 } from "react";
71941
72279
 
71942
72280
  // src/shared/defect-fields.types.ts
71943
72281
  var DEFECT_TRADES = [
@@ -72045,7 +72383,7 @@ function DefectFieldsRow({
72045
72383
  locationRequired,
72046
72384
  tradeRequired
72047
72385
  }) {
72048
- const dedupedSuggestions = useMemo10(
72386
+ const dedupedSuggestions = useMemo11(
72049
72387
  () => Array.from(new Set(locationSuggestions.filter((s) => s.length > 0))),
72050
72388
  [locationSuggestions]
72051
72389
  );
@@ -72122,7 +72460,7 @@ function DefectFieldsRow({
72122
72460
  }
72123
72461
 
72124
72462
  // src/components/editor/RepairItemsPanel.tsx
72125
- import { useEffect as useEffect24, useState as useState28 } from "react";
72463
+ import { useEffect as useEffect25, useState as useState28 } from "react";
72126
72464
  import { jsx as jsx55, jsxs as jsxs43 } from "react/jsx-runtime";
72127
72465
  function RepairItemsPanel({
72128
72466
  attached,
@@ -72133,7 +72471,7 @@ function RepairItemsPanel({
72133
72471
  const [open, setOpen] = useState28(false);
72134
72472
  const [q, setQ] = useState28("");
72135
72473
  const repairItems = useFieldDataSource().resources.repairItems;
72136
- useEffect24(() => {
72474
+ useEffect25(() => {
72137
72475
  let cancelled = false;
72138
72476
  void (repairItems?.list() ?? Promise.resolve([])).then((items) => {
72139
72477
  if (!cancelled) setCatalog(items);
@@ -72545,7 +72883,7 @@ function CannedCommentTabs({
72545
72883
  }
72546
72884
 
72547
72885
  // src/components/media-studio/ItemPhotoStrip.tsx
72548
- import { useEffect as useEffect25, useRef as useRef32, useState as useState29 } from "react";
72886
+ import { useEffect as useEffect26, useRef as useRef33, useState as useState29 } from "react";
72549
72887
  import Sortable2 from "sortablejs";
72550
72888
  import { Fragment as Fragment10, jsx as jsx58, jsxs as jsxs46 } from "react/jsx-runtime";
72551
72889
  function formatDuration(sec) {
@@ -72572,10 +72910,10 @@ function ItemPhotoStrip({
72572
72910
  videoPosterUrl,
72573
72911
  pendingPhotoUrl
72574
72912
  }) {
72575
- const rowRef = useRef32(null);
72913
+ const rowRef = useRef33(null);
72576
72914
  const [selecting, setSelecting] = useState29(false);
72577
72915
  const [sel, setSel] = useState29(/* @__PURE__ */ new Set());
72578
- const lp = useRef32(void 0);
72916
+ const lp = useRef33(void 0);
72579
72917
  const toggle = (i) => setSel((prev) => {
72580
72918
  const next = new Set(prev);
72581
72919
  if (next.has(i)) next.delete(i);
@@ -72595,11 +72933,11 @@ function ItemPhotoStrip({
72595
72933
  }, 450);
72596
72934
  };
72597
72935
  const cancelLongPress = () => clearTimeout(lp.current);
72598
- const photosRef = useRef32(photos);
72936
+ const photosRef = useRef33(photos);
72599
72937
  photosRef.current = photos;
72600
- const onReorderRef = useRef32(onReorder);
72938
+ const onReorderRef = useRef33(onReorder);
72601
72939
  onReorderRef.current = onReorder;
72602
- useEffect25(() => {
72940
+ useEffect26(() => {
72603
72941
  if (!rowRef.current || !onReorder) return;
72604
72942
  const s = Sortable2.create(rowRef.current, {
72605
72943
  animation: 150,
@@ -73029,7 +73367,7 @@ function FormField({
73029
73367
  }
73030
73368
 
73031
73369
  // src/components/editor/item-editor-ui.ts
73032
- import { useCallback as useCallback26, useState as useState31 } from "react";
73370
+ import { useCallback as useCallback27, useState as useState31 } from "react";
73033
73371
  var INITIAL_ITEM_EDITOR_UI = {
73034
73372
  activeTab: "information",
73035
73373
  defectQuery: "",
@@ -73042,7 +73380,7 @@ var INITIAL_ITEM_EDITOR_UI = {
73042
73380
  };
73043
73381
  function useItemEditorUiStore() {
73044
73382
  const [byItem, setByItem] = useState31({});
73045
- return useCallback26(
73383
+ return useCallback27(
73046
73384
  (itemId) => ({
73047
73385
  ui: byItem[itemId] ?? INITIAL_ITEM_EDITOR_UI,
73048
73386
  patch: (change) => setByItem((prev) => ({ ...prev, [itemId]: { ...prev[itemId] ?? INITIAL_ITEM_EDITOR_UI, ...change } }))
@@ -73067,17 +73405,29 @@ function useItemEditorUi(external) {
73067
73405
  }
73068
73406
 
73069
73407
  // src/hooks/useNotesCommitOnLeave.ts
73070
- import { useEffect as useEffect26, useRef as useRef33 } from "react";
73408
+ import { useCallback as useCallback28, useEffect as useEffect27, useMemo as useMemo12, useRef as useRef34 } from "react";
73409
+ var IDLE_MS = 1e3;
73071
73410
  function useNotesCommitOnLeave(itemId, commit, flushRef) {
73072
- const pending = useRef33(null);
73073
- const latest = useRef33({ itemId, commit });
73411
+ const pending = useRef34(null);
73412
+ const idle = useRef34(null);
73413
+ const composing = useRef34(false);
73414
+ const latest = useRef34({ itemId, commit });
73074
73415
  latest.current = { itemId, commit };
73075
- useEffect26(() => {
73076
- const flush = () => {
73077
- const held = pending.current;
73078
- pending.current = null;
73079
- if (held && held.itemId === latest.current.itemId) latest.current.commit(held.text);
73080
- };
73416
+ const cancelIdle = useCallback28(() => {
73417
+ if (idle.current !== null) clearTimeout(idle.current);
73418
+ idle.current = null;
73419
+ }, []);
73420
+ const flush = useCallback28(() => {
73421
+ cancelIdle();
73422
+ const held = pending.current;
73423
+ pending.current = null;
73424
+ if (held) latest.current.commit(held.text, held.itemId);
73425
+ }, [cancelIdle]);
73426
+ useEffect27(() => () => {
73427
+ composing.current = false;
73428
+ flush();
73429
+ }, [itemId, flush]);
73430
+ useEffect27(() => {
73081
73431
  const onHide = () => {
73082
73432
  if (document.visibilityState === "hidden") flush();
73083
73433
  };
@@ -73091,12 +73441,35 @@ function useNotesCommitOnLeave(itemId, commit, flushRef) {
73091
73441
  flush();
73092
73442
  };
73093
73443
  }, []);
73444
+ const armIdle = useCallback28(() => {
73445
+ cancelIdle();
73446
+ idle.current = setTimeout(function onIdle() {
73447
+ if (composing.current) idle.current = setTimeout(onIdle, IDLE_MS);
73448
+ else flush();
73449
+ }, IDLE_MS);
73450
+ }, [cancelIdle, flush]);
73451
+ const composition = useMemo12(
73452
+ () => ({
73453
+ onCompositionStart: () => {
73454
+ composing.current = true;
73455
+ },
73456
+ onCompositionEnd: () => {
73457
+ composing.current = false;
73458
+ if (pending.current) armIdle();
73459
+ }
73460
+ }),
73461
+ [armIdle]
73462
+ );
73094
73463
  return {
73464
+ composition,
73095
73465
  track: (text) => {
73096
73466
  pending.current = { itemId: latest.current.itemId, text };
73467
+ armIdle();
73097
73468
  },
73098
73469
  settle: () => {
73099
73470
  pending.current = null;
73471
+ composing.current = false;
73472
+ cancelIdle();
73100
73473
  }
73101
73474
  };
73102
73475
  }
@@ -73164,10 +73537,10 @@ function ItemEditor({
73164
73537
  const { urls } = useFieldDataSource();
73165
73538
  const { activeTab, setActiveTab, defectQuery, setDefectQuery, customFormOpen, setCustomFormOpen, customTitle, setCustomTitle, customComment, setCustomComment, customCategory, setCustomCategory, customTrade, setCustomTrade, saveToLibrary, setSaveToLibrary } = useItemEditorUi(uiControl);
73166
73539
  const notesCommit = useNotesCommitOnLeave(item?.id, onNotesBlur, notesFlushRef);
73167
- const notesRef = useRef34(null);
73540
+ const notesRef = useRef35(null);
73168
73541
  const [taOpen, setTaOpen] = useState32(false);
73169
73542
  const [taQuery, setTaQuery] = useState32("");
73170
- const taEntries = useMemo11(() => flattenItemTabs(item?.tabs), [item?.tabs]);
73543
+ const taEntries = useMemo13(() => flattenItemTabs(item?.tabs), [item?.tabs]);
73171
73544
  const ta = useCommentTypeahead(taEntries, taQuery, { max: 8 });
73172
73545
  const insertPick = (commentText) => {
73173
73546
  const el = notesRef.current;
@@ -73186,7 +73559,7 @@ function ItemEditor({
73186
73559
  });
73187
73560
  };
73188
73561
  const [libraryMatches, setLibraryMatches] = useState32([]);
73189
- useEffect27(() => {
73562
+ useEffect28(() => {
73190
73563
  const q = defectQuery.trim();
73191
73564
  if (activeTab !== "defects" || q.length < 2 || !onSearchLibrary) {
73192
73565
  setLibraryMatches([]);
@@ -73361,6 +73734,7 @@ function ItemEditor({
73361
73734
  "textarea",
73362
73735
  {
73363
73736
  id: "notes-textarea",
73737
+ ...notesCommit.composition,
73364
73738
  ref: notesRef,
73365
73739
  value: result.notes || "",
73366
73740
  onChange: (e) => {
@@ -73372,7 +73746,7 @@ function ItemEditor({
73372
73746
  },
73373
73747
  onBlur: (e) => {
73374
73748
  notesCommit.settle();
73375
- onNotesBlur(e.target.value);
73749
+ onNotesBlur(e.target.value, item.id);
73376
73750
  setTaOpen(false);
73377
73751
  },
73378
73752
  onKeyDown: (e) => {
@@ -73433,7 +73807,7 @@ function ItemEditor({
73433
73807
  onAccept: (text) => {
73434
73808
  onNotes(text);
73435
73809
  notesCommit.settle();
73436
- onNotesBlur(text);
73810
+ onNotesBlur(text, item.id);
73437
73811
  }
73438
73812
  }
73439
73813
  ),
@@ -73541,6 +73915,21 @@ function ItemEditor({
73541
73915
  ] });
73542
73916
  }
73543
73917
 
73918
+ // src/hooks/useHasFinePointer.ts
73919
+ import { useSyncExternalStore as useSyncExternalStore3 } from "react";
73920
+ var FINE = "(any-hover: hover) and (any-pointer: fine)";
73921
+ function subscribe(onChange) {
73922
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
73923
+ };
73924
+ const mq = window.matchMedia(FINE);
73925
+ mq.addEventListener("change", onChange);
73926
+ return () => mq.removeEventListener("change", onChange);
73927
+ }
73928
+ var snapshot = () => typeof window === "undefined" || typeof window.matchMedia !== "function" || window.matchMedia(FINE).matches;
73929
+ function useHasFinePointer() {
73930
+ return useSyncExternalStore3(subscribe, snapshot, () => true);
73931
+ }
73932
+
73544
73933
  // src/components/editor/ItemPaneEmptyState.tsx
73545
73934
  import { jsx as jsx64, jsxs as jsxs52 } from "react/jsx-runtime";
73546
73935
  function firstItemToOpen(items, isRated) {
@@ -73550,6 +73939,7 @@ function firstItemToOpen(items, isRated) {
73550
73939
  }
73551
73940
  function ItemPaneEmptyState({ items, isRated, onOpenItem }) {
73552
73941
  const next = firstItemToOpen(items, isRated);
73942
+ const fine = useHasFinePointer();
73553
73943
  if (!next) {
73554
73944
  return /* @__PURE__ */ jsx64("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx64(
73555
73945
  EmptyState,
@@ -73578,7 +73968,7 @@ function ItemPaneEmptyState({ items, isRated, onOpenItem }) {
73578
73968
  children: index_exports.editor_pane_open_item({ label: next.item.label })
73579
73969
  }
73580
73970
  ),
73581
- /* @__PURE__ */ jsxs52("p", { className: "text-[11px] text-ih-fg-3", children: [
73971
+ fine && /* @__PURE__ */ jsxs52("p", { className: "text-[11px] text-ih-fg-3", children: [
73582
73972
  index_exports.editor_route_navigate_hint_press(),
73583
73973
  " ",
73584
73974
  /* @__PURE__ */ jsx64("kbd", { className: "px-1.5 py-0.5 bg-ih-bg-muted rounded text-[10px] font-mono border", children: "J" }),
@@ -73594,20 +73984,27 @@ function ItemPaneEmptyState({ items, isRated, onOpenItem }) {
73594
73984
  }
73595
73985
 
73596
73986
  // src/components/editor/TagChipRow.tsx
73597
- import { useMemo as useMemo12 } from "react";
73987
+ import { useMemo as useMemo14 } from "react";
73598
73988
  import { jsx as jsx65, jsxs as jsxs53 } from "react/jsx-runtime";
73599
- function TagChipRow({ pinnedTags, activeTagIds, onToggle, onOpenLibrary }) {
73600
- const sorted = useMemo12(() => pinnedTags.slice(0, 5), [pinnedTags]);
73601
- if (sorted.length === 0) return null;
73602
- return /* @__PURE__ */ jsxs53("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
73989
+ var COARSE = "[@media(pointer:coarse)]:min-h-9 [@media(pointer:coarse)]:px-3 [@media(pointer:coarse)]:text-xs";
73990
+ function TagChipRow({ pinnedTags, chosenTags, activeTagIds, onToggle, onOpenLibrary }) {
73991
+ const sorted = useMemo14(() => {
73992
+ const pinned = pinnedTags.slice(0, 5);
73993
+ const shown = new Set(pinned.map((t) => t.id));
73994
+ const unpinned = (chosenTags ?? []).filter((t) => !shown.has(t.id)).map((t) => ({ ...t, color: t.color ?? "" }));
73995
+ return [...pinned, ...unpinned];
73996
+ }, [pinnedTags, chosenTags]);
73997
+ if (sorted.length === 0 && !onOpenLibrary) return null;
73998
+ return /* @__PURE__ */ jsxs53("div", { className: "mt-2 flex flex-wrap items-center gap-1.5 [@media(pointer:coarse)]:gap-2", children: [
73603
73999
  sorted.map((tag) => {
73604
74000
  const active = activeTagIds.has(tag.id);
74001
+ const colour = safeCssColor(tag.color);
73605
74002
  return /* @__PURE__ */ jsx65(
73606
74003
  "button",
73607
74004
  {
73608
74005
  onClick: () => onToggle(tag),
73609
- className: `px-2 py-0.5 rounded-full text-[11px] font-bold transition-colors border ${active ? "text-white border-transparent" : "text-ih-fg-3 border-ih-border bg-transparent hover:bg-ih-bg-muted"}`,
73610
- style: active ? { backgroundColor: tag.color } : {},
74006
+ className: `px-2 py-0.5 rounded-full text-[11px] font-bold transition-colors border ${COARSE} ${active ? `${colour ? "text-white" : "bg-ih-bg-muted text-ih-fg-1"} border-transparent` : "text-ih-fg-3 border-ih-border bg-transparent hover:bg-ih-bg-muted"}`,
74007
+ style: active && colour ? { backgroundColor: colour, color: readableTextOn(colour) ?? void 0 } : {},
73611
74008
  children: tag.name
73612
74009
  },
73613
74010
  tag.id
@@ -73619,7 +74016,7 @@ function TagChipRow({ pinnedTags, activeTagIds, onToggle, onOpenLibrary }) {
73619
74016
  variant: "ghost",
73620
74017
  size: "sm",
73621
74018
  onClick: onOpenLibrary,
73622
- className: "h-auto px-2 py-0.5 rounded-full border border-dashed border-ih-border text-ih-fg-4 hover:bg-ih-bg-muted",
74019
+ className: `h-auto px-2 py-0.5 rounded-full border border-dashed border-ih-border text-ih-fg-3 hover:bg-ih-bg-muted ${COARSE}`,
73623
74020
  "aria-label": index_exports.editor_tagchiprow_open_library_aria(),
73624
74021
  children: index_exports.editor_tagchiprow_more()
73625
74022
  }
@@ -73628,7 +74025,7 @@ function TagChipRow({ pinnedTags, activeTagIds, onToggle, onOpenLibrary }) {
73628
74025
  }
73629
74026
 
73630
74027
  // src/components/editor/SideRail.tsx
73631
- import { useState as useState35, useEffect as useEffect30 } from "react";
74028
+ import { useState as useState35, useEffect as useEffect31 } from "react";
73632
74029
 
73633
74030
  // src/shared/photo-name.ts
73634
74031
  var UUID_TAIL = /_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}_(.+)$/i;
@@ -73642,7 +74039,7 @@ function withDownload(url) {
73642
74039
  }
73643
74040
 
73644
74041
  // src/components/media-studio/PhotoGallery.tsx
73645
- import { useCallback as useCallback28, useEffect as useEffect29, useRef as useRef36, useState as useState34 } from "react";
74042
+ import { useCallback as useCallback30, useEffect as useEffect30, useRef as useRef37, useState as useState34 } from "react";
73646
74043
 
73647
74044
  // src/components/media-studio/PhotoGrid.tsx
73648
74045
  import { RowsPhotoAlbum } from "react-photo-album";
@@ -73666,6 +74063,7 @@ import Zoom from "yet-another-react-lightbox/plugins/zoom";
73666
74063
  import "yet-another-react-lightbox/styles.css";
73667
74064
  import { jsx as jsx67 } from "react/jsx-runtime";
73668
74065
  function PhotoLightbox({ slides, index, open, onClose, toolbarButtons, toolbarWrap, renderSlide }) {
74066
+ useLayer(open, { onEscape: onClose, lockScroll: true, traps: true });
73669
74067
  return /* @__PURE__ */ jsx67(
73670
74068
  Lightbox,
73671
74069
  {
@@ -73683,13 +74081,13 @@ function PhotoLightbox({ slides, index, open, onClose, toolbarButtons, toolbarWr
73683
74081
  }
73684
74082
 
73685
74083
  // src/hooks/useBreakpoint.ts
73686
- import { useCallback as useCallback27, useEffect as useEffect28, useRef as useRef35, useState as useState33, useSyncExternalStore as useSyncExternalStore2 } from "react";
74084
+ import { useCallback as useCallback29, useEffect as useEffect29, useRef as useRef36, useState as useState33, useSyncExternalStore as useSyncExternalStore4 } from "react";
73687
74085
  function useIsMobile() {
73688
74086
  const [isMobile, setIsMobile] = useState33(() => {
73689
74087
  if (typeof window === "undefined") return false;
73690
74088
  return window.matchMedia("(max-width: 767.98px)").matches;
73691
74089
  });
73692
- useEffect28(() => {
74090
+ useEffect29(() => {
73693
74091
  if (typeof window === "undefined") return;
73694
74092
  const mq = window.matchMedia("(max-width: 767.98px)");
73695
74093
  const fn = (e) => setIsMobile(e.matches);
@@ -73716,12 +74114,12 @@ function subscribeLayout(onChange) {
73716
74114
  };
73717
74115
  }
73718
74116
  function useEditorLayout(initialLayout = "desktop", onObserved) {
73719
- const serverSnapshot = useCallback27(() => initialLayout, [initialLayout]);
73720
- const layout = useSyncExternalStore2(subscribeLayout, readLayout, serverSnapshot);
73721
- const observer = useRef35(onObserved);
74117
+ const serverSnapshot = useCallback29(() => initialLayout, [initialLayout]);
74118
+ const layout = useSyncExternalStore4(subscribeLayout, readLayout, serverSnapshot);
74119
+ const observer = useRef36(onObserved);
73722
74120
  observer.current = onObserved;
73723
- const told = useRef35(null);
73724
- useEffect28(() => {
74121
+ const told = useRef36(null);
74122
+ useEffect29(() => {
73725
74123
  const seen = readLayout();
73726
74124
  if (told.current === seen) return;
73727
74125
  told.current = seen;
@@ -73731,7 +74129,7 @@ function useEditorLayout(initialLayout = "desktop", onObserved) {
73731
74129
  }
73732
74130
  function useMaxWidth(px) {
73733
74131
  const query = `(max-width: ${px}px)`;
73734
- const subscribe = useCallback27(
74132
+ const subscribe2 = useCallback29(
73735
74133
  (onChange) => {
73736
74134
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
73737
74135
  };
@@ -73741,11 +74139,11 @@ function useMaxWidth(px) {
73741
74139
  },
73742
74140
  [query]
73743
74141
  );
73744
- const getSnapshot = useCallback27(
74142
+ const getSnapshot = useCallback29(
73745
74143
  () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(query).matches,
73746
74144
  [query]
73747
74145
  );
73748
- return useSyncExternalStore2(subscribe, getSnapshot, () => false);
74146
+ return useSyncExternalStore4(subscribe2, getSnapshot, () => false);
73749
74147
  }
73750
74148
 
73751
74149
  // src/components/media-studio/VideoPlayer.tsx
@@ -74042,22 +74440,23 @@ var GALLERY_UNHANDLED_ACTIONS = ["crop", "rotate", "caption", "revert", "delete"
74042
74440
  function PhotoGallery({ inspectionId, onSetCover, onAnnotate }) {
74043
74441
  const mediaPool = useFieldDataSource().resources.mediaPool;
74044
74442
  const readOnly = useReadOnly();
74443
+ const photoUrlVersion = usePhotoUrlVersion();
74045
74444
  const [listing, setListing] = useState34(null);
74046
74445
  const [lightbox, setLightbox] = useState34(null);
74047
74446
  const photos = listing?.photos ?? [];
74048
- const latestRead = useRef36(0);
74049
- const load = useCallback28(() => {
74447
+ const latestRead = useRef37(0);
74448
+ const load = useCallback30(() => {
74050
74449
  const read = latestRead.current += 1;
74051
74450
  void (mediaPool?.list(inspectionId) ?? Promise.resolve({ photos: [], answered: false })).then((next) => {
74052
74451
  if (read === latestRead.current) setListing(next);
74053
74452
  });
74054
74453
  }, [mediaPool, inspectionId]);
74055
- useEffect29(() => {
74454
+ useEffect30(() => {
74056
74455
  if (inspectionId) load();
74057
74456
  return () => {
74058
74457
  latestRead.current += 1;
74059
74458
  };
74060
- }, [inspectionId, load]);
74459
+ }, [inspectionId, load, photoUrlVersion]);
74061
74460
  if (!listing) return /* @__PURE__ */ jsx70("p", { className: "text-[13px] text-ih-fg-3 text-center py-8", children: index_exports.media_gallery_loading() });
74062
74461
  if (!listing.answered) {
74063
74462
  return /* @__PURE__ */ jsxs56("div", { className: "text-center py-8 space-y-2", children: [
@@ -74117,6 +74516,10 @@ function CommentLibraryList({ serverComments, selectedIndex, sort, onInsertText,
74117
74516
 
74118
74517
  // src/components/editor/SideRail.tsx
74119
74518
  import { jsx as jsx72, jsxs as jsxs58 } from "react/jsx-runtime";
74519
+ function ratingPillStyle(bg) {
74520
+ const color = readableTextOn(bg);
74521
+ return color ? { backgroundColor: bg, color } : { backgroundColor: bg };
74522
+ }
74120
74523
  var TABS = [
74121
74524
  { id: "preview", label: () => index_exports.editor_siderail_tab_preview(), icon: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" },
74122
74525
  { id: "library", label: () => index_exports.editor_siderail_tab_library(), icon: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" },
@@ -74128,6 +74531,7 @@ function SideRail({ mode, activeItem, activeResult, getRatingColor, getRatingLab
74128
74531
  const [activeTab, setActiveTab] = useState35("preview");
74129
74532
  const [openState, setOpen] = useState35(initialOpen ?? false);
74130
74533
  const open = fill || openState;
74534
+ const ratingColor = safeCssColor(getRatingColor?.(String(activeResult?.rating ?? ""))) ?? "#6b7280";
74131
74535
  const hideLibrary = mode === "fill" && !!activeItem && activeItem.type !== "rich";
74132
74536
  const visibleTabs = TABS.filter((t) => {
74133
74537
  if (t.id === "photos" && (mode !== "fill" || !resources.mediaPool)) return false;
@@ -74136,7 +74540,7 @@ function SideRail({ mode, activeItem, activeResult, getRatingColor, getRatingLab
74136
74540
  });
74137
74541
  const photoKeys = (Array.isArray(activeResult?.photos) ? activeResult.photos : []).map((p) => typeof p === "string" ? p : p?.mediaType === "video" ? "" : p?.key ?? "").filter((k) => k !== "");
74138
74542
  const effectiveTab = visibleTabs.some((t) => t.id === activeTab) ? activeTab : "preview";
74139
- useEffect30(() => {
74543
+ useEffect31(() => {
74140
74544
  if (hideLibrary && activeTab === "library") {
74141
74545
  setActiveTab("preview");
74142
74546
  onLibraryTabChange?.(false);
@@ -74162,7 +74566,7 @@ function SideRail({ mode, activeItem, activeResult, getRatingColor, getRatingLab
74162
74566
  };
74163
74567
  return /* @__PURE__ */ jsxs58("div", { className: "flex h-full", children: [
74164
74568
  open && /* @__PURE__ */ jsxs58("div", { className: `${fill ? "flex-1 min-w-0" : "w-64"} border-l border-ih-border bg-ih-bg-card flex flex-col overflow-hidden`, children: [
74165
- /* @__PURE__ */ jsxs58("div", { className: "flex items-center justify-between px-3 py-2 border-b border-ih-border", children: [
74569
+ /* @__PURE__ */ jsxs58("div", { className: `flex items-center justify-between ${fill ? "px-3" : "pl-9 pr-3"} py-2 border-b border-ih-border`, children: [
74166
74570
  /* @__PURE__ */ jsx72("span", { className: "text-[11px] font-bold uppercase tracking-[0.15em] text-ih-fg-3 capitalize", children: effectiveTab }),
74167
74571
  !fill && /* @__PURE__ */ jsx72(IconButton, { onClick: closePanel, "aria-label": index_exports.editor_siderail_close_panel(), size: "sm", className: "w-6 h-6 text-ih-fg-4 hover:text-ih-fg-2", children: /* @__PURE__ */ jsx72(Icon, { name: "x", size: 14 }) })
74168
74572
  ] }),
@@ -74172,8 +74576,8 @@ function SideRail({ mode, activeItem, activeResult, getRatingColor, getRatingLab
74172
74576
  Boolean(activeResult.rating) && /* @__PURE__ */ jsx72("div", { children: /* @__PURE__ */ jsx72(
74173
74577
  "span",
74174
74578
  {
74175
- className: "inline-block px-2 py-0.5 rounded-full text-[10px] font-bold text-white",
74176
- style: { backgroundColor: getRatingColor?.(activeResult.rating) || "#6b7280" },
74579
+ className: `inline-block px-2 py-0.5 rounded-full text-[10px] font-bold ${isFallbackRatingColor(ratingColor) ? "bg-ih-bg-muted text-ih-fg-1" : "text-white"}`,
74580
+ style: isFallbackRatingColor(ratingColor) ? void 0 : ratingPillStyle(ratingColor),
74177
74581
  children: getRatingLabel?.(activeResult.rating) || activeResult.rating
74178
74582
  }
74179
74583
  ) }),
@@ -74281,7 +74685,7 @@ function SideRail({ mode, activeItem, activeResult, getRatingColor, getRatingLab
74281
74685
  className: `relative w-10 flex flex-col items-center gap-0.5 py-2.5 rounded-r-md transition-all ${effectiveTab === tab.id && open ? "bg-ih-bg-card text-ih-primary-text shadow-ih-card border-l-2 border-ih-primary -ml-px" : "text-ih-fg-3 hover:text-ih-fg-2"}`,
74282
74686
  title: tab.label(),
74283
74687
  children: [
74284
- tab.id === "photos" && (photoCount ?? 0) > 0 && /* @__PURE__ */ jsx72("span", { className: "absolute -top-0.5 -right-0.5 inline-flex items-center justify-center min-w-[16px] h-4 px-1 rounded-full bg-ih-primary text-ih-fg-inverse text-[9px] font-bold leading-none", children: photoCount }),
74688
+ tab.id === "photos" && (photoCount ?? 0) > 0 && /* @__PURE__ */ jsx72("span", { className: "absolute top-0.5 right-1 inline-flex items-center justify-center min-w-[16px] h-4 px-1 rounded-full bg-ih-primary text-ih-fg-inverse text-[9px] font-bold leading-none", children: photoCount }),
74285
74689
  /* @__PURE__ */ jsx72("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx72("path", { d: tab.icon }) }),
74286
74690
  /* @__PURE__ */ jsx72("span", { className: "text-[8px] font-bold uppercase tracking-[0.1em]", style: { writingMode: "vertical-rl", transform: "rotate(180deg)" }, children: tab.label() })
74287
74691
  ]
@@ -74292,18 +74696,18 @@ function SideRail({ mode, activeItem, activeResult, getRatingColor, getRatingLab
74292
74696
  }
74293
74697
 
74294
74698
  // src/components/editor/SpeedMode.tsx
74295
- import { useState as useState36, useEffect as useEffect32, useCallback as useCallback30, useRef as useRef40 } from "react";
74699
+ import { useState as useState36, useEffect as useEffect33, useCallback as useCallback32, useRef as useRef41 } from "react";
74296
74700
 
74297
74701
  // src/hooks/usePointerGesture.ts
74298
- import { useCallback as useCallback29, useRef as useRef37 } from "react";
74702
+ import { useCallback as useCallback31, useRef as useRef38 } from "react";
74299
74703
  function usePointerGesture(h) {
74300
- const startRef = useRef37(null);
74301
- const longPressTimer = useRef37(null);
74302
- const fired = useRef37(false);
74704
+ const startRef = useRef38(null);
74705
+ const longPressTimer = useRef38(null);
74706
+ const fired = useRef38(false);
74303
74707
  const threshold = h.swipeThresholdPx ?? 60;
74304
74708
  const maxDuration = h.swipeMaxDurationMs ?? 500;
74305
74709
  const longPressMs = h.longPressMs ?? 500;
74306
- const onPointerDown = useCallback29((e) => {
74710
+ const onPointerDown = useCallback31((e) => {
74307
74711
  startRef.current = { x: e.clientX, y: e.clientY, t: Date.now() };
74308
74712
  fired.current = false;
74309
74713
  if (h.onLongPress) {
@@ -74315,14 +74719,14 @@ function usePointerGesture(h) {
74315
74719
  }, longPressMs);
74316
74720
  }
74317
74721
  }, [h.onLongPress, longPressMs]);
74318
- const cancel = useCallback29(() => {
74722
+ const cancel = useCallback31(() => {
74319
74723
  if (longPressTimer.current) {
74320
74724
  clearTimeout(longPressTimer.current);
74321
74725
  longPressTimer.current = null;
74322
74726
  }
74323
74727
  startRef.current = null;
74324
74728
  }, []);
74325
- const onPointerMove = useCallback29((e) => {
74729
+ const onPointerMove = useCallback31((e) => {
74326
74730
  if (!startRef.current || fired.current) return;
74327
74731
  const dx = e.clientX - startRef.current.x;
74328
74732
  const dy = e.clientY - startRef.current.y;
@@ -74333,7 +74737,7 @@ function usePointerGesture(h) {
74333
74737
  }
74334
74738
  }
74335
74739
  }, []);
74336
- const onPointerUp = useCallback29((e) => {
74740
+ const onPointerUp = useCallback31((e) => {
74337
74741
  if (!startRef.current || fired.current) {
74338
74742
  cancel();
74339
74743
  return;
@@ -74352,10 +74756,10 @@ function usePointerGesture(h) {
74352
74756
  }
74353
74757
 
74354
74758
  // src/components/MobileBottomDrawer.tsx
74355
- import { useRef as useRef38 } from "react";
74759
+ import { useRef as useRef39 } from "react";
74356
74760
  import { Fragment as Fragment12, jsx as jsx73, jsxs as jsxs59 } from "react/jsx-runtime";
74357
74761
  function MobileBottomDrawer({ open, onClose, title, children, heightFraction = 0.7 }) {
74358
- const sheetRef = useRef38(null);
74762
+ const sheetRef = useRef39(null);
74359
74763
  useDialogBehavior(open, onClose, sheetRef);
74360
74764
  return /* @__PURE__ */ jsxs59(Fragment12, { children: [
74361
74765
  /* @__PURE__ */ jsx73(
@@ -74375,7 +74779,7 @@ function MobileBottomDrawer({ open, onClose, title, children, heightFraction = 0
74375
74779
  "aria-label": title,
74376
74780
  inert: !open,
74377
74781
  "aria-hidden": open ? void 0 : true,
74378
- className: `fixed left-0 right-0 bottom-0 z-50 bg-ih-bg-card rounded-t-2xl shadow-ih-popover flex flex-col outline-none transition-transform ${open ? "translate-y-0" : "translate-y-full"}`,
74782
+ className: `fixed left-0 right-0 bottom-0 z-50 pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)] bg-ih-bg-card rounded-t-2xl shadow-ih-popover flex flex-col outline-none transition-transform ${open ? "translate-y-0" : "translate-y-full"}`,
74379
74783
  style: { height: `${Math.round(heightFraction * 100)}vh`, maxHeight: `${Math.round(heightFraction * 100)}dvh` },
74380
74784
  children: [
74381
74785
  /* @__PURE__ */ jsx73("div", { className: "flex justify-center pt-2 pb-1", children: /* @__PURE__ */ jsx73("div", { className: "w-10 h-1 rounded-full bg-ih-border" }) }),
@@ -74388,13 +74792,13 @@ function MobileBottomDrawer({ open, onClose, title, children, heightFraction = 0
74388
74792
  }
74389
74793
 
74390
74794
  // src/components/editor/SpeedModeUndoToast.tsx
74391
- import { useEffect as useEffect31, useRef as useRef39 } from "react";
74795
+ import { useEffect as useEffect32, useRef as useRef40 } from "react";
74392
74796
  import { jsx as jsx74, jsxs as jsxs60 } from "react/jsx-runtime";
74393
74797
  var BOX = "px-4 py-2.5 bg-ih-bg-card border border-ih-border rounded-lg shadow-ih-popover flex items-center gap-3 text-[13px]";
74394
74798
  function SpeedModeUndoToast({ pending, onDismiss, durationMs = 3e3, placement = "floating" }) {
74395
- const dismissRef = useRef39(onDismiss);
74799
+ const dismissRef = useRef40(onDismiss);
74396
74800
  dismissRef.current = onDismiss;
74397
- useEffect31(() => {
74801
+ useEffect32(() => {
74398
74802
  if (!pending) return;
74399
74803
  const t = setTimeout(() => dismissRef.current(), durationMs);
74400
74804
  return () => clearTimeout(t);
@@ -74486,17 +74890,17 @@ function SpeedMode({
74486
74890
  const hosted = onPendingUndoChange !== void 0;
74487
74891
  const pendingUndo = hosted ? hostUndo ?? null : ownUndo;
74488
74892
  const setPendingUndo = hosted ? onPendingUndoChange : setOwnUndo;
74489
- const undoRef = useRef40(pendingUndo);
74893
+ const undoRef = useRef41(pendingUndo);
74490
74894
  undoRef.current = pendingUndo;
74491
74895
  const [showCoach, setShowCoach] = useState36(false);
74492
- useEffect32(() => {
74896
+ useEffect33(() => {
74493
74897
  if (shouldShowSpeedModeCoach()) setShowCoach(true);
74494
74898
  }, []);
74495
- const dismissCoach = useCallback30(() => {
74899
+ const dismissCoach = useCallback32(() => {
74496
74900
  setShowCoach(false);
74497
74901
  markSpeedModeCoached();
74498
74902
  }, []);
74499
- useEffect32(() => {
74903
+ useEffect33(() => {
74500
74904
  if (!showCoach) return;
74501
74905
  window.addEventListener("keydown", dismissCoach);
74502
74906
  return () => window.removeEventListener("keydown", dismissCoach);
@@ -74506,10 +74910,10 @@ function SpeedMode({
74506
74910
  onSwipeRight: () => onPrevItem?.(),
74507
74911
  onLongPress: () => setShowJumpTo(true)
74508
74912
  });
74509
- useEffect32(() => {
74913
+ useEffect33(() => {
74510
74914
  if (undoRef.current && !undoRef.current.keep) setPendingUndo(null);
74511
74915
  }, [item?.id]);
74512
- const rootRef = useRef40(null);
74916
+ const rootRef = useRef41(null);
74513
74917
  useDialogBehavior(!!item, onExit, rootRef, rootRef, { trapTab: false });
74514
74918
  if (!item) return null;
74515
74919
  const levels = ratingLevels && ratingLevels.length > 0 ? ratingLevels : SPEED_FALLBACK_LEVELS;
@@ -74587,7 +74991,7 @@ function SpeedMode({
74587
74991
  ] }),
74588
74992
  touch && /* @__PURE__ */ jsx75(SpeedModeUndoToast, { pending: pendingUndo, onDismiss: () => setPendingUndo(null), placement: "inline" }),
74589
74993
  touch && !finished && /* @__PURE__ */ jsx75("div", { "data-testid": "speedmode-ratings", className: "flex flex-wrap gap-2 px-4 pt-3 shrink-0", children: tiles }),
74590
- /* @__PURE__ */ jsxs61("div", { className: "h-10 flex items-center justify-center text-[11px] text-ih-fg-3 border-t border-ih-border", children: [
74994
+ !touch && /* @__PURE__ */ jsxs61("div", { className: "h-10 flex items-center justify-center text-[11px] text-ih-fg-3 border-t border-ih-border", children: [
74591
74995
  index_exports.editor_speedmode_footer_press(),
74592
74996
  " ",
74593
74997
  /* @__PURE__ */ jsx75("kbd", { className: "mx-1 px-1.5 py-0.5 bg-ih-bg-muted rounded text-[10px] font-mono border border-ih-border", children: "Z" }),
@@ -74675,6 +75079,7 @@ import { jsx as jsx76, jsxs as jsxs62 } from "react/jsx-runtime";
74675
75079
  function FooterBar({ connected = false, status, roster = [] }) {
74676
75080
  const effectiveStatus = status ?? (connected ? "connected" : "connecting");
74677
75081
  const [shortcutsOpen, setShortcutsOpen] = useState37(false);
75082
+ const fine = useHasFinePointer();
74678
75083
  const people = [...new Map(roster.map((u) => [u.userId, u])).values()];
74679
75084
  const { commentsLibrary } = useEditorCapabilities();
74680
75085
  const SHORTCUTS = [
@@ -74691,8 +75096,8 @@ function FooterBar({ connected = false, status, roster = [] }) {
74691
75096
  { keys: ["\u2318", "\\"], desc: index_exports.editor_footer_shortcut_sidebar() },
74692
75097
  { keys: ["?"], desc: index_exports.editor_footer_shortcut_help() }
74693
75098
  ];
74694
- return /* @__PURE__ */ jsxs62("div", { className: "hidden md:flex fixed bottom-0 inset-x-0 z-30 bg-ih-bg-card border-t border-ih-border px-4 py-1.5 items-center gap-3 text-[11px] text-ih-fg-3", children: [
74695
- /* @__PURE__ */ jsxs62("div", { className: "relative", children: [
75099
+ return /* @__PURE__ */ jsxs62("div", { className: "hidden md:flex fixed bottom-0 inset-x-0 z-30 bg-ih-bg-card border-t border-ih-border pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] py-1.5 items-center gap-3 text-[11px] text-ih-fg-3", children: [
75100
+ fine && /* @__PURE__ */ jsxs62("div", { className: "relative", children: [
74696
75101
  /* @__PURE__ */ jsx76(
74697
75102
  Button,
74698
75103
  {
@@ -74749,7 +75154,7 @@ function BatchActionBar({
74749
75154
  value: level.id,
74750
75155
  label: String(idx + 1),
74751
75156
  tone: "neutral",
74752
- color: getRatingColor(level.id)
75157
+ color: safeCssColor(getRatingColor(level.id)) ?? void 0
74753
75158
  }));
74754
75159
  return /* @__PURE__ */ jsxs63("div", { className: "flex items-center gap-2 bg-ih-bg-card border-t border-ih-border px-4 py-2", children: [
74755
75160
  /* @__PURE__ */ jsx77(
@@ -74875,12 +75280,13 @@ function InspectorToolsDock({
74875
75280
  }) {
74876
75281
  const [dockOpen, setDockOpen] = useState38(false);
74877
75282
  const readOnly = useReadOnly();
75283
+ const fine = useHasFinePointer();
74878
75284
  const TILES = [
74879
75285
  { id: "speed-mode", label: index_exports.editor_dock_speed_mode(), iconPath: "M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z", hotkey: "Z" },
74880
75286
  { id: "burst-camera", label: index_exports.editor_dock_burst_camera(), iconPath: "M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316zM16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0z" },
74881
75287
  { id: "photo-studio", label: index_exports.editor_dock_photo_studio(), iconPath: "M9.53 16.122a3 3 0 00-5.78 1.128 2.25 2.25 0 01-2.4 2.245 4.5 4.5 0 008.4-2.245c0-.399-.078-.78-.22-1.128zm0 0a15.998 15.998 0 003.388-1.62m-5.043-.025a15.994 15.994 0 011.622-3.395m3.42 3.42a15.995 15.995 0 004.764-4.648l3.876-5.814a1.151 1.151 0 00-1.597-1.597L14.146 6.32a15.996 15.996 0 00-4.649 4.763m3.42 3.42a6.776 6.776 0 00-3.42-3.42" },
74882
75288
  { id: "shortcuts", label: index_exports.editor_shortcuts_label(), iconPath: "M6 6.878V6a2.25 2.25 0 012.25-2.25h7.5A2.25 2.25 0 0118 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 004.5 9v.878m13.5-3A2.25 2.25 0 0119.5 9v.878m0 0a2.246 2.246 0 00-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0121 12v6a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 18v-6c0-.98.626-1.813 1.5-2.122", hotkey: "?" }
74883
- ].filter((t) => (t.id !== "burst-camera" || Boolean(activeItemId)) && (!readOnly || t.id === "shortcuts"));
75289
+ ].filter((t) => (t.id !== "burst-camera" || Boolean(activeItemId)) && (!readOnly || t.id === "shortcuts") && (fine || t.id !== "shortcuts"));
74884
75290
  useLayer(dockOpen && !hidden, { onEscape: () => setDockOpen(false) });
74885
75291
  if (hidden) return null;
74886
75292
  const handlers = {
@@ -74938,10 +75344,10 @@ function resolveActivePropertyPreset(propertyType, commercialSubtype, presets) {
74938
75344
  }
74939
75345
 
74940
75346
  // src/components/editor/InspectionSettingsSheet.tsx
74941
- import { useState as useState41, useEffect as useEffect34, useRef as useRef43, useCallback as useCallback33 } from "react";
75347
+ import { useState as useState41, useEffect as useEffect35, useRef as useRef44, useCallback as useCallback35 } from "react";
74942
75348
 
74943
75349
  // src/components/TemplateCombobox.tsx
74944
- import { useState as useState39, useEffect as useEffect33, useRef as useRef41, useCallback as useCallback31 } from "react";
75350
+ import { useState as useState39, useEffect as useEffect34, useRef as useRef42, useCallback as useCallback33 } from "react";
74945
75351
  import { Fragment as Fragment13, jsx as jsx80, jsxs as jsxs66 } from "react/jsx-runtime";
74946
75352
  function TemplateCombobox({
74947
75353
  value,
@@ -74956,23 +75362,23 @@ function TemplateCombobox({
74956
75362
  const [hasMore, setHasMore] = useState39(false);
74957
75363
  const [page, setPage] = useState39(1);
74958
75364
  const [activeIdx, setActiveIdx] = useState39(-1);
74959
- const inputRef = useRef41(null);
74960
- const containerRef = useRef41(null);
74961
- const debounceRef = useRef41(null);
75365
+ const inputRef = useRef42(null);
75366
+ const containerRef = useRef42(null);
75367
+ const debounceRef = useRef42(null);
74962
75368
  const templateSearch = useFieldDataSource().resources.templateSearch;
74963
75369
  const [isLoading, setIsLoading] = useState39(false);
74964
75370
  const [isLoadingMore, setIsLoadingMore] = useState39(false);
74965
- const searchSeq = useRef41(0);
74966
- const moreSeq = useRef41(0);
74967
- const mounted = useRef41(true);
74968
- useEffect33(() => {
75371
+ const searchSeq = useRef42(0);
75372
+ const moreSeq = useRef42(0);
75373
+ const mounted = useRef42(true);
75374
+ useEffect34(() => {
74969
75375
  mounted.current = true;
74970
75376
  return () => {
74971
75377
  mounted.current = false;
74972
75378
  };
74973
75379
  }, []);
74974
75380
  const selectedName = [...initialTemplates, ...results].find((t) => t.id === value)?.name ?? "";
74975
- const doSearch = useCallback31((q, pg) => {
75381
+ const doSearch = useCallback33((q, pg) => {
74976
75382
  if (!templateSearch) return;
74977
75383
  const seq = searchSeq.current += 1;
74978
75384
  moreSeq.current += 1;
@@ -74989,7 +75395,7 @@ function TemplateCombobox({
74989
75395
  if (mounted.current && seq === searchSeq.current) setIsLoading(false);
74990
75396
  });
74991
75397
  }, [templateSearch]);
74992
- useEffect33(() => {
75398
+ useEffect34(() => {
74993
75399
  if (!open) return;
74994
75400
  if (initialTemplates.length > 0 && !query) {
74995
75401
  setResults(initialTemplates);
@@ -75001,7 +75407,7 @@ function TemplateCombobox({
75001
75407
  setActiveIdx(-1);
75002
75408
  setTimeout(() => inputRef.current?.focus(), 0);
75003
75409
  }, [open]);
75004
- useEffect33(() => {
75410
+ useEffect34(() => {
75005
75411
  if (!open) return;
75006
75412
  if (debounceRef.current) clearTimeout(debounceRef.current);
75007
75413
  debounceRef.current = setTimeout(() => {
@@ -75013,7 +75419,7 @@ function TemplateCombobox({
75013
75419
  if (debounceRef.current) clearTimeout(debounceRef.current);
75014
75420
  };
75015
75421
  }, [query, open]);
75016
- useEffect33(() => {
75422
+ useEffect34(() => {
75017
75423
  if (!open) return;
75018
75424
  const handler = (e) => {
75019
75425
  if (!containerRef.current?.contains(e.target)) setOpen(false);
@@ -75136,7 +75542,7 @@ function TemplateCombobox({
75136
75542
  }
75137
75543
 
75138
75544
  // src/components/media-studio/PhotoCropper.tsx
75139
- import { useState as useState40, useCallback as useCallback32, useRef as useRef42 } from "react";
75545
+ import { useState as useState40, useCallback as useCallback34, useRef as useRef43 } from "react";
75140
75546
  import Cropper from "react-easy-crop";
75141
75547
  import { jsx as jsx81, jsxs as jsxs67 } from "react/jsx-runtime";
75142
75548
  var RATIOS = {
@@ -75172,14 +75578,14 @@ function PhotoCropper({
75172
75578
  const [rotation, setRotation] = useState40(0);
75173
75579
  const [pixels, setPixels] = useState40(null);
75174
75580
  const [busy, setBusy] = useState40(false);
75175
- const inFlight2 = useRef42(false);
75581
+ const inFlight2 = useRef43(false);
75176
75582
  const [bakeError, setBakeError] = useState40(null);
75177
75583
  const [sourceLongEdge, setSourceLongEdge] = useState40(void 0);
75178
75584
  const isFree = aspectKey === "free";
75179
75585
  const baseRatio = isFree ? void 0 : RATIOS[aspectKey];
75180
75586
  const ratio = baseRatio == null ? void 0 : portrait ? 1 / baseRatio : baseRatio;
75181
- const onCropComplete = useCallback32((_a, areaPixels) => setPixels(areaPixels), []);
75182
- const onMediaLoaded = useCallback32((mediaSize) => {
75587
+ const onCropComplete = useCallback34((_a, areaPixels) => setPixels(areaPixels), []);
75588
+ const onMediaLoaded = useCallback34((mediaSize) => {
75183
75589
  setSourceLongEdge(Math.max(mediaSize.naturalWidth, mediaSize.naturalHeight));
75184
75590
  }, []);
75185
75591
  async function handleSave() {
@@ -75342,14 +75748,14 @@ function InspectionSettingsSheet({ open, onClose, inspectionId, onTemplateApplie
75342
75748
  requireDefectFieldsOverride: "",
75343
75749
  profileOverride: ""
75344
75750
  });
75345
- const templateIdAtOpen = useRef43("");
75751
+ const templateIdAtOpen = useRef44("");
75346
75752
  const { run: runSave } = useFieldCommand(inspectionId);
75347
- const templateChangedAtSubmit = useRef43(false);
75348
- const mounted = useRef43(true);
75349
- const idleTimer = useRef43(void 0);
75350
- const onTemplateAppliedRef = useRef43(onTemplateApplied);
75753
+ const templateChangedAtSubmit = useRef44(false);
75754
+ const mounted = useRef44(true);
75755
+ const idleTimer = useRef44(void 0);
75756
+ const onTemplateAppliedRef = useRef44(onTemplateApplied);
75351
75757
  onTemplateAppliedRef.current = onTemplateApplied;
75352
- useEffect34(() => {
75758
+ useEffect35(() => {
75353
75759
  mounted.current = true;
75354
75760
  return () => {
75355
75761
  mounted.current = false;
@@ -75360,10 +75766,10 @@ function InspectionSettingsSheet({ open, onClose, inspectionId, onTemplateApplie
75360
75766
  const [coverKey, setCoverKey] = useState41("");
75361
75767
  const { run: runCover, busy: coverBusy } = useFieldCommand(inspectionId);
75362
75768
  const [uploadingCover, setUploadingCover] = useState41(false);
75363
- const coverFileRef = useRef43(null);
75769
+ const coverFileRef = useRef44(null);
75364
75770
  const [cropSource, setCropSource] = useState41(null);
75365
75771
  const [originalQuality, setOriginalQuality] = useState41(false);
75366
- useEffect34(() => {
75772
+ useEffect35(() => {
75367
75773
  try {
75368
75774
  setOriginalQuality(localStorage.getItem(ORIGINAL_QUALITY_KEY) === "1");
75369
75775
  } catch {
@@ -75378,7 +75784,7 @@ function InspectionSettingsSheet({ open, onClose, inspectionId, onTemplateApplie
75378
75784
  } catch {
75379
75785
  }
75380
75786
  }
75381
- const applyLoaded = useCallback33((d) => {
75787
+ const applyLoaded = useCallback35((d) => {
75382
75788
  const insp = d.inspection;
75383
75789
  if (insp) {
75384
75790
  const loadedTemplateId = insp.templateId || "";
@@ -75396,8 +75802,8 @@ function InspectionSettingsSheet({ open, onClose, inspectionId, onTemplateApplie
75396
75802
  setCoverKey(insp?.coverPhotoId ?? insp?.coverPhoto ?? "");
75397
75803
  setLoading(false);
75398
75804
  }, []);
75399
- const loadSeq = useRef43(0);
75400
- const loadSheet = useCallback33(() => {
75805
+ const loadSeq = useRef44(0);
75806
+ const loadSheet = useCallback35(() => {
75401
75807
  const seq = loadSeq.current += 1;
75402
75808
  setLoading(true);
75403
75809
  setLoadFailed(false);
@@ -75411,7 +75817,7 @@ function InspectionSettingsSheet({ open, onClose, inspectionId, onTemplateApplie
75411
75817
  applyLoaded(d);
75412
75818
  });
75413
75819
  }, [sheet, inspectionId, applyLoaded]);
75414
- useEffect34(() => {
75820
+ useEffect35(() => {
75415
75821
  if (open && inspectionId) loadSheet();
75416
75822
  return () => {
75417
75823
  loadSeq.current += 1;
@@ -75644,7 +76050,7 @@ function InspectionSettingsSheet({ open, onClose, inspectionId, onTemplateApplie
75644
76050
  }
75645
76051
 
75646
76052
  // src/components/media-studio/VideoCapture.tsx
75647
- import { useRef as useRef44, useState as useState42 } from "react";
76053
+ import { useRef as useRef45, useState as useState42 } from "react";
75648
76054
  import { jsx as jsx84, jsxs as jsxs69 } from "react/jsx-runtime";
75649
76055
  var MAX_VIDEO_BYTES = 200 * 1024 * 1024;
75650
76056
  var MAX_VIDEO_SEC = 30;
@@ -75660,7 +76066,7 @@ function validateVideoFile(file) {
75660
76066
  }
75661
76067
  function VideoCapture({ inspectionId, provider, itemId, onClose, onUploaded }) {
75662
76068
  const video = useFieldDataSource().resources.video;
75663
- const inputRef = useRef44(null);
76069
+ const inputRef = useRef45(null);
75664
76070
  const [phase, setPhase] = useState42("pick");
75665
76071
  const [error, setError] = useState42(null);
75666
76072
  const [progress, setProgress] = useState42(0);
@@ -76081,7 +76487,7 @@ function AddMediaChooser({ onClose, onTakePhoto, onAddFromLibrary, onPickVideo }
76081
76487
  }
76082
76488
 
76083
76489
  // src/hooks/useStructureEdit.ts
76084
- import { useState as useState43, useCallback as useCallback35, useEffect as useEffect35, useRef as useRef45 } from "react";
76490
+ import { useState as useState43, useCallback as useCallback37, useEffect as useEffect36, useRef as useRef46 } from "react";
76085
76491
 
76086
76492
  // src/shared/structure-ops.ts
76087
76493
  var SECTION_KEYS = /* @__PURE__ */ new Set([
@@ -76140,8 +76546,8 @@ function buildNewItem(label, type) {
76140
76546
  }
76141
76547
  return { id, label, type };
76142
76548
  }
76143
- function stripRuntimeKeys(snapshot) {
76144
- const clone = structuredClone(snapshot);
76549
+ function stripRuntimeKeys(snapshot2) {
76550
+ const clone = structuredClone(snapshot2);
76145
76551
  clone.sections = clone.sections.map((rawSec) => {
76146
76552
  const sec = {};
76147
76553
  for (const key of SECTION_KEYS) {
@@ -76165,20 +76571,20 @@ function stripRuntimeKeys(snapshot) {
76165
76571
  });
76166
76572
  return clone;
76167
76573
  }
76168
- function addSection(snapshot, title) {
76574
+ function addSection(snapshot2, title) {
76169
76575
  const newSec = { id: newId("sec"), title, items: [] };
76170
76576
  const result = {
76171
- ...snapshot,
76172
- sections: [...snapshot.sections, newSec]
76577
+ ...snapshot2,
76578
+ sections: [...snapshot2.sections, newSec]
76173
76579
  };
76174
76580
  return stripRuntimeKeys(result);
76175
76581
  }
76176
- function duplicateSection(snapshot, sectionId) {
76177
- const idx = snapshot.sections.findIndex((s) => s.id === sectionId);
76582
+ function duplicateSection(snapshot2, sectionId) {
76583
+ const idx = snapshot2.sections.findIndex((s) => s.id === sectionId);
76178
76584
  if (idx === -1) {
76179
- return stripRuntimeKeys({ ...snapshot, sections: [...snapshot.sections] });
76585
+ return stripRuntimeKeys({ ...snapshot2, sections: [...snapshot2.sections] });
76180
76586
  }
76181
- const source = snapshot.sections[idx];
76587
+ const source = snapshot2.sections[idx];
76182
76588
  const idMap = new Map(source.items.map((item) => [item.id, newId("item")]));
76183
76589
  const renamed = source.items.map((item) => ({
76184
76590
  ...item,
@@ -76191,74 +76597,74 @@ function duplicateSection(snapshot, sectionId) {
76191
76597
  items: clonedItems
76192
76598
  };
76193
76599
  const sections = [
76194
- ...snapshot.sections.slice(0, idx + 1),
76600
+ ...snapshot2.sections.slice(0, idx + 1),
76195
76601
  cloned,
76196
- ...snapshot.sections.slice(idx + 1)
76602
+ ...snapshot2.sections.slice(idx + 1)
76197
76603
  ];
76198
- return stripRuntimeKeys({ ...snapshot, sections });
76604
+ return stripRuntimeKeys({ ...snapshot2, sections });
76199
76605
  }
76200
- function deleteSection(snapshot, sectionId) {
76201
- const sections = snapshot.sections.filter((s) => s.id !== sectionId);
76202
- return stripRuntimeKeys({ ...snapshot, sections });
76606
+ function deleteSection(snapshot2, sectionId) {
76607
+ const sections = snapshot2.sections.filter((s) => s.id !== sectionId);
76608
+ return stripRuntimeKeys({ ...snapshot2, sections });
76203
76609
  }
76204
- function moveSection(snapshot, sectionId, dir) {
76205
- const idx = snapshot.sections.findIndex((s) => s.id === sectionId);
76610
+ function moveSection(snapshot2, sectionId, dir) {
76611
+ const idx = snapshot2.sections.findIndex((s) => s.id === sectionId);
76206
76612
  if (idx === -1) {
76207
- return stripRuntimeKeys({ ...snapshot, sections: [...snapshot.sections] });
76613
+ return stripRuntimeKeys({ ...snapshot2, sections: [...snapshot2.sections] });
76208
76614
  }
76209
76615
  const targetIdx = idx + dir;
76210
- if (targetIdx < 0 || targetIdx >= snapshot.sections.length) {
76211
- return stripRuntimeKeys({ ...snapshot, sections: [...snapshot.sections] });
76616
+ if (targetIdx < 0 || targetIdx >= snapshot2.sections.length) {
76617
+ return stripRuntimeKeys({ ...snapshot2, sections: [...snapshot2.sections] });
76212
76618
  }
76213
- const sections = [...snapshot.sections];
76619
+ const sections = [...snapshot2.sections];
76214
76620
  [sections[idx], sections[targetIdx]] = [sections[targetIdx], sections[idx]];
76215
- return stripRuntimeKeys({ ...snapshot, sections });
76621
+ return stripRuntimeKeys({ ...snapshot2, sections });
76216
76622
  }
76217
- function reorderSection(snapshot, fromId, toId) {
76218
- const from = snapshot.sections.findIndex((s) => s.id === fromId);
76219
- const to = snapshot.sections.findIndex((s) => s.id === toId);
76623
+ function reorderSection(snapshot2, fromId, toId) {
76624
+ const from = snapshot2.sections.findIndex((s) => s.id === fromId);
76625
+ const to = snapshot2.sections.findIndex((s) => s.id === toId);
76220
76626
  if (from === -1 || to === -1 || from === to) {
76221
- return stripRuntimeKeys({ ...snapshot, sections: [...snapshot.sections] });
76627
+ return stripRuntimeKeys({ ...snapshot2, sections: [...snapshot2.sections] });
76222
76628
  }
76223
- const sections = [...snapshot.sections];
76629
+ const sections = [...snapshot2.sections];
76224
76630
  const [moved] = sections.splice(from, 1);
76225
76631
  sections.splice(to, 0, moved);
76226
- return stripRuntimeKeys({ ...snapshot, sections });
76632
+ return stripRuntimeKeys({ ...snapshot2, sections });
76227
76633
  }
76228
- function renameSection(snapshot, sectionId, title) {
76229
- const sections = snapshot.sections.map((s) => s.id === sectionId ? { ...s, title } : s);
76230
- return stripRuntimeKeys({ ...snapshot, sections });
76634
+ function renameSection(snapshot2, sectionId, title) {
76635
+ const sections = snapshot2.sections.map((s) => s.id === sectionId ? { ...s, title } : s);
76636
+ return stripRuntimeKeys({ ...snapshot2, sections });
76231
76637
  }
76232
- function mapItems(snapshot, sectionId, fn) {
76233
- const sections = snapshot.sections.map((sec) => sec.id !== sectionId ? sec : { ...sec, items: fn(sec.items) });
76234
- return stripRuntimeKeys({ ...snapshot, sections });
76638
+ function mapItems(snapshot2, sectionId, fn) {
76639
+ const sections = snapshot2.sections.map((sec) => sec.id !== sectionId ? sec : { ...sec, items: fn(sec.items) });
76640
+ return stripRuntimeKeys({ ...snapshot2, sections });
76235
76641
  }
76236
- function addItem(snapshot, sectionId, label, type, seed = {}) {
76237
- return mapItems(snapshot, sectionId, (items) => [...items, { ...buildNewItem(label, type), ...seed, parentId: null }]);
76642
+ function addItem(snapshot2, sectionId, label, type, seed = {}) {
76643
+ return mapItems(snapshot2, sectionId, (items) => [...items, { ...buildNewItem(label, type), ...seed, parentId: null }]);
76238
76644
  }
76239
- function duplicateItem(snapshot, sectionId, itemId) {
76240
- return mapItems(snapshot, sectionId, (items) => duplicateSubtree(items, itemId, () => newId("item")));
76645
+ function duplicateItem(snapshot2, sectionId, itemId) {
76646
+ return mapItems(snapshot2, sectionId, (items) => duplicateSubtree(items, itemId, () => newId("item")));
76241
76647
  }
76242
- function deleteItem(snapshot, sectionId, itemId) {
76243
- return mapItems(snapshot, sectionId, (items) => deleteSubtree(items, itemId));
76648
+ function deleteItem(snapshot2, sectionId, itemId) {
76649
+ return mapItems(snapshot2, sectionId, (items) => deleteSubtree(items, itemId));
76244
76650
  }
76245
- function moveItem(snapshot, sectionId, itemId, dir) {
76246
- return mapItems(snapshot, sectionId, (items) => moveSubtreeAmongSiblings(items, itemId, dir));
76651
+ function moveItem(snapshot2, sectionId, itemId, dir) {
76652
+ return mapItems(snapshot2, sectionId, (items) => moveSubtreeAmongSiblings(items, itemId, dir));
76247
76653
  }
76248
- function renameItem(snapshot, sectionId, itemId, label) {
76249
- const sections = snapshot.sections.map((sec) => {
76654
+ function renameItem(snapshot2, sectionId, itemId, label) {
76655
+ const sections = snapshot2.sections.map((sec) => {
76250
76656
  if (sec.id !== sectionId) return sec;
76251
76657
  return { ...sec, items: sec.items.map((it) => it.id === itemId ? { ...it, label } : it) };
76252
76658
  });
76253
- return stripRuntimeKeys({ ...snapshot, sections });
76659
+ return stripRuntimeKeys({ ...snapshot2, sections });
76254
76660
  }
76255
76661
 
76256
76662
  // src/hooks/useRestructureSubmit.ts
76257
- import { useCallback as useCallback34 } from "react";
76663
+ import { useCallback as useCallback36 } from "react";
76258
76664
  function useRestructureSubmit(inspectionId) {
76259
76665
  const { submit } = useLatestWinsCommand(inspectionId);
76260
76666
  const { notify } = useFieldDataSource();
76261
- return useCallback34(
76667
+ return useCallback36(
76262
76668
  async (cmd) => {
76263
76669
  const result = await submit(cmd);
76264
76670
  if (!isIgnored(result) && !result.ok) notify(result.error || index_exports.editor_structure_save_failed(), "error");
@@ -76278,15 +76684,15 @@ function useStructureEdit({
76278
76684
  activeUnitId = null,
76279
76685
  onApply
76280
76686
  }) {
76281
- const snapshotRef = useRef45(rawSnapshot);
76282
- useEffect35(() => {
76687
+ const snapshotRef = useRef46(rawSnapshot);
76688
+ useEffect36(() => {
76283
76689
  snapshotRef.current = rawSnapshot;
76284
76690
  }, [rawSnapshot]);
76285
76691
  const submitRestructure = useRestructureSubmit(inspectionId);
76286
76692
  const { run: saveTemplate } = useFieldCommand(inspectionId);
76287
- const onApplyRef = useRef45(onApply);
76693
+ const onApplyRef = useRef46(onApply);
76288
76694
  onApplyRef.current = onApply;
76289
- const applyStructure = useCallback35(
76695
+ const applyStructure = useCallback37(
76290
76696
  (next) => {
76291
76697
  onApplyRef.current?.(next);
76292
76698
  snapshotRef.current = next;
@@ -76295,7 +76701,7 @@ function useStructureEdit({
76295
76701
  [submitRestructure, collabEditing]
76296
76702
  );
76297
76703
  const [deletePending, setDeletePending] = useState43(null);
76298
- const itemImpact = useCallback35(
76704
+ const itemImpact = useCallback37(
76299
76705
  (sectionId, itemId) => {
76300
76706
  const ck = findingKey(activeUnitId, sectionId, itemId);
76301
76707
  const r = results[ck] || (activeUnitId == null ? results[itemId] : void 0);
@@ -76308,7 +76714,7 @@ function useStructureEdit({
76308
76714
  },
76309
76715
  [results, activeUnitId]
76310
76716
  );
76311
- const handleDeleteSection = useCallback35(
76717
+ const handleDeleteSection = useCallback37(
76312
76718
  (id) => {
76313
76719
  const sec = snapshotRef.current.sections.find((s) => s.id === id);
76314
76720
  if (!sec) return;
@@ -76331,7 +76737,7 @@ function useStructureEdit({
76331
76737
  },
76332
76738
  [itemImpact]
76333
76739
  );
76334
- const handleDeleteItem = useCallback35(
76740
+ const handleDeleteItem = useCallback37(
76335
76741
  (sectionId, itemId) => {
76336
76742
  const sec = snapshotRef.current.sections.find((s) => s.id === sectionId);
76337
76743
  const items = sec?.items ?? [];
@@ -76349,7 +76755,7 @@ function useStructureEdit({
76349
76755
  },
76350
76756
  [itemImpact]
76351
76757
  );
76352
- const confirmDelete = useCallback35(() => {
76758
+ const confirmDelete = useCallback37(() => {
76353
76759
  const pending = deletePending;
76354
76760
  setDeletePending(null);
76355
76761
  if (!pending) return;
@@ -76359,74 +76765,74 @@ function useStructureEdit({
76359
76765
  applyStructure(deleteSection(snapshotRef.current, pending.sectionId));
76360
76766
  }
76361
76767
  }, [deletePending, applyStructure]);
76362
- const cancelDelete = useCallback35(() => {
76768
+ const cancelDelete = useCallback37(() => {
76363
76769
  setDeletePending(null);
76364
76770
  }, []);
76365
76771
  const [addSectionPromptOpen, setAddSectionPromptOpen] = useState43(false);
76366
76772
  const [addSectionTitle, setAddSectionTitle] = useState43("");
76367
- const openAddSectionPrompt = useCallback35(() => {
76773
+ const openAddSectionPrompt = useCallback37(() => {
76368
76774
  setAddSectionTitle("");
76369
76775
  setAddSectionPromptOpen(true);
76370
76776
  }, []);
76371
- const closeAddSectionPrompt = useCallback35(() => {
76777
+ const closeAddSectionPrompt = useCallback37(() => {
76372
76778
  setAddSectionPromptOpen(false);
76373
76779
  }, []);
76374
- const submitAddSection = useCallback35(() => {
76780
+ const submitAddSection = useCallback37(() => {
76375
76781
  const title = addSectionTitle.trim() || index_exports.helper_structure_new_section_default();
76376
76782
  setAddSectionPromptOpen(false);
76377
76783
  setAddSectionTitle("");
76378
76784
  applyStructure(addSection(snapshotRef.current, title));
76379
76785
  }, [addSectionTitle, applyStructure]);
76380
- const handleDuplicateSection = useCallback35(
76786
+ const handleDuplicateSection = useCallback37(
76381
76787
  (id) => {
76382
76788
  applyStructure(duplicateSection(snapshotRef.current, id));
76383
76789
  },
76384
76790
  [applyStructure]
76385
76791
  );
76386
- const handleMoveSection = useCallback35(
76792
+ const handleMoveSection = useCallback37(
76387
76793
  (id, dir) => {
76388
76794
  applyStructure(moveSection(snapshotRef.current, id, dir));
76389
76795
  },
76390
76796
  [applyStructure]
76391
76797
  );
76392
- const handleReorderSection = useCallback35(
76798
+ const handleReorderSection = useCallback37(
76393
76799
  (fromId, toId) => {
76394
76800
  applyStructure(reorderSection(snapshotRef.current, fromId, toId));
76395
76801
  },
76396
76802
  [applyStructure]
76397
76803
  );
76398
- const handleRenameSection = useCallback35(
76804
+ const handleRenameSection = useCallback37(
76399
76805
  (id, title) => {
76400
76806
  applyStructure(renameSection(snapshotRef.current, id, title));
76401
76807
  },
76402
76808
  [applyStructure]
76403
76809
  );
76404
- const handleDuplicateItem = useCallback35(
76810
+ const handleDuplicateItem = useCallback37(
76405
76811
  (sectionId, itemId) => {
76406
76812
  applyStructure(duplicateItem(snapshotRef.current, sectionId, itemId));
76407
76813
  },
76408
76814
  [applyStructure]
76409
76815
  );
76410
- const handleMoveItem = useCallback35(
76816
+ const handleMoveItem = useCallback37(
76411
76817
  (sectionId, itemId, dir) => {
76412
76818
  applyStructure(moveItem(snapshotRef.current, sectionId, itemId, dir));
76413
76819
  },
76414
76820
  [applyStructure]
76415
76821
  );
76416
- const handleRenameItem = useCallback35(
76822
+ const handleRenameItem = useCallback37(
76417
76823
  (sectionId, itemId, label) => {
76418
76824
  applyStructure(renameItem(snapshotRef.current, sectionId, itemId, label));
76419
76825
  },
76420
76826
  [applyStructure]
76421
76827
  );
76422
76828
  const [addItemPending, setAddItemPending] = useState43(null);
76423
- const openAddItemPrompt = useCallback35((sectionId) => {
76829
+ const openAddItemPrompt = useCallback37((sectionId) => {
76424
76830
  setAddItemPending({ sectionId });
76425
76831
  }, []);
76426
- const closeAddItemPrompt = useCallback35(() => {
76832
+ const closeAddItemPrompt = useCallback37(() => {
76427
76833
  setAddItemPending(null);
76428
76834
  }, []);
76429
- const submitAddItem = useCallback35(
76835
+ const submitAddItem = useCallback37(
76430
76836
  (label, type) => {
76431
76837
  const pending = addItemPending;
76432
76838
  setAddItemPending(null);
@@ -76438,25 +76844,25 @@ function useStructureEdit({
76438
76844
  );
76439
76845
  const [saveTemplatePending, setSaveTemplatePending] = useState43(null);
76440
76846
  const [saveTemplateName, setSaveTemplateName] = useState43("");
76441
- const openSaveTemplate = useCallback35((mode) => {
76847
+ const openSaveTemplate = useCallback37((mode) => {
76442
76848
  setSaveTemplateName("");
76443
76849
  setSaveTemplatePending({ mode });
76444
76850
  }, []);
76445
- const closeSaveTemplate = useCallback35(() => {
76851
+ const closeSaveTemplate = useCallback37(() => {
76446
76852
  setSaveTemplatePending(null);
76447
76853
  }, []);
76448
- const submitSaveTemplate = useCallback35(() => {
76854
+ const submitSaveTemplate = useCallback37(() => {
76449
76855
  const pending = saveTemplatePending;
76450
76856
  setSaveTemplatePending(null);
76451
76857
  if (!pending) return;
76452
- const snapshot = snapshotRef.current;
76858
+ const snapshot2 = snapshotRef.current;
76453
76859
  void saveTemplate(
76454
76860
  pending.mode === "new" ? {
76455
76861
  type: "save-structure-template",
76456
76862
  mode: "new",
76457
- snapshot,
76863
+ snapshot: snapshot2,
76458
76864
  name: saveTemplateName.trim() || index_exports.helper_structure_custom_template_default()
76459
- } : { type: "save-structure-template", mode: "back", snapshot, templateId: templateId ?? "" }
76865
+ } : { type: "save-structure-template", mode: "back", snapshot: snapshot2, templateId: templateId ?? "" }
76460
76866
  );
76461
76867
  }, [saveTemplatePending, saveTemplateName, templateId, saveTemplate]);
76462
76868
  return {
@@ -76498,7 +76904,7 @@ function useStructureEdit({
76498
76904
  }
76499
76905
 
76500
76906
  // src/components/editor/PublishModal.tsx
76501
- import { useEffect as useEffect36, useRef as useRef46, useState as useState44 } from "react";
76907
+ import { useEffect as useEffect37, useRef as useRef47, useState as useState44 } from "react";
76502
76908
 
76503
76909
  // src/shared/display-prefs.ts
76504
76910
  var DEFAULT_DISPLAY_PREFS = { dateFormat: "us", timeFormat: "12h" };
@@ -76568,15 +76974,15 @@ function PublishModal({ open, inspectionId, progress, status, publishError, isSu
76568
76974
  const [receiptOpen, setReceiptOpen] = useState44(false);
76569
76975
  const latch = useActionLatch(isSubmitting);
76570
76976
  const publish = (markComplete) => latch(() => onPublish(markComplete, extras()));
76571
- const submitSeen = useRef46(false);
76977
+ const submitSeen = useRef47(false);
76572
76978
  const [inspectionHref, setInspectionHref] = useState44(null);
76573
- useEffect36(() => {
76979
+ useEffect37(() => {
76574
76980
  if (isSubmitting) submitSeen.current = true;
76575
76981
  }, [isSubmitting]);
76576
- useEffect36(() => {
76982
+ useEffect37(() => {
76577
76983
  if (publishError) submitSeen.current = false;
76578
76984
  }, [publishError]);
76579
- useEffect36(() => {
76985
+ useEffect37(() => {
76580
76986
  if (open || !submitSeen.current) return;
76581
76987
  submitSeen.current = false;
76582
76988
  if (publishError) return;
@@ -76709,16 +77115,17 @@ function PublishModal({ open, inspectionId, progress, status, publishError, isSu
76709
77115
  }
76710
77116
 
76711
77117
  // src/components/SignaturePad.tsx
76712
- import { useRef as useRef47, useState as useState45, useEffect as useEffect37 } from "react";
77118
+ import { useRef as useRef48, useState as useState45, useEffect as useEffect38 } from "react";
76713
77119
  import { jsx as jsx88, jsxs as jsxs73 } from "react/jsx-runtime";
77120
+ var PAPER = { backgroundColor: "#ffffff", borderColor: "#cbd5e1" };
76714
77121
  function SignaturePad({ onSubmit, onCancel, label }) {
76715
- const canvasRef = useRef47(null);
77122
+ const canvasRef = useRef48(null);
76716
77123
  const [drawing, setDrawing] = useState45(false);
76717
77124
  const [hasInk, setHasInk] = useState45(false);
76718
77125
  const [busy, setBusy] = useState45(false);
76719
- const inFlight2 = useRef47(false);
77126
+ const inFlight2 = useRef48(false);
76720
77127
  const [error, setError] = useState45(null);
76721
- useEffect37(() => {
77128
+ useEffect38(() => {
76722
77129
  const canvas = canvasRef.current;
76723
77130
  if (!canvas) return;
76724
77131
  const ctx = canvas.getContext("2d");
@@ -76731,7 +77138,9 @@ function SignaturePad({ onSubmit, onCancel, label }) {
76731
77138
  const canvas = canvasRef.current;
76732
77139
  const rect = canvas.getBoundingClientRect();
76733
77140
  const point = "touches" in e ? e.touches[0] : e;
76734
- return { x: point.clientX - rect.left, y: point.clientY - rect.top };
77141
+ const sx = rect.width > 0 ? canvas.width / rect.width : 1;
77142
+ const sy = rect.height > 0 ? canvas.height / rect.height : 1;
77143
+ return { x: (point.clientX - rect.left) * sx, y: (point.clientY - rect.top) * sy };
76735
77144
  };
76736
77145
  const start = (e) => {
76737
77146
  const ctx = canvasRef.current?.getContext("2d");
@@ -76784,7 +77193,8 @@ function SignaturePad({ onSubmit, onCancel, label }) {
76784
77193
  ref: canvasRef,
76785
77194
  width: 400,
76786
77195
  height: 150,
76787
- className: "border border-ih-border bg-ih-bg-card rounded cursor-crosshair touch-none",
77196
+ style: PAPER,
77197
+ className: "block w-full max-w-[400px] border rounded cursor-crosshair touch-none",
76788
77198
  onMouseDown: start,
76789
77199
  onMouseMove: move,
76790
77200
  onMouseUp: end,
@@ -76839,7 +77249,7 @@ function SignModal({ open, onSubmit, onCancel, failed }) {
76839
77249
  }
76840
77250
 
76841
77251
  // src/components/collab/VersionHistoryPanel.tsx
76842
- import { useCallback as useCallback37, useEffect as useEffect39, useState as useState46 } from "react";
77252
+ import { useCallback as useCallback39, useEffect as useEffect40, useState as useState46 } from "react";
76843
77253
 
76844
77254
  // src/shared/snapshot-diff.ts
76845
77255
  var SCALAR_FIELDS = [
@@ -77123,20 +77533,20 @@ function VersionCompare({
77123
77533
  }
77124
77534
 
77125
77535
  // src/components/collab/useReturnFocusAfterCompare.ts
77126
- import { useCallback as useCallback36, useEffect as useEffect38, useRef as useRef48 } from "react";
77536
+ import { useCallback as useCallback38, useEffect as useEffect39, useRef as useRef49 } from "react";
77127
77537
  function useReturnFocusAfterCompare(confirmOpen) {
77128
- const opener = useRef48(null);
77129
- const fromCompare = useRef48(false);
77130
- useEffect38(() => {
77538
+ const opener = useRef49(null);
77539
+ const fromCompare = useRef49(false);
77540
+ useEffect39(() => {
77131
77541
  if (confirmOpen || !fromCompare.current) return;
77132
77542
  fromCompare.current = false;
77133
77543
  const active = document.activeElement;
77134
77544
  if (opener.current?.isConnected && (!active || active === document.body)) opener.current.focus();
77135
77545
  }, [confirmOpen]);
77136
- const rememberOpener = useCallback36((el) => {
77546
+ const rememberOpener = useCallback38((el) => {
77137
77547
  opener.current = el;
77138
77548
  }, []);
77139
- const markFromCompare = useCallback36(() => {
77549
+ const markFromCompare = useCallback38(() => {
77140
77550
  fromCompare.current = true;
77141
77551
  }, []);
77142
77552
  return { rememberOpener, markFromCompare };
@@ -77208,7 +77618,7 @@ function VersionHistoryPanel({
77208
77618
  dateFormat: session.tenantDateFormat,
77209
77619
  timeFormat: session.tenantTimeFormat
77210
77620
  };
77211
- const handleCompare = useCallback37(
77621
+ const handleCompare = useCallback39(
77212
77622
  async (seq) => {
77213
77623
  if (!currentResults) return;
77214
77624
  setCompareSeq(seq);
@@ -77231,7 +77641,7 @@ function VersionHistoryPanel({
77231
77641
  },
77232
77642
  [versionHistory, inspectionId, currentResults]
77233
77643
  );
77234
- const handleRecoverField = useCallback37(
77644
+ const handleRecoverField = useCallback39(
77235
77645
  (findingKey3, field, value) => {
77236
77646
  if (!doc) return;
77237
77647
  if (!SCALAR_FIELD_SET.has(field)) return;
@@ -77253,7 +77663,7 @@ function VersionHistoryPanel({
77253
77663
  },
77254
77664
  [doc, currentResults]
77255
77665
  );
77256
- const loadSnapshots = useCallback37(async () => {
77666
+ const loadSnapshots = useCallback39(async () => {
77257
77667
  setLoadState("loading");
77258
77668
  try {
77259
77669
  const listed = await versionHistory?.list(inspectionId);
@@ -77265,7 +77675,7 @@ function VersionHistoryPanel({
77265
77675
  setLoadState("error");
77266
77676
  }
77267
77677
  }, [versionHistory, inspectionId]);
77268
- useEffect39(() => {
77678
+ useEffect40(() => {
77269
77679
  if (!open) return;
77270
77680
  setConfirmSeq(null);
77271
77681
  setRestoreError(null);
@@ -77519,20 +77929,20 @@ function AddGroupInstanceHost({
77519
77929
  }
77520
77930
 
77521
77931
  // src/components/editor/BurstCamera.tsx
77522
- import { useState as useState48, useRef as useRef49, useCallback as useCallback38, useEffect as useEffect40 } from "react";
77932
+ import { useState as useState48, useRef as useRef50, useCallback as useCallback40, useEffect as useEffect41 } from "react";
77523
77933
  import { jsx as jsx94, jsxs as jsxs78 } from "react/jsx-runtime";
77524
77934
  function BurstCamera({ open, onClose, onCommit }) {
77525
- const videoRef = useRef49(null);
77526
- const canvasRef = useRef49(null);
77527
- const streamRef = useRef49(null);
77528
- const burstTimerRef = useRef49(null);
77935
+ const videoRef = useRef50(null);
77936
+ const canvasRef = useRef50(null);
77937
+ const streamRef = useRef50(null);
77938
+ const burstTimerRef = useRef50(null);
77529
77939
  const [captures, setCaptures] = useState48([]);
77530
77940
  const [burstActive, setBurstActive] = useState48(false);
77531
77941
  const [burstCount, setBurstCount] = useState48(0);
77532
77942
  const [uploading, setUploading] = useState48(false);
77533
- const committing = useRef49(false);
77943
+ const committing = useRef50(false);
77534
77944
  const [facing, setFacing] = useState48("environment");
77535
- const startCamera = useCallback38(async (facingMode) => {
77945
+ const startCamera = useCallback40(async (facingMode) => {
77536
77946
  try {
77537
77947
  const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode } });
77538
77948
  streamRef.current = stream;
@@ -77541,11 +77951,11 @@ function BurstCamera({ open, onClose, onCommit }) {
77541
77951
  onClose();
77542
77952
  }
77543
77953
  }, [onClose]);
77544
- const stopCamera = useCallback38(() => {
77954
+ const stopCamera = useCallback40(() => {
77545
77955
  streamRef.current?.getTracks().forEach((t) => t.stop());
77546
77956
  streamRef.current = null;
77547
77957
  }, []);
77548
- useEffect40(() => {
77958
+ useEffect41(() => {
77549
77959
  if (open) {
77550
77960
  startCamera(facing);
77551
77961
  } else {
@@ -77673,7 +78083,7 @@ function BurstCamera({ open, onClose, onCommit }) {
77673
78083
  }
77674
78084
 
77675
78085
  // src/components/media-studio/PhotoAnnotator.tsx
77676
- import { useState as useState50, useEffect as useEffect42, useCallback as useCallback40, useRef as useRef50, Fragment as Fragment19 } from "react";
78086
+ import { useState as useState50, useEffect as useEffect43, useCallback as useCallback42, useRef as useRef51, Fragment as Fragment19 } from "react";
77677
78087
  import { Stage, Layer, Image as KonvaImage, Circle, Arrow, Line, Label, Tag, Text } from "react-konva";
77678
78088
 
77679
78089
  // src/components/media-studio/annotations.ts
@@ -77877,7 +78287,7 @@ function MeasureCalibration({
77877
78287
  }
77878
78288
 
77879
78289
  // src/components/media-studio/usePhotoSource.ts
77880
- import { useCallback as useCallback39, useEffect as useEffect41, useState as useState49 } from "react";
78290
+ import { useCallback as useCallback41, useEffect as useEffect42, useState as useState49 } from "react";
77881
78291
  var FALLBACK = { w: 600, h: 400 };
77882
78292
  function usePhotoSource(photoUrl, enabled) {
77883
78293
  const [image, setImage] = useState49(null);
@@ -77885,7 +78295,7 @@ function usePhotoSource(photoUrl, enabled) {
77885
78295
  const [fitScale, setFitScale] = useState49(1);
77886
78296
  const [failed, setFailed] = useState49(false);
77887
78297
  const [attempt, setAttempt] = useState49(0);
77888
- useEffect41(() => {
78298
+ useEffect42(() => {
77889
78299
  if (!enabled) return;
77890
78300
  setFailed(false);
77891
78301
  setImage(null);
@@ -77916,7 +78326,7 @@ function usePhotoSource(photoUrl, enabled) {
77916
78326
  cancelled = true;
77917
78327
  };
77918
78328
  }, [enabled, photoUrl, attempt]);
77919
- const retry = useCallback39(() => setAttempt((n) => n + 1), []);
78329
+ const retry = useCallback41(() => setAttempt((n) => n + 1), []);
77920
78330
  return { image, natural, fitScale, failed, retry };
77921
78331
  }
77922
78332
 
@@ -77937,7 +78347,7 @@ function PhotoAnnotator({
77937
78347
  onClose
77938
78348
  }) {
77939
78349
  const [mounted, setMounted] = useState50(false);
77940
- useEffect42(() => setMounted(true), []);
78350
+ useEffect43(() => setMounted(true), []);
77941
78351
  const [tool, setTool] = useState50("circle");
77942
78352
  const [zoom, setZoom] = useState50(1);
77943
78353
  const [annotations, setAnnotations] = useState50([]);
@@ -77952,16 +78362,16 @@ function PhotoAnnotator({
77952
78362
  const [isDrawingFreehand, setIsDrawingFreehand] = useState50(false);
77953
78363
  const [labelInput, setLabelInput] = useState50(null);
77954
78364
  const [labelText, setLabelText] = useState50("");
77955
- const labelInputRef = useRef50(null);
78365
+ const labelInputRef = useRef51(null);
77956
78366
  const [showCalibration, setShowCalibration] = useState50(false);
77957
78367
  const [pxPerUnit, setPxPerUnit] = useState50(null);
77958
78368
  const [calibLine, setCalibLine] = useState50(null);
77959
78369
  const [calibKnown, setCalibKnown] = useState50("");
77960
78370
  const [calibUnit, setCalibUnit] = useState50("in");
77961
78371
  const [measures, setMeasures] = useState50([]);
77962
- const stageRef = useRef50(null);
78372
+ const stageRef = useRef51(null);
77963
78373
  const scale = fitScale * zoom;
77964
- useEffect42(() => {
78374
+ useEffect43(() => {
77965
78375
  if (!open) return;
77966
78376
  const doc = deserializeMeasureDoc(initialAnnotationsJson);
77967
78377
  setAnnotations(doc.annotations.filter((a) => a.kind !== "measure"));
@@ -77983,7 +78393,7 @@ function PhotoAnnotator({
77983
78393
  setCalibKnown("");
77984
78394
  setCalibUnit(doc.calibration?.calibUnit ?? "in");
77985
78395
  }, [open, sectionName, initialAnnotationsJson]);
77986
- useEffect42(() => {
78396
+ useEffect43(() => {
77987
78397
  if (labelInput) {
77988
78398
  const t = setTimeout(() => labelInputRef.current?.focus(), 50);
77989
78399
  return () => clearTimeout(t);
@@ -77994,14 +78404,14 @@ function PhotoAnnotator({
77994
78404
  // stack hears one the label box is already gone: this is always "close the annotator".
77995
78405
  onEscape: onClose
77996
78406
  });
77997
- const naturalPointer = useCallback40(() => {
78407
+ const naturalPointer = useCallback42(() => {
77998
78408
  const stage = stageRef.current;
77999
78409
  if (!stage) return null;
78000
78410
  const p = stage.getPointerPosition();
78001
78411
  if (!p) return null;
78002
78412
  return { x: p.x / scale, y: p.y / scale };
78003
78413
  }, [scale]);
78004
- const handleStageClick = useCallback40(() => {
78414
+ const handleStageClick = useCallback42(() => {
78005
78415
  if (labelInput) return;
78006
78416
  const pos = naturalPointer();
78007
78417
  if (!pos) return;
@@ -78035,20 +78445,20 @@ function PhotoAnnotator({
78035
78445
  }
78036
78446
  }
78037
78447
  }, [tool, arrowStart, measureStart, pxPerUnit, naturalPointer, labelInput]);
78038
- const handleMouseDown = useCallback40(() => {
78448
+ const handleMouseDown = useCallback42(() => {
78039
78449
  if (tool !== "free" || labelInput) return;
78040
78450
  const pos = naturalPointer();
78041
78451
  if (!pos) return;
78042
78452
  setFreehandPoints([pos]);
78043
78453
  setIsDrawingFreehand(true);
78044
78454
  }, [tool, labelInput, naturalPointer]);
78045
- const handleMouseMove = useCallback40(() => {
78455
+ const handleMouseMove = useCallback42(() => {
78046
78456
  if (!isDrawingFreehand || tool !== "free") return;
78047
78457
  const pos = naturalPointer();
78048
78458
  if (!pos) return;
78049
78459
  setFreehandPoints((prev) => [...prev, pos]);
78050
78460
  }, [isDrawingFreehand, tool, naturalPointer]);
78051
- const handleMouseUp = useCallback40(() => {
78461
+ const handleMouseUp = useCallback42(() => {
78052
78462
  if (!isDrawingFreehand || tool !== "free") return;
78053
78463
  if (freehandPoints.length > 1) {
78054
78464
  setAnnotations((prev) => [
@@ -78059,11 +78469,11 @@ function PhotoAnnotator({
78059
78469
  setFreehandPoints([]);
78060
78470
  setIsDrawingFreehand(false);
78061
78471
  }, [isDrawingFreehand, tool, freehandPoints]);
78062
- const cancelLabel = useCallback40(() => {
78472
+ const cancelLabel = useCallback42(() => {
78063
78473
  setLabelInput(null);
78064
78474
  setLabelText("");
78065
78475
  }, []);
78066
- const commitLabel = useCallback40(() => {
78476
+ const commitLabel = useCallback42(() => {
78067
78477
  if (labelInput && labelText.trim()) {
78068
78478
  setAnnotations((prev) => [
78069
78479
  ...prev,
@@ -78072,7 +78482,7 @@ function PhotoAnnotator({
78072
78482
  }
78073
78483
  cancelLabel();
78074
78484
  }, [labelInput, labelText, cancelLabel]);
78075
- const commitCalibration = useCallback40(() => {
78485
+ const commitCalibration = useCallback42(() => {
78076
78486
  const known = parseFloat(calibKnown);
78077
78487
  if (calibLine && known > 0) {
78078
78488
  const dx = calibLine.b.x - calibLine.a.x;
@@ -78087,11 +78497,11 @@ function PhotoAnnotator({
78087
78497
  setCalibLine(null);
78088
78498
  setCalibKnown("");
78089
78499
  }, [calibLine, calibKnown]);
78090
- const undoLast = useCallback40(() => setAnnotations((prev) => prev.slice(0, -1)), []);
78091
- const zoomIn = useCallback40(() => setZoom((z) => Math.min(z + 0.25, 4)), []);
78092
- const zoomOut = useCallback40(() => setZoom((z) => Math.max(z - 0.25, 0.5)), []);
78093
- const zoomReset = useCallback40(() => setZoom(1), []);
78094
- const handleSave = useCallback40(async () => {
78500
+ const undoLast = useCallback42(() => setAnnotations((prev) => prev.slice(0, -1)), []);
78501
+ const zoomIn = useCallback42(() => setZoom((z) => Math.min(z + 0.25, 4)), []);
78502
+ const zoomOut = useCallback42(() => setZoom((z) => Math.max(z - 0.25, 0.5)), []);
78503
+ const zoomReset = useCallback42(() => setZoom(1), []);
78504
+ const handleSave = useCallback42(async () => {
78095
78505
  const stage = stageRef.current;
78096
78506
  if (!stage) return;
78097
78507
  let blob;
@@ -78498,7 +78908,7 @@ function StructureDeleteModal({ open, title, noun = "section", impact, onConfirm
78498
78908
  }
78499
78909
 
78500
78910
  // src/components/editor/AddSectionPromptModal.tsx
78501
- import { useRef as useRef51 } from "react";
78911
+ import { useRef as useRef52 } from "react";
78502
78912
  import { Fragment as Fragment22, jsx as jsx101, jsxs as jsxs85 } from "react/jsx-runtime";
78503
78913
  function AddSectionPromptModal({
78504
78914
  open,
@@ -78507,7 +78917,7 @@ function AddSectionPromptModal({
78507
78917
  onConfirm,
78508
78918
  onCancel
78509
78919
  }) {
78510
- const inputRef = useRef51(null);
78920
+ const inputRef = useRef52(null);
78511
78921
  return /* @__PURE__ */ jsx101(
78512
78922
  Modal,
78513
78923
  {
@@ -78556,12 +78966,12 @@ function AddSectionPromptModal({
78556
78966
  }
78557
78967
 
78558
78968
  // src/components/editor/AddItemTypeModal.tsx
78559
- import { useState as useState51, useEffect as useEffect43, useRef as useRef52 } from "react";
78969
+ import { useState as useState51, useEffect as useEffect44, useRef as useRef53 } from "react";
78560
78970
  import { Fragment as Fragment23, jsx as jsx102, jsxs as jsxs86 } from "react/jsx-runtime";
78561
78971
  function AddItemTypeModal({ open, onConfirm, onCancel }) {
78562
78972
  const [label, setLabel] = useState51("");
78563
78973
  const [type, setType] = useState51("rich");
78564
- const labelRef = useRef52(null);
78974
+ const labelRef = useRef53(null);
78565
78975
  const ITEM_TYPES = [
78566
78976
  { value: "rich", label: index_exports.editor_additem_type_rich() },
78567
78977
  { value: "boolean", label: index_exports.editor_additem_type_boolean() },
@@ -78573,7 +78983,7 @@ function AddItemTypeModal({ open, onConfirm, onCancel }) {
78573
78983
  { value: "date", label: index_exports.editor_additem_type_date() },
78574
78984
  { value: "photo_only", label: index_exports.editor_additem_type_photo_only() }
78575
78985
  ];
78576
- useEffect43(() => {
78986
+ useEffect44(() => {
78577
78987
  if (open) {
78578
78988
  setLabel("");
78579
78989
  setType("rich");
@@ -78649,11 +79059,11 @@ function AddItemTypeModal({ open, onConfirm, onCancel }) {
78649
79059
  }
78650
79060
 
78651
79061
  // src/components/editor/SaveTemplateModal.tsx
78652
- import { useRef as useRef53 } from "react";
79062
+ import { useRef as useRef54 } from "react";
78653
79063
  import { Fragment as Fragment24, jsx as jsx103, jsxs as jsxs87 } from "react/jsx-runtime";
78654
79064
  function SaveTemplateModal({ mode, name, onChangeName, onConfirm, onCancel }) {
78655
79065
  const isNew = mode === "new";
78656
- const nameRef = useRef53(null);
79066
+ const nameRef = useRef54(null);
78657
79067
  return /* @__PURE__ */ jsxs87(
78658
79068
  Modal,
78659
79069
  {
@@ -78707,10 +79117,10 @@ function SaveTemplateModal({ mode, name, onChangeName, onConfirm, onCancel }) {
78707
79117
  }
78708
79118
 
78709
79119
  // src/components/editor/CommentLibraryDrawer.tsx
78710
- import { useRef as useRef54 } from "react";
79120
+ import { useRef as useRef55 } from "react";
78711
79121
  import { Fragment as Fragment25, jsx as jsx104, jsxs as jsxs88 } from "react/jsx-runtime";
78712
79122
  function CommentLibraryDrawer({ open, comments, state, serverComments, onInsert, onClose, sheet = false }) {
78713
- const searchRef = useRef54(null);
79123
+ const searchRef = useRef55(null);
78714
79124
  const locale = useFieldDataSource().session().locale;
78715
79125
  const searchBlock = /* @__PURE__ */ jsxs88("div", { className: "px-4 py-2", children: [
78716
79126
  /* @__PURE__ */ jsx104(
@@ -78838,7 +79248,7 @@ function CommentLibraryDrawer({ open, comments, state, serverComments, onInsert,
78838
79248
  }
78839
79249
 
78840
79250
  // src/components/editor/SectionPickerModal.tsx
78841
- import { useRef as useRef55 } from "react";
79251
+ import { useRef as useRef56 } from "react";
78842
79252
  import { jsx as jsx105, jsxs as jsxs89 } from "react/jsx-runtime";
78843
79253
  function SectionPickerModal({
78844
79254
  open,
@@ -78849,7 +79259,7 @@ function SectionPickerModal({
78849
79259
  pickSection,
78850
79260
  closeSectionPicker
78851
79261
  }) {
78852
- const inputRef = useRef55(null);
79262
+ const inputRef = useRef56(null);
78853
79263
  return /* @__PURE__ */ jsxs89(Modal, { open, onClose: closeSectionPicker, title: index_exports.editor_sectionpicker_title(), size: "md", initialFocusRef: inputRef, children: [
78854
79264
  /* @__PURE__ */ jsx105(
78855
79265
  "input",
@@ -78886,34 +79296,37 @@ import { jsx as jsx106, jsxs as jsxs90 } from "react/jsx-runtime";
78886
79296
  function TagPickerModal({ open, activeItemId, tagsByItem, presetTags, onToggle, onClose }) {
78887
79297
  const selected = tagsByItem[activeItemId] || [];
78888
79298
  return /* @__PURE__ */ jsxs90(Modal, { open, onClose, title: index_exports.editor_tagpicker_title(), size: "sm", children: [
78889
- /* @__PURE__ */ jsx106("div", { className: "space-y-1.5", children: presetTags.map((tag) => {
78890
- const isActive = selected.some((t) => t.id === tag.id);
78891
- return /* @__PURE__ */ jsxs90(
78892
- "button",
78893
- {
78894
- onClick: () => onToggle(tag),
78895
- className: `w-full text-left px-3 py-2.5 rounded-lg text-[13px] font-medium flex items-center gap-3 transition-colors ${isActive ? "bg-ih-bg-muted ring-1 ring-inset" : "hover:bg-ih-bg-muted"}`,
78896
- style: isActive ? { "--tw-ring-color": tag.color } : void 0,
78897
- children: [
78898
- /* @__PURE__ */ jsx106(
78899
- "span",
78900
- {
78901
- className: "w-3 h-3 rounded-full flex-shrink-0",
78902
- style: { backgroundColor: tag.color }
78903
- }
78904
- ),
78905
- /* @__PURE__ */ jsx106("span", { className: "flex-1 text-ih-fg-1", children: tag.name }),
78906
- isActive && /* @__PURE__ */ jsx106("svg", { className: "w-4 h-4 text-ih-ok", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx106("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) })
78907
- ]
78908
- },
78909
- tag.id
78910
- );
78911
- }) }),
79299
+ /* @__PURE__ */ jsxs90("div", { className: "space-y-1.5", children: [
79300
+ presetTags.length === 0 && /* @__PURE__ */ jsx106("p", { className: "text-[13px] text-ih-fg-3", children: index_exports.editor_tagpicker_empty() }),
79301
+ presetTags.map((tag) => {
79302
+ const isActive = selected.some((t) => t.id === tag.id);
79303
+ return /* @__PURE__ */ jsxs90(
79304
+ "button",
79305
+ {
79306
+ onClick: () => onToggle(tag),
79307
+ className: `w-full text-left px-3 py-2.5 rounded-lg text-[13px] font-medium flex items-center gap-3 transition-colors ${isActive ? "bg-ih-bg-muted ring-1 ring-inset" : "hover:bg-ih-bg-muted"}`,
79308
+ style: isActive && safeCssColor(tag.color) ? { "--tw-ring-color": safeCssColor(tag.color) } : void 0,
79309
+ children: [
79310
+ /* @__PURE__ */ jsx106(
79311
+ "span",
79312
+ {
79313
+ className: "w-3 h-3 rounded-full flex-shrink-0 bg-ih-border-strong",
79314
+ style: safeCssColor(tag.color) ? { backgroundColor: safeCssColor(tag.color) } : void 0
79315
+ }
79316
+ ),
79317
+ /* @__PURE__ */ jsx106("span", { className: "flex-1 text-ih-fg-1", children: tag.name }),
79318
+ isActive && /* @__PURE__ */ jsx106("svg", { className: "w-4 h-4 text-ih-ok", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx106("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) })
79319
+ ]
79320
+ },
79321
+ tag.id
79322
+ );
79323
+ })
79324
+ ] }),
78912
79325
  selected.length > 0 && /* @__PURE__ */ jsx106("div", { className: "mt-3 pt-3 border-t border-ih-border", children: /* @__PURE__ */ jsx106("div", { className: "flex flex-wrap gap-1.5", children: selected.map((tag) => /* @__PURE__ */ jsx106(
78913
79326
  "span",
78914
79327
  {
78915
79328
  className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold text-white",
78916
- style: { backgroundColor: tag.color || "#6b7280" },
79329
+ style: { backgroundColor: safeCssColor(tag.color) ?? "#6b7280", color: readableTextOn(safeCssColor(tag.color) ?? "#6b7280") ?? void 0 },
78917
79330
  children: tag.name
78918
79331
  },
78919
79332
  tag.id
@@ -79197,10 +79610,10 @@ function BulkCreateForm({ busy, onSubmit }) {
79197
79610
  }
79198
79611
 
79199
79612
  // src/components/editor/CostItemsHost.tsx
79200
- import { useEffect as useEffect45, useState as useState55 } from "react";
79613
+ import { useEffect as useEffect46, useState as useState55 } from "react";
79201
79614
 
79202
79615
  // src/components/editor/CostItemsPanel.tsx
79203
- import { useEffect as useEffect44, useRef as useRef56, useState as useState54 } from "react";
79616
+ import { useEffect as useEffect45, useRef as useRef57, useState as useState54 } from "react";
79204
79617
 
79205
79618
  // src/shared/money.ts
79206
79619
  function formatDollars(cents, opts) {
@@ -79301,7 +79714,7 @@ function CostExportButtons({
79301
79714
  }
79302
79715
 
79303
79716
  // src/components/editor/cost-items-rows.ts
79304
- import { useCallback as useCallback41 } from "react";
79717
+ import { useCallback as useCallback43 } from "react";
79305
79718
  var THRESHOLD_CENTS = 3e5;
79306
79719
  function rowTotalCents(item) {
79307
79720
  if (item.costMethod === "unit") return (item.quantity ?? 0) * (item.unitCostCents ?? 0);
@@ -79337,7 +79750,7 @@ function isTempId(id) {
79337
79750
  }
79338
79751
  function useCostItemWrite(inspectionId) {
79339
79752
  const costItems = useFieldDataSource().resources.costItems;
79340
- return useCallback41(
79753
+ return useCallback43(
79341
79754
  async (intent, item) => {
79342
79755
  if (!costItems) return { success: false };
79343
79756
  try {
@@ -79363,12 +79776,12 @@ function CostItemsPanel({
79363
79776
  const [rows, setRows] = useState54(items);
79364
79777
  const [saving, setSaving] = useState54({});
79365
79778
  const [failure, setFailure] = useState54(null);
79366
- const pendingRemovalRef = useRef56(/* @__PURE__ */ new Set());
79367
- const writingRef = useRef56(/* @__PURE__ */ new Set());
79368
- const removingRef = useRef56(/* @__PURE__ */ new Set());
79369
- const dirtyRef = useRef56(/* @__PURE__ */ new Set());
79370
- const rowsRef = useRef56(rows);
79371
- useEffect44(() => {
79779
+ const pendingRemovalRef = useRef57(/* @__PURE__ */ new Set());
79780
+ const writingRef = useRef57(/* @__PURE__ */ new Set());
79781
+ const removingRef = useRef57(/* @__PURE__ */ new Set());
79782
+ const dirtyRef = useRef57(/* @__PURE__ */ new Set());
79783
+ const rowsRef = useRef57(rows);
79784
+ useEffect45(() => {
79372
79785
  rowsRef.current = rows;
79373
79786
  });
79374
79787
  const bucketTotals = { immediate: 0, short_term: 0, long_term: 0 };
@@ -79726,7 +80139,7 @@ function CostItemsHost({
79726
80139
  }) {
79727
80140
  const costItems = useFieldDataSource().resources.costItems;
79728
80141
  const [data, setData] = useState55(null);
79729
- useEffect45(() => {
80142
+ useEffect46(() => {
79730
80143
  if (!open) return;
79731
80144
  let cancelled = false;
79732
80145
  setData(null);
@@ -80102,7 +80515,7 @@ function AppBar({ platform, sectionTitle, itemLabel, onBack, onMore, tablet = fa
80102
80515
  "header",
80103
80516
  {
80104
80517
  "data-platform": platform,
80105
- className: `sticky top-0 z-30 ${height} bg-ih-bg-card border-b border-ih-border flex items-center px-2 gap-2`,
80518
+ className: `sticky top-0 z-30 ${height} bg-ih-bg-card border-b border-ih-border flex items-center pl-[max(0.5rem,env(safe-area-inset-left))] pr-[max(0.5rem,env(safe-area-inset-right))] gap-2`,
80106
80519
  children: [
80107
80520
  /* @__PURE__ */ jsx113(IconButton, { onClick: onBack, className: BTN, "aria-label": index_exports.common_back(), children: /* @__PURE__ */ jsx113(BackGlyph, { platform }) }),
80108
80521
  tablet && onOpenSections && /* @__PURE__ */ jsx113(IconButton, { onClick: onOpenSections, className: BTN, "aria-label": index_exports.editor_mobile_drawer_sections(), children: "\u2630" }),
@@ -80166,7 +80579,7 @@ function TabletTree({ appBar, listHeader, sectionTitle, list, main, emptyTemplat
80166
80579
  "aside",
80167
80580
  {
80168
80581
  "data-testid": "tablet-list-pane",
80169
- className: "w-[clamp(280px,36vw,360px)] shrink-0 min-w-0 min-h-0 border-r border-ih-border flex flex-col",
80582
+ className: "w-[clamp(280px,36vw,360px)] shrink-0 min-w-0 min-h-0 pl-[env(safe-area-inset-left)] border-r border-ih-border flex flex-col",
80170
80583
  children: [
80171
80584
  listHeader,
80172
80585
  /* @__PURE__ */ jsx116("div", { className: "sticky top-0 z-10 shrink-0 px-3 py-1.5 bg-ih-bg-card border-b border-ih-border text-[12px] font-bold text-ih-fg-2 truncate", children: sectionTitle }),
@@ -80178,7 +80591,7 @@ function TabletTree({ appBar, listHeader, sectionTitle, list, main, emptyTemplat
80178
80591
  "main",
80179
80592
  {
80180
80593
  "data-testid": "tablet-editor-pane",
80181
- className: "flex-1 min-w-0 min-h-0 overflow-y-auto overscroll-contain px-5 py-4 pb-24 [@media(max-height:499px)]:py-2",
80594
+ className: "flex-1 min-w-0 min-h-0 overflow-y-auto overscroll-contain pl-5 pr-[max(1.25rem,env(safe-area-inset-right))] py-4 pb-24 [@media(max-height:499px)]:py-2",
80182
80595
  children: main
80183
80596
  }
80184
80597
  )
@@ -80190,9 +80603,9 @@ function TabletTree({ appBar, listHeader, sectionTitle, list, main, emptyTemplat
80190
80603
  }
80191
80604
 
80192
80605
  // src/hooks/useResetBatchOnPhone.ts
80193
- import { useEffect as useEffect46 } from "react";
80606
+ import { useEffect as useEffect47 } from "react";
80194
80607
  function useResetBatchOnPhone(isPhone, batchMode, setBatchMode, setBatchSelected) {
80195
- useEffect46(() => {
80608
+ useEffect47(() => {
80196
80609
  if (!isPhone || !batchMode) return;
80197
80610
  setBatchMode(false);
80198
80611
  setBatchSelected({});
@@ -80229,7 +80642,7 @@ function InspectionIdentity({ address, id, date, status }) {
80229
80642
  // src/components/editor/MobileDrawerTriggers.tsx
80230
80643
  import { jsx as jsx118, jsxs as jsxs99 } from "react/jsx-runtime";
80231
80644
  function MobileDrawerTriggers({ onOpen, showTheme = true }) {
80232
- return /* @__PURE__ */ jsx118("nav", { "aria-label": index_exports.editor_mobile_nav_label(), className: "fixed left-0 right-0 bottom-0 z-30 h-[calc(3.5rem+env(safe-area-inset-bottom))] pb-[env(safe-area-inset-bottom)] bg-ih-bg-card border-t border-ih-border flex", children: [
80645
+ return /* @__PURE__ */ jsx118("nav", { "aria-label": index_exports.editor_mobile_nav_label(), className: "fixed left-0 right-0 bottom-0 z-30 h-[calc(3.5rem+env(safe-area-inset-bottom))] pb-[env(safe-area-inset-bottom)] pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)] bg-ih-bg-card border-t border-ih-border flex", children: [
80233
80646
  { id: "sections", label: index_exports.editor_mobile_drawer_sections(), icon: "\u2630" },
80234
80647
  { id: "items", label: index_exports.editor_mobile_drawer_items(), icon: "\u2263" },
80235
80648
  { id: "preview", label: index_exports.editor_route_drawer_preview(), icon: "\u{1F441}" },
@@ -80327,7 +80740,7 @@ function MobileFinishDrawer({
80327
80740
  }
80328
80741
 
80329
80742
  // src/components/editor/PropertyInfoForm.tsx
80330
- import { useMemo as useMemo13, useCallback as useCallback42, useEffect as useEffect47, useRef as useRef57, useState as useState57 } from "react";
80743
+ import { useMemo as useMemo15, useCallback as useCallback44, useEffect as useEffect48, useRef as useRef58, useState as useState57 } from "react";
80331
80744
 
80332
80745
  // src/shared/property-facts-keys.ts
80333
80746
  var DEDICATED_FACT_KEYS = [
@@ -80374,8 +80787,8 @@ function PropertyInfoForm({ inspection, templateFields, propertyAddress, onSave,
80374
80787
  const readOnly = useReadOnly();
80375
80788
  const defaultFields = inspection.propertyType === "commercial" ? commercialFields() : residentialFields();
80376
80789
  const metaFields = templateFields?.length ? templateFields : defaultFields;
80377
- const filled = useMemo13(() => metaFields.filter((f) => inspection[f.id]).length, [metaFields, inspection]);
80378
- const groups = useMemo13(() => {
80790
+ const filled = useMemo15(() => metaFields.filter((f) => inspection[f.id]).length, [metaFields, inspection]);
80791
+ const groups = useMemo15(() => {
80379
80792
  const seen = /* @__PURE__ */ new Set();
80380
80793
  const result = [];
80381
80794
  for (const f of metaFields) {
@@ -80387,7 +80800,7 @@ function PropertyInfoForm({ inspection, templateFields, propertyAddress, onSave,
80387
80800
  }
80388
80801
  return result;
80389
80802
  }, [metaFields]);
80390
- const fieldsByGroup = useCallback42(
80803
+ const fieldsByGroup = useCallback44(
80391
80804
  (group) => metaFields.filter((f) => (f.group || index_exports.editor_property_group_general()) === group),
80392
80805
  [metaFields]
80393
80806
  );
@@ -80411,10 +80824,10 @@ function PropertyInfoForm({ inspection, templateFields, propertyAddress, onSave,
80411
80824
  const { run, busy: autofilling } = useFieldCommand(String(inspection.id));
80412
80825
  const [autofillMsg, setAutofillMsg] = useState57(null);
80413
80826
  const addressString = (propertyAddress || inspection.propertyAddress || "").trim();
80414
- const latest = useRef57({ metaFields, inspection, onSave, onCommit });
80827
+ const latest = useRef58({ metaFields, inspection, onSave, onCommit });
80415
80828
  latest.current = { metaFields, inspection, onSave, onCommit };
80416
- const mounted = useRef57(true);
80417
- useEffect47(() => {
80829
+ const mounted = useRef58(true);
80830
+ useEffect48(() => {
80418
80831
  mounted.current = true;
80419
80832
  return () => {
80420
80833
  mounted.current = false;
@@ -80460,7 +80873,7 @@ function PropertyInfoForm({ inspection, templateFields, propertyAddress, onSave,
80460
80873
  if (isIgnored(answer) || !mounted.current) return;
80461
80874
  if (answer.data) applyAutofill(answer.data.facts, answer.data.reason);
80462
80875
  }
80463
- return /* @__PURE__ */ jsxs101("div", { className: "px-6 py-6 max-w-5xl", "data-testid": "property-info-form", children: [
80876
+ return /* @__PURE__ */ jsxs101("div", { className: "px-0 md:px-6 py-6 max-w-5xl", "data-testid": "property-info-form", children: [
80464
80877
  /* @__PURE__ */ jsxs101("header", { className: "mb-6", children: [
80465
80878
  /* @__PURE__ */ jsxs101("div", { className: "flex items-center gap-2 mb-1", children: [
80466
80879
  /* @__PURE__ */ jsx120("p", { className: "text-[10px] font-bold uppercase tracking-[0.2em] text-ih-fg-3", children: index_exports.editor_property_progress({ filled, total: metaFields.length }) }),
@@ -80533,7 +80946,7 @@ function PropertyInfoForm({ inspection, templateFields, propertyAddress, onSave,
80533
80946
  }
80534
80947
 
80535
80948
  // src/components/statutory/StatutoryDetailsHost.tsx
80536
- import { useCallback as useCallback43 } from "react";
80949
+ import { useCallback as useCallback45 } from "react";
80537
80950
 
80538
80951
  // src/components/statutory/StatutoryDetailsPanel.tsx
80539
80952
  import { useId as useId3, useState as useState58 } from "react";
@@ -80602,7 +81015,7 @@ function StatutoryDetailsHost({
80602
81015
  details
80603
81016
  }) {
80604
81017
  const { run, busy } = useFieldCommand(inspectionId);
80605
- const onCommit = useCallback43((next) => {
81018
+ const onCommit = useCallback45((next) => {
80606
81019
  void run({ type: "save-statutory-details", payload: next });
80607
81020
  }, [run]);
80608
81021
  if (!details) return null;
@@ -80725,7 +81138,7 @@ function PcaNarrativePanel({ inspectionId, narrative }) {
80725
81138
  }
80726
81139
 
80727
81140
  // src/components/inspection-edit/CompliancePanel.tsx
80728
- import { useRef as useRef58, useState as useState60 } from "react";
81141
+ import { useRef as useRef59, useState as useState60 } from "react";
80729
81142
 
80730
81143
  // src/components/ConfirmDialog.tsx
80731
81144
  import { Fragment as Fragment32, jsx as jsx125, jsxs as jsxs105 } from "react/jsx-runtime";
@@ -81158,7 +81571,7 @@ function RelianceFieldRow({
81158
81571
  }) {
81159
81572
  const { run, busy } = useFieldCommand(inspectionId);
81160
81573
  const [value, setValue2] = useState60(initial);
81161
- const savedRef = useRef58(initial);
81574
+ const savedRef = useRef59(initial);
81162
81575
  const commit = async () => {
81163
81576
  if (value === savedRef.current) return;
81164
81577
  const answer = await run({ type: "save-pca-narrative", key: fieldKey, value });
@@ -81273,7 +81686,7 @@ function CommercialReportControls({
81273
81686
  }
81274
81687
 
81275
81688
  // src/components/editor/PropertyTypeControl.tsx
81276
- import { useEffect as useEffect48, useRef as useRef59, useState as useState61 } from "react";
81689
+ import { useEffect as useEffect49, useRef as useRef60, useState as useState61 } from "react";
81277
81690
 
81278
81691
  // src/shared/inspection-property-type.ts
81279
81692
  var INSPECTION_PROPERTY_TYPES = [
@@ -81303,9 +81716,9 @@ function PropertyTypeControl({
81303
81716
  const costItems = dataSource.resources.costItems;
81304
81717
  const [costCount, setCostCount] = useState61(0);
81305
81718
  const [counting, setCounting] = useState61(false);
81306
- const countRead = useRef59(0);
81307
- const mounted = useRef59(true);
81308
- useEffect48(() => {
81719
+ const countRead = useRef60(0);
81720
+ const mounted = useRef60(true);
81721
+ useEffect49(() => {
81309
81722
  mounted.current = true;
81310
81723
  return () => {
81311
81724
  mounted.current = false;
@@ -81350,7 +81763,7 @@ function PropertyTypeControl({
81350
81763
  }
81351
81764
  void save(next);
81352
81765
  }
81353
- useEffect48(() => {
81766
+ useEffect49(() => {
81354
81767
  if (pending !== null && !busy && propertyType === (pending === "" ? null : pending)) {
81355
81768
  setPending(null);
81356
81769
  }
@@ -81481,11 +81894,11 @@ function InspectionOverview({
81481
81894
  }
81482
81895
 
81483
81896
  // src/components/editor/BreadcrumbDropdown.tsx
81484
- import { useRef as useRef60, useState as useState62 } from "react";
81897
+ import { useRef as useRef61, useState as useState62 } from "react";
81485
81898
  import { Fragment as Fragment36, jsx as jsx130, jsxs as jsxs110 } from "react/jsx-runtime";
81486
81899
  function BreadcrumbDropdown({ units, activeUnitId, onSelect }) {
81487
81900
  const [open, setOpen] = useState62(false);
81488
- const anchorRef = useRef60(null);
81901
+ const anchorRef = useRef61(null);
81489
81902
  const COMMON_LABEL = index_exports.editor_scope_common();
81490
81903
  const scopes = units.filter((u) => (u.kind ?? "unit") === "unit").slice().sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
81491
81904
  const active = activeUnitId ? scopes.find((u) => u.id === activeUnitId) : null;
@@ -81692,28 +82105,76 @@ function ReadOnlyItemView({ item, sectionTitle, result, ratingLevels, inspection
81692
82105
  ] });
81693
82106
  }
81694
82107
 
82108
+ // src/components/editor/FinishFieldworkConfirm.tsx
82109
+ import { useCallback as useCallback46, useState as useState63 } from "react";
82110
+ import { jsx as jsx132 } from "react/jsx-runtime";
82111
+ function useFinishFieldworkConfirm(finish, busy) {
82112
+ const [open, setOpen] = useState63(false);
82113
+ const latch = useActionLatch(busy);
82114
+ const request = useCallback46(() => setOpen(true), []);
82115
+ const dialog = /* @__PURE__ */ jsx132(
82116
+ ConfirmDialog,
82117
+ {
82118
+ open,
82119
+ title: index_exports.editor_finish_fieldwork_confirm_title(),
82120
+ message: index_exports.editor_finish_fieldwork_confirm_body(),
82121
+ confirmLabel: index_exports.editor_finish_fieldwork(),
82122
+ tone: "default",
82123
+ busy,
82124
+ onConfirm: () => latch(() => void finish().catch(() => void 0).finally(() => setOpen(false))),
82125
+ onCancel: () => setOpen(false)
82126
+ }
82127
+ );
82128
+ return { request, dialog };
82129
+ }
82130
+
82131
+ // src/hooks/useScrollTopOnDetails.ts
82132
+ import { useEffect as useEffect50 } from "react";
82133
+ function useScrollTopOnDetails(activeView) {
82134
+ useEffect50(() => {
82135
+ if (activeView !== "property") return;
82136
+ window.scrollTo(0, 0);
82137
+ document.querySelectorAll("main").forEach((el) => {
82138
+ el.scrollTop = 0;
82139
+ });
82140
+ }, [activeView]);
82141
+ }
82142
+
82143
+ // src/hooks/notes-commit-target.ts
82144
+ function notesCommitTarget(input) {
82145
+ const itemId = input.itemId ?? input.activeItemId;
82146
+ const sectionId = input.itemId ? input.sectionIdForItem(input.itemId) : input.currentSectionId;
82147
+ return itemId && sectionId ? { sectionId, itemId } : null;
82148
+ }
82149
+
81695
82150
  // src/components/editor/ItemFilterBar.tsx
81696
- import { jsx as jsx132, jsxs as jsxs112 } from "react/jsx-runtime";
82151
+ import { jsx as jsx133, jsxs as jsxs112 } from "react/jsx-runtime";
81697
82152
  function ItemFilterBar({ state, formProgress, isMobile }) {
81698
82153
  const readOnly = useReadOnly();
81699
82154
  return /* @__PURE__ */ jsxs112("div", { className: "flex flex-wrap items-center gap-1 px-3 py-1.5 border-b border-ih-border", children: [
81700
- /* @__PURE__ */ jsx132(
82155
+ /* @__PURE__ */ jsx133(
81701
82156
  SegmentedControl,
81702
82157
  {
81703
82158
  ariaLabel: index_exports.editor_route_item_filter(),
82159
+ className: "w-full min-w-0 [&>button]:min-w-0 [&>button]:pointer-coarse:min-w-11 [&>button]:px-1.5 [&>button>span:last-child]:min-w-0",
81704
82160
  value: state.itemFilter,
81705
82161
  onChange: (v) => state.setItemFilter(v),
81706
- options: ["all", "unrated", "issues", "flagged"].map((f) => ({
81707
- value: f,
81708
- label: /* @__PURE__ */ jsxs112("span", { className: "whitespace-nowrap", children: [
81709
- f === "all" ? index_exports.editor_route_filter_all() : f === "unrated" ? index_exports.editor_route_filter_unrated() : f === "issues" ? index_exports.editor_route_filter_issues() : index_exports.editor_route_filter_flagged(),
81710
- f !== "all" && /* @__PURE__ */ jsx132("span", { className: "ml-1 text-[10px]", children: f === "unrated" ? state.filterCounts.unrated : f === "issues" ? state.filterCounts.issues : state.filterCounts.flagged })
81711
- ] })
81712
- }))
82162
+ options: ["all", "unrated", "issues", "flagged"].map((f) => {
82163
+ const text = f === "all" ? index_exports.editor_route_filter_all() : f === "unrated" ? index_exports.editor_route_filter_unrated() : f === "issues" ? index_exports.editor_route_filter_issues() : index_exports.editor_route_filter_flagged();
82164
+ const count = f === "all" ? null : state.filterCounts[f];
82165
+ return {
82166
+ value: f,
82167
+ title: count === null ? index_exports.editor_route_filter_all_in_section() : index_exports.editor_route_filter_count_in_section({ label: text, count }),
82168
+ label: /* @__PURE__ */ jsxs112("span", { className: "flex min-w-0 items-baseline gap-1", children: [
82169
+ /* @__PURE__ */ jsx133("span", { className: "min-w-0 truncate", children: text }),
82170
+ count !== null && /* @__PURE__ */ jsx133("span", { className: "shrink-0 tabular-nums text-[10px]", children: count })
82171
+ ] })
82172
+ };
82173
+ })
81713
82174
  }
81714
82175
  ),
81715
- formProgress && /* @__PURE__ */ jsx132("span", { className: "ml-auto text-[11px] text-ih-fg-3 whitespace-nowrap", children: index_exports.statutory_form_completeness({ answered: String(formProgress.answered), total: String(formProgress.total) }) }),
81716
- !readOnly && /* @__PURE__ */ jsx132(
82176
+ formProgress && /* @__PURE__ */ jsx133("span", { className: "ml-auto text-[11px] text-ih-fg-3 whitespace-nowrap", children: index_exports.statutory_form_completeness({ answered: String(formProgress.answered), total: String(formProgress.total) }) }),
82177
+ !readOnly && /* @__PURE__ */ jsx133(
81717
82178
  IconButton,
81718
82179
  {
81719
82180
  onClick: () => {
@@ -81729,28 +82190,28 @@ function ItemFilterBar({ state, formProgress, isMobile }) {
81729
82190
  className: isMobile ? "ml-auto w-11 h-11 touch-manipulation" : "ml-auto",
81730
82191
  title: state.batchMode ? index_exports.editor_route_exit_batch_mode() : index_exports.editor_route_batch_mode(),
81731
82192
  "aria-label": state.batchMode ? index_exports.editor_route_exit_batch_mode() : index_exports.editor_route_batch_select_items(),
81732
- children: /* @__PURE__ */ jsx132("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx132("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" }) })
82193
+ children: /* @__PURE__ */ jsx133("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx133("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" }) })
81733
82194
  }
81734
82195
  )
81735
82196
  ] });
81736
82197
  }
81737
82198
 
81738
82199
  // src/components/editor/UnitControls.tsx
81739
- import { jsx as jsx133, jsxs as jsxs113 } from "react/jsx-runtime";
81740
- var UNITS_ICON = /* @__PURE__ */ jsx133("svg", { className: "w-3.5 h-3.5 text-ih-fg-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx133("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" }) });
81741
- var COST_ICON = /* @__PURE__ */ jsx133("svg", { className: "w-3.5 h-3.5 text-ih-fg-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx133("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8c-1.66 0-3 .9-3 2s1.34 2 3 2 3 .9 3 2-1.34 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V6m0 12v-2m0-10a4 4 0 100 8 4 4 0 000-8z" }) });
82200
+ import { jsx as jsx134, jsxs as jsxs113 } from "react/jsx-runtime";
82201
+ var UNITS_ICON = /* @__PURE__ */ jsx134("svg", { className: "w-3.5 h-3.5 text-ih-fg-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx134("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" }) });
82202
+ var COST_ICON = /* @__PURE__ */ jsx134("svg", { className: "w-3.5 h-3.5 text-ih-fg-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx134("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8c-1.66 0-3 .9-3 2s1.34 2 3 2 3 .9 3 2-1.34 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V6m0 12v-2m0-10a4 4 0 100 8 4 4 0 000-8z" }) });
81742
82203
  function UnitControls({ isPerUnit, units, activeUnitId, onSelectScope, onOpenUnits, onOpenCostItems }) {
81743
82204
  const caps = useEditorCapabilities();
81744
82205
  const readOnly = useReadOnly();
81745
82206
  return /* @__PURE__ */ jsxs113("div", { className: "flex items-center gap-2", children: [
81746
- isPerUnit && /* @__PURE__ */ jsx133(BreadcrumbDropdown, { units, activeUnitId, onSelect: onSelectScope }),
81747
- !readOnly && /* @__PURE__ */ jsx133(Button, { variant: "secondary", size: "sm", onClick: onOpenUnits, className: "hidden lg:inline-flex", title: index_exports.editor_route_manage_units(), "aria-label": index_exports.editor_route_units(), icon: UNITS_ICON, children: /* @__PURE__ */ jsx133("span", { className: "hidden min-[1700px]:inline", children: index_exports.editor_route_units() }) }),
81748
- !readOnly && caps.costItems && /* @__PURE__ */ jsx133(Button, { variant: "secondary", size: "sm", onClick: onOpenCostItems, className: "hidden lg:inline-flex", title: index_exports.editor_route_cost_items_title(), "aria-label": index_exports.editor_route_cost_items(), icon: COST_ICON, children: /* @__PURE__ */ jsx133("span", { className: "hidden min-[1700px]:inline", children: index_exports.editor_route_cost_items() }) })
82207
+ isPerUnit && /* @__PURE__ */ jsx134(BreadcrumbDropdown, { units, activeUnitId, onSelect: onSelectScope }),
82208
+ !readOnly && /* @__PURE__ */ jsx134(Button, { variant: "secondary", size: "sm", onClick: onOpenUnits, className: "hidden lg:inline-flex", title: index_exports.editor_route_manage_units(), "aria-label": index_exports.editor_route_units(), icon: UNITS_ICON, children: /* @__PURE__ */ jsx134("span", { className: "hidden min-[1700px]:inline", children: index_exports.editor_route_units() }) }),
82209
+ !readOnly && caps.costItems && /* @__PURE__ */ jsx134(Button, { variant: "secondary", size: "sm", onClick: onOpenCostItems, className: "hidden lg:inline-flex", title: index_exports.editor_route_cost_items_title(), "aria-label": index_exports.editor_route_cost_items(), icon: COST_ICON, children: /* @__PURE__ */ jsx134("span", { className: "hidden min-[1700px]:inline", children: index_exports.editor_route_cost_items() }) })
81749
82210
  ] });
81750
82211
  }
81751
82212
 
81752
82213
  // src/editor/FieldEditor.tsx
81753
- import { Fragment as Fragment37, jsx as jsx134, jsxs as jsxs114 } from "react/jsx-runtime";
82214
+ import { Fragment as Fragment37, jsx as jsx135, jsxs as jsxs114 } from "react/jsx-runtime";
81754
82215
  function originalQualityEnabled() {
81755
82216
  try {
81756
82217
  return typeof localStorage !== "undefined" && localStorage.getItem(ORIGINAL_QUALITY_KEY) === "1";
@@ -81765,18 +82226,18 @@ function FieldEditorInner({ data }) {
81765
82226
  const caps = useEditorCapabilities();
81766
82227
  const fieldSession = dataSource.session();
81767
82228
  const displayLocale = fieldSession.locale;
81768
- const notify = useCallback44(
82229
+ const notify = useCallback47(
81769
82230
  (message, variant, opts) => dataSource.notify(message, variant, opts),
81770
82231
  [dataSource]
81771
82232
  );
81772
82233
  const ensureHuman = useFieldEnsureHuman();
81773
82234
  const savePropertyFacts = usePropertyFactsSave(String(data.inspection.id));
81774
- const cameraInputRef = useRef61(null);
81775
- const libraryInputRef = useRef61(null);
81776
- const [addMediaChooser, setAddMediaChooser] = useState63(null);
81777
- const [videoCaptureTarget, setVideoCaptureTarget] = useState63(null);
81778
- const [activeUnitId, setActiveUnitId] = useState63(null);
81779
- const seededInspection = useMemo14(() => {
82235
+ const cameraInputRef = useRef62(null);
82236
+ const libraryInputRef = useRef62(null);
82237
+ const [addMediaChooser, setAddMediaChooser] = useState64(null);
82238
+ const [videoCaptureTarget, setVideoCaptureTarget] = useState64(null);
82239
+ const [activeUnitId, setActiveUnitId] = useState64(null);
82240
+ const seededInspection = useMemo16(() => {
81780
82241
  const insp = data.inspection;
81781
82242
  const envelope = insp.propertyFacts ?? {};
81782
82243
  return { ...envelope, ...insp };
@@ -81789,7 +82250,7 @@ function FieldEditorInner({ data }) {
81789
82250
  ratingLevels: data.ratingLevels,
81790
82251
  activeUnitId
81791
82252
  });
81792
- const activePropertyPreset = useMemo14(() => {
82253
+ const activePropertyPreset = useMemo16(() => {
81793
82254
  const insp = state.inspection;
81794
82255
  return resolveActivePropertyPreset(
81795
82256
  insp.propertyType,
@@ -81797,7 +82258,7 @@ function FieldEditorInner({ data }) {
81797
82258
  data.commercialPresets
81798
82259
  );
81799
82260
  }, [state.inspection, data.commercialPresets]);
81800
- const drainRef = useRef61(() => {
82261
+ const drainRef = useRef62(() => {
81801
82262
  });
81802
82263
  const collab = useResultsDoc(String(data.inspection.id), () => drainRef.current());
81803
82264
  const writeDoc = readOnly ? null : collab?.doc ?? null;
@@ -81818,7 +82279,7 @@ function FieldEditorInner({ data }) {
81818
82279
  });
81819
82280
  const { prefs: inspectionPrefs, loaded: prefsLoaded } = useInspectionPrefs();
81820
82281
  const tagLibrary = data.tagLibrary ?? [];
81821
- const catColor = useMemo14(() => {
82282
+ const catColor = useMemo16(() => {
81822
82283
  const map = /* @__PURE__ */ new Map();
81823
82284
  for (const c of data.defectCategories ?? []) {
81824
82285
  map.set(c.name, c.color);
@@ -81826,18 +82287,18 @@ function FieldEditorInner({ data }) {
81826
82287
  }
81827
82288
  return map;
81828
82289
  }, [data.defectCategories]);
81829
- const pinnedTags = useMemo14(() => {
82290
+ const pinnedTags = useMemo16(() => {
81830
82291
  return inspectionPrefs.pinnedTagIds.map((id) => tagLibrary.find((t) => t.id === id)).filter((t) => Boolean(t));
81831
82292
  }, [inspectionPrefs.pinnedTagIds, tagLibrary]);
81832
- const activeTagIds = useMemo14(() => {
82293
+ const activeTagIds = useMemo16(() => {
81833
82294
  if (!state.activeItemId) return /* @__PURE__ */ new Set();
81834
82295
  const tags = state.tagsByItem?.[state.activeItemId] || [];
81835
82296
  return new Set(tags.map((t) => t.id));
81836
82297
  }, [state.activeItemId, state.tagsByItem]);
81837
- const [publishReadiness, setPublishReadiness] = useState63(null);
81838
- const [showPublishGate, setShowPublishGate] = useState63(false);
82298
+ const [publishReadiness, setPublishReadiness] = useState64(null);
82299
+ const [showPublishGate, setShowPublishGate] = useState64(false);
81839
82300
  const activeResult = state.activeItemId ? findings.getResult(state.activeItemId, state.currentSection?.id) : null;
81840
- const defectStates = useMemo14(() => {
82301
+ const defectStates = useMemo16(() => {
81841
82302
  const map = /* @__PURE__ */ new Map();
81842
82303
  const defects = activeResult?.tabs;
81843
82304
  const rows = Array.isArray(defects?.defects) ? defects.defects : [];
@@ -81853,7 +82314,7 @@ function FieldEditorInner({ data }) {
81853
82314
  }
81854
82315
  return map;
81855
82316
  }, [activeResult]);
81856
- const inspectionPhotoCount = useMemo14(() => {
82317
+ const inspectionPhotoCount = useMemo16(() => {
81857
82318
  const keys = /* @__PURE__ */ new Set();
81858
82319
  for (const value of Object.values(state.results)) {
81859
82320
  const photos = value?.photos;
@@ -81864,7 +82325,7 @@ function FieldEditorInner({ data }) {
81864
82325
  }
81865
82326
  return keys.size;
81866
82327
  }, [state.results]);
81867
- const locationSuggestions = useMemo14(() => {
82328
+ const locationSuggestions = useMemo16(() => {
81868
82329
  const set = /* @__PURE__ */ new Set();
81869
82330
  for (const value of Object.values(state.results)) {
81870
82331
  const tabs = value?.tabs;
@@ -81875,7 +82336,7 @@ function FieldEditorInner({ data }) {
81875
82336
  }
81876
82337
  return Array.from(set);
81877
82338
  }, [state.results]);
81878
- const missingFields = useMemo14(() => {
82339
+ const missingFields = useMemo16(() => {
81879
82340
  const map = /* @__PURE__ */ new Map();
81880
82341
  if (!publishReadiness) return map;
81881
82342
  for (const b of publishReadiness.blockingDefects) {
@@ -81886,7 +82347,7 @@ function FieldEditorInner({ data }) {
81886
82347
  }
81887
82348
  return map;
81888
82349
  }, [publishReadiness]);
81889
- const requiredDefectFields = useMemo14(() => {
82350
+ const requiredDefectFields = useMemo16(() => {
81890
82351
  const override = data.inspection.requireDefectFieldsOverride;
81891
82352
  const effective = override ?? inspectionPrefs.requireDefectFields;
81892
82353
  return {
@@ -81898,9 +82359,9 @@ function FieldEditorInner({ data }) {
81898
82359
  inspectionId: String(state.inspection.id),
81899
82360
  severityForRatingId: state.severityForRatingId
81900
82361
  });
81901
- const [serverComments, setServerComments] = useState63([]);
81902
- const [librarySideOpen, setLibrarySideOpen] = useState63(false);
81903
- useEffect49(() => {
82362
+ const [serverComments, setServerComments] = useState64([]);
82363
+ const [librarySideOpen, setLibrarySideOpen] = useState64(false);
82364
+ useEffect51(() => {
81904
82365
  if (!state.showCommentLibrary && !librarySideOpen) {
81905
82366
  setServerComments([]);
81906
82367
  return;
@@ -81947,11 +82408,11 @@ function FieldEditorInner({ data }) {
81947
82408
  const units = data.units ?? [];
81948
82409
  const unitInspectionMode = data.unitInspectionMode ?? "tagged";
81949
82410
  const isPerUnit = unitInspectionMode === "per_unit";
81950
- const [unitsManagerOpen, setUnitsManagerOpen] = useState63(false);
82411
+ const [unitsManagerOpen, setUnitsManagerOpen] = useState64(false);
81951
82412
  const showUnitsSurface = data.inspection.propertyType === "commercial";
81952
- const [costItemsOpen, setCostItemsOpen] = useState63(false);
82413
+ const [costItemsOpen, setCostItemsOpen] = useState64(false);
81953
82414
  const collabSynced = Boolean(collab?.synced);
81954
- const mergeScopeSlice = useCallback44(
82415
+ const mergeScopeSlice = useCallback47(
81955
82416
  (slice) => state.setResults((prev) => ({ ...prev, ...slice })),
81956
82417
  [state.setResults]
81957
82418
  );
@@ -81960,14 +82421,14 @@ function FieldEditorInner({ data }) {
81960
82421
  collabSynced,
81961
82422
  onSlice: mergeScopeSlice
81962
82423
  });
81963
- const requestScope = useCallback44(
82424
+ const requestScope = useCallback47(
81964
82425
  (unitId) => {
81965
82426
  setActiveUnitId(unitId);
81966
82427
  loadScope(unitId);
81967
82428
  },
81968
82429
  [loadScope]
81969
82430
  );
81970
- useEffect49(() => {
82431
+ useEffect51(() => {
81971
82432
  if (activeUnitId == null) return;
81972
82433
  if (!isPerUnit || !units.some((u) => u.id === activeUnitId)) {
81973
82434
  setActiveUnitId(null);
@@ -81981,29 +82442,32 @@ function FieldEditorInner({ data }) {
81981
82442
  userName: "Inspector",
81982
82443
  enabled: true
81983
82444
  });
81984
- useEffect49(() => {
82445
+ useEffect51(() => {
81985
82446
  presence.setFocus(state.activeItemId);
81986
82447
  }, [state.activeItemId]);
81987
- const [tagPickerOpen, setTagPickerOpen] = useState63(false);
81988
- const [autoSign, setAutoSign] = useState63(
82448
+ const [tagPickerOpen, setTagPickerOpen] = useState64(false);
82449
+ const [autoSign, setAutoSign] = useState64(
81989
82450
  !!state.inspection.autoSignOnPublish
81990
82451
  );
81991
- const [signModalOpen, setSignModalOpen] = useState63(false);
82452
+ const [signModalOpen, setSignModalOpen] = useState64(false);
81992
82453
  const inspectorSignature = useInspectorSignature(String(data.inspection.id), () => setSignModalOpen(false));
81993
- const [versionHistoryOpen, setVersionHistoryOpen] = useState63(false);
81994
- useEffect49(() => {
82454
+ const [versionHistoryOpen, setVersionHistoryOpen] = useState64(false);
82455
+ useEffect51(() => {
81995
82456
  setAutoSign(!!state.inspection.autoSignOnPublish);
81996
82457
  }, [state.inspection]);
81997
- const handleAutoSignToggle = useCallback44((checked) => setAutoSign(checked), []);
82458
+ const handleAutoSignToggle = useCallback47((checked) => setAutoSign(checked), []);
81998
82459
  const publishFlow = usePublishFlow(String(state.inspection.id), {
81999
82460
  confirmHuman: ensureHuman,
82000
82461
  // The editor's inspection copy is frozen at mount (see useInspectionState), so revalidation alone
82001
82462
  // leaves the header badge and the toolbar button stale: patch the status locally as structural edits do.
82002
82463
  onMarkedComplete: () => state.setInspection((prev) => ({ ...prev, status: INSPECTION_STATUS_COMPLETED })),
82003
- onFieldworkFinished: () => state.setInspection((prev) => ({ ...prev, status: INSPECTION_STATUS_COMPLETED })),
82464
+ onFieldworkFinished: () => {
82465
+ state.setInspection((prev) => ({ ...prev, status: INSPECTION_STATUS_COMPLETED }));
82466
+ notify(index_exports.editor_finish_fieldwork_done(), "success", { durationMs: 3e3 });
82467
+ },
82004
82468
  onPublished: () => state.setShowPublishModal(false)
82005
82469
  });
82006
- const handlePublishClick = useCallback44(async () => {
82470
+ const handlePublishClick = useCallback47(async () => {
82007
82471
  publishFlow.clearPublishError();
82008
82472
  const readiness = await dataSource.resources.publishReadiness?.check(String(state.inspection.id)) ?? null;
82009
82473
  if (readiness && (!readiness.ready || (readiness.warningDefects?.length ?? 0) > 0)) {
@@ -82013,25 +82477,24 @@ function FieldEditorInner({ data }) {
82013
82477
  }
82014
82478
  state.setShowPublishModal(true);
82015
82479
  }, [dataSource, state.inspection.id, state.setShowPublishModal, publishFlow.clearPublishError]);
82016
- useEffect49(() => {
82480
+ useEffect51(() => {
82017
82481
  if (publishFlow.finishError) notify(publishFlow.finishError, "error", { durationMs: 8e3 });
82018
82482
  }, [publishFlow.finishError, notify]);
82019
- const handleFinishFieldwork = useCallback44(() => {
82020
- void publishFlow.finishFieldwork();
82021
- }, [publishFlow]);
82483
+ useScrollTopOnDetails(state.activeView);
82484
+ const { request: handleFinishFieldwork, dialog: finishDialog } = useFinishFieldworkConfirm(publishFlow.finishFieldwork, publishFlow.finishing);
82022
82485
  const commercialSave = useCommercialReportSave(String(state.inspection.id), (patch) => {
82023
82486
  state.setInspection((prev) => ({ ...prev, ...patch }));
82024
82487
  });
82025
- const handleItemAttribute = useCallback44((itemId, attributeId, value) => {
82488
+ const handleItemAttribute = useCallback47((itemId, attributeId, value) => {
82026
82489
  const sectionId = state.sectionIdForItem(itemId);
82027
82490
  if (sectionId) findings.setItemAttribute(sectionId, itemId, attributeId, value);
82028
82491
  }, [findings, state.sectionIdForItem]);
82029
- const [photoStudioOpen, setPhotoStudioOpen] = useState63(false);
82030
- const [photoStudioUrl, setPhotoStudioUrl] = useState63(null);
82031
- const [photoStudioKey, setPhotoStudioKey] = useState63(null);
82032
- const [photoStudioIndex, setPhotoStudioIndex] = useState63(0);
82033
- const [photoStudioTotal, setPhotoStudioTotal] = useState63(0);
82034
- const openPhotoStudio = useCallback44(
82492
+ const [photoStudioOpen, setPhotoStudioOpen] = useState64(false);
82493
+ const [photoStudioUrl, setPhotoStudioUrl] = useState64(null);
82494
+ const [photoStudioKey, setPhotoStudioKey] = useState64(null);
82495
+ const [photoStudioIndex, setPhotoStudioIndex] = useState64(0);
82496
+ const [photoStudioTotal, setPhotoStudioTotal] = useState64(0);
82497
+ const openPhotoStudio = useCallback47(
82035
82498
  (next) => {
82036
82499
  setPhotoStudioUrl(next.url);
82037
82500
  setPhotoStudioKey(next.key);
@@ -82044,10 +82507,10 @@ function FieldEditorInner({ data }) {
82044
82507
  const coverWrites = useReportCoverWrites(String(state.inspection.id));
82045
82508
  const statutoryGroups = useStatutoryGroups(data.templateSnapshot);
82046
82509
  const canEditStructure = useStructuralEditingAllowed(data.templateSnapshot) && !readOnly;
82047
- const [addingToGroup, setAddingToGroup] = useState63(null);
82510
+ const [addingToGroup, setAddingToGroup] = useState64(null);
82048
82511
  const statutoryInstance = useAddStatutoryInstance(String(data.inspection.id));
82049
82512
  const formProgress = useStatutoryFormProgress(data.templateSnapshot, state.results);
82050
- const revisionBannerEl = data.revisionStatus ? /* @__PURE__ */ jsx134("div", { className: "mb-4", children: /* @__PURE__ */ jsx134(
82513
+ const revisionBannerEl = data.revisionStatus ? /* @__PURE__ */ jsx135("div", { className: "mb-4", children: /* @__PURE__ */ jsx135(
82051
82514
  RevisionBanner,
82052
82515
  {
82053
82516
  status: data.revisionStatus,
@@ -82112,36 +82575,33 @@ function FieldEditorInner({ data }) {
82112
82575
  const layout = useEditorLayout(data.initialLayout, slots.onLayoutObserved);
82113
82576
  const isPhone = layout === "phone";
82114
82577
  const isMobile = layout !== "desktop";
82115
- const [mobileDrawer, setMobileDrawer] = useState63(null);
82578
+ const [mobileDrawer, setMobileDrawer] = useState64(null);
82116
82579
  const itemUi = useItemEditorUiStore();
82117
- const notesFlushRef = useRef61(null);
82580
+ const notesFlushRef = useRef62(null);
82118
82581
  useResetBatchOnPhone(isPhone, state.batchMode, state.setBatchMode, state.setBatchSelected);
82119
- const PRESET_TAGS = useMemo14(() => [
82582
+ const { toggle: toggleItemTag, canSave: canSaveTags } = useItemTags(String(state.inspection.id), state.setTagsByItem);
82583
+ const PRESET_TAGS = useMemo16(() => tagLibrary.length > 0 || canSaveTags ? tagLibrary : [
82120
82584
  { id: "follow-up", name: "Follow Up", color: "#ef4444" },
82121
82585
  { id: "urgent", name: "Urgent", color: "#f97316" },
82122
82586
  { id: "photo-needed", name: "Photo Needed", color: "#eab308" },
82123
82587
  { id: "re-inspect", name: "Re-inspect", color: "#3b82f6" },
82124
82588
  { id: "client-question", name: "Client Question", color: "#a855f7" }
82125
- ], []);
82126
- const toggleTag = useCallback44((tag) => {
82589
+ ], [tagLibrary, canSaveTags]);
82590
+ const toggleTag = useCallback47((tag) => {
82127
82591
  if (!state.activeItemId) return;
82128
- const current = state.tagsByItem[state.activeItemId] || [];
82129
- const exists = current.some((t) => t.id === tag.id);
82130
- const updated = exists ? current.filter((t) => t.id !== tag.id) : [...current, tag];
82131
- state.setTagsByItem((prev) => ({
82132
- ...prev,
82133
- [state.activeItemId]: updated
82134
- }));
82135
- }, [state.activeItemId, state.tagsByItem, state.setTagsByItem]);
82136
- const tagChipRow = state.activeItemId && !readOnly ? /* @__PURE__ */ jsx134(
82592
+ toggleItemTag(state.activeItemId, tag, state.tagsByItem[state.activeItemId] || []);
82593
+ }, [state.activeItemId, state.tagsByItem, toggleItemTag]);
82594
+ const tagChipRow = state.activeItemId && !readOnly ? /* @__PURE__ */ jsx135(
82137
82595
  TagChipRow,
82138
82596
  {
82139
82597
  pinnedTags,
82598
+ chosenTags: state.tagsByItem?.[state.activeItemId],
82140
82599
  activeTagIds,
82141
- onToggle: (tag) => toggleTag(tag)
82600
+ onToggle: (tag) => toggleTag(tag),
82601
+ onOpenLibrary: () => setTagPickerOpen(true)
82142
82602
  }
82143
82603
  ) : null;
82144
- const handleCloneLast = useCallback44((scope) => {
82604
+ const handleCloneLast = useCallback47((scope) => {
82145
82605
  if (!state.activeItemId || !state.currentSection) return;
82146
82606
  findings.cloneLast(
82147
82607
  state.currentSection.id,
@@ -82150,7 +82610,7 @@ function FieldEditorInner({ data }) {
82150
82610
  scope
82151
82611
  );
82152
82612
  }, [findings, state.activeItemId, state.currentSection, state.currentSectionItems]);
82153
- useEffect49(() => {
82613
+ useEffect51(() => {
82154
82614
  const submitting = photoUpload.busy;
82155
82615
  const failed = photoUpload.failedSinceIdle;
82156
82616
  if (submitting) {
@@ -82167,11 +82627,11 @@ function FieldEditorInner({ data }) {
82167
82627
  }
82168
82628
  }
82169
82629
  }, [photoUpload.busy]);
82170
- const commitRating = useCallback44(
82630
+ const commitRating = useCallback47(
82171
82631
  (sectionId, itemId, rating) => findings.setRating(sectionId, itemId, rating),
82172
82632
  [findings]
82173
82633
  );
82174
- const handleRating = useCallback44(
82634
+ const handleRating = useCallback47(
82175
82635
  (rating, source = "pointer") => {
82176
82636
  if (!state.activeItemId || !state.currentSection) return;
82177
82637
  commitRating(state.currentSection.id, state.activeItemId, rating);
@@ -82203,14 +82663,14 @@ function FieldEditorInner({ data }) {
82203
82663
  },
82204
82664
  [state.activeItemId, state.currentSection, commitRating, state.advanceToNextUnrated, state.ratingLevels, prefsLoaded, inspectionPrefs.autoAdvance, inspectionPrefs.autoAdvanceDelayMs, notify]
82205
82665
  );
82206
- const commentLibraryItems = useMemo14(
82666
+ const commentLibraryItems = useMemo16(
82207
82667
  () => comments.getFilteredComments(
82208
82668
  state.commentLibraryFilter,
82209
82669
  state.commentLibrarySearch
82210
82670
  ),
82211
82671
  [comments, state.commentLibraryFilter, state.commentLibrarySearch]
82212
82672
  );
82213
- const buildSpeedQueue = useCallback44(() => {
82673
+ const buildSpeedQueue = useCallback47(() => {
82214
82674
  const flatItems = [];
82215
82675
  for (let s = 0; s < state.sections.length; s++) {
82216
82676
  const sec = state.sections[s];
@@ -82229,8 +82689,8 @@ function FieldEditorInner({ data }) {
82229
82689
  const queue = flatItems.map((it, idx) => ({ idx, rating: it.rating })).filter((x) => !x.rating).map((x) => x.idx);
82230
82690
  return { flatItems, queue };
82231
82691
  }, [state.sections, state.getResult]);
82232
- const speedUnrated = useMemo14(() => isMobile && !readOnly ? countUnratedItems(state.sections, state.getResult) : 0, [isMobile, readOnly, state.sections, state.getResult, state.results]);
82233
- const toggleSpeedMode = useCallback44(() => {
82692
+ const speedUnrated = useMemo16(() => isMobile && !readOnly ? countUnratedItems(state.sections, state.getResult) : 0, [isMobile, readOnly, state.sections, state.getResult, state.results]);
82693
+ const toggleSpeedMode = useCallback47(() => {
82234
82694
  if (!state.speedMode) {
82235
82695
  const { flatItems, queue } = buildSpeedQueue();
82236
82696
  if (queue.length === 0) return;
@@ -82242,7 +82702,7 @@ function FieldEditorInner({ data }) {
82242
82702
  state.setSpeedMode(false);
82243
82703
  }
82244
82704
  }, [state, buildSpeedQueue]);
82245
- const openSpeedMode = useCallback44(() => {
82705
+ const openSpeedMode = useCallback47(() => {
82246
82706
  if (!state.speedMode) toggleSpeedMode();
82247
82707
  }, [state.speedMode, toggleSpeedMode]);
82248
82708
  const { speedRate, undoSpeedRating, finishedItem, pendingUndo, setPendingUndo } = useSpeedRating({
@@ -82252,23 +82712,23 @@ function FieldEditorInner({ data }) {
82252
82712
  activeUnitId,
82253
82713
  onUndoUnavailable: () => dataSource.notify(index_exports.editor_speedmode_undo_unavailable(), "error")
82254
82714
  });
82255
- const speedItem = useMemo14(() => {
82715
+ const speedItem = useMemo16(() => {
82256
82716
  if (!state.speedMode) return null;
82257
82717
  const idx = state.speedQueue[state.speedCurrent];
82258
82718
  return idx != null ? state.speedItemsRef.current[idx] || null : null;
82259
82719
  }, [state.speedMode, state.speedQueue, state.speedCurrent]);
82260
- const speedNext = useCallback44(() => {
82720
+ const speedNext = useCallback47(() => {
82261
82721
  if (state.speedCurrent < state.speedQueue.length - 1) {
82262
82722
  state.setSpeedCurrent(state.speedCurrent + 1);
82263
82723
  }
82264
82724
  }, [state.speedCurrent, state.speedQueue.length, state.setSpeedCurrent]);
82265
- const speedPrev = useCallback44(() => {
82725
+ const speedPrev = useCallback47(() => {
82266
82726
  if (state.speedCurrent > 0) {
82267
82727
  state.setSpeedCurrent(state.speedCurrent - 1);
82268
82728
  }
82269
82729
  }, [state.speedCurrent, state.setSpeedCurrent]);
82270
82730
  const shownSpeedItem = speedItem ?? finishedItem;
82271
- const speedModeEl = state.speedMode && shownSpeedItem ? /* @__PURE__ */ jsx134(
82731
+ const speedModeEl = state.speedMode && shownSpeedItem ? /* @__PURE__ */ jsx135(
82272
82732
  SpeedMode,
82273
82733
  {
82274
82734
  item: {
@@ -82306,9 +82766,9 @@ function FieldEditorInner({ data }) {
82306
82766
  sections: state.sections
82307
82767
  }
82308
82768
  ) : null;
82309
- const pendingPhotoTargetRef = useRef61(null);
82769
+ const pendingPhotoTargetRef = useRef62(null);
82310
82770
  const MAX_BATCH_PHOTOS = 20;
82311
- const handlePhotoUpload = useCallback44(
82771
+ const handlePhotoUpload = useCallback47(
82312
82772
  (e) => {
82313
82773
  const all = Array.from(e.target.files ?? []);
82314
82774
  if (all.length === 0 || !state.activeItemId || readOnly) return;
@@ -82342,7 +82802,7 @@ function FieldEditorInner({ data }) {
82342
82802
  },
82343
82803
  [state.activeItemId, state.inspection.id, dataSource, photoUpload.submit, notify, writeDoc, readOnly, state.sectionIdForItem, state.currentSection, activeUnitId]
82344
82804
  );
82345
- const handleBurstCommit = useCallback44(
82805
+ const handleBurstCommit = useCallback47(
82346
82806
  (blobs) => {
82347
82807
  if (blobs.length === 0 || readOnly) return;
82348
82808
  if (!state.burstCameraItemId) {
@@ -82363,8 +82823,8 @@ function FieldEditorInner({ data }) {
82363
82823
  },
82364
82824
  [state.burstCameraItemId, state.sectionIdForItem, state.currentSection, activeUnitId, photoUpload.submit, notify, readOnly]
82365
82825
  );
82366
- const processedUploads = useRef61(/* @__PURE__ */ new WeakSet());
82367
- useEffect49(() => {
82826
+ const processedUploads = useRef62(/* @__PURE__ */ new WeakSet());
82827
+ useEffect51(() => {
82368
82828
  const settled = photoUpload.outcomes.filter((o) => !processedUploads.current.has(o));
82369
82829
  if (settled.length === 0) return;
82370
82830
  for (const o of settled) processedUploads.current.add(o);
@@ -82403,17 +82863,17 @@ function FieldEditorInner({ data }) {
82403
82863
  }
82404
82864
  }
82405
82865
  }, [photoUpload.outcomes, photoUpload.consume, findings, notify]);
82406
- const openSnippets = useCallback44(() => {
82866
+ const openSnippets = useCallback47(() => {
82407
82867
  if (!state.activeItemId) return;
82408
82868
  state.setCommentLibraryFilter("my-snippets");
82409
82869
  state.setCommentLibrarySearch("");
82410
82870
  state.setCommentLibrarySelectedIdx(0);
82411
82871
  state.setShowCommentLibrary(true);
82412
82872
  }, [state]);
82413
- const onInsert = useCallback44((sectionId, itemId, text) => {
82873
+ const onInsert = useCallback47((sectionId, itemId, text) => {
82414
82874
  findings.insertComment(sectionId, itemId, text);
82415
82875
  }, [findings]);
82416
- const keyboardHandlers = useMemo14(
82876
+ const keyboardHandlers = useMemo16(
82417
82877
  () => ({
82418
82878
  onRate: (level) => {
82419
82879
  if (state.activeItemId && state.currentSection && state.ratingLevels[level - 1]) {
@@ -82557,7 +83017,7 @@ function FieldEditorInner({ data }) {
82557
83017
  ]
82558
83018
  );
82559
83019
  useKeyboard(readOnly ? readOnlyKeyboard(keyboardHandlers) : keyboardHandlers, true);
82560
- const visibleItems = useMemo14(() => {
83020
+ const visibleItems = useMemo16(() => {
82561
83021
  return state.currentSectionItems.filter((item) => {
82562
83022
  if (!state.itemPassesFilter(item, state.currentSection?.id)) return false;
82563
83023
  if (state.searchNeedle && !state.itemMatchesSearch(state.currentSection, item))
@@ -82565,7 +83025,7 @@ function FieldEditorInner({ data }) {
82565
83025
  return true;
82566
83026
  });
82567
83027
  }, [state]);
82568
- const sectionRailEl = /* @__PURE__ */ jsx134(
83028
+ const sectionRailEl = /* @__PURE__ */ jsx135(
82569
83029
  SectionRail,
82570
83030
  {
82571
83031
  mode: "fill",
@@ -82594,7 +83054,7 @@ function FieldEditorInner({ data }) {
82594
83054
  onRenameSection: readOnly ? void 0 : structure.renameSection
82595
83055
  }
82596
83056
  );
82597
- const itemListEl = /* @__PURE__ */ jsx134(
83057
+ const itemListEl = /* @__PURE__ */ jsx135(
82598
83058
  ItemList,
82599
83059
  {
82600
83060
  mode: "fill",
@@ -82607,6 +83067,7 @@ function FieldEditorInner({ data }) {
82607
83067
  if (isMobile) setMobileDrawer(null);
82608
83068
  },
82609
83069
  results: state.results,
83070
+ getRatingColor: state.getRatingColor,
82610
83071
  activeUnitId,
82611
83072
  batchMode: state.batchMode,
82612
83073
  batchSelected: state.batchSelected,
@@ -82620,10 +83081,11 @@ function FieldEditorInner({ data }) {
82620
83081
  onRenameItem: canEditStructure ? (itemId, label) => structure.renameItem(state.currentSection?.id || "", itemId, label) : void 0,
82621
83082
  groups: statutoryGroups,
82622
83083
  onAddGroupInstance: readOnly ? void 0 : (groupId) => setAddingToGroup(groupId),
82623
- pane: layout === "tablet"
83084
+ pane: layout === "tablet",
83085
+ fullWidth: isPhone
82624
83086
  }
82625
83087
  );
82626
- const itemEditorEl = state.activeItemId ? readOnly ? /* @__PURE__ */ jsx134(ReadOnlyItemView, { item: state.activeItem, sectionTitle: state.currentSection?.title, result: findings.getResult(state.activeItemId, state.currentSection?.id), ratingLevels: state.ratingLevels, inspectionId: String(state.inspection.id), coverKey, onOpenPhoto, videoPosterUrl, pendingPhotoUrl }) : /* @__PURE__ */ jsx134(
83088
+ const itemEditorEl = state.activeItemId ? readOnly ? /* @__PURE__ */ jsx135(ReadOnlyItemView, { item: state.activeItem, sectionTitle: state.currentSection?.title, result: findings.getResult(state.activeItemId, state.currentSection?.id), ratingLevels: state.ratingLevels, inspectionId: String(state.inspection.id), coverKey, onOpenPhoto, videoPosterUrl, pendingPhotoUrl }) : /* @__PURE__ */ jsx135(
82627
83089
  ItemEditor,
82628
83090
  {
82629
83091
  uiControl: itemUi(state.activeItemId),
@@ -82678,14 +83140,9 @@ function FieldEditorInner({ data }) {
82678
83140
  );
82679
83141
  }
82680
83142
  },
82681
- onNotesBlur: (notes) => {
82682
- if (state.activeItemId && state.currentSection) {
82683
- findings.commitNotes(
82684
- state.currentSection.id,
82685
- state.activeItemId,
82686
- notes
82687
- );
82688
- }
83143
+ onNotesBlur: (notes, itemId) => {
83144
+ const target = notesCommitTarget({ itemId, activeItemId: state.activeItemId, currentSectionId: state.currentSection?.id, sectionIdForItem: state.sectionIdForItem });
83145
+ if (target) findings.commitNotes(target.sectionId, target.itemId, notes);
82689
83146
  },
82690
83147
  onValue: (value) => {
82691
83148
  if (state.activeItemId && state.currentSection) {
@@ -82763,7 +83220,7 @@ function FieldEditorInner({ data }) {
82763
83220
  ) : (
82764
83221
  /* F12 — the pane offers the next item instead of naming two keys. Items come from the
82765
83222
  * SECTION, not `visibleItems`: a filter emptying the list is not an empty section. */
82766
- /* @__PURE__ */ jsx134(
83223
+ /* @__PURE__ */ jsx135(
82767
83224
  ItemPaneEmptyState,
82768
83225
  {
82769
83226
  items: state.currentSectionItems.map((it) => ({ id: it.id, label: it.label || it.name || "" })),
@@ -82775,7 +83232,7 @@ function FieldEditorInner({ data }) {
82775
83232
  }
82776
83233
  )
82777
83234
  );
82778
- const sideRailEl = /* @__PURE__ */ jsx134(
83235
+ const sideRailEl = /* @__PURE__ */ jsx135(
82779
83236
  SideRail,
82780
83237
  {
82781
83238
  fill: isMobile,
@@ -82803,9 +83260,9 @@ function FieldEditorInner({ data }) {
82803
83260
  }
82804
83261
  );
82805
83262
  const emptyTemplateEl = state.sections.length === 0 ? /* @__PURE__ */ jsxs114("div", { className: "flex flex-col items-center justify-center h-full gap-4 text-center px-6", children: [
82806
- /* @__PURE__ */ jsx134("p", { className: "text-[15px] font-semibold text-ih-fg-1", children: index_exports.editor_route_empty_template_title() }),
82807
- /* @__PURE__ */ jsx134("p", { className: "text-[13px] text-ih-fg-3 max-w-sm", children: index_exports.editor_route_empty_template_desc() }),
82808
- caps.settingsSheet && !readOnly && /* @__PURE__ */ jsx134(
83263
+ /* @__PURE__ */ jsx135("p", { className: "text-[15px] font-semibold text-ih-fg-1", children: index_exports.editor_route_empty_template_title() }),
83264
+ /* @__PURE__ */ jsx135("p", { className: "text-[13px] text-ih-fg-3 max-w-sm", children: index_exports.editor_route_empty_template_desc() }),
83265
+ caps.settingsSheet && !readOnly && /* @__PURE__ */ jsx135(
82809
83266
  Button,
82810
83267
  {
82811
83268
  variant: "primary",
@@ -82814,7 +83271,7 @@ function FieldEditorInner({ data }) {
82814
83271
  }
82815
83272
  )
82816
83273
  ] }) : null;
82817
- const propertyOverviewEl = /* @__PURE__ */ jsx134(
83274
+ const propertyOverviewEl = /* @__PURE__ */ jsx135(
82818
83275
  InspectionOverview,
82819
83276
  {
82820
83277
  inspection: state.inspection,
@@ -82833,7 +83290,7 @@ function FieldEditorInner({ data }) {
82833
83290
  }
82834
83291
  );
82835
83292
  const photoInputsEl = /* @__PURE__ */ jsxs114(Fragment37, { children: [
82836
- /* @__PURE__ */ jsx134(
83293
+ /* @__PURE__ */ jsx135(
82837
83294
  "input",
82838
83295
  {
82839
83296
  ref: cameraInputRef,
@@ -82844,7 +83301,7 @@ function FieldEditorInner({ data }) {
82844
83301
  onChange: handlePhotoUpload
82845
83302
  }
82846
83303
  ),
82847
- /* @__PURE__ */ jsx134(
83304
+ /* @__PURE__ */ jsx135(
82848
83305
  "input",
82849
83306
  {
82850
83307
  ref: libraryInputRef,
@@ -82857,7 +83314,7 @@ function FieldEditorInner({ data }) {
82857
83314
  )
82858
83315
  ] });
82859
83316
  const addMediaOverlaysEl = /* @__PURE__ */ jsxs114(Fragment37, { children: [
82860
- addMediaChooser && /* @__PURE__ */ jsx134(
83317
+ addMediaChooser && /* @__PURE__ */ jsx135(
82861
83318
  AddMediaChooser,
82862
83319
  {
82863
83320
  onClose: () => setAddMediaChooser(null),
@@ -82876,7 +83333,7 @@ function FieldEditorInner({ data }) {
82876
83333
  }
82877
83334
  }
82878
83335
  ),
82879
- videoCaptureTarget && /* @__PURE__ */ jsx134(
83336
+ videoCaptureTarget && /* @__PURE__ */ jsx135(
82880
83337
  VideoCapture,
82881
83338
  {
82882
83339
  inspectionId: String(state.inspection.id),
@@ -82891,7 +83348,7 @@ function FieldEditorInner({ data }) {
82891
83348
  )
82892
83349
  ] });
82893
83350
  const finishActionsEl = /* @__PURE__ */ jsxs114(Fragment37, { children: [
82894
- caps.settingsSheet && /* @__PURE__ */ jsx134(
83351
+ caps.settingsSheet && /* @__PURE__ */ jsx135(
82895
83352
  InspectionSettingsSheet,
82896
83353
  {
82897
83354
  open: state.settingsOpen,
@@ -82900,7 +83357,7 @@ function FieldEditorInner({ data }) {
82900
83357
  onTemplateApplied: restartAfterTemplate
82901
83358
  }
82902
83359
  ),
82903
- /* @__PURE__ */ jsx134(
83360
+ /* @__PURE__ */ jsx135(
82904
83361
  PublishModal,
82905
83362
  {
82906
83363
  open: state.showPublishModal,
@@ -82922,7 +83379,7 @@ function FieldEditorInner({ data }) {
82922
83379
  onAutoSignToggle: handleAutoSignToggle
82923
83380
  }
82924
83381
  ),
82925
- /* @__PURE__ */ jsx134(
83382
+ /* @__PURE__ */ jsx135(
82926
83383
  SignModal,
82927
83384
  {
82928
83385
  open: signModalOpen,
@@ -82931,7 +83388,7 @@ function FieldEditorInner({ data }) {
82931
83388
  failed: inspectorSignature.failed
82932
83389
  }
82933
83390
  ),
82934
- /* @__PURE__ */ jsx134(
83391
+ /* @__PURE__ */ jsx135(
82935
83392
  PublishGateModal,
82936
83393
  {
82937
83394
  open: showPublishGate,
@@ -82955,7 +83412,7 @@ function FieldEditorInner({ data }) {
82955
83412
  }
82956
83413
  )
82957
83414
  ] });
82958
- const sharedOverlaysEl = /* @__PURE__ */ jsx134(
83415
+ const sharedOverlaysEl = /* @__PURE__ */ jsx135(
82959
83416
  SharedOverlays,
82960
83417
  {
82961
83418
  state,
@@ -82992,8 +83449,8 @@ function FieldEditorInner({ data }) {
82992
83449
  setCostItemsOpen
82993
83450
  }
82994
83451
  );
82995
- const itemFilterBarEl = /* @__PURE__ */ jsx134(ItemFilterBar, { state, formProgress, isMobile });
82996
- const batchBarEl = state.batchMode && state.selectedBatchCount > 0 ? /* @__PURE__ */ jsx134(
83452
+ const itemFilterBarEl = /* @__PURE__ */ jsx135(ItemFilterBar, { state, formProgress, isMobile });
83453
+ const batchBarEl = state.batchMode && state.selectedBatchCount > 0 ? /* @__PURE__ */ jsx135(
82997
83454
  BatchActionBar,
82998
83455
  {
82999
83456
  count: state.selectedBatchCount,
@@ -83028,7 +83485,7 @@ function FieldEditorInner({ data }) {
83028
83485
  }
83029
83486
  }
83030
83487
  ) : null;
83031
- const toolsDockEl = /* @__PURE__ */ jsx134(
83488
+ const toolsDockEl = /* @__PURE__ */ jsx135(
83032
83489
  InspectorToolsDock,
83033
83490
  {
83034
83491
  onToggleSpeedMode: toggleSpeedMode,
@@ -83076,18 +83533,18 @@ function FieldEditorInner({ data }) {
83076
83533
  sectionTitle: state.activeView === "property" ? "" : state.currentSection?.title ?? "",
83077
83534
  itemLabel: state.activeView === "property" ? index_exports.editor_shared_inspection_details() : (state.activeItem?.label || state.activeItem?.name) ?? index_exports.editor_route_select_an_item()
83078
83535
  };
83079
- const scopeSwitcherEl = showUnitsSurface && isPerUnit ? /* @__PURE__ */ jsx134(BreadcrumbDropdown, { units, activeUnitId, onSelect: requestScope }) : null;
83080
- const saveStatusEl = /* @__PURE__ */ jsx134(SaveStatusIndicator, { status: state.saveStatus });
83536
+ const scopeSwitcherEl = showUnitsSurface && isPerUnit ? /* @__PURE__ */ jsx135(BreadcrumbDropdown, { units, activeUnitId, onSelect: requestScope }) : null;
83537
+ const saveStatusEl = /* @__PURE__ */ jsx135(SaveStatusIndicator, { status: state.saveStatus });
83081
83538
  const { pdf } = slots;
83082
83539
  const mobileDrawersEl = /* @__PURE__ */ jsxs114(Fragment37, { children: [
83083
- /* @__PURE__ */ jsx134(MobileBottomDrawer, { open: mobileDrawer === "sections", onClose: () => setMobileDrawer(null), title: index_exports.editor_route_drawer_sections(), children: sectionRailEl }),
83540
+ /* @__PURE__ */ jsx135(MobileBottomDrawer, { open: mobileDrawer === "sections", onClose: () => setMobileDrawer(null), title: index_exports.editor_route_drawer_sections(), children: sectionRailEl }),
83084
83541
  isPhone && /* @__PURE__ */ jsxs114(MobileBottomDrawer, { open: mobileDrawer === "items", onClose: () => setMobileDrawer(null), title: index_exports.editor_route_drawer_items(), children: [
83085
- scopeSwitcherEl && /* @__PURE__ */ jsx134("div", { className: "px-2 pt-2", children: scopeSwitcherEl }),
83086
- speedUnrated > 0 && /* @__PURE__ */ jsx134("div", { className: "px-2 pt-2", children: /* @__PURE__ */ jsx134(SpeedEntryButton, { unrated: speedUnrated, onOpen: openSpeedFromDrawer, className: "w-full" }) }),
83542
+ scopeSwitcherEl && /* @__PURE__ */ jsx135("div", { className: "px-2 pt-2", children: scopeSwitcherEl }),
83543
+ speedUnrated > 0 && /* @__PURE__ */ jsx135("div", { className: "px-2 pt-2", children: /* @__PURE__ */ jsx135(SpeedEntryButton, { unrated: speedUnrated, onOpen: openSpeedFromDrawer, className: "w-full" }) }),
83087
83544
  itemListEl
83088
83545
  ] }),
83089
- /* @__PURE__ */ jsx134(MobileBottomDrawer, { open: mobileDrawer === "preview", onClose: () => setMobileDrawer(null), title: index_exports.editor_route_drawer_preview(), children: sideRailEl }),
83090
- /* @__PURE__ */ jsx134(
83546
+ /* @__PURE__ */ jsx135(MobileBottomDrawer, { open: mobileDrawer === "preview", onClose: () => setMobileDrawer(null), title: index_exports.editor_route_drawer_preview(), children: sideRailEl }),
83547
+ /* @__PURE__ */ jsx135(
83091
83548
  MobileFinishDrawer,
83092
83549
  {
83093
83550
  open: mobileDrawer === "actions",
@@ -83116,9 +83573,9 @@ function FieldEditorInner({ data }) {
83116
83573
  onOpenCostItems: showUnitsSurface && caps.costItems && !readOnly ? () => setCostItemsOpen(true) : null
83117
83574
  }
83118
83575
  ),
83119
- /* @__PURE__ */ jsx134(MobileBottomDrawer, { open: mobileDrawer === "theme", onClose: () => setMobileDrawer(null), title: index_exports.nav_theme_label(), children: /* @__PURE__ */ jsx134("div", { className: "p-4 [&_button]:min-h-11 [&_button]:min-w-11 [&_button]:touch-manipulation", children: slots.renderThemeControl?.({}) }) })
83576
+ /* @__PURE__ */ jsx135(MobileBottomDrawer, { open: mobileDrawer === "theme", onClose: () => setMobileDrawer(null), title: index_exports.nav_theme_label(), children: /* @__PURE__ */ jsx135("div", { className: "p-4 [&_button]:min-h-11 [&_button]:min-w-11 [&_button]:touch-manipulation", children: slots.renderThemeControl?.({}) }) })
83120
83577
  ] });
83121
- useEffect49(() => {
83578
+ useEffect51(() => {
83122
83579
  if (!readOnly) return;
83123
83580
  state.setShowPublishModal(false);
83124
83581
  setShowPublishGate(false);
@@ -83134,16 +83591,17 @@ function FieldEditorInner({ data }) {
83134
83591
  addMediaOverlaysEl,
83135
83592
  sharedOverlaysEl,
83136
83593
  speedModeEl,
83137
- !isPhone && state.showCheatsheet && /* @__PURE__ */ jsx134(KeyboardHud, { onClose: () => state.setShowCheatsheet(false) }),
83594
+ !isPhone && state.showCheatsheet && /* @__PURE__ */ jsx135(KeyboardHud, { onClose: () => state.setShowCheatsheet(false) }),
83138
83595
  isMobile && mobileDrawersEl,
83139
83596
  finishActionsEl,
83597
+ finishDialog,
83140
83598
  unsavedGuardEl
83141
83599
  ] });
83142
83600
  if (isPhone) {
83143
83601
  return /* @__PURE__ */ jsxs114(Fragment37, { children: [
83144
83602
  overlaysEl,
83145
83603
  /* @__PURE__ */ jsxs114("div", { "data-layout": "phone", "data-collab-synced": collabSynced ? "true" : "false", className: "min-h-dvh pb-[calc(3.5rem+env(safe-area-inset-bottom))]", children: [
83146
- /* @__PURE__ */ jsx134(
83604
+ /* @__PURE__ */ jsx135(
83147
83605
  AppBar,
83148
83606
  {
83149
83607
  platform: dataSource.platform,
@@ -83155,19 +83613,19 @@ function FieldEditorInner({ data }) {
83155
83613
  ),
83156
83614
  /* @__PURE__ */ jsxs114("main", { className: "p-4", children: [
83157
83615
  revisionBannerEl,
83158
- emptyTemplateEl ?? (state.activeView === "property" ? propertyOverviewEl : state.activeItemId ? itemEditorEl : /* @__PURE__ */ jsx134("p", { className: "text-center text-ih-fg-3 mt-12", children: index_exports.editor_route_mobile_begin() }))
83616
+ emptyTemplateEl ?? (state.activeView === "property" ? propertyOverviewEl : state.activeItemId ? itemEditorEl : /* @__PURE__ */ jsx135("p", { className: "text-center text-ih-fg-3 mt-12", children: index_exports.editor_route_mobile_begin() }))
83159
83617
  ] }),
83160
- /* @__PURE__ */ jsx134(MobileDrawerTriggers, { onOpen: (id) => setMobileDrawer(id), showTheme: caps.themeControl })
83618
+ /* @__PURE__ */ jsx135(MobileDrawerTriggers, { onOpen: (id) => setMobileDrawer(id), showTheme: caps.themeControl })
83161
83619
  ] }, "phone")
83162
83620
  ] });
83163
83621
  }
83164
83622
  if (layout === "tablet") {
83165
83623
  return /* @__PURE__ */ jsxs114(Fragment37, { children: [
83166
83624
  overlaysEl,
83167
- /* @__PURE__ */ jsx134(
83625
+ /* @__PURE__ */ jsx135(
83168
83626
  TabletTree,
83169
83627
  {
83170
- appBar: /* @__PURE__ */ jsx134(
83628
+ appBar: /* @__PURE__ */ jsx135(
83171
83629
  AppBar,
83172
83630
  {
83173
83631
  tablet: true,
@@ -83176,17 +83634,17 @@ function FieldEditorInner({ data }) {
83176
83634
  onBack: () => goBack(false),
83177
83635
  onOpenSections: () => setMobileDrawer("sections"),
83178
83636
  onMore: () => setMobileDrawer("actions"),
83179
- actions: /* @__PURE__ */ jsx134(TabletActions, { onPreview: () => setMobileDrawer("preview"), onPublish: handlePublishClick }),
83637
+ actions: /* @__PURE__ */ jsx135(TabletActions, { onPreview: () => setMobileDrawer("preview"), onPublish: handlePublishClick }),
83180
83638
  status: saveStatusEl
83181
83639
  }
83182
83640
  ),
83183
83641
  collabSynced,
83184
- footer: /* @__PURE__ */ jsx134(FooterBar, { connected: presence.connected, status: presence.status, roster: presence.roster }),
83642
+ footer: /* @__PURE__ */ jsx135(FooterBar, { connected: presence.connected, status: presence.status, roster: presence.roster }),
83185
83643
  listHeader: /* @__PURE__ */ jsxs114(Fragment37, { children: [
83186
- /* @__PURE__ */ jsx134(InspectionIdentity, { address: state.inspection.propertyAddress, id: String(state.inspection.id), date: state.formattedDate, status: String(state.inspection.status) }),
83187
- scopeSwitcherEl && /* @__PURE__ */ jsx134("div", { className: "px-3 py-2 border-b border-ih-border", children: scopeSwitcherEl }),
83644
+ /* @__PURE__ */ jsx135(InspectionIdentity, { address: state.inspection.propertyAddress, id: String(state.inspection.id), date: state.formattedDate, status: String(state.inspection.status) }),
83645
+ scopeSwitcherEl && /* @__PURE__ */ jsx135("div", { className: "px-3 py-2 border-b border-ih-border", children: scopeSwitcherEl }),
83188
83646
  itemFilterBarEl,
83189
- speedUnrated > 0 && /* @__PURE__ */ jsx134("div", { className: "px-3 py-2 border-b border-ih-border", children: /* @__PURE__ */ jsx134(SpeedEntryButton, { unrated: speedUnrated, onOpen: openSpeedMode, className: "w-full" }) })
83647
+ speedUnrated > 0 && /* @__PURE__ */ jsx135("div", { className: "px-3 py-2 border-b border-ih-border", children: /* @__PURE__ */ jsx135(SpeedEntryButton, { unrated: speedUnrated, onOpen: openSpeedMode, className: "w-full" }) })
83190
83648
  ] }),
83191
83649
  sectionTitle: state.currentSection?.title ?? "",
83192
83650
  list: itemListEl,
@@ -83211,7 +83669,7 @@ function FieldEditorInner({ data }) {
83211
83669
  "data-collab-synced": collabSynced ? "true" : "false",
83212
83670
  className: "flex h-screen bg-ih-bg-card overflow-x-auto",
83213
83671
  children: [
83214
- /* @__PURE__ */ jsx134(
83672
+ /* @__PURE__ */ jsx135(
83215
83673
  EditorHeader,
83216
83674
  {
83217
83675
  state,
@@ -83226,33 +83684,33 @@ function FieldEditorInner({ data }) {
83226
83684
  onSaveAsNewTemplate: () => structure.openSaveTemplate("new"),
83227
83685
  onUpdateSourceTemplate: () => structure.openSaveTemplate("back"),
83228
83686
  canUpdateSourceTemplate: structure.canSaveBack,
83229
- perUnitControls: showUnitsSurface ? /* @__PURE__ */ jsx134(UnitControls, { isPerUnit, units, activeUnitId, onSelectScope: requestScope, onOpenUnits: () => setUnitsManagerOpen(true), onOpenCostItems: () => setCostItemsOpen(true) }) : void 0
83687
+ perUnitControls: showUnitsSurface ? /* @__PURE__ */ jsx135(UnitControls, { isPerUnit, units, activeUnitId, onSelectScope: requestScope, onOpenUnits: () => setUnitsManagerOpen(true), onOpenCostItems: () => setCostItemsOpen(true) }) : void 0
83230
83688
  }
83231
83689
  ),
83232
- /* @__PURE__ */ jsx134("div", { className: "flex flex-1 pt-14 pb-9 min-w-[1024px]", children: emptyTemplateEl ? /* @__PURE__ */ jsx134("div", { className: "flex-1 flex", children: emptyTemplateEl }) : /* @__PURE__ */ jsxs114(Fragment37, { children: [
83690
+ /* @__PURE__ */ jsx135("div", { className: "flex flex-1 pt-14 pb-9 min-w-[1024px]", children: emptyTemplateEl ? /* @__PURE__ */ jsx135("div", { className: "flex-1 flex", children: emptyTemplateEl }) : /* @__PURE__ */ jsxs114(Fragment37, { children: [
83233
83691
  !state.itemFullscreen && sectionRailEl,
83234
83692
  !state.itemFullscreen && /* @__PURE__ */ jsxs114("div", { className: "w-[280px] flex-shrink-0 border-r border-ih-border flex flex-col overflow-hidden relative", children: [
83235
83693
  itemFilterBarEl,
83236
83694
  itemListEl
83237
83695
  ] }),
83238
83696
  /* @__PURE__ */ jsxs114("div", { className: "flex-1 min-w-0 relative flex flex-col border-t-2 border-ih-primary", children: [
83239
- state.activeView !== "property" && /* @__PURE__ */ jsx134("div", { className: "absolute top-2.5 right-2.5 z-10", children: /* @__PURE__ */ jsx134(FullscreenToggle, { active: state.itemFullscreen, onToggle: () => state.setItemFullscreen(!state.itemFullscreen) }) }),
83697
+ state.activeView !== "property" && /* @__PURE__ */ jsx135("div", { className: "absolute top-2.5 right-2.5 z-10", children: /* @__PURE__ */ jsx135(FullscreenToggle, { active: state.itemFullscreen, onToggle: () => state.setItemFullscreen(!state.itemFullscreen) }) }),
83240
83698
  /* @__PURE__ */ jsxs114("main", { className: "flex-1 overflow-y-auto p-6", children: [
83241
83699
  revisionBannerEl,
83242
83700
  state.activeView === "property" ? propertyOverviewEl : itemEditorEl
83243
83701
  ] })
83244
83702
  ] }),
83245
- !state.itemFullscreen && (state.sideRailCollapsed ? /* @__PURE__ */ jsx134("div", { className: "w-8 flex-shrink-0 border-l border-ih-border flex flex-col items-center pt-3", children: /* @__PURE__ */ jsx134(
83703
+ !state.itemFullscreen && (state.sideRailCollapsed ? /* @__PURE__ */ jsx135("div", { className: "w-8 flex-shrink-0 border-l border-ih-border flex flex-col items-center pt-3", children: /* @__PURE__ */ jsx135(
83246
83704
  IconButton,
83247
83705
  {
83248
83706
  "aria-label": index_exports.editor_route_expand_photo_rail(),
83249
83707
  onClick: () => state.setSideRailCollapsed(false),
83250
83708
  size: "sm",
83251
83709
  title: index_exports.editor_route_expand_photo_rail(),
83252
- children: /* @__PURE__ */ jsx134("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx134("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
83710
+ children: /* @__PURE__ */ jsx135("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx135("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
83253
83711
  }
83254
83712
  ) }) : /* @__PURE__ */ jsxs114("div", { className: "relative flex-shrink-0", children: [
83255
- /* @__PURE__ */ jsx134(
83713
+ /* @__PURE__ */ jsx135(
83256
83714
  IconButton,
83257
83715
  {
83258
83716
  "aria-label": index_exports.editor_route_collapse_photo_rail(),
@@ -83260,14 +83718,14 @@ function FieldEditorInner({ data }) {
83260
83718
  size: "sm",
83261
83719
  className: "absolute top-3 left-1 z-10 w-6 h-6",
83262
83720
  title: index_exports.editor_route_collapse_photo_rail(),
83263
- children: /* @__PURE__ */ jsx134("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx134("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
83721
+ children: /* @__PURE__ */ jsx135("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx135("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
83264
83722
  }
83265
83723
  ),
83266
83724
  sideRailEl
83267
83725
  ] }))
83268
83726
  ] }) }),
83269
83727
  batchBarEl,
83270
- /* @__PURE__ */ jsx134(FooterBar, { connected: presence.connected, status: presence.status, roster: presence.roster }),
83728
+ /* @__PURE__ */ jsx135(FooterBar, { connected: presence.connected, status: presence.status, roster: presence.roster }),
83271
83729
  toolsDockEl
83272
83730
  ]
83273
83731
  },
@@ -83276,7 +83734,7 @@ function FieldEditorInner({ data }) {
83276
83734
  ] });
83277
83735
  }
83278
83736
  function FieldEditor({ data, readOnly = false }) {
83279
- return /* @__PURE__ */ jsx134(ReadOnlyScope, { readOnly, children: /* @__PURE__ */ jsx134(FieldEditorInner, { data }) });
83737
+ return /* @__PURE__ */ jsx135(ReadOnlyScope, { readOnly, children: /* @__PURE__ */ jsx135(FieldEditorInner, { data }) });
83280
83738
  }
83281
83739
 
83282
83740
  // src/shared/pca-narrative.ts