@okf/ootils 1.45.0 → 1.47.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/universal.js CHANGED
@@ -625,11 +625,44 @@ var BlockRegistry = class {
625
625
  return value === cond.equals;
626
626
  });
627
627
  }
628
- /** Check if a specific block has a specific capability. */
629
- hasCapability(compType, capability) {
630
- const block = this.blocks.get(compType);
631
- if (!block) return false;
632
- return !!block.capabilities[capability];
628
+ /**
629
+ * Check if a specific block has a specific capability.
630
+ *
631
+ * Type-level by default (keyed off `compType`). Pass the block INSTANCE as the
632
+ * optional third arg to honour per-instance overrides — today the only one is
633
+ * the annotation kill-switch: an annotation-capable block type returns `false`
634
+ * for the `annotation` capability when the instance sets
635
+ * `props.annotations.disable: true` in its tpl config. All other capabilities
636
+ * (and the no-instance form) fall through to the plain type-level check, so
637
+ * existing 2-arg callers are unaffected.
638
+ */
639
+ hasCapability(compType, capability, block) {
640
+ const def = this.blocks.get(compType);
641
+ if (!def) return false;
642
+ if (!def.capabilities[capability]) return false;
643
+ if (capability === "annotation" && block && this.isAnnotationDisabledForBlock(block)) {
644
+ return false;
645
+ }
646
+ return true;
647
+ }
648
+ /**
649
+ * Per-instance annotation kill-switch.
650
+ *
651
+ * A block type may be annotation-capable at the registry level (e.g.
652
+ * LexicalTextEditor has `capabilities.annotation === true`), but an individual
653
+ * block instance can be opted out of annotation entirely by setting
654
+ * `props.annotations.disable: true` in its tpl config. Generic and config-only:
655
+ * to stop annotation on any field, add that prop — no code change.
656
+ *
657
+ * The single home for this `props.annotations.disable` path (note: `annotations`
658
+ * plural, distinct from the legacy singular `annotation.enable`). Consumed
659
+ * internally by `hasCapability` and `getAnnotationEnabledBlocks`, and directly
660
+ * by consumers that must actively suppress annotation UI (e.g. the FE block
661
+ * generators forcing disableViewHighlights/disableCreateHighlights), where a
662
+ * type-level `false` isn't enough on its own.
663
+ */
664
+ isAnnotationDisabledForBlock(block) {
665
+ return block?.props?.annotations?.disable === true;
633
666
  }
634
667
  /**
635
668
  * Returns true if the block stores its value as an array (Mongo schema type is Array).
@@ -659,10 +692,19 @@ var BlockRegistry = class {
659
692
  * For backwards compat with un-migrated blocks (e.g. deprecated KPRichInput/RichTextEditor),
660
693
  * falls back to the legacy per-instance `props.annotation.enable` toggle.
661
694
  *
662
- * Today: every registered annotation-capable block (e.g. LexicalTextEditor) is auto-enabled.
695
+ * Per-instance kill-switch: any block is excluded when its tpl config sets
696
+ * `props.annotations.disable: true`, regardless of type-level capability. This
697
+ * is the single, config-only way to turn off annotation for a specific field —
698
+ * and because this method is the central gate used across every repo, the
699
+ * switch applies everywhere (annotate UI, AI auto-annotation, explorer,
700
+ * chunking/anno sync, filters).
701
+ *
702
+ * Today: every registered annotation-capable block (e.g. LexicalTextEditor) is
703
+ * auto-enabled unless explicitly disabled on the instance.
663
704
  */
664
705
  getAnnotationEnabledBlocks(allBlocks) {
665
706
  return allBlocks.filter((block) => {
707
+ if (this.isAnnotationDisabledForBlock(block)) return false;
666
708
  const blockDef = this.blocks.get(block.comp);
667
709
  if (blockDef) return !!blockDef.capabilities.annotation;
668
710
  return block.props?.annotation?.enable === true;
@@ -2324,6 +2366,24 @@ var processAuthorAndCommonFilters = (allTpls, filterScopes, annoEnabledBlocks =
2324
2366
  filterType: "dateRangeType",
2325
2367
  valuePath: "kp_date_published"
2326
2368
  }
2369
+ },
2370
+ // "Documents" filter — narrows results to specific chosen documents of the
2371
+ // datasets in view, matched on their own _id. Options list those datasets'
2372
+ // documents by title (documentsType fetch, which uses the ambient contentTypes);
2373
+ // the backend matches { _id: { $in: [ids] } } via target valuePath "_id"
2374
+ // (getValuePathQuery ObjectId-casts any `_id`-ending path).
2375
+ {
2376
+ filterId: "documentsFilter",
2377
+ blockId: "documentsFilter",
2378
+ display: "Documents",
2379
+ value: "documents",
2380
+ filterKey: generateFilterKey({
2381
+ filterType: "valuePathType",
2382
+ valuePath: "_id",
2383
+ scope: "doc"
2384
+ }),
2385
+ source: { filterType: "documentsType", scope: "tags" },
2386
+ target: { filterType: "valuePathType", valuePath: "_id" }
2327
2387
  }
2328
2388
  ]
2329
2389
  }] : [];
@@ -2684,12 +2744,19 @@ var _self_managed_buildDocHierarchyConfig = ({
2684
2744
  const t = allTpls.find((tp) => tp.kp_content_type === ct);
2685
2745
  if (t?.general?.content?.title) rollupDisplayMap[ct] = t.general.content.title;
2686
2746
  }
2747
+ const childByParentId = {};
2748
+ for (const r of rollupsWithParents) {
2749
+ if (r.parentFilterId && r.isTagRollup && r.source?.filterType === "tagType") {
2750
+ childByParentId[r.parentFilterId] = r;
2751
+ }
2752
+ }
2687
2753
  return {
2688
2754
  contentType: tpl.kp_content_type,
2689
2755
  display: tplData?.general?.content?.title || tpl.kp_content_type,
2690
2756
  filters: filtersWithParents,
2691
2757
  rollups: rollupsWithParents,
2692
- rollupDisplayMap
2758
+ rollupDisplayMap,
2759
+ childByParentId
2693
2760
  };
2694
2761
  });
2695
2762
  const filteredPerDataset = perDataset.filter(
@@ -553,11 +553,44 @@ var BlockRegistry = class {
553
553
  return value === cond.equals;
554
554
  });
555
555
  }
556
- /** Check if a specific block has a specific capability. */
557
- hasCapability(compType, capability) {
558
- const block = this.blocks.get(compType);
559
- if (!block) return false;
560
- return !!block.capabilities[capability];
556
+ /**
557
+ * Check if a specific block has a specific capability.
558
+ *
559
+ * Type-level by default (keyed off `compType`). Pass the block INSTANCE as the
560
+ * optional third arg to honour per-instance overrides — today the only one is
561
+ * the annotation kill-switch: an annotation-capable block type returns `false`
562
+ * for the `annotation` capability when the instance sets
563
+ * `props.annotations.disable: true` in its tpl config. All other capabilities
564
+ * (and the no-instance form) fall through to the plain type-level check, so
565
+ * existing 2-arg callers are unaffected.
566
+ */
567
+ hasCapability(compType, capability, block) {
568
+ const def = this.blocks.get(compType);
569
+ if (!def) return false;
570
+ if (!def.capabilities[capability]) return false;
571
+ if (capability === "annotation" && block && this.isAnnotationDisabledForBlock(block)) {
572
+ return false;
573
+ }
574
+ return true;
575
+ }
576
+ /**
577
+ * Per-instance annotation kill-switch.
578
+ *
579
+ * A block type may be annotation-capable at the registry level (e.g.
580
+ * LexicalTextEditor has `capabilities.annotation === true`), but an individual
581
+ * block instance can be opted out of annotation entirely by setting
582
+ * `props.annotations.disable: true` in its tpl config. Generic and config-only:
583
+ * to stop annotation on any field, add that prop — no code change.
584
+ *
585
+ * The single home for this `props.annotations.disable` path (note: `annotations`
586
+ * plural, distinct from the legacy singular `annotation.enable`). Consumed
587
+ * internally by `hasCapability` and `getAnnotationEnabledBlocks`, and directly
588
+ * by consumers that must actively suppress annotation UI (e.g. the FE block
589
+ * generators forcing disableViewHighlights/disableCreateHighlights), where a
590
+ * type-level `false` isn't enough on its own.
591
+ */
592
+ isAnnotationDisabledForBlock(block) {
593
+ return block?.props?.annotations?.disable === true;
561
594
  }
562
595
  /**
563
596
  * Returns true if the block stores its value as an array (Mongo schema type is Array).
@@ -587,10 +620,19 @@ var BlockRegistry = class {
587
620
  * For backwards compat with un-migrated blocks (e.g. deprecated KPRichInput/RichTextEditor),
588
621
  * falls back to the legacy per-instance `props.annotation.enable` toggle.
589
622
  *
590
- * Today: every registered annotation-capable block (e.g. LexicalTextEditor) is auto-enabled.
623
+ * Per-instance kill-switch: any block is excluded when its tpl config sets
624
+ * `props.annotations.disable: true`, regardless of type-level capability. This
625
+ * is the single, config-only way to turn off annotation for a specific field —
626
+ * and because this method is the central gate used across every repo, the
627
+ * switch applies everywhere (annotate UI, AI auto-annotation, explorer,
628
+ * chunking/anno sync, filters).
629
+ *
630
+ * Today: every registered annotation-capable block (e.g. LexicalTextEditor) is
631
+ * auto-enabled unless explicitly disabled on the instance.
591
632
  */
592
633
  getAnnotationEnabledBlocks(allBlocks) {
593
634
  return allBlocks.filter((block) => {
635
+ if (this.isAnnotationDisabledForBlock(block)) return false;
594
636
  const blockDef = this.blocks.get(block.comp);
595
637
  if (blockDef) return !!blockDef.capabilities.annotation;
596
638
  return block.props?.annotation?.enable === true;
@@ -2252,6 +2294,24 @@ var processAuthorAndCommonFilters = (allTpls, filterScopes, annoEnabledBlocks =
2252
2294
  filterType: "dateRangeType",
2253
2295
  valuePath: "kp_date_published"
2254
2296
  }
2297
+ },
2298
+ // "Documents" filter — narrows results to specific chosen documents of the
2299
+ // datasets in view, matched on their own _id. Options list those datasets'
2300
+ // documents by title (documentsType fetch, which uses the ambient contentTypes);
2301
+ // the backend matches { _id: { $in: [ids] } } via target valuePath "_id"
2302
+ // (getValuePathQuery ObjectId-casts any `_id`-ending path).
2303
+ {
2304
+ filterId: "documentsFilter",
2305
+ blockId: "documentsFilter",
2306
+ display: "Documents",
2307
+ value: "documents",
2308
+ filterKey: generateFilterKey({
2309
+ filterType: "valuePathType",
2310
+ valuePath: "_id",
2311
+ scope: "doc"
2312
+ }),
2313
+ source: { filterType: "documentsType", scope: "tags" },
2314
+ target: { filterType: "valuePathType", valuePath: "_id" }
2255
2315
  }
2256
2316
  ]
2257
2317
  }] : [];
@@ -2612,12 +2672,19 @@ var _self_managed_buildDocHierarchyConfig = ({
2612
2672
  const t = allTpls.find((tp) => tp.kp_content_type === ct);
2613
2673
  if (t?.general?.content?.title) rollupDisplayMap[ct] = t.general.content.title;
2614
2674
  }
2675
+ const childByParentId = {};
2676
+ for (const r of rollupsWithParents) {
2677
+ if (r.parentFilterId && r.isTagRollup && r.source?.filterType === "tagType") {
2678
+ childByParentId[r.parentFilterId] = r;
2679
+ }
2680
+ }
2615
2681
  return {
2616
2682
  contentType: tpl.kp_content_type,
2617
2683
  display: tplData?.general?.content?.title || tpl.kp_content_type,
2618
2684
  filters: filtersWithParents,
2619
2685
  rollups: rollupsWithParents,
2620
- rollupDisplayMap
2686
+ rollupDisplayMap,
2687
+ childByParentId
2621
2688
  };
2622
2689
  });
2623
2690
  const filteredPerDataset = perDataset.filter(
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.45.0",
6
+ "version": "1.47.0",
7
7
  "description": "Utility functions for both browser and Node.js",
8
8
  "main": "dist/index.js",
9
9
  "module": "dist/index.mjs",