@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/LICENSE +374 -0
- package/README.md +20 -0
- package/dist/{diff-uFiP8hD5.d.ts → diff-9dRTXeJa.d.ts} +1 -1
- package/dist/editor.css +210 -0
- package/dist/index.d.ts +141 -4
- package/dist/index.js +474 -11
- package/dist/index.js.map +1 -1
- package/dist/native.d.ts +73 -0
- package/dist/native.js +255 -0
- package/dist/native.js.map +1 -0
- package/dist/revisions.d.ts +2 -2
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -226,15 +226,15 @@ function useSelectionToolbar(editor) {
|
|
|
226
226
|
setSelection(null);
|
|
227
227
|
return;
|
|
228
228
|
}
|
|
229
|
-
const
|
|
230
|
-
if (
|
|
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:
|
|
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
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
wrap.appendChild(
|
|
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(
|
|
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(
|
|
687
|
+
while (start > 0 && WORD.test(text3[start - 1]) && WORD.test(text3[start])) start--;
|
|
688
688
|
let end = to;
|
|
689
|
-
while (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
|