@bendyline/docblocks-react 2.7.6 → 2.7.7

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.
@@ -15,15 +15,15 @@ This notice is included in the published npm tarball. Transitive packages instal
15
15
  | Package | Version | License | Source |
16
16
  | --- | --- | --- | --- |
17
17
  | @antfu/install-pkg | 2.1.0 | MIT | https://github.com/antfu-collective/install-pkg |
18
- | @bendyline/squisq | 2.13.0 | MIT | https://github.com/bendyline/squisq |
18
+ | @bendyline/squisq | 2.13.3 | MIT | https://github.com/bendyline/squisq |
19
19
  | @bendyline/squisq-calc | 2.11.2 | MIT | https://github.com/bendyline/squisq |
20
- | @bendyline/squisq-editor-react | 2.13.2 | MIT | https://github.com/bendyline/squisq |
21
- | @bendyline/squisq-formats | 2.8.1 | MIT | https://github.com/bendyline/squisq |
20
+ | @bendyline/squisq-editor-react | 2.13.5 | MIT | https://github.com/bendyline/squisq |
21
+ | @bendyline/squisq-formats | 2.8.4 | MIT | https://github.com/bendyline/squisq |
22
22
  | @bendyline/squisq-graph | 2.13.0 | MIT | https://github.com/bendyline/squisq |
23
- | @bendyline/squisq-grid-react | 2.11.29 | MIT | https://github.com/bendyline/squisq |
24
- | @bendyline/squisq-react | 2.12.9 | MIT | https://github.com/bendyline/squisq |
25
- | @bendyline/squisq-video | 2.4.6 | MIT | https://github.com/bendyline/squisq |
26
- | @bendyline/squisq-video-react | 2.5.10 | MIT | https://github.com/bendyline/squisq |
23
+ | @bendyline/squisq-grid-react | 2.11.32 | MIT | https://github.com/bendyline/squisq |
24
+ | @bendyline/squisq-react | 2.12.12 | MIT | https://github.com/bendyline/squisq |
25
+ | @bendyline/squisq-video | 2.4.9 | MIT | https://github.com/bendyline/squisq |
26
+ | @bendyline/squisq-video-react | 2.5.13 | MIT | https://github.com/bendyline/squisq |
27
27
  | @braintree/sanitize-url | 7.1.2 | MIT | https://github.com/braintree/sanitize-url |
28
28
  | @chevrotain/types | 11.1.2 | Apache-2.0 | https://github.com/Chevrotain/chevrotain |
29
29
  | @ffmpeg/core | 0.12.10 | GPL-2.0-or-later | https://github.com/ffmpegwasm/ffmpeg.wasm |
@@ -438,7 +438,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
438
438
  SOFTWARE.
439
439
 
440
440
  ==============================================================================
441
- Components: @bendyline/squisq-calc@2.11.2, @bendyline/squisq-editor-react@2.13.2, @bendyline/squisq-formats@2.8.1, @bendyline/squisq-graph@2.13.0, @bendyline/squisq-grid-react@2.11.29, @bendyline/squisq-react@2.12.9, @bendyline/squisq-video-react@2.5.10, @bendyline/squisq-video@2.4.6, @bendyline/squisq@2.13.0
441
+ Components: @bendyline/squisq-calc@2.11.2, @bendyline/squisq-editor-react@2.13.5, @bendyline/squisq-formats@2.8.4, @bendyline/squisq-graph@2.13.0, @bendyline/squisq-grid-react@2.11.32, @bendyline/squisq-react@2.12.12, @bendyline/squisq-video-react@2.5.13, @bendyline/squisq-video@2.4.9, @bendyline/squisq@2.13.3
442
442
  Source files: node_modules/@bendyline/squisq-calc/LICENSE, node_modules/@bendyline/squisq-editor-react/LICENSE, node_modules/@bendyline/squisq-formats/LICENSE, node_modules/@bendyline/squisq-graph/LICENSE, node_modules/@bendyline/squisq-grid-react/LICENSE, node_modules/@bendyline/squisq-react/LICENSE, node_modules/@bendyline/squisq-video-react/LICENSE, node_modules/@bendyline/squisq-video/LICENSE, node_modules/@bendyline/squisq/LICENSE
443
443
  ==============================================================================
444
444
  MIT License
@@ -638,7 +638,7 @@ The above copyright notice and this permission notice shall be included in all c
638
638
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
639
639
 
640
640
  ==============================================================================
641
- Components: @bendyline/squisq-editor-react@2.13.2
641
+ Components: @bendyline/squisq-editor-react@2.13.5
642
642
  Source files: node_modules/@bendyline/squisq-editor-react/NOTICE.md
643
643
  ==============================================================================
644
644
  # Third-Party Notices for @bendyline/squisq-editor-react
@@ -687,7 +687,7 @@ Exact third-party license texts for the bundled adapter, loader, state helper,
687
687
  and Font Awesome assets are shipped in THIRD_PARTY_LICENSES.txt.
688
688
 
689
689
  ==============================================================================
690
- Components: @bendyline/squisq-editor-react@2.13.2, @tiptap/core@2.27.3, @tiptap/extension-blockquote@2.27.3, @tiptap/extension-bold@2.27.3, @tiptap/extension-bubble-menu@2.27.3, @tiptap/extension-bullet-list@2.27.3, @tiptap/extension-code-block@2.27.3, @tiptap/extension-code@2.27.3, @tiptap/extension-document@2.27.3, @tiptap/extension-dropcursor@2.27.3, @tiptap/extension-floating-menu@2.27.3, @tiptap/extension-gapcursor@2.27.3, @tiptap/extension-hard-break@2.27.3, @tiptap/extension-heading@2.27.3, @tiptap/extension-history@2.27.3, @tiptap/extension-horizontal-rule@2.27.3, @tiptap/extension-image@2.27.3, @tiptap/extension-italic@2.27.3, @tiptap/extension-link@2.27.3, @tiptap/extension-list-item@2.27.3, @tiptap/extension-mention@2.27.3, @tiptap/extension-ordered-list@2.27.3, @tiptap/extension-paragraph@2.27.3, @tiptap/extension-placeholder@2.27.3, @tiptap/extension-strike@2.27.3, @tiptap/extension-table-cell@2.27.3, @tiptap/extension-table-header@2.27.3, @tiptap/extension-table-row@2.27.3, @tiptap/extension-table@2.27.3, @tiptap/extension-task-item@2.27.3, @tiptap/extension-task-list@2.27.3, @tiptap/extension-text-style@2.27.3, @tiptap/extension-text@2.27.3, @tiptap/pm@2.27.3, @tiptap/react@2.27.3, @tiptap/starter-kit@2.27.3, @tiptap/suggestion@2.27.3
690
+ Components: @bendyline/squisq-editor-react@2.13.5, @tiptap/core@2.27.3, @tiptap/extension-blockquote@2.27.3, @tiptap/extension-bold@2.27.3, @tiptap/extension-bubble-menu@2.27.3, @tiptap/extension-bullet-list@2.27.3, @tiptap/extension-code-block@2.27.3, @tiptap/extension-code@2.27.3, @tiptap/extension-document@2.27.3, @tiptap/extension-dropcursor@2.27.3, @tiptap/extension-floating-menu@2.27.3, @tiptap/extension-gapcursor@2.27.3, @tiptap/extension-hard-break@2.27.3, @tiptap/extension-heading@2.27.3, @tiptap/extension-history@2.27.3, @tiptap/extension-horizontal-rule@2.27.3, @tiptap/extension-image@2.27.3, @tiptap/extension-italic@2.27.3, @tiptap/extension-link@2.27.3, @tiptap/extension-list-item@2.27.3, @tiptap/extension-mention@2.27.3, @tiptap/extension-ordered-list@2.27.3, @tiptap/extension-paragraph@2.27.3, @tiptap/extension-placeholder@2.27.3, @tiptap/extension-strike@2.27.3, @tiptap/extension-table-cell@2.27.3, @tiptap/extension-table-header@2.27.3, @tiptap/extension-table-row@2.27.3, @tiptap/extension-table@2.27.3, @tiptap/extension-task-item@2.27.3, @tiptap/extension-task-list@2.27.3, @tiptap/extension-text-style@2.27.3, @tiptap/extension-text@2.27.3, @tiptap/pm@2.27.3, @tiptap/react@2.27.3, @tiptap/starter-kit@2.27.3, @tiptap/suggestion@2.27.3
691
691
  Source files: node_modules/@bendyline/squisq-editor-react/THIRD_PARTY_LICENSES.txt
692
692
  ==============================================================================
693
693
  THIRD-PARTY LICENSES FOR @bendyline/squisq-editor-react
@@ -1628,7 +1628,7 @@ The above copyright notice and this permission notice shall be included in all c
1628
1628
  **THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.**
1629
1629
 
1630
1630
  ==============================================================================
1631
- Components: @bendyline/squisq-formats@2.8.1
1631
+ Components: @bendyline/squisq-formats@2.8.4
1632
1632
  Source files: node_modules/@bendyline/squisq-formats/NOTICE.md
1633
1633
  ==============================================================================
1634
1634
  # Third-Party Notices for @bendyline/squisq-formats
@@ -1654,7 +1654,7 @@ texts for these dependencies are included in their respective npm
1654
1654
  distributions and source repositories.
1655
1655
 
1656
1656
  ==============================================================================
1657
- Components: @bendyline/squisq-formats@2.8.1
1657
+ Components: @bendyline/squisq-formats@2.8.4
1658
1658
  Source files: node_modules/@bendyline/squisq-formats/THIRD_PARTY_LICENSES.txt
1659
1659
  ==============================================================================
1660
1660
  THIRD-PARTY LICENSES FOR @bendyline/squisq-formats
@@ -2058,7 +2058,7 @@ repository license, the pinned upstream copy is vendored and identified below.
2058
2058
  COMPONENTS
2059
2059
 
2060
2060
  ==============================================================================
2061
- Components: @bendyline/squisq-grid-react@2.11.29
2061
+ Components: @bendyline/squisq-grid-react@2.11.32
2062
2062
  Source files: node_modules/@bendyline/squisq-grid-react/NOTICE.md
2063
2063
  ==============================================================================
2064
2064
  # Third-Party Notices for @bendyline/squisq-grid-react
@@ -2079,7 +2079,7 @@ Copyright and complete license texts for these dependencies are included in
2079
2079
  their respective npm distributions and source repositories.
2080
2080
 
2081
2081
  ==============================================================================
2082
- Components: @bendyline/squisq-grid-react@2.11.29
2082
+ Components: @bendyline/squisq-grid-react@2.11.32
2083
2083
  Source files: node_modules/@bendyline/squisq-grid-react/THIRD_PARTY_LICENSES.txt
2084
2084
  ==============================================================================
2085
2085
  THIRD-PARTY LICENSES FOR @bendyline/squisq-grid-react
@@ -2091,7 +2091,7 @@ repository license, the pinned upstream copy is vendored and identified below.
2091
2091
  COMPONENTS
2092
2092
 
2093
2093
  ==============================================================================
2094
- Components: @bendyline/squisq-react@2.12.9
2094
+ Components: @bendyline/squisq-react@2.12.12
2095
2095
  Source files: node_modules/@bendyline/squisq-react/NOTICE.md
2096
2096
  ==============================================================================
2097
2097
  # Third-Party Notices for @bendyline/squisq-react
@@ -2116,7 +2116,7 @@ third-party license texts for the complete standalone bundle graph are shipped
2116
2116
  in THIRD_PARTY_LICENSES.txt.
2117
2117
 
2118
2118
  ==============================================================================
2119
- Components: @bendyline/squisq-react@2.12.9
2119
+ Components: @bendyline/squisq-react@2.12.12
2120
2120
  Source files: node_modules/@bendyline/squisq-react/THIRD_PARTY_LICENSES.txt
2121
2121
  ==============================================================================
2122
2122
  THIRD-PARTY LICENSES FOR @bendyline/squisq-react
@@ -6603,7 +6603,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
6603
6603
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6604
6604
 
6605
6605
  ==============================================================================
6606
- Components: @bendyline/squisq-video-react@2.5.10
6606
+ Components: @bendyline/squisq-video-react@2.5.13
6607
6607
  Source files: node_modules/@bendyline/squisq-video-react/COPYING.GPL-2.0.txt
6608
6608
  ==============================================================================
6609
6609
  GNU GENERAL PUBLIC LICENSE
@@ -6946,7 +6946,7 @@ library. If this is what you want to do, use the GNU Lesser General
6946
6946
  Public License instead of this License.
6947
6947
 
6948
6948
  ==============================================================================
6949
- Components: @bendyline/squisq-video-react@2.5.10
6949
+ Components: @bendyline/squisq-video-react@2.5.13
6950
6950
  Source files: node_modules/@bendyline/squisq-video-react/NOTICE.md
6951
6951
  ==============================================================================
6952
6952
  # Third-Party Notices for @bendyline/squisq-video-react
@@ -7002,7 +7002,7 @@ at the version listed above and from https://github.com/Vanilagy/mediabunny.
7002
7002
  The complete MPL-2.0 text is shipped in THIRD_PARTY_LICENSES.txt.
7003
7003
 
7004
7004
  ==============================================================================
7005
- Components: @bendyline/squisq-video-react@2.5.10, @ffmpeg/core@0.12.10, @ffmpeg/ffmpeg@0.12.15, @ffmpeg/types@0.12.4, @ffmpeg/util@0.12.2
7005
+ Components: @bendyline/squisq-video-react@2.5.13, @ffmpeg/core@0.12.10, @ffmpeg/ffmpeg@0.12.15, @ffmpeg/types@0.12.4, @ffmpeg/util@0.12.2
7006
7006
  Source files: node_modules/@bendyline/squisq-video-react/THIRD_PARTY_LICENSES.txt
7007
7007
  ==============================================================================
7008
7008
  THIRD-PARTY LICENSES FOR @bendyline/squisq-video-react
@@ -7444,7 +7444,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7444
7444
  SOFTWARE.
7445
7445
 
7446
7446
  ==============================================================================
7447
- Components: @bendyline/squisq-video@2.4.6
7447
+ Components: @bendyline/squisq-video@2.4.9
7448
7448
  Source files: node_modules/@bendyline/squisq-video/NOTICE.md
7449
7449
  ==============================================================================
7450
7450
  # Third-Party Notices for @bendyline/squisq-video
@@ -7469,7 +7469,7 @@ Copyright and complete license texts for the listed dependencies are included
7469
7469
  in their respective npm distributions and source repositories.
7470
7470
 
7471
7471
  ==============================================================================
7472
- Components: @bendyline/squisq-video@2.4.6
7472
+ Components: @bendyline/squisq-video@2.4.9
7473
7473
  Source files: node_modules/@bendyline/squisq-video/THIRD_PARTY_LICENSES.txt
7474
7474
  ==============================================================================
7475
7475
  THIRD-PARTY LICENSES FOR @bendyline/squisq-video
@@ -7481,7 +7481,7 @@ repository license, the pinned upstream copy is vendored and identified below.
7481
7481
  COMPONENTS
7482
7482
 
7483
7483
  ==============================================================================
7484
- Components: @bendyline/squisq@2.13.0
7484
+ Components: @bendyline/squisq@2.13.3
7485
7485
  Source files: node_modules/@bendyline/squisq/NOTICE.md
7486
7486
  ==============================================================================
7487
7487
  # Third-Party Notices for @bendyline/squisq
@@ -7512,7 +7512,7 @@ third-party license texts for bundled code and data are shipped in
7512
7512
  THIRD_PARTY_LICENSES.txt.
7513
7513
 
7514
7514
  ==============================================================================
7515
- Components: @bendyline/squisq@2.13.0
7515
+ Components: @bendyline/squisq@2.13.3
7516
7516
  Source files: node_modules/@bendyline/squisq/THIRD_PARTY_LICENSES.txt
7517
7517
  ==============================================================================
7518
7518
  THIRD-PARTY LICENSES FOR @bendyline/squisq
@@ -2,10 +2,10 @@ import {
2
2
  AiDiagramDialog,
3
3
  AiIllustratePanel,
4
4
  DiagramPreview
5
- } from "./chunk-45YTIVYS.js";
6
- import "./chunk-NOYDVL6M.js";
5
+ } from "./chunk-5OIHB3SR.js";
6
+ import "./chunk-6QHZNL6C.js";
7
7
  import "./chunk-KHQ44ZFA.js";
8
- import "./chunk-T42BH6YX.js";
8
+ import "./chunk-N7AKLJPY.js";
9
9
  import "./chunk-K4I3NJFL.js";
10
10
  import "./chunk-VJPGZCFS.js";
11
11
  export {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AiDiagramDialog
3
- } from "./chunk-45YTIVYS.js";
3
+ } from "./chunk-5OIHB3SR.js";
4
4
  import {
5
5
  AiGenerationStatus,
6
6
  STUDY_AID_KINDS,
@@ -13,7 +13,7 @@ import {
13
13
  factsNeedReview,
14
14
  useAiKnowledge,
15
15
  useFactCheck
16
- } from "./chunk-NOYDVL6M.js";
16
+ } from "./chunk-6QHZNL6C.js";
17
17
  import {
18
18
  useConfirmDialog
19
19
  } from "./chunk-KBUHWZCF.js";
@@ -37,7 +37,7 @@ import {
37
37
  reviewDocumentCharacters,
38
38
  sanitizeGeneratedMarkdown,
39
39
  withoutPictureFindings
40
- } from "./chunk-T42BH6YX.js";
40
+ } from "./chunk-N7AKLJPY.js";
41
41
  import {
42
42
  unavailableSentence,
43
43
  useAiStatus
@@ -62,6 +62,75 @@ import {
62
62
  useEditorContextMenuItems
63
63
  } from "@bendyline/squisq-editor-react";
64
64
 
65
+ // src/Ai/knowledge-citations.ts
66
+ var MARKER = /[ \t]?(?<![\]\\\p{L}_])\[(\d{1,3}(?:\s*(?:[-–]|,)\s*\d{1,3})*)\](?![(:[])/gu;
67
+ function citationNumbers(inner) {
68
+ const numbers = [];
69
+ for (const part of inner.split(",")) {
70
+ const range = /^\s*(\d{1,3})\s*[-–]\s*(\d{1,3})\s*$/u.exec(part);
71
+ if (range) {
72
+ const from = Number(range[1]);
73
+ const to = Number(range[2]);
74
+ for (let n = Math.min(from, to); n <= Math.max(from, to) && numbers.length < 50; n++) {
75
+ numbers.push(n);
76
+ }
77
+ } else if (/^\s*\d{1,3}\s*$/u.test(part)) numbers.push(Number(part));
78
+ }
79
+ return [...new Set(numbers)];
80
+ }
81
+ function mapProse(markdown, transform) {
82
+ let fence = null;
83
+ return markdown.split("\n").map((line) => {
84
+ const opener = /^\s{0,3}(`{3,}|~{3,})/u.exec(line)?.[1];
85
+ if (fence !== null) {
86
+ if (opener && opener[0] === fence[0] && opener.length >= fence.length) fence = null;
87
+ return line;
88
+ }
89
+ if (opener) {
90
+ fence = opener;
91
+ return line;
92
+ }
93
+ return transform(line);
94
+ }).join("\n");
95
+ }
96
+ function stripCitationMarkers(markdown) {
97
+ return mapProse(markdown, (line) => line.replace(MARKER, ""));
98
+ }
99
+ function citationsToFootnotes(markdown, evidence, catalogNames, document2) {
100
+ const taken = new Set([...document2.matchAll(/\[\^([^\]\s]+)\]/gu)].map((match) => match[1]));
101
+ const labels = /* @__PURE__ */ new Map();
102
+ let next = 1;
103
+ const labelFor = (n) => {
104
+ const passage = evidence[n - 1];
105
+ const article = passage.title ? `${passage.catalogId}\0${passage.title}` : `#${n}`;
106
+ const known = labels.get(article);
107
+ if (known) return known.label;
108
+ let label = `source-${next++}`;
109
+ while (taken.has(label)) label = `source-${next++}`;
110
+ labels.set(article, { label, passage });
111
+ return label;
112
+ };
113
+ const body = mapProse(
114
+ markdown,
115
+ (line) => line.replace(
116
+ MARKER,
117
+ (_marker, inner) => [
118
+ ...new Set(
119
+ citationNumbers(inner).filter((n) => n >= 1 && n <= evidence.length).map(labelFor)
120
+ )
121
+ ].map((label) => `[^${label}]`).join("")
122
+ )
123
+ );
124
+ if (labels.size === 0) return body;
125
+ const notes = [...labels.values()].map(({ label, passage }) => {
126
+ const catalog = catalogNames.get(passage.catalogId) ?? passage.catalogId;
127
+ return `[^${label}]: ${passage.title || "Untitled article"}, from the ${catalog} knowledge catalog.`;
128
+ });
129
+ return `${body.trimEnd()}
130
+
131
+ ${notes.join("\n")}`;
132
+ }
133
+
65
134
  // src/Ai/draft-capacity.ts
66
135
  import { AI_WIRE_LIMITS } from "@bendyline/docblocks/host";
67
136
  function estimateTokens(text) {
@@ -92,7 +161,7 @@ function draftCapacityNotice(request, model, expectedOutput = "") {
92
161
  // src/Ai/AiEditorAssistant.tsx
93
162
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
94
163
  var AiStudyDialog = lazy(
95
- () => import("./AiStudyDialog-DQZVTBI7.js").then((module) => ({ default: module.AiStudyDialog }))
164
+ () => import("./AiStudyDialog-JPVFFZBB.js").then((module) => ({ default: module.AiStudyDialog }))
96
165
  );
97
166
  function AiToolbarControl({
98
167
  ai,
@@ -349,13 +418,13 @@ function AiDraftDialog({
349
418
  const handleRef = useRef(null);
350
419
  const requestRef = useRef(0);
351
420
  const { confirm: confirmAction, confirmDialog } = useConfirmDialog();
352
- const { knowledge, ai: knowledgeAi, reset: resetKnowledge } = useAiKnowledge(ai);
421
+ const { knowledge, citable, ai: knowledgeAi, reset: resetKnowledge } = useAiKnowledge(ai);
353
422
  const selectedMarkdown = capturedSelection.markdown ?? capturedSelection.text;
354
423
  const factsGiven = useMemo(
355
424
  () => originalRequest?.messages.filter((message) => message.role === "user").map((message) => message.content).join("\n\n") ?? "",
356
425
  [originalRequest]
357
426
  );
358
- const facts = useFactCheck(ai, running ? null : draft, knowledge, factsGiven);
427
+ const facts = useFactCheck(ai, running ? null : draft, knowledge, factsGiven, citable);
359
428
  const draftRequest = useMemo(
360
429
  () => ({
361
430
  ...buildDraftRequest({
@@ -479,7 +548,7 @@ function AiDraftDialog({
479
548
  if (running) return;
480
549
  const replacement = sanitizeGeneratedMarkdown(draft ?? "", documentSource);
481
550
  if (!replacement) return;
482
- const factCheck = !incomplete && ai.checkFacts && factCheckRequest(replacement, knowledge, factsGiven);
551
+ const factCheck = !incomplete && ai.checkFacts && factCheckRequest(replacement, knowledge, factsGiven, citable);
483
552
  if (factCheck) {
484
553
  const previousRequest = requestRef.current;
485
554
  const checked = await ai.checkFacts?.(factCheck);
@@ -518,7 +587,13 @@ function AiDraftDialog({
518
587
  setError("The selected text changed. Close this dialog and select it again.");
519
588
  return;
520
589
  }
521
- if (!replaceSelection(replacement)) {
590
+ const inserted = knowledge?.outcome !== "used" ? replacement : mode === "compose" && citable ? citationsToFootnotes(
591
+ replacement,
592
+ knowledge.evidence,
593
+ new Map(knowledge.sources.map((source) => [source.catalogId, source.name])),
594
+ documentSource
595
+ ) : stripCitationMarkers(replacement);
596
+ if (!replaceSelection(inserted)) {
522
597
  setError("DocBlocks could not apply the draft in the current editor view.");
523
598
  return;
524
599
  }
@@ -537,7 +612,8 @@ function AiDraftDialog({
537
612
  confirmAction,
538
613
  ai,
539
614
  knowledge,
540
- factsGiven
615
+ factsGiven,
616
+ citable
541
617
  ]);
542
618
  const dialog = /* @__PURE__ */ jsxs(
543
619
  Dialog,
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  AiKnowledgeIndicator,
14
14
  useAiKnowledge
15
- } from "./chunk-NOYDVL6M.js";
15
+ } from "./chunk-6QHZNL6C.js";
16
16
  import "./chunk-KHQ44ZFA.js";
17
17
  import {
18
18
  AiReportButton,
@@ -30,7 +30,7 @@ import {
30
30
  extractNumbers,
31
31
  sanitizeGeneratedMarkdown,
32
32
  stripReasoning
33
- } from "./chunk-T42BH6YX.js";
33
+ } from "./chunk-N7AKLJPY.js";
34
34
  import {
35
35
  unavailableSentence,
36
36
  useAiStatus
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ExportDialog
3
- } from "./chunk-GFK4TCKN.js";
3
+ } from "./chunk-G63YXKBC.js";
4
4
  import "./chunk-H3BK5MK6.js";
5
5
  import "./chunk-6MSHXQNB.js";
6
6
  import "./chunk-VJPGZCFS.js";
@@ -39,6 +39,7 @@ import {
39
39
  useRef,
40
40
  useMemo
41
41
  } from "react";
42
+ import { createPortal } from "react-dom";
42
43
  import {
43
44
  useEditorContext,
44
45
  usePreviewSettings,
@@ -66,7 +67,7 @@ function saveOpenAfterExport(value) {
66
67
  // src/Export/ExportToolbarControls.tsx
67
68
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
68
69
  var ExportDialog = lazy(
69
- () => import("./ExportDialog-LGWBXIWN.js").then((module) => ({ default: module.ExportDialog }))
70
+ () => import("./ExportDialog-EYDGTJ3S.js").then((module) => ({ default: module.ExportDialog }))
70
71
  );
71
72
  var ShareDialog = lazy(
72
73
  () => import("./ShareDialog-PF7D7ORP.js").then((module) => ({ default: module.ShareDialog }))
@@ -180,6 +181,8 @@ function ExportToolbarControls({
180
181
  const [exportError, setExportError] = useState(null);
181
182
  const [openAfterSave, setOpenAfterSave] = useState(loadOpenAfterExport);
182
183
  const [openError, setOpenError] = useState(null);
184
+ const [exportCompletion, setExportCompletion] = useState(null);
185
+ useEffect(() => setExportCompletion(null), [selectedFile]);
183
186
  const handleOpenAfterSaveChange = useCallback((checked) => {
184
187
  setOpenAfterSave(checked);
185
188
  saveOpenAfterExport(checked);
@@ -359,8 +362,8 @@ function ExportToolbarControls({
359
362
  [destinationAdapter]
360
363
  );
361
364
  const openSaved = useCallback(
362
- async (target) => {
363
- if (!openAfterSave || !destinationAdapter?.openSaved) return;
365
+ async (target, requested = false) => {
366
+ if (!requested && !openAfterSave || !destinationAdapter?.openSaved) return;
364
367
  try {
365
368
  await destinationAdapter.openSaved(target);
366
369
  } catch (caught) {
@@ -376,6 +379,7 @@ function ExportToolbarControls({
376
379
  setExportError(null);
377
380
  setOpenError(null);
378
381
  setExportNotice(null);
382
+ setExportCompletion(null);
379
383
  try {
380
384
  let exportTarget = destinationTarget;
381
385
  if (!fileShare && destinationAdapter?.pickBeforeSave) {
@@ -399,6 +403,12 @@ function ExportToolbarControls({
399
403
  } : saveBlob
400
404
  );
401
405
  setDialogOpen(false);
406
+ if (!fileShare) {
407
+ setExportCompletion({
408
+ filename: buildExportFilename(selectedFile, opts),
409
+ target: savedTarget
410
+ });
411
+ }
402
412
  if (result.warnings.length > 0) setExportNotice(result.warnings);
403
413
  if (savedTarget) await openSaved(savedTarget);
404
414
  } catch (caught) {
@@ -426,6 +436,7 @@ function ExportToolbarControls({
426
436
  setExportError(null);
427
437
  setOpenError(null);
428
438
  setExportNotice(null);
439
+ setExportCompletion(null);
429
440
  try {
430
441
  saveExportOptions(lastOptions);
431
442
  let quickTarget = null;
@@ -450,6 +461,10 @@ function ExportToolbarControls({
450
461
  savedTarget = await saveToDestination(blob, filename, quickTarget);
451
462
  } : saveBlob
452
463
  );
464
+ setExportCompletion({
465
+ filename: buildExportFilename(selectedFile, lastOptions),
466
+ target: savedTarget
467
+ });
453
468
  if (result.warnings.length > 0) setExportNotice(result.warnings);
454
469
  if (savedTarget) await openSaved(savedTarget);
455
470
  } catch (caught) {
@@ -705,6 +720,32 @@ function ExportToolbarControls({
705
720
  }
706
721
  ),
707
722
  openError && !videoModalOpen && /* @__PURE__ */ jsx(Dialog, { title: "Open saved export", onClose: () => setOpenError(null), children: /* @__PURE__ */ jsx("p", { role: "alert", children: openError }) }),
723
+ exportCompletion && !dialogOpen && !exportError && !openError && !exportNotice && createPortal(
724
+ /* @__PURE__ */ jsxs("aside", { className: "db-export-completion", "aria-label": "Export result", children: [
725
+ /* @__PURE__ */ jsx("p", { role: "status", children: exportCompletion.target ? `Saved ${exportCompletion.target.displayPath}` : !destinationAdapter && !saveBlob ? `Sent ${exportCompletion.filename} to your browser\u2019s downloads.` : `Exported ${exportCompletion.filename}` }),
726
+ /* @__PURE__ */ jsxs("div", { className: "db-export-completion-actions", children: [
727
+ exportCompletion.target && destinationAdapter?.openSaved && /* @__PURE__ */ jsx(
728
+ "button",
729
+ {
730
+ type: "button",
731
+ className: "db-export-btn db-export-btn--primary",
732
+ onClick: () => void openSaved(exportCompletion.target, true),
733
+ children: "Open exported file"
734
+ }
735
+ ),
736
+ /* @__PURE__ */ jsx(
737
+ "button",
738
+ {
739
+ type: "button",
740
+ className: "db-export-btn db-export-btn--secondary",
741
+ onClick: () => setExportCompletion(null),
742
+ children: "Dismiss"
743
+ }
744
+ )
745
+ ] })
746
+ ] }),
747
+ document.body
748
+ ),
708
749
  exportNotice && !dialogOpen && !exportError && !openError && /* @__PURE__ */ jsx(
709
750
  Dialog,
710
751
  {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PresentationMaker
3
- } from "./chunk-PRUEYW3G.js";
4
- import "./chunk-T42BH6YX.js";
3
+ } from "./chunk-T2PA7QEF.js";
4
+ import "./chunk-N7AKLJPY.js";
5
5
  import "./chunk-K4I3NJFL.js";
6
6
  import "./chunk-VJPGZCFS.js";
7
7
  export {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AiKnowledgeIndicator,
3
3
  useAiKnowledge
4
- } from "./chunk-NOYDVL6M.js";
4
+ } from "./chunk-6QHZNL6C.js";
5
5
  import {
6
6
  AI_DIAGRAM_DESCRIPTION_CHARACTERS,
7
7
  AiReportButton,
@@ -27,7 +27,7 @@ import {
27
27
  stripReasoning,
28
28
  svgPicture,
29
29
  validateSvgPicture
30
- } from "./chunk-T42BH6YX.js";
30
+ } from "./chunk-N7AKLJPY.js";
31
31
  import {
32
32
  unavailableSentence,
33
33
  useAiStatus
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-KHQ44ZFA.js";
6
6
 
7
7
  // src/Ai/AiKnowledgeIndicator.tsx
8
- import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  var QUIET_NOTES = {
10
10
  none: "Your knowledge catalogs were searched, and nothing in them matched this request.",
11
11
  skipped: "Your knowledge catalogs could not be searched for this request. A long document can leave no room for reference passages."
@@ -46,7 +46,7 @@ function AiKnowledgeIndicator({ knowledge }) {
46
46
  ] });
47
47
  }
48
48
  function markDetails(text, details) {
49
- const terms = details.filter(Boolean).map((detail) => detail.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).sort((a, b) => b.length - a.length);
49
+ const terms = details.map((detail) => detail.replace(/ next to .+$/u, "")).filter(Boolean).map((detail) => detail.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).sort((a, b) => b.length - a.length);
50
50
  if (terms.length === 0) return [text];
51
51
  const pattern = new RegExp(
52
52
  `(?<![\\p{L}\\p{N}])(${terms.join("|")})(?=(?:st|nd|rd|th)?(?![\\p{L}\\p{N}]))`,
@@ -75,7 +75,15 @@ function AiFactsIndicator({ check }) {
75
75
  " to check"
76
76
  ] }),
77
77
  /* @__PURE__ */ jsx("p", { children: "Neither your document, your instructions nor the reference passages show the marked details. The model may have written them from memory." }),
78
- /* @__PURE__ */ jsx("ul", { children: check.unsourced.map((sentence, index) => /* @__PURE__ */ jsx("li", { children: markDetails(sentence.text, sentence.missing) }, index)) })
78
+ /* @__PURE__ */ jsx("ul", { children: check.unsourced.map((sentence, index) => /* @__PURE__ */ jsx("li", { children: sentence.problem === "bad-citation" ? /* @__PURE__ */ jsxs(Fragment, { children: [
79
+ sentence.text,
80
+ " ",
81
+ /* @__PURE__ */ jsxs("span", { className: "db-ai-facts-note", children: [
82
+ "It cites ",
83
+ sentence.missing.join(", "),
84
+ ", which is not one of the reference passages."
85
+ ] })
86
+ ] }) : markDetails(sentence.text, sentence.missing) }, index)) })
79
87
  ] });
80
88
  }
81
89
 
@@ -101,19 +109,33 @@ function mergeAiKnowledge(current, next) {
101
109
  }
102
110
  }
103
111
  const evidence = [...current.evidence];
104
- for (const text of next.evidence) if (!evidence.includes(text)) evidence.push(text);
112
+ for (const passage of next.evidence) {
113
+ if (!evidence.some((known) => known.text === passage.text)) evidence.push(passage);
114
+ }
105
115
  return { outcome: "used", sources, evidence };
106
116
  }
117
+ function sameEvidence(a, b) {
118
+ return a.length === b.length && a.every((passage, index) => passage.text === b[index]?.text);
119
+ }
120
+ function numberingHolds(current, next) {
121
+ return current?.outcome !== "used" || next.outcome !== "used" || sameEvidence(current.evidence, next.evidence);
122
+ }
107
123
  function useAiKnowledge(ai) {
108
- const [knowledge, setKnowledge] = useState(null);
124
+ const [run, setRun] = useState({
125
+ knowledge: null,
126
+ citable: true
127
+ });
109
128
  const runRef = useRef(0);
110
129
  const helpers = useMemo(() => {
111
130
  const tapped = {
112
131
  chat(request, onEvent) {
113
- const run = runRef.current;
132
+ const started = runRef.current;
114
133
  return ai.chat(request, (event) => {
115
- if (event.kind === "knowledge" && run === runRef.current) {
116
- setKnowledge((current) => mergeAiKnowledge(current, event.knowledge));
134
+ if (event.kind === "knowledge" && started === runRef.current) {
135
+ setRun((current) => ({
136
+ knowledge: mergeAiKnowledge(current.knowledge, event.knowledge),
137
+ citable: current.citable && numberingHolds(current.knowledge, event.knowledge)
138
+ }));
117
139
  }
118
140
  onEvent(event);
119
141
  });
@@ -121,32 +143,38 @@ function useAiKnowledge(ai) {
121
143
  };
122
144
  const reset = () => {
123
145
  runRef.current += 1;
124
- setKnowledge(null);
146
+ setRun({ knowledge: null, citable: true });
125
147
  };
126
148
  return { ai: tapped, reset };
127
149
  }, [ai]);
128
- return { knowledge, ...helpers };
150
+ return { knowledge: run.knowledge, citable: run.citable, ...helpers };
129
151
  }
130
- function factCheckRequest(text, knowledge, given) {
152
+ function factCheckRequest(text, knowledge, given, citable) {
131
153
  if (text.length > AI_WIRE_LIMITS.factCheckTextCharacters) return null;
132
154
  const evidence = [];
133
155
  let total = 0;
134
156
  for (const passage of knowledge?.outcome === "used" ? knowledge.evidence : []) {
135
- total += passage.length;
157
+ total += passage.text.length;
136
158
  if (evidence.length >= AI_WIRE_LIMITS.factCheckEvidence || total > AI_WIRE_LIMITS.factCheckEvidenceCharacters) {
137
159
  break;
138
160
  }
139
- evidence.push(passage);
161
+ evidence.push(passage.text);
140
162
  }
141
- return { text, evidence, given: given.slice(0, AI_WIRE_LIMITS.factCheckGivenCharacters) };
163
+ return {
164
+ text,
165
+ evidence,
166
+ given: given.slice(0, AI_WIRE_LIMITS.factCheckGivenCharacters),
167
+ // Passages were numbered for citing only when there were passages.
168
+ citations: citable && evidence.length > 0
169
+ };
142
170
  }
143
171
  function factsNeedReview(check) {
144
172
  return check.unsourced.some((sentence) => sentence.strong) || check.unsourced.length >= 3;
145
173
  }
146
- function useFactCheck(ai, text, knowledge, given) {
174
+ function useFactCheck(ai, text, knowledge, given, citable) {
147
175
  const [result, setResult] = useState(null);
148
176
  useEffect(() => {
149
- const request = text?.trim() && ai.checkFacts ? factCheckRequest(text, knowledge, given) : null;
177
+ const request = text?.trim() && ai.checkFacts ? factCheckRequest(text, knowledge, given, citable) : null;
150
178
  if (!request) {
151
179
  setResult(null);
152
180
  return;
@@ -166,7 +194,7 @@ function useFactCheck(ai, text, knowledge, given) {
166
194
  live = false;
167
195
  clearTimeout(timer);
168
196
  };
169
- }, [ai, text, knowledge, given]);
197
+ }, [ai, text, knowledge, given, citable]);
170
198
  return result;
171
199
  }
172
200
 
@@ -343,11 +343,14 @@ function ExportDialog({
343
343
  }
344
344
  )
345
345
  ] }),
346
- /* @__PURE__ */ jsxs("p", { className: "db-export-more-formats", children: [
347
- "Need spreadsheets, animated media, portable bundles, or automation?",
348
- " ",
349
- /* @__PURE__ */ jsx("a", { href: "https://docblocks.com/cli/", target: "_blank", rel: "noopener noreferrer", children: "More formats with the DocBlocks CLI" }),
350
- "."
346
+ /* @__PURE__ */ jsxs("details", { className: "db-export-more-formats", children: [
347
+ /* @__PURE__ */ jsx("summary", { children: "More formats and automation" }),
348
+ /* @__PURE__ */ jsxs("p", { children: [
349
+ "Need spreadsheets, portable bundles, or batch exports?",
350
+ " ",
351
+ /* @__PURE__ */ jsx("a", { href: "https://docblocks.com/cli/", target: "_blank", rel: "noopener noreferrer", children: "Explore the DocBlocks CLI" }),
352
+ "."
353
+ ] })
351
354
  ] }),
352
355
  error && /* @__PURE__ */ jsx("p", { className: "db-export-error", role: "alert", children: error })
353
356
  ]