mindoodb-app-sdk 0.0.71 → 0.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -525,9 +525,12 @@ only database. The launch context then carries `embed` (`docId`, `databaseId`,
525
525
  `intent`, `hostAppLabel`): open that document, hide "New"/"Open", and finish with
526
526
 
527
527
  ```ts
528
- await session.embedding.complete({ title }); // or session.embedding.cancel()
528
+ await session.embedding.complete({ subject, summary, stats: { words } }); // or session.embedding.cancel()
529
529
  ```
530
530
 
531
+ `complete` takes plain JSON; `MindooDBAppEmbeddingResult` (`subject`, optional
532
+ `summary`, `thumbnail` as a small data URL, `stats`) is the shape hosts expect.
533
+
531
534
  Haven writes every change back to the host's database as it happens and only lets the
532
535
  component create documents that are children of the root.
533
536
 
@@ -628,6 +631,101 @@ never does is closed without asking; `embed.onDirtyChange` lets the host mark it
628
631
  dot in its tab). `embed.save()` rejects with `unsupported` when the component has no save
629
632
  handler.
630
633
 
634
+ **Checking before saving.** A component can tell hosts more about itself in
635
+ `supports`, and hosts filter on it:
636
+
637
+ ```json
638
+ "supports": ["inspect:mindoo.inspect.document@1", "validate", "issues", "reveal"]
639
+ ```
640
+
641
+ ```ts
642
+ const [letterEditor] = await session.components.list({ requires: ["inspect:mindoo.inspect.document@1", "validate"] });
643
+ ```
644
+
645
+ - `inspect:<contract>`: the component describes its document in a shared shape:
646
+ `mindoo.inspect.document@1` (Word/Markdown: subject, outline with anchors, open
647
+ placeholders and MERGEFIELDs, open comments and tracked changes, word count, text on
648
+ request), `mindoo.inspect.spreadsheet@1` (sheets, named ranges, requested cells,
649
+ formula errors with address), `mindoo.inspect.slides@1` (slide count, titles, empty
650
+ placeholders). Anchors: `{ kind: "heading", id }`, `{ kind: "cell", ref: "Sheet1!B3" }`,
651
+ `{ kind: "slide", index }`.
652
+ - `validate`: it asks before it saves, so the host can stop a save.
653
+ - `issues` / `reveal`: it shows a host's findings and scrolls to an anchor.
654
+
655
+ Changing `supports` does not ask users again; it grants no access.
656
+
657
+ ```ts
658
+ // host
659
+ const letter = await embed.inspect<MindooDBAppDocumentInspection>(); // { outline, placeholders, … }
660
+ const rules = defineDocumentRules<MindooDBAppDocumentInspection>({
661
+ contract: MINDOODB_INSPECT_DOCUMENT,
662
+ rules: [mindooDBAppInspectRules.noOpenPlaceholders(), mindooDBAppInspectRules.noOpenTrackedChanges("warning")],
663
+ });
664
+ rules.attach(embed); // = embed.onBeforeSave(rules.beforeSave, { inspect: rules.request })
665
+ embed.onAfterSave(() => refreshTree());
666
+ await embed.showIssues(rules.checkRoot(doc.data)); // the root summary, see below
667
+ await embed.reveal({ kind: "heading", id: "h-3" });
668
+ await embed.callTool("text_insert", { text: "…" }); // any of its document agent tools
669
+
670
+ // component
671
+ session.embedding.provideInspection((request) => describeDocument(request));
672
+ session.embedding.onShowIssues((issues) => showIssuesPanel(issues));
673
+ session.embedding.onReveal((anchor) => scrollTo(anchor));
674
+ await session.embedding.save(async ({ inspection }) => {
675
+ await db.documents.update({ docId, set: { ...changes, ...(inspection ? { inspection } : {}) } });
676
+ }); // throws MindooDBAppEmbedValidationError (with .issues) when the host's check stopped it
677
+ ```
678
+
679
+ Haven never holds writes back: the component asks first (`embedding.validate`, which
680
+ `embedding.save` calls), Haven fetches the inspection the host asked for, passes it with
681
+ the root document to the host's `onBeforeSave` and answers the component: an `error`
682
+ stops the save, a `warning` asks the user whether to save anyway, and when the host does
683
+ not answer within about 5 s the component saves (`timedOut`). This covers saves the
684
+ user, the host (`embed.save()`) and agents start; an agent tool saves with
685
+ `save(flush, { interactive: false })`, where warnings do not stop it, and reports the
686
+ error's issues as its tool error. Standalone, `validate` answers `ok` at once.
687
+
688
+ `save` also hands `flush` a small summary of the inspection (at most 8 KiB, lists and
689
+ text shortened, counts exact) as an atomic value for the root's `inspection` field, so
690
+ hosts can check documents they do not have open: `rules.checkRoot(root)`, or
691
+ `readMindooDBAppInspectionSummary(root)`. Write it in standalone saves too.
692
+
693
+ In tests, the mock plays the components with `embedInspection`, `embedTools` and
694
+ `confirmSaveWarnings`, and records `getEmbedRequests(embedId)`; `validateEmbed(embedId)`
695
+ runs the host's check as if the component was about to save. For an app under test that
696
+ is a component, `embeddingValidation` plays the host, and `requestEmbeddingInspection`,
697
+ `requestEmbeddingShowIssues`, `requestEmbeddingReveal`, `getEmbeddingValidations` and
698
+ `getEmbeddingSaveCount` drive and observe it.
699
+
700
+ **Fields, templates and export.** Three more `supports` entries:
701
+
702
+ ```json
703
+ "supports": ["variables", "template", "export:pdf", "export:png"]
704
+ ```
705
+
706
+ ```ts
707
+ // host
708
+ const embed = await session.embeds.open({
709
+ componentKey, databaseId: "crmdata", docId: newDoc.id, container,
710
+ template: { docId: templateId }, // the new document starts as a copy
711
+ variables: [{ key: "company.name", label: "Firma", value: "Müller GmbH", group: "Firma" }],
712
+ });
713
+ await embed.setVariables(nextVariables); // e.g. after the contact changed
714
+ const pdf = await embed.export({ format: "pdf" }); // a File
715
+ const preview = await embed.export({ format: "png", maxWidth: 480 });
716
+
717
+ // component
718
+ const variables = await session.embedding.getVariables(); // offer them under "Insert field ▸"
719
+ session.embedding.onVariablesChange((next) => refreshFields(next));
720
+ session.embedding.provideExport(async ({ format }) => (format === "pdf" ? await renderPdf() : await renderPng()));
721
+ // launchContext.embed.template: { docId } of the template in the sandbox (read only); copy it while the root is empty
722
+ ```
723
+
724
+ `resolveMindooDBAppVariables(text, variables)` fills `{{key}}` placeholders for editors
725
+ without real fields. In tests, `embedExport` plays the components, `getEmbedRequests`
726
+ records exports, and for an app that is a component `embeddingVariables`,
727
+ `requestEmbeddingVariables` and `requestEmbeddingExport` drive it.
728
+
631
729
  Havens that predate this lay the component over the container instead, above the host's
632
730
  frame (`embed.placement === "overlay"`). The host then cannot draw on top of it: hide it
633
731
  with `embed.setVisible(false)` while a menu or dialog of the host should be visible.