@lalternative/editor 0.9.0 → 0.10.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
@@ -226,15 +226,15 @@ function useSelectionToolbar(editor) {
226
226
  setSelection(null);
227
227
  return;
228
228
  }
229
- const text2 = state.doc.textBetween(from, to, " ").trim();
230
- if (text2 === "") {
229
+ const text3 = state.doc.textBetween(from, to, " ").trim();
230
+ if (text3 === "") {
231
231
  setSelection(null);
232
232
  return;
233
233
  }
234
234
  const start = editor.view.coordsAtPos(from);
235
235
  const end = editor.view.coordsAtPos(to);
236
236
  setSelection({
237
- text: text2,
237
+ text: text3,
238
238
  rect: {
239
239
  x: (start.left + end.right) / 2,
240
240
  top: Math.min(start.top, end.top),
@@ -655,10 +655,10 @@ function renderProposal(s, actions) {
655
655
  const wrap = document.createElement("span");
656
656
  wrap.className = "lalt-suggestion";
657
657
  wrap.setAttribute("contenteditable", "false");
658
- const text2 = document.createElement("span");
659
- text2.className = "lalt-suggestion__new";
660
- text2.textContent = s.after;
661
- wrap.appendChild(text2);
658
+ const text3 = document.createElement("span");
659
+ text3.className = "lalt-suggestion__new";
660
+ text3.textContent = s.after;
661
+ wrap.appendChild(text3);
662
662
  if (actions) {
663
663
  wrap.appendChild(button("reject", actions.rejectLabel, "\u2715", () => actions.onReject(s.id)));
664
664
  wrap.appendChild(button("accept", actions.acceptLabel, "\u2713", () => actions.onAccept(s.id)));
@@ -681,12 +681,12 @@ function button(kind, label, glyph, onClick) {
681
681
 
682
682
  // src/passage.ts
683
683
  var WORD = /[\p{L}\p{N}'’-]/u;
684
- function expandToWords(text2, from, to) {
684
+ function expandToWords(text3, from, to) {
685
685
  if (from >= to) return { from, to };
686
686
  let start = from;
687
- while (start > 0 && WORD.test(text2[start - 1]) && WORD.test(text2[start])) start--;
687
+ while (start > 0 && WORD.test(text3[start - 1]) && WORD.test(text3[start])) start--;
688
688
  let end = to;
689
- while (end < text2.length && WORD.test(text2[end]) && WORD.test(text2[end - 1])) end++;
689
+ while (end < text3.length && WORD.test(text3[end]) && WORD.test(text3[end - 1])) end++;
690
690
  return { from: start, to: end };
691
691
  }
692
692
  function splitRevision(before, after) {
@@ -772,29 +772,492 @@ function text(node) {
772
772
  }
773
773
  return out;
774
774
  }
775
+
776
+ // src/writing.ts
777
+ var MIN_PASSAGE_CHARS = 12;
778
+ function canAssist(passage) {
779
+ return passage.trim().length >= MIN_PASSAGE_CHARS;
780
+ }
781
+ var WritingNotAvailableError = class extends Error {
782
+ constructor() {
783
+ super("writing assistance is not configured");
784
+ this.name = "WritingNotAvailableError";
785
+ }
786
+ };
787
+ var defaultRevisionPresets = [
788
+ "Rends-le plus concret",
789
+ "Raccourcis",
790
+ "Simplifie",
791
+ "Rends-le plus direct"
792
+ ];
793
+ function normalizeInstruction(instruction) {
794
+ const trimmed = instruction.trim();
795
+ return trimmed === "" ? null : trimmed;
796
+ }
797
+ function proposalChanges(passage, proposal) {
798
+ return splitRevision(passage, proposal);
799
+ }
800
+ function hostOf(url) {
801
+ try {
802
+ return new URL(url).hostname.replace(/^www\./, "");
803
+ } catch {
804
+ return url;
805
+ }
806
+ }
807
+
808
+ // src/RevisionPrompt.tsx
809
+ import { useState as useState5 } from "react";
810
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
811
+ var defaultRevisionPromptLabels = {
812
+ title: "Retravailler le passage",
813
+ instruction: "Consigne",
814
+ placeholder: "Ex. : ajoute un exemple concret",
815
+ submit: "Retravailler",
816
+ busy: "R\xE9\xE9criture\u2026",
817
+ proposal: "Proposition",
818
+ unchanged: "Le mod\xE8le n\u2019a rien chang\xE9.",
819
+ accept: "Remplacer",
820
+ reject: "Garder l\u2019original",
821
+ close: "Fermer"
822
+ };
823
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
824
+ function RevisionPrompt({
825
+ passage,
826
+ revise,
827
+ onAccept,
828
+ onClose,
829
+ presets = defaultRevisionPresets,
830
+ labels = defaultRevisionPromptLabels,
831
+ formatError = errorMessage
832
+ }) {
833
+ const [instruction, setInstruction] = useState5("");
834
+ const [busy, setBusy] = useState5(false);
835
+ const [error, setError] = useState5(null);
836
+ const [proposal, setProposal] = useState5(null);
837
+ const send = async (value) => {
838
+ const normalized = normalizeInstruction(value);
839
+ if (!normalized || busy) return;
840
+ setBusy(true);
841
+ setError(null);
842
+ try {
843
+ setProposal(await revise(passage, normalized));
844
+ } catch (err) {
845
+ setError(formatError(err));
846
+ } finally {
847
+ setBusy(false);
848
+ }
849
+ };
850
+ const unchanged = proposal !== null && proposalChanges(passage, proposal).length === 0;
851
+ return /* @__PURE__ */ jsx4("div", { className: "lalt-dialog", role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: /* @__PURE__ */ jsxs4("div", { className: "lalt-dialog__panel", children: [
852
+ /* @__PURE__ */ jsxs4("div", { className: "lalt-dialog__header", children: [
853
+ /* @__PURE__ */ jsx4("h2", { className: "lalt-dialog__title", children: labels.title }),
854
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "lalt-dialog__close", "aria-label": labels.close, onClick: onClose, children: "\xD7" })
855
+ ] }),
856
+ /* @__PURE__ */ jsx4("blockquote", { className: "lalt-dialog__passage", children: passage }),
857
+ proposal !== null ? /* @__PURE__ */ jsxs4(Fragment, { children: [
858
+ /* @__PURE__ */ jsx4("p", { className: "lalt-dialog__caption", children: labels.proposal }),
859
+ /* @__PURE__ */ jsx4("div", { className: "lalt-dialog__proposal", children: unchanged ? labels.unchanged : proposal }),
860
+ /* @__PURE__ */ jsxs4("div", { className: "lalt-dialog__actions", children: [
861
+ /* @__PURE__ */ jsx4(
862
+ "button",
863
+ {
864
+ type: "button",
865
+ className: "lalt-dialog__primary",
866
+ disabled: unchanged,
867
+ onClick: () => onAccept(proposal),
868
+ children: labels.accept
869
+ }
870
+ ),
871
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "lalt-dialog__secondary", onClick: onClose, children: labels.reject })
872
+ ] })
873
+ ] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
874
+ /* @__PURE__ */ jsx4("label", { className: "lalt-dialog__caption", htmlFor: "lalt-revision-instruction", children: labels.instruction }),
875
+ /* @__PURE__ */ jsx4(
876
+ "textarea",
877
+ {
878
+ id: "lalt-revision-instruction",
879
+ className: "lalt-dialog__input",
880
+ autoFocus: true,
881
+ rows: 2,
882
+ value: instruction,
883
+ placeholder: labels.placeholder,
884
+ onChange: (e) => setInstruction(e.target.value),
885
+ onKeyDown: (e) => {
886
+ if (e.key === "Enter" && !e.shiftKey) {
887
+ e.preventDefault();
888
+ void send(instruction);
889
+ }
890
+ }
891
+ }
892
+ ),
893
+ /* @__PURE__ */ jsx4("div", { className: "lalt-dialog__presets", children: presets.map((preset) => /* @__PURE__ */ jsx4(
894
+ "button",
895
+ {
896
+ type: "button",
897
+ className: "lalt-dialog__chip",
898
+ disabled: busy,
899
+ onClick: () => void send(preset),
900
+ children: preset
901
+ },
902
+ preset
903
+ )) }),
904
+ error && /* @__PURE__ */ jsx4("p", { className: "lalt-dialog__error", role: "alert", children: error }),
905
+ /* @__PURE__ */ jsx4(
906
+ "button",
907
+ {
908
+ type: "button",
909
+ className: "lalt-dialog__primary",
910
+ disabled: busy || normalizeInstruction(instruction) === null,
911
+ onClick: () => void send(instruction),
912
+ children: busy ? labels.busy : labels.submit
913
+ }
914
+ )
915
+ ] })
916
+ ] }) });
917
+ }
918
+
919
+ // src/SourcesPanel.tsx
920
+ import { useEffect as useEffect5, useState as useState6 } from "react";
921
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
922
+ var defaultSourcesPanelLabels = {
923
+ title: "Sources pour ce passage",
924
+ searching: "Recherche\u2026",
925
+ empty: "Aucune source trouv\xE9e.",
926
+ open: "Ouvrir",
927
+ insert: "Ins\xE9rer",
928
+ close: "Fermer"
929
+ };
930
+ var errorMessage2 = (error) => error instanceof Error ? error.message : String(error);
931
+ function SourcesPanel({
932
+ passage,
933
+ findSources,
934
+ onInsert,
935
+ onClose,
936
+ renderActions,
937
+ labels = defaultSourcesPanelLabels,
938
+ formatError = errorMessage2
939
+ }) {
940
+ const [sources, setSources] = useState6([]);
941
+ const [busy, setBusy] = useState6(true);
942
+ const [error, setError] = useState6(null);
943
+ useEffect5(() => {
944
+ let current = true;
945
+ setBusy(true);
946
+ setError(null);
947
+ findSources(passage).then((found) => current && setSources(found)).catch((err) => current && setError(formatError(err))).finally(() => current && setBusy(false));
948
+ return () => {
949
+ current = false;
950
+ };
951
+ }, [passage, findSources, formatError]);
952
+ return /* @__PURE__ */ jsx5("div", { className: "lalt-dialog", role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: /* @__PURE__ */ jsxs5("div", { className: "lalt-dialog__panel", children: [
953
+ /* @__PURE__ */ jsxs5("div", { className: "lalt-dialog__header", children: [
954
+ /* @__PURE__ */ jsx5("h2", { className: "lalt-dialog__title", children: labels.title }),
955
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "lalt-dialog__close", "aria-label": labels.close, onClick: onClose, children: "\xD7" })
956
+ ] }),
957
+ /* @__PURE__ */ jsx5("blockquote", { className: "lalt-dialog__passage", children: passage }),
958
+ busy && /* @__PURE__ */ jsx5("p", { className: "lalt-dialog__status", children: labels.searching }),
959
+ error && /* @__PURE__ */ jsx5("p", { className: "lalt-dialog__error", role: "alert", children: error }),
960
+ !busy && !error && sources.length === 0 && /* @__PURE__ */ jsx5("p", { className: "lalt-dialog__status", children: labels.empty }),
961
+ /* @__PURE__ */ jsx5("ul", { className: "lalt-sources", children: sources.map((source) => /* @__PURE__ */ jsxs5("li", { className: "lalt-sources__item", children: [
962
+ /* @__PURE__ */ jsx5("p", { className: "lalt-sources__title", children: source.title || source.url }),
963
+ source.excerpt && /* @__PURE__ */ jsx5("p", { className: "lalt-sources__excerpt", children: source.excerpt }),
964
+ /* @__PURE__ */ jsxs5("div", { className: "lalt-sources__footer", children: [
965
+ /* @__PURE__ */ jsx5("span", { className: "lalt-sources__host", children: hostOf(source.url) }),
966
+ /* @__PURE__ */ jsx5("a", { className: "lalt-dialog__chip", href: source.url, target: "_blank", rel: "noopener noreferrer", children: labels.open }),
967
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "lalt-dialog__chip", onClick: () => onInsert(source), children: labels.insert }),
968
+ renderActions?.(source)
969
+ ] })
970
+ ] }, source.url)) })
971
+ ] }) });
972
+ }
973
+
974
+ // src/dictation.ts
975
+ function isDictationSupported(env = browserEnvironment()) {
976
+ return env.hasGetUserMedia && env.hasMediaRecorder;
977
+ }
978
+ function browserEnvironment() {
979
+ return {
980
+ hasGetUserMedia: typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia,
981
+ hasMediaRecorder: typeof MediaRecorder !== "undefined"
982
+ };
983
+ }
984
+ var MIME_CANDIDATES = [
985
+ "audio/webm;codecs=opus",
986
+ "audio/webm",
987
+ "audio/ogg;codecs=opus",
988
+ "audio/ogg",
989
+ "audio/mp4"
990
+ ];
991
+ function pickMimeType(isTypeSupported = typeof MediaRecorder === "undefined" ? void 0 : (type) => MediaRecorder.isTypeSupported(type)) {
992
+ if (!isTypeSupported) return void 0;
993
+ return MIME_CANDIDATES.find((type) => isTypeSupported(type));
994
+ }
995
+ function spacedTranscript(preceding, transcript) {
996
+ const text3 = transcript.trim();
997
+ if (text3 === "") return "";
998
+ return preceding === "" || /\s$/.test(preceding) ? text3 : ` ${text3}`;
999
+ }
1000
+ function insertTranscript(editor, transcript) {
1001
+ const { from } = editor.state.selection;
1002
+ const preceding = editor.state.doc.textBetween(Math.max(0, from - 1), from, "\n", " ");
1003
+ const text3 = spacedTranscript(preceding, transcript);
1004
+ if (text3 === "") return false;
1005
+ return editor.chain().focus().insertContent(text3).run();
1006
+ }
1007
+
1008
+ // src/useDictation.ts
1009
+ import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
1010
+ function useDictation({ transcribe }) {
1011
+ const [state, setState] = useState7("idle");
1012
+ const [supported, setSupported] = useState7(false);
1013
+ const [error, setError] = useState7(null);
1014
+ const recorder = useRef3(null);
1015
+ const stream = useRef3(null);
1016
+ const transcribeRef = useRef3(transcribe);
1017
+ transcribeRef.current = transcribe;
1018
+ const release = useCallback2(() => {
1019
+ stream.current?.getTracks().forEach((track) => track.stop());
1020
+ stream.current = null;
1021
+ recorder.current = null;
1022
+ }, []);
1023
+ useEffect6(() => {
1024
+ setSupported(isDictationSupported());
1025
+ return release;
1026
+ }, [release]);
1027
+ const start = useCallback2(async () => {
1028
+ if (recorder.current) return;
1029
+ if (!isDictationSupported()) {
1030
+ setError(new Error("dictation is not supported in this browser"));
1031
+ return;
1032
+ }
1033
+ setError(null);
1034
+ try {
1035
+ stream.current = await navigator.mediaDevices.getUserMedia({ audio: true });
1036
+ const mimeType = pickMimeType();
1037
+ const next = new MediaRecorder(stream.current, mimeType ? { mimeType } : void 0);
1038
+ next.start();
1039
+ recorder.current = next;
1040
+ setState("recording");
1041
+ } catch (err) {
1042
+ release();
1043
+ setError(err instanceof Error ? err : new Error(String(err)));
1044
+ setState("idle");
1045
+ }
1046
+ }, [release]);
1047
+ const stop = useCallback2(async () => {
1048
+ const active = recorder.current;
1049
+ if (!active) return null;
1050
+ const audio = await new Promise((resolve, reject) => {
1051
+ const chunks = [];
1052
+ active.ondataavailable = (event) => {
1053
+ if (event.data.size > 0) chunks.push(event.data);
1054
+ };
1055
+ active.onerror = () => reject(new Error("recording failed"));
1056
+ active.onstop = () => resolve(new Blob(chunks, { type: active.mimeType || "audio/webm" }));
1057
+ active.stop();
1058
+ }).finally(release);
1059
+ if (audio.size === 0) {
1060
+ setState("idle");
1061
+ return null;
1062
+ }
1063
+ setState("transcribing");
1064
+ try {
1065
+ const text3 = (await transcribeRef.current(audio)).trim();
1066
+ return text3 === "" ? null : text3;
1067
+ } catch (err) {
1068
+ setError(err instanceof Error ? err : new Error(String(err)));
1069
+ return null;
1070
+ } finally {
1071
+ setState("idle");
1072
+ }
1073
+ }, [release]);
1074
+ const toggle = useCallback2(async () => {
1075
+ if (recorder.current) return stop();
1076
+ await start();
1077
+ return null;
1078
+ }, [start, stop]);
1079
+ return { state, supported, error, start, stop, toggle };
1080
+ }
1081
+
1082
+ // src/DictationButton.tsx
1083
+ import { useEffect as useEffect7 } from "react";
1084
+ import { jsx as jsx6 } from "react/jsx-runtime";
1085
+ var defaultDictationLabels = {
1086
+ start: "Dicter",
1087
+ stop: "Arr\xEAter la dict\xE9e",
1088
+ transcribing: "Transcription\u2026",
1089
+ unsupported: "La dict\xE9e n\u2019est pas disponible dans ce navigateur"
1090
+ };
1091
+ function DictationButton({
1092
+ transcribe,
1093
+ onTranscript,
1094
+ onError,
1095
+ labels = defaultDictationLabels,
1096
+ icon = /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: "\u25CF" }),
1097
+ disabled = false,
1098
+ className
1099
+ }) {
1100
+ const { state, supported, error, toggle } = useDictation({ transcribe });
1101
+ useEffect7(() => {
1102
+ if (error) onError?.(error);
1103
+ }, [error, onError]);
1104
+ const label = !supported ? labels.unsupported : state === "recording" ? labels.stop : state === "transcribing" ? labels.transcribing : labels.start;
1105
+ return /* @__PURE__ */ jsx6(
1106
+ "button",
1107
+ {
1108
+ type: "button",
1109
+ className: ["lalt-dictation", className].filter(Boolean).join(" "),
1110
+ "data-state": state,
1111
+ "aria-pressed": state === "recording",
1112
+ "aria-label": label,
1113
+ title: error?.message ?? label,
1114
+ disabled: disabled || !supported || state === "transcribing",
1115
+ onMouseDown: (e) => e.preventDefault(),
1116
+ onClick: async () => {
1117
+ const text3 = await toggle();
1118
+ if (text3) onTranscript(text3);
1119
+ },
1120
+ children: icon
1121
+ }
1122
+ );
1123
+ }
1124
+
1125
+ // src/answer.ts
1126
+ function text2(value, marks) {
1127
+ return marks ? { type: "text", text: value, marks } : { type: "text", text: value };
1128
+ }
1129
+ function paragraph(content) {
1130
+ return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
1131
+ }
1132
+ function link(value, href) {
1133
+ return text2(value, [{ type: "link", attrs: { href } }]);
1134
+ }
1135
+ var MARKER = /\[(\d+)\]/g;
1136
+ function linkMarkers(block2, citations) {
1137
+ const hrefs = new Map(citations.filter((c) => c.href).map((c) => [c.index, c.href]));
1138
+ const nodes = [];
1139
+ let last = 0;
1140
+ for (const match of block2.matchAll(MARKER)) {
1141
+ const href = hrefs.get(Number(match[1]));
1142
+ if (!href) continue;
1143
+ const at = match.index ?? 0;
1144
+ if (at > last) nodes.push(text2(block2.slice(last, at)));
1145
+ nodes.push(link(match[0], href));
1146
+ last = at + match[0].length;
1147
+ }
1148
+ if (last < block2.length) nodes.push(text2(block2.slice(last)));
1149
+ return nodes;
1150
+ }
1151
+ function prose(answer, citations) {
1152
+ return answer.split(/\n{2,}/).map((block2) => block2.trim()).filter((block2) => block2 !== "").map((block2) => paragraph(linkMarkers(block2, citations)));
1153
+ }
1154
+ function sourcesList(citations, sourcesLabel, numbered) {
1155
+ const linkable = citations.filter((c) => !!c.href);
1156
+ if (linkable.length === 0) return [];
1157
+ return [
1158
+ paragraph([text2(sourcesLabel, [{ type: "italic" }])]),
1159
+ {
1160
+ type: "bulletList",
1161
+ content: linkable.map((c) => {
1162
+ const title = c.title.trim() || c.href;
1163
+ return {
1164
+ type: "listItem",
1165
+ content: [paragraph([link(numbered ? `[${c.index}] ${title}` : title, c.href)])]
1166
+ };
1167
+ })
1168
+ }
1169
+ ];
1170
+ }
1171
+ function webSourceToNodes(source) {
1172
+ const nodes = [paragraph([link(source.title.trim() || source.url, source.url)])];
1173
+ const excerpt = source.excerpt?.trim();
1174
+ if (excerpt) nodes.push(paragraph([text2(excerpt)]));
1175
+ nodes.push(paragraph([text2(hostOf(source.url), [{ type: "italic" }])]));
1176
+ return [{ type: "blockquote", content: nodes }];
1177
+ }
1178
+ function answerToNodes(params) {
1179
+ const nodes = [];
1180
+ const question = params.question.trim();
1181
+ if (question) nodes.push({ type: "blockquote", content: [paragraph([text2(question)])] });
1182
+ nodes.push(...prose(params.answer, params.citations));
1183
+ nodes.push(...sourcesList(params.citations, params.sourcesLabel, true));
1184
+ return nodes;
1185
+ }
1186
+ function headingFromQuestion(question) {
1187
+ const trimmed = question.trim().replace(/\s*[?!.]+\s*$/, "");
1188
+ if (!trimmed) return "";
1189
+ return trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
1190
+ }
1191
+ function conversationToNodes(params) {
1192
+ const nodes = [];
1193
+ const sources = [];
1194
+ const seen = /* @__PURE__ */ new Set();
1195
+ let question = "";
1196
+ for (const turn of params.turns) {
1197
+ if (turn.role === "user") {
1198
+ question = turn.content;
1199
+ continue;
1200
+ }
1201
+ if (turn.clarify) continue;
1202
+ const heading = headingFromQuestion(question);
1203
+ if (heading) nodes.push({ type: "heading", attrs: { level: 3 }, content: [text2(heading)] });
1204
+ nodes.push(...prose(turn.content, turn.citations ?? []));
1205
+ for (const citation of turn.citations ?? []) {
1206
+ if (!citation.href || seen.has(citation.href)) continue;
1207
+ seen.add(citation.href);
1208
+ sources.push(citation);
1209
+ }
1210
+ question = "";
1211
+ }
1212
+ nodes.push(...sourcesList(sources, params.sourcesLabel, false));
1213
+ return nodes;
1214
+ }
775
1215
  export {
1216
+ DictationButton,
776
1217
  EditorScreen,
777
1218
  InlineSuggestions,
1219
+ MIN_PASSAGE_CHARS,
1220
+ RevisionPrompt,
778
1221
  SelectionToolbar,
779
1222
  SlashCommands,
780
1223
  SlashMenu,
1224
+ SourcesPanel,
1225
+ WritingNotAvailableError,
1226
+ answerToNodes,
781
1227
  blockFormats,
782
1228
  blocksFromDoc,
1229
+ canAssist,
1230
+ conversationToNodes,
1231
+ defaultDictationLabels,
783
1232
  defaultFormatLabels,
1233
+ defaultRevisionPresets,
1234
+ defaultRevisionPromptLabels,
784
1235
  defaultSaveLabels,
785
1236
  defaultSlashItems,
786
1237
  defaultSlashLabels,
1238
+ defaultSourcesPanelLabels,
787
1239
  diffBlocks,
788
1240
  docFromBlocks,
789
1241
  docToMarkdown,
790
1242
  expandToWords,
791
1243
  filterSlashItems,
1244
+ headingFromQuestion,
1245
+ hostOf,
792
1246
  inlineSuggestionsKey,
1247
+ insertTranscript,
1248
+ isDictationSupported,
1249
+ linkMarkers,
1250
+ normalizeInstruction,
1251
+ pickMimeType,
793
1252
  placeToolbar,
1253
+ proposalChanges,
794
1254
  readViewport,
795
1255
  shapesFromDoc,
1256
+ spacedTranscript,
796
1257
  splitRevision,
1258
+ useDictation,
797
1259
  useSelectionToolbar,
798
- useViewport
1260
+ useViewport,
1261
+ webSourceToNodes
799
1262
  };
800
1263
  //# sourceMappingURL=index.js.map