rte-builder 1.0.0 → 2.0.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
@@ -6971,7 +6971,11 @@ var init_LexicalEditorComponent = __esm({
6971
6971
  // src/index.tsx
6972
6972
  var index_exports = {};
6973
6973
  __export(index_exports, {
6974
+ CollaborationProvider: () => CollaborationProvider,
6975
+ CommentsPanel: () => CommentsPanel,
6976
+ CommentsProvider: () => CommentsProvider,
6974
6977
  DEFAULT_FEATURES: () => DEFAULT_FEATURES,
6978
+ DEFAULT_REACTION_EMOJIS: () => DEFAULT_REACTION_EMOJIS,
6975
6979
  EMOJI_CATEGORIES: () => EMOJI_CATEGORIES,
6976
6980
  EditorRegistry: () => EditorRegistry,
6977
6981
  Emoji: () => Emoji,
@@ -6979,12 +6983,15 @@ __export(index_exports, {
6979
6983
  Fullscreen: () => Fullscreen,
6980
6984
  Indent: () => Indent,
6981
6985
  LineHeight: () => LineHeight,
6986
+ PresenceIndicator: () => PresenceIndicator,
6982
6987
  Print: () => Print,
6983
6988
  RichTextEditor: () => RichTextEditor,
6984
6989
  TipTapAdapter: () => TipTapAdapter,
6985
6990
  TipTapEditorComponent: () => TipTapEditorComponent,
6986
6991
  TipTapToolbar: () => TipTapToolbar,
6987
6992
  UnifiedEditor: () => UnifiedEditor,
6993
+ VersionHistoryPanel: () => VersionHistoryPanel,
6994
+ VersionHistoryProvider: () => VersionHistoryProvider,
6988
6995
  Video: () => Video,
6989
6996
  blogToolbar: () => blogToolbar,
6990
6997
  codeToolbar: () => codeToolbar,
@@ -7006,7 +7013,13 @@ __export(index_exports, {
7006
7013
  registerAdapter: () => registerAdapter,
7007
7014
  simpleToolbar: () => simpleToolbar,
7008
7015
  toolbarPresets: () => toolbarPresets,
7009
- unregisterAdapter: () => unregisterAdapter
7016
+ unregisterAdapter: () => unregisterAdapter,
7017
+ useCollaboration: () => useCollaboration,
7018
+ useCollaborationOptional: () => useCollaborationOptional,
7019
+ useComments: () => useComments,
7020
+ useCommentsOptional: () => useCommentsOptional,
7021
+ useVersionHistory: () => useVersionHistory,
7022
+ useVersionHistoryOptional: () => useVersionHistoryOptional
7010
7023
  });
7011
7024
  module.exports = __toCommonJS(index_exports);
7012
7025
 
@@ -8675,9 +8688,1382 @@ init_Emoji();
8675
8688
  init_Fullscreen();
8676
8689
  init_Print();
8677
8690
  init_Indent();
8691
+
8692
+ // src/collaboration/CollaborationContext.tsx
8693
+ var import_react12 = require("react");
8694
+ var import_jsx_runtime10 = require("react/jsx-runtime");
8695
+ var initialState = {
8696
+ status: "disconnected",
8697
+ users: [],
8698
+ error: void 0,
8699
+ isHost: false
8700
+ };
8701
+ function collaborationReducer(state, action) {
8702
+ switch (action.type) {
8703
+ case "SET_STATUS":
8704
+ return { ...state, status: action.status, error: action.status === "error" ? state.error : void 0 };
8705
+ case "SET_USERS":
8706
+ return { ...state, users: action.users };
8707
+ case "ADD_USER":
8708
+ if (state.users.find((u) => u.id === action.user.id)) {
8709
+ return {
8710
+ ...state,
8711
+ users: state.users.map((u) => u.id === action.user.id ? action.user : u)
8712
+ };
8713
+ }
8714
+ return { ...state, users: [...state.users, action.user] };
8715
+ case "REMOVE_USER":
8716
+ return { ...state, users: state.users.filter((u) => u.id !== action.userId) };
8717
+ case "UPDATE_CURSOR":
8718
+ return {
8719
+ ...state,
8720
+ users: state.users.map(
8721
+ (u) => u.id === action.userId ? { ...u, cursor: action.cursor, lastActive: Date.now() } : u
8722
+ )
8723
+ };
8724
+ case "SET_ERROR":
8725
+ return { ...state, status: "error", error: action.error };
8726
+ case "CLEAR_ERROR":
8727
+ return { ...state, error: void 0 };
8728
+ case "SET_HOST":
8729
+ return { ...state, isHost: action.isHost };
8730
+ default:
8731
+ return state;
8732
+ }
8733
+ }
8734
+ var CollaborationContext = (0, import_react12.createContext)(null);
8735
+ function generateUserColor() {
8736
+ const colors = [
8737
+ "#f87171",
8738
+ "#fb923c",
8739
+ "#fbbf24",
8740
+ "#a3e635",
8741
+ "#4ade80",
8742
+ "#2dd4bf",
8743
+ "#22d3ee",
8744
+ "#60a5fa",
8745
+ "#a78bfa",
8746
+ "#e879f9"
8747
+ ];
8748
+ return colors[Math.floor(Math.random() * colors.length)];
8749
+ }
8750
+ function CollaborationProvider({
8751
+ children,
8752
+ config,
8753
+ onStatusChange,
8754
+ onUsersChange
8755
+ }) {
8756
+ const [state, dispatch] = (0, import_react12.useReducer)(collaborationReducer, initialState);
8757
+ const wsRef = (0, import_react12.useRef)(null);
8758
+ const reconnectTimeoutRef = (0, import_react12.useRef)(null);
8759
+ const reconnectAttemptsRef = (0, import_react12.useRef)(0);
8760
+ (0, import_react12.useEffect)(() => {
8761
+ onStatusChange?.(state.status);
8762
+ }, [state.status, onStatusChange]);
8763
+ (0, import_react12.useEffect)(() => {
8764
+ onUsersChange?.(state.users);
8765
+ }, [state.users, onUsersChange]);
8766
+ const connect = (0, import_react12.useCallback)(async () => {
8767
+ if (!config) return;
8768
+ dispatch({ type: "SET_STATUS", status: "connecting" });
8769
+ try {
8770
+ if (config.provider === "websocket" && config.serverUrl) {
8771
+ const url = new URL(config.serverUrl);
8772
+ url.searchParams.set("room", config.roomId);
8773
+ if (config.token) {
8774
+ url.searchParams.set("token", config.token);
8775
+ }
8776
+ const ws = new WebSocket(url.toString());
8777
+ wsRef.current = ws;
8778
+ ws.onopen = () => {
8779
+ dispatch({ type: "SET_STATUS", status: "connected" });
8780
+ reconnectAttemptsRef.current = 0;
8781
+ ws.send(JSON.stringify({
8782
+ type: "join",
8783
+ user: {
8784
+ id: config.user.id,
8785
+ name: config.user.name,
8786
+ avatar: config.user.avatar,
8787
+ color: config.user.color || generateUserColor()
8788
+ }
8789
+ }));
8790
+ };
8791
+ ws.onmessage = (event) => {
8792
+ try {
8793
+ const message = JSON.parse(event.data);
8794
+ handleCollaborationEvent(message);
8795
+ } catch {
8796
+ console.error("Failed to parse collaboration message");
8797
+ }
8798
+ };
8799
+ ws.onclose = () => {
8800
+ dispatch({ type: "SET_STATUS", status: "disconnected" });
8801
+ handleReconnect();
8802
+ };
8803
+ ws.onerror = () => {
8804
+ dispatch({ type: "SET_ERROR", error: "Connection error" });
8805
+ };
8806
+ } else if (config.provider === "webrtc") {
8807
+ dispatch({ type: "SET_ERROR", error: "WebRTC provider not yet implemented" });
8808
+ } else if (config.provider === "custom") {
8809
+ dispatch({ type: "SET_STATUS", status: "connected" });
8810
+ }
8811
+ } catch (error) {
8812
+ dispatch({ type: "SET_ERROR", error: error instanceof Error ? error.message : "Connection failed" });
8813
+ }
8814
+ }, [config]);
8815
+ const handleCollaborationEvent = (0, import_react12.useCallback)((event) => {
8816
+ switch (event.type) {
8817
+ case "user-joined":
8818
+ dispatch({ type: "ADD_USER", user: event.user });
8819
+ break;
8820
+ case "user-left":
8821
+ dispatch({ type: "REMOVE_USER", userId: event.userId });
8822
+ break;
8823
+ case "cursor-moved":
8824
+ dispatch({ type: "UPDATE_CURSOR", userId: event.userId, cursor: event.cursor });
8825
+ break;
8826
+ case "status-changed":
8827
+ dispatch({ type: "SET_STATUS", status: event.status });
8828
+ break;
8829
+ case "error":
8830
+ dispatch({ type: "SET_ERROR", error: event.message });
8831
+ break;
8832
+ }
8833
+ }, []);
8834
+ const handleReconnect = (0, import_react12.useCallback)(() => {
8835
+ if (!config?.autoReconnect) return;
8836
+ const maxAttempts = config.maxReconnectAttempts ?? 5;
8837
+ const interval = config.reconnectInterval ?? 3e3;
8838
+ if (reconnectAttemptsRef.current >= maxAttempts) {
8839
+ dispatch({ type: "SET_ERROR", error: "Max reconnection attempts reached" });
8840
+ return;
8841
+ }
8842
+ dispatch({ type: "SET_STATUS", status: "reconnecting" });
8843
+ reconnectAttemptsRef.current++;
8844
+ reconnectTimeoutRef.current = setTimeout(() => {
8845
+ connect();
8846
+ }, interval);
8847
+ }, [config, connect]);
8848
+ const disconnect = (0, import_react12.useCallback)(() => {
8849
+ if (reconnectTimeoutRef.current) {
8850
+ clearTimeout(reconnectTimeoutRef.current);
8851
+ }
8852
+ if (wsRef.current) {
8853
+ wsRef.current.close();
8854
+ wsRef.current = null;
8855
+ }
8856
+ dispatch({ type: "SET_STATUS", status: "disconnected" });
8857
+ dispatch({ type: "SET_USERS", users: [] });
8858
+ }, []);
8859
+ const updateCursor = (0, import_react12.useCallback)((cursor) => {
8860
+ if (!config || !wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
8861
+ wsRef.current.send(JSON.stringify({
8862
+ type: "cursor",
8863
+ userId: config.user.id,
8864
+ cursor
8865
+ }));
8866
+ }, [config]);
8867
+ (0, import_react12.useEffect)(() => {
8868
+ return () => {
8869
+ disconnect();
8870
+ };
8871
+ }, [disconnect]);
8872
+ const value = {
8873
+ state,
8874
+ config: config ?? null,
8875
+ connect,
8876
+ disconnect,
8877
+ updateCursor,
8878
+ isEnabled: !!config
8879
+ };
8880
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CollaborationContext.Provider, { value, children });
8881
+ }
8882
+ function useCollaboration() {
8883
+ const context = (0, import_react12.useContext)(CollaborationContext);
8884
+ if (!context) {
8885
+ throw new Error("useCollaboration must be used within a CollaborationProvider");
8886
+ }
8887
+ return context;
8888
+ }
8889
+ function useCollaborationOptional() {
8890
+ return (0, import_react12.useContext)(CollaborationContext);
8891
+ }
8892
+
8893
+ // src/collaboration/PresenceIndicator.tsx
8894
+ var import_jsx_runtime11 = require("react/jsx-runtime");
8895
+ function getInitials(name) {
8896
+ return name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
8897
+ }
8898
+ function UserAvatar({ user, showName }) {
8899
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
8900
+ "div",
8901
+ {
8902
+ className: "rte-presence-avatar",
8903
+ style: { borderColor: user.color },
8904
+ title: user.name,
8905
+ children: [
8906
+ user.avatar ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: user.avatar, alt: user.name }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { backgroundColor: user.color }, children: getInitials(user.name) }),
8907
+ user.isActive && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rte-presence-active-dot" }),
8908
+ showName && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rte-presence-name", children: user.name })
8909
+ ]
8910
+ }
8911
+ );
8912
+ }
8913
+ function StatusBadge({ status }) {
8914
+ const statusConfig = {
8915
+ connected: { label: "Connected", color: "#22c55e" },
8916
+ connecting: { label: "Connecting...", color: "#eab308" },
8917
+ reconnecting: { label: "Reconnecting...", color: "#f97316" },
8918
+ disconnected: { label: "Disconnected", color: "#6b7280" },
8919
+ error: { label: "Error", color: "#ef4444" }
8920
+ };
8921
+ const config = statusConfig[status] || statusConfig.disconnected;
8922
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rte-presence-status", style: { color: config.color }, children: [
8923
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rte-presence-status-dot", style: { backgroundColor: config.color } }),
8924
+ config.label
8925
+ ] });
8926
+ }
8927
+ function PresenceIndicator({
8928
+ maxAvatars = 5,
8929
+ showNames = false,
8930
+ className = ""
8931
+ }) {
8932
+ const collaboration = useCollaborationOptional();
8933
+ if (!collaboration?.isEnabled) {
8934
+ return null;
8935
+ }
8936
+ const { state } = collaboration;
8937
+ const visibleUsers = state.users.slice(0, maxAvatars);
8938
+ const remainingCount = Math.max(0, state.users.length - maxAvatars);
8939
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: `rte-presence-indicator ${className}`, children: [
8940
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StatusBadge, { status: state.status }),
8941
+ state.users.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rte-presence-avatars", children: [
8942
+ visibleUsers.map((user) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(UserAvatar, { user, showName: showNames }, user.id)),
8943
+ remainingCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rte-presence-avatar rte-presence-more", children: [
8944
+ "+",
8945
+ remainingCount
8946
+ ] })
8947
+ ] }),
8948
+ state.error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rte-presence-error", title: state.error, children: "!" })
8949
+ ] });
8950
+ }
8951
+
8952
+ // src/comments/types.ts
8953
+ var DEFAULT_REACTION_EMOJIS = ["\u{1F44D}", "\u{1F44E}", "\u2764\uFE0F", "\u{1F389}", "\u{1F604}", "\u{1F615}", "\u{1F440}", "\u{1F680}"];
8954
+
8955
+ // src/comments/CommentsContext.tsx
8956
+ var import_react13 = require("react");
8957
+ var import_jsx_runtime12 = require("react/jsx-runtime");
8958
+ var initialState2 = {
8959
+ threads: [],
8960
+ activeThreadId: null,
8961
+ isPanelOpen: false,
8962
+ filter: "all",
8963
+ currentUser: null
8964
+ };
8965
+ function generateId() {
8966
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
8967
+ }
8968
+ function commentsReducer(state, action) {
8969
+ switch (action.type) {
8970
+ case "SET_THREADS":
8971
+ return { ...state, threads: action.threads };
8972
+ case "ADD_THREAD":
8973
+ return { ...state, threads: [...state.threads, action.thread] };
8974
+ case "DELETE_THREAD":
8975
+ return {
8976
+ ...state,
8977
+ threads: state.threads.filter((t) => t.id !== action.threadId),
8978
+ activeThreadId: state.activeThreadId === action.threadId ? null : state.activeThreadId
8979
+ };
8980
+ case "RESOLVE_THREAD":
8981
+ return {
8982
+ ...state,
8983
+ threads: state.threads.map(
8984
+ (t) => t.id === action.threadId ? { ...t, status: "resolved", resolvedAt: Date.now(), resolvedBy: action.resolvedBy } : t
8985
+ )
8986
+ };
8987
+ case "REOPEN_THREAD":
8988
+ return {
8989
+ ...state,
8990
+ threads: state.threads.map(
8991
+ (t) => t.id === action.threadId ? { ...t, status: "open", resolvedAt: void 0, resolvedBy: void 0 } : t
8992
+ )
8993
+ };
8994
+ case "ADD_COMMENT":
8995
+ return {
8996
+ ...state,
8997
+ threads: state.threads.map(
8998
+ (t) => t.id === action.threadId ? { ...t, comments: [...t.comments, action.comment] } : t
8999
+ )
9000
+ };
9001
+ case "UPDATE_COMMENT":
9002
+ return {
9003
+ ...state,
9004
+ threads: state.threads.map(
9005
+ (t) => t.id === action.threadId ? {
9006
+ ...t,
9007
+ comments: t.comments.map(
9008
+ (c) => c.id === action.comment.id ? action.comment : c
9009
+ )
9010
+ } : t
9011
+ )
9012
+ };
9013
+ case "DELETE_COMMENT":
9014
+ return {
9015
+ ...state,
9016
+ threads: state.threads.map(
9017
+ (t) => t.id === action.threadId ? { ...t, comments: t.comments.filter((c) => c.id !== action.commentId) } : t
9018
+ )
9019
+ };
9020
+ case "ADD_REACTION":
9021
+ return {
9022
+ ...state,
9023
+ threads: state.threads.map(
9024
+ (t) => t.id === action.threadId ? {
9025
+ ...t,
9026
+ comments: t.comments.map((c) => {
9027
+ if (c.id !== action.commentId) return c;
9028
+ const reactions = c.reactions || [];
9029
+ const existingReaction = reactions.find((r) => r.emoji === action.emoji);
9030
+ if (existingReaction) {
9031
+ return {
9032
+ ...c,
9033
+ reactions: reactions.map(
9034
+ (r) => r.emoji === action.emoji ? { ...r, users: [...r.users, action.user] } : r
9035
+ )
9036
+ };
9037
+ }
9038
+ return {
9039
+ ...c,
9040
+ reactions: [...reactions, { emoji: action.emoji, users: [action.user] }]
9041
+ };
9042
+ })
9043
+ } : t
9044
+ )
9045
+ };
9046
+ case "REMOVE_REACTION":
9047
+ return {
9048
+ ...state,
9049
+ threads: state.threads.map(
9050
+ (t) => t.id === action.threadId ? {
9051
+ ...t,
9052
+ comments: t.comments.map((c) => {
9053
+ if (c.id !== action.commentId) return c;
9054
+ return {
9055
+ ...c,
9056
+ reactions: (c.reactions || []).map(
9057
+ (r) => r.emoji === action.emoji ? { ...r, users: r.users.filter((u) => u.id !== action.userId) } : r
9058
+ ).filter((r) => r.users.length > 0)
9059
+ };
9060
+ })
9061
+ } : t
9062
+ )
9063
+ };
9064
+ case "SET_ACTIVE_THREAD":
9065
+ return { ...state, activeThreadId: action.threadId };
9066
+ case "TOGGLE_PANEL":
9067
+ return { ...state, isPanelOpen: action.isOpen ?? !state.isPanelOpen };
9068
+ case "SET_FILTER":
9069
+ return { ...state, filter: action.filter };
9070
+ case "SET_CURRENT_USER":
9071
+ return { ...state, currentUser: action.user };
9072
+ default:
9073
+ return state;
9074
+ }
9075
+ }
9076
+ var CommentsContext = (0, import_react13.createContext)(null);
9077
+ function CommentsProvider({
9078
+ children,
9079
+ config,
9080
+ initialThreads = [],
9081
+ onThreadsChange
9082
+ }) {
9083
+ const [state, dispatch] = (0, import_react13.useReducer)(commentsReducer, {
9084
+ ...initialState2,
9085
+ threads: initialThreads,
9086
+ currentUser: config?.currentUser ?? null
9087
+ });
9088
+ const subscribersRef = { current: /* @__PURE__ */ new Set() };
9089
+ (0, import_react13.useEffect)(() => {
9090
+ if (config?.onLoad) {
9091
+ config.onLoad().then((threads) => {
9092
+ dispatch({ type: "SET_THREADS", threads });
9093
+ });
9094
+ }
9095
+ }, [config]);
9096
+ (0, import_react13.useEffect)(() => {
9097
+ onThreadsChange?.(state.threads);
9098
+ if (config?.onSave) {
9099
+ config.onSave(state.threads);
9100
+ }
9101
+ }, [state.threads, onThreadsChange, config]);
9102
+ (0, import_react13.useEffect)(() => {
9103
+ if (config?.currentUser) {
9104
+ dispatch({ type: "SET_CURRENT_USER", user: config.currentUser });
9105
+ }
9106
+ }, [config?.currentUser]);
9107
+ const emitEvent = (0, import_react13.useCallback)((event) => {
9108
+ subscribersRef.current.forEach((callback) => callback(event));
9109
+ }, []);
9110
+ const createThread = (0, import_react13.useCallback)((range, initialComment) => {
9111
+ if (!state.currentUser) return null;
9112
+ const threadId = generateId();
9113
+ const commentId = generateId();
9114
+ const now = Date.now();
9115
+ const comment = {
9116
+ id: commentId,
9117
+ threadId,
9118
+ content: initialComment,
9119
+ author: state.currentUser,
9120
+ createdAt: now,
9121
+ isEdited: false
9122
+ };
9123
+ const thread = {
9124
+ id: threadId,
9125
+ range,
9126
+ comments: [comment],
9127
+ status: "open",
9128
+ createdAt: now
9129
+ };
9130
+ dispatch({ type: "ADD_THREAD", thread });
9131
+ emitEvent({ type: "thread-created", thread });
9132
+ return thread;
9133
+ }, [state.currentUser, emitEvent]);
9134
+ const deleteThread = (0, import_react13.useCallback)((threadId) => {
9135
+ if (config?.allowDelete === false) return;
9136
+ dispatch({ type: "DELETE_THREAD", threadId });
9137
+ emitEvent({ type: "thread-deleted", threadId });
9138
+ }, [config, emitEvent]);
9139
+ const resolveThread = (0, import_react13.useCallback)((threadId) => {
9140
+ if (!state.currentUser || config?.allowResolve === false) return;
9141
+ dispatch({ type: "RESOLVE_THREAD", threadId, resolvedBy: state.currentUser });
9142
+ emitEvent({ type: "thread-resolved", threadId, resolvedBy: state.currentUser });
9143
+ }, [state.currentUser, config, emitEvent]);
9144
+ const reopenThread = (0, import_react13.useCallback)((threadId) => {
9145
+ dispatch({ type: "REOPEN_THREAD", threadId });
9146
+ emitEvent({ type: "thread-reopened", threadId });
9147
+ }, [emitEvent]);
9148
+ const addComment = (0, import_react13.useCallback)((threadId, content) => {
9149
+ if (!state.currentUser) return null;
9150
+ const comment = {
9151
+ id: generateId(),
9152
+ threadId,
9153
+ content,
9154
+ author: state.currentUser,
9155
+ createdAt: Date.now(),
9156
+ isEdited: false
9157
+ };
9158
+ dispatch({ type: "ADD_COMMENT", threadId, comment });
9159
+ emitEvent({ type: "comment-added", threadId, comment });
9160
+ return comment;
9161
+ }, [state.currentUser, emitEvent]);
9162
+ const updateComment = (0, import_react13.useCallback)((threadId, commentId, content) => {
9163
+ if (config?.allowEdit === false) return;
9164
+ const thread = state.threads.find((t) => t.id === threadId);
9165
+ const existingComment = thread?.comments.find((c) => c.id === commentId);
9166
+ if (!existingComment) return;
9167
+ const updatedComment = {
9168
+ ...existingComment,
9169
+ content,
9170
+ updatedAt: Date.now(),
9171
+ isEdited: true
9172
+ };
9173
+ dispatch({ type: "UPDATE_COMMENT", threadId, comment: updatedComment });
9174
+ emitEvent({ type: "comment-updated", threadId, comment: updatedComment });
9175
+ }, [state.threads, config, emitEvent]);
9176
+ const deleteComment = (0, import_react13.useCallback)((threadId, commentId) => {
9177
+ if (config?.allowDelete === false) return;
9178
+ dispatch({ type: "DELETE_COMMENT", threadId, commentId });
9179
+ emitEvent({ type: "comment-deleted", threadId, commentId });
9180
+ }, [config, emitEvent]);
9181
+ const addReaction = (0, import_react13.useCallback)((threadId, commentId, emoji) => {
9182
+ if (!state.currentUser || config?.allowReactions === false) return;
9183
+ const allowedEmojis = config?.reactionEmojis ?? DEFAULT_REACTION_EMOJIS;
9184
+ if (!allowedEmojis.includes(emoji)) return;
9185
+ dispatch({ type: "ADD_REACTION", threadId, commentId, emoji, user: state.currentUser });
9186
+ emitEvent({ type: "reaction-added", threadId, commentId, emoji, user: state.currentUser });
9187
+ }, [state.currentUser, config, emitEvent]);
9188
+ const removeReaction = (0, import_react13.useCallback)((threadId, commentId, emoji) => {
9189
+ if (!state.currentUser) return;
9190
+ dispatch({ type: "REMOVE_REACTION", threadId, commentId, emoji, userId: state.currentUser.id });
9191
+ emitEvent({ type: "reaction-removed", threadId, commentId, emoji, userId: state.currentUser.id });
9192
+ }, [state.currentUser, emitEvent]);
9193
+ const setActiveThread = (0, import_react13.useCallback)((threadId) => {
9194
+ dispatch({ type: "SET_ACTIVE_THREAD", threadId });
9195
+ }, []);
9196
+ const togglePanel = (0, import_react13.useCallback)((isOpen) => {
9197
+ dispatch({ type: "TOGGLE_PANEL", isOpen });
9198
+ }, []);
9199
+ const setFilter = (0, import_react13.useCallback)((filter) => {
9200
+ dispatch({ type: "SET_FILTER", filter });
9201
+ }, []);
9202
+ const getThreadByRange = (0, import_react13.useCallback)((from, to) => {
9203
+ return state.threads.find((t) => t.range.from === from && t.range.to === to);
9204
+ }, [state.threads]);
9205
+ const getFilteredThreads = (0, import_react13.useCallback)(() => {
9206
+ if (state.filter === "all") return state.threads;
9207
+ return state.threads.filter((t) => t.status === state.filter);
9208
+ }, [state.threads, state.filter]);
9209
+ const subscribe = (0, import_react13.useCallback)((callback) => {
9210
+ subscribersRef.current.add(callback);
9211
+ return () => {
9212
+ subscribersRef.current.delete(callback);
9213
+ };
9214
+ }, []);
9215
+ const value = {
9216
+ state,
9217
+ config: config ?? null,
9218
+ createThread,
9219
+ deleteThread,
9220
+ resolveThread,
9221
+ reopenThread,
9222
+ addComment,
9223
+ updateComment,
9224
+ deleteComment,
9225
+ addReaction,
9226
+ removeReaction,
9227
+ setActiveThread,
9228
+ togglePanel,
9229
+ setFilter,
9230
+ getThreadByRange,
9231
+ getFilteredThreads,
9232
+ subscribe,
9233
+ isEnabled: !!config
9234
+ };
9235
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CommentsContext.Provider, { value, children });
9236
+ }
9237
+ function useComments() {
9238
+ const context = (0, import_react13.useContext)(CommentsContext);
9239
+ if (!context) {
9240
+ throw new Error("useComments must be used within a CommentsProvider");
9241
+ }
9242
+ return context;
9243
+ }
9244
+ function useCommentsOptional() {
9245
+ return (0, import_react13.useContext)(CommentsContext);
9246
+ }
9247
+
9248
+ // src/comments/CommentsPanel.tsx
9249
+ var import_react14 = require("react");
9250
+ var import_jsx_runtime13 = require("react/jsx-runtime");
9251
+ function formatRelativeTime(timestamp) {
9252
+ const now = Date.now();
9253
+ const diff = now - timestamp;
9254
+ const minutes = Math.floor(diff / 6e4);
9255
+ const hours = Math.floor(diff / 36e5);
9256
+ const days = Math.floor(diff / 864e5);
9257
+ if (minutes < 1) return "Just now";
9258
+ if (minutes < 60) return `${minutes}m ago`;
9259
+ if (hours < 24) return `${hours}h ago`;
9260
+ if (days < 7) return `${days}d ago`;
9261
+ return new Date(timestamp).toLocaleDateString();
9262
+ }
9263
+ function CommentItem({
9264
+ comment,
9265
+ threadId,
9266
+ isFirst
9267
+ }) {
9268
+ const comments = useCommentsOptional();
9269
+ const [isEditing, setIsEditing] = (0, import_react14.useState)(false);
9270
+ const [editContent, setEditContent] = (0, import_react14.useState)(comment.content);
9271
+ const [showReactions, setShowReactions] = (0, import_react14.useState)(false);
9272
+ if (!comments) return null;
9273
+ const { config, updateComment, deleteComment, addReaction, removeReaction, state } = comments;
9274
+ const canEdit = config?.allowEdit !== false && comment.author.id === state.currentUser?.id;
9275
+ const canDelete = config?.allowDelete !== false && comment.author.id === state.currentUser?.id;
9276
+ const canReact = config?.allowReactions !== false;
9277
+ const reactionEmojis = config?.reactionEmojis ?? DEFAULT_REACTION_EMOJIS;
9278
+ const handleSaveEdit = () => {
9279
+ updateComment(threadId, comment.id, editContent);
9280
+ setIsEditing(false);
9281
+ };
9282
+ const handleToggleReaction = (emoji) => {
9283
+ const reaction = comment.reactions?.find((r) => r.emoji === emoji);
9284
+ const hasReacted = reaction?.users.some((u) => u.id === state.currentUser?.id);
9285
+ if (hasReacted) {
9286
+ removeReaction(threadId, comment.id, emoji);
9287
+ } else {
9288
+ addReaction(threadId, comment.id, emoji);
9289
+ }
9290
+ setShowReactions(false);
9291
+ };
9292
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `rte-comment ${isFirst ? "rte-comment-first" : ""}`, children: [
9293
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comment-header", children: [
9294
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comment-author", children: [
9295
+ comment.author.avatar ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: comment.author.avatar, alt: comment.author.name, className: "rte-comment-avatar" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comment-avatar-placeholder", children: comment.author.name.charAt(0).toUpperCase() }),
9296
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rte-comment-author-name", children: comment.author.name })
9297
+ ] }),
9298
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rte-comment-time", children: [
9299
+ formatRelativeTime(comment.createdAt),
9300
+ comment.isEdited && " (edited)"
9301
+ ] })
9302
+ ] }),
9303
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comment-edit", children: [
9304
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9305
+ "textarea",
9306
+ {
9307
+ value: editContent,
9308
+ onChange: (e) => setEditContent(e.target.value),
9309
+ className: "rte-comment-edit-input"
9310
+ }
9311
+ ),
9312
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comment-edit-actions", children: [
9313
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: handleSaveEdit, className: "rte-comment-btn-save", children: "Save" }),
9314
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setIsEditing(false), className: "rte-comment-btn-cancel", children: "Cancel" })
9315
+ ] })
9316
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comment-content", dangerouslySetInnerHTML: { __html: comment.content } }),
9317
+ comment.reactions && comment.reactions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comment-reactions", children: comment.reactions.map((reaction) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
9318
+ "button",
9319
+ {
9320
+ className: `rte-comment-reaction ${reaction.users.some((u) => u.id === state.currentUser?.id) ? "active" : ""}`,
9321
+ onClick: () => handleToggleReaction(reaction.emoji),
9322
+ title: reaction.users.map((u) => u.name).join(", "),
9323
+ children: [
9324
+ reaction.emoji,
9325
+ " ",
9326
+ reaction.users.length
9327
+ ]
9328
+ },
9329
+ reaction.emoji
9330
+ )) }),
9331
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comment-actions", children: [
9332
+ canReact && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comment-reaction-picker", children: [
9333
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9334
+ "button",
9335
+ {
9336
+ className: "rte-comment-action-btn",
9337
+ onClick: () => setShowReactions(!showReactions),
9338
+ children: "\u{1F60A}"
9339
+ }
9340
+ ),
9341
+ showReactions && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comment-reaction-dropdown", children: reactionEmojis.map((emoji) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => handleToggleReaction(emoji), children: emoji }, emoji)) })
9342
+ ] }),
9343
+ canEdit && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "rte-comment-action-btn", onClick: () => setIsEditing(true), children: "Edit" }),
9344
+ canDelete && !isFirst && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9345
+ "button",
9346
+ {
9347
+ className: "rte-comment-action-btn rte-comment-action-delete",
9348
+ onClick: () => deleteComment(threadId, comment.id),
9349
+ children: "Delete"
9350
+ }
9351
+ )
9352
+ ] })
9353
+ ] });
9354
+ }
9355
+ function ThreadItem({ thread }) {
9356
+ const comments = useCommentsOptional();
9357
+ const [replyContent, setReplyContent] = (0, import_react14.useState)("");
9358
+ const [showReply, setShowReply] = (0, import_react14.useState)(false);
9359
+ if (!comments) return null;
9360
+ const { state, setActiveThread, addComment, resolveThread, reopenThread, deleteThread, config } = comments;
9361
+ const isActive = state.activeThreadId === thread.id;
9362
+ const canResolve = config?.allowResolve !== false;
9363
+ const handleReply = () => {
9364
+ if (replyContent.trim()) {
9365
+ addComment(thread.id, replyContent);
9366
+ setReplyContent("");
9367
+ setShowReply(false);
9368
+ }
9369
+ };
9370
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
9371
+ "div",
9372
+ {
9373
+ className: `rte-thread ${isActive ? "rte-thread-active" : ""} ${thread.status === "resolved" ? "rte-thread-resolved" : ""}`,
9374
+ onClick: () => setActiveThread(thread.id),
9375
+ children: [
9376
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-thread-header", children: [
9377
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-thread-quote", children: [
9378
+ '"',
9379
+ thread.range.text.slice(0, 50),
9380
+ '..."'
9381
+ ] }),
9382
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-thread-meta", children: [
9383
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: `rte-thread-status rte-thread-status-${thread.status}`, children: thread.status }),
9384
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rte-thread-count", children: [
9385
+ thread.comments.length,
9386
+ " comment",
9387
+ thread.comments.length !== 1 ? "s" : ""
9388
+ ] })
9389
+ ] })
9390
+ ] }),
9391
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-thread-comments", children: thread.comments.map((comment, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9392
+ CommentItem,
9393
+ {
9394
+ comment,
9395
+ threadId: thread.id,
9396
+ isFirst: index === 0
9397
+ },
9398
+ comment.id
9399
+ )) }),
9400
+ thread.status === "open" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-thread-reply", children: showReply ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
9401
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9402
+ "textarea",
9403
+ {
9404
+ value: replyContent,
9405
+ onChange: (e) => setReplyContent(e.target.value),
9406
+ placeholder: "Write a reply...",
9407
+ className: "rte-thread-reply-input"
9408
+ }
9409
+ ),
9410
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-thread-reply-actions", children: [
9411
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: handleReply, className: "rte-btn-primary", disabled: !replyContent.trim(), children: "Reply" }),
9412
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowReply(false), className: "rte-btn-secondary", children: "Cancel" })
9413
+ ] })
9414
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowReply(true), className: "rte-thread-reply-btn", children: "Reply" }) }),
9415
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-thread-actions", children: [
9416
+ canResolve && thread.status === "open" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => resolveThread(thread.id), className: "rte-btn-resolve", children: "\u2713 Resolve" }),
9417
+ thread.status === "resolved" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => reopenThread(thread.id), className: "rte-btn-reopen", children: "Reopen" }),
9418
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9419
+ "button",
9420
+ {
9421
+ onClick: (e) => {
9422
+ e.stopPropagation();
9423
+ deleteThread(thread.id);
9424
+ },
9425
+ className: "rte-btn-delete-thread",
9426
+ children: "Delete"
9427
+ }
9428
+ )
9429
+ ] })
9430
+ ]
9431
+ }
9432
+ );
9433
+ }
9434
+ function CommentsPanel({ position = "right", className = "" }) {
9435
+ const comments = useCommentsOptional();
9436
+ if (!comments?.isEnabled || !comments.state.isPanelOpen) {
9437
+ return null;
9438
+ }
9439
+ const { state, togglePanel, setFilter, getFilteredThreads } = comments;
9440
+ const filteredThreads = getFilteredThreads();
9441
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `rte-comments-panel rte-comments-panel-${position} ${className}`, children: [
9442
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rte-comments-panel-header", children: [
9443
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h3", { children: "Comments" }),
9444
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => togglePanel(false), className: "rte-comments-close-btn", children: "\xD7" })
9445
+ ] }),
9446
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comments-filters", children: ["all", "open", "resolved"].map((filter) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
9447
+ "button",
9448
+ {
9449
+ className: `rte-comments-filter ${state.filter === filter ? "active" : ""}`,
9450
+ onClick: () => setFilter(filter),
9451
+ children: [
9452
+ filter.charAt(0).toUpperCase() + filter.slice(1),
9453
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rte-comments-filter-count", children: filter === "all" ? state.threads.length : state.threads.filter((t) => t.status === filter).length })
9454
+ ]
9455
+ },
9456
+ filter
9457
+ )) }),
9458
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comments-list", children: filteredThreads.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rte-comments-empty", children: state.filter === "all" ? "No comments yet. Select text and add a comment." : `No ${state.filter} comments.` }) : filteredThreads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ThreadItem, { thread }, thread.id)) })
9459
+ ] });
9460
+ }
9461
+
9462
+ // src/history/VersionHistoryContext.tsx
9463
+ var import_react15 = require("react");
9464
+ var import_jsx_runtime14 = require("react/jsx-runtime");
9465
+ var initialState3 = {
9466
+ versions: [],
9467
+ viewingVersionId: null,
9468
+ isPanelOpen: false,
9469
+ isComparing: false,
9470
+ compareFromId: null,
9471
+ compareToId: null,
9472
+ autoSaveEnabled: true,
9473
+ autoSaveInterval: 6e4
9474
+ };
9475
+ function generateId2() {
9476
+ return `v-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
9477
+ }
9478
+ function versionHistoryReducer(state, action) {
9479
+ switch (action.type) {
9480
+ case "SET_VERSIONS":
9481
+ return { ...state, versions: action.versions };
9482
+ case "ADD_VERSION":
9483
+ return {
9484
+ ...state,
9485
+ versions: [action.version, ...state.versions]
9486
+ };
9487
+ case "DELETE_VERSION":
9488
+ return {
9489
+ ...state,
9490
+ versions: state.versions.filter((v) => v.id !== action.versionId),
9491
+ viewingVersionId: state.viewingVersionId === action.versionId ? null : state.viewingVersionId
9492
+ };
9493
+ case "PIN_VERSION":
9494
+ return {
9495
+ ...state,
9496
+ versions: state.versions.map(
9497
+ (v) => v.id === action.versionId ? { ...v, isPinned: true } : v
9498
+ )
9499
+ };
9500
+ case "UNPIN_VERSION":
9501
+ return {
9502
+ ...state,
9503
+ versions: state.versions.map(
9504
+ (v) => v.id === action.versionId ? { ...v, isPinned: false } : v
9505
+ )
9506
+ };
9507
+ case "RENAME_VERSION":
9508
+ return {
9509
+ ...state,
9510
+ versions: state.versions.map(
9511
+ (v) => v.id === action.versionId ? { ...v, title: action.title } : v
9512
+ )
9513
+ };
9514
+ case "SET_VIEWING_VERSION":
9515
+ return { ...state, viewingVersionId: action.versionId };
9516
+ case "TOGGLE_PANEL":
9517
+ return { ...state, isPanelOpen: action.isOpen ?? !state.isPanelOpen };
9518
+ case "START_COMPARE":
9519
+ return {
9520
+ ...state,
9521
+ isComparing: true,
9522
+ compareFromId: action.fromId,
9523
+ compareToId: action.toId
9524
+ };
9525
+ case "STOP_COMPARE":
9526
+ return {
9527
+ ...state,
9528
+ isComparing: false,
9529
+ compareFromId: null,
9530
+ compareToId: null
9531
+ };
9532
+ case "SET_AUTO_SAVE":
9533
+ return { ...state, autoSaveEnabled: action.enabled };
9534
+ case "SET_AUTO_SAVE_INTERVAL":
9535
+ return { ...state, autoSaveInterval: action.interval };
9536
+ default:
9537
+ return state;
9538
+ }
9539
+ }
9540
+ function countWords(text) {
9541
+ return text.trim().split(/\s+/).filter(Boolean).length;
9542
+ }
9543
+ function computeDiff(oldText, newText) {
9544
+ const changes = [];
9545
+ const oldLines = oldText.split("\n");
9546
+ const newLines = newText.split("\n");
9547
+ let i = 0;
9548
+ let j = 0;
9549
+ while (i < oldLines.length || j < newLines.length) {
9550
+ if (i >= oldLines.length) {
9551
+ changes.push({
9552
+ type: "addition",
9553
+ content: newLines[j],
9554
+ position: j
9555
+ });
9556
+ j++;
9557
+ } else if (j >= newLines.length) {
9558
+ changes.push({
9559
+ type: "deletion",
9560
+ content: oldLines[i],
9561
+ position: i
9562
+ });
9563
+ i++;
9564
+ } else if (oldLines[i] === newLines[j]) {
9565
+ i++;
9566
+ j++;
9567
+ } else {
9568
+ const oldLineInNew = newLines.indexOf(oldLines[i], j);
9569
+ const newLineInOld = oldLines.indexOf(newLines[j], i);
9570
+ if (oldLineInNew === -1 && newLineInOld === -1) {
9571
+ changes.push({
9572
+ type: "modification",
9573
+ content: `${oldLines[i]} -> ${newLines[j]}`,
9574
+ position: i
9575
+ });
9576
+ i++;
9577
+ j++;
9578
+ } else if (oldLineInNew === -1) {
9579
+ changes.push({
9580
+ type: "deletion",
9581
+ content: oldLines[i],
9582
+ position: i
9583
+ });
9584
+ i++;
9585
+ } else {
9586
+ changes.push({
9587
+ type: "addition",
9588
+ content: newLines[j],
9589
+ position: j
9590
+ });
9591
+ j++;
9592
+ }
9593
+ }
9594
+ }
9595
+ return changes;
9596
+ }
9597
+ var VersionHistoryContext = (0, import_react15.createContext)(null);
9598
+ function VersionHistoryProvider({
9599
+ children,
9600
+ config,
9601
+ initialVersions = [],
9602
+ onVersionsChange,
9603
+ getCurrentContent
9604
+ }) {
9605
+ const [state, dispatch] = (0, import_react15.useReducer)(versionHistoryReducer, {
9606
+ ...initialState3,
9607
+ versions: initialVersions,
9608
+ autoSaveEnabled: config?.autoSave ?? true,
9609
+ autoSaveInterval: config?.autoSaveInterval ?? 6e4
9610
+ });
9611
+ const subscribersRef = (0, import_react15.useRef)(/* @__PURE__ */ new Set());
9612
+ const autoSaveTimerRef = (0, import_react15.useRef)(null);
9613
+ const lastContentRef = (0, import_react15.useRef)("");
9614
+ const versionNumberRef = (0, import_react15.useRef)(initialVersions.length);
9615
+ (0, import_react15.useEffect)(() => {
9616
+ if (config?.onLoad) {
9617
+ config.onLoad().then((versions) => {
9618
+ dispatch({ type: "SET_VERSIONS", versions });
9619
+ versionNumberRef.current = versions.length;
9620
+ });
9621
+ }
9622
+ }, [config]);
9623
+ (0, import_react15.useEffect)(() => {
9624
+ onVersionsChange?.(state.versions);
9625
+ if (config?.onSave) {
9626
+ config.onSave(state.versions);
9627
+ }
9628
+ }, [state.versions, onVersionsChange, config]);
9629
+ (0, import_react15.useEffect)(() => {
9630
+ if (!state.autoSaveEnabled || !config || !getCurrentContent) return;
9631
+ autoSaveTimerRef.current = setInterval(() => {
9632
+ const { html, json } = getCurrentContent();
9633
+ if (html !== lastContentRef.current) {
9634
+ lastContentRef.current = html;
9635
+ createVersion(html, json, { isAutoSave: true });
9636
+ }
9637
+ }, state.autoSaveInterval);
9638
+ return () => {
9639
+ if (autoSaveTimerRef.current) {
9640
+ clearInterval(autoSaveTimerRef.current);
9641
+ }
9642
+ };
9643
+ }, [state.autoSaveEnabled, state.autoSaveInterval, config, getCurrentContent]);
9644
+ const emitEvent = (0, import_react15.useCallback)((event) => {
9645
+ subscribersRef.current.forEach((callback) => callback(event));
9646
+ }, []);
9647
+ const createVersion = (0, import_react15.useCallback)((content, jsonContent, options) => {
9648
+ if (!config?.currentUser) return null;
9649
+ const maxVersions = config.maxVersions ?? 100;
9650
+ let versions = state.versions;
9651
+ if (versions.length >= maxVersions) {
9652
+ const oldestUnpinned = [...versions].reverse().find((v) => !v.isPinned);
9653
+ if (oldestUnpinned) {
9654
+ versions = versions.filter((v) => v.id !== oldestUnpinned.id);
9655
+ }
9656
+ }
9657
+ versionNumberRef.current++;
9658
+ const textContent = content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
9659
+ const version = {
9660
+ id: generateId2(),
9661
+ number: versionNumberRef.current,
9662
+ title: options?.title,
9663
+ content,
9664
+ jsonContent,
9665
+ textContent,
9666
+ author: config.currentUser,
9667
+ createdAt: Date.now(),
9668
+ wordCount: countWords(textContent),
9669
+ characterCount: textContent.length,
9670
+ isAutoSave: options?.isAutoSave ?? false,
9671
+ isPinned: false
9672
+ };
9673
+ dispatch({ type: "ADD_VERSION", version });
9674
+ emitEvent({ type: "version-created", version });
9675
+ return version;
9676
+ }, [config, state.versions, emitEvent]);
9677
+ const deleteVersion = (0, import_react15.useCallback)((versionId) => {
9678
+ dispatch({ type: "DELETE_VERSION", versionId });
9679
+ emitEvent({ type: "version-deleted", versionId });
9680
+ }, [emitEvent]);
9681
+ const restoreVersion = (0, import_react15.useCallback)((versionId) => {
9682
+ const version = state.versions.find((v) => v.id === versionId);
9683
+ if (!version) return null;
9684
+ config?.onRestore?.(version);
9685
+ emitEvent({ type: "version-restored", versionId });
9686
+ return version.content;
9687
+ }, [state.versions, config, emitEvent]);
9688
+ const pinVersion = (0, import_react15.useCallback)((versionId) => {
9689
+ dispatch({ type: "PIN_VERSION", versionId });
9690
+ emitEvent({ type: "version-pinned", versionId });
9691
+ }, [emitEvent]);
9692
+ const unpinVersion = (0, import_react15.useCallback)((versionId) => {
9693
+ dispatch({ type: "UNPIN_VERSION", versionId });
9694
+ emitEvent({ type: "version-unpinned", versionId });
9695
+ }, [emitEvent]);
9696
+ const renameVersion = (0, import_react15.useCallback)((versionId, title) => {
9697
+ dispatch({ type: "RENAME_VERSION", versionId, title });
9698
+ emitEvent({ type: "version-renamed", versionId, title });
9699
+ }, [emitEvent]);
9700
+ const viewVersion = (0, import_react15.useCallback)((versionId) => {
9701
+ dispatch({ type: "SET_VIEWING_VERSION", versionId });
9702
+ }, []);
9703
+ const getVersionContent = (0, import_react15.useCallback)((versionId) => {
9704
+ const version = state.versions.find((v) => v.id === versionId);
9705
+ return version?.content ?? null;
9706
+ }, [state.versions]);
9707
+ const compareVersions = (0, import_react15.useCallback)((fromId, toId) => {
9708
+ const fromVersion = state.versions.find((v) => v.id === fromId);
9709
+ const toVersion = state.versions.find((v) => v.id === toId);
9710
+ if (!fromVersion || !toVersion) return null;
9711
+ const changes = computeDiff(fromVersion.textContent, toVersion.textContent);
9712
+ return {
9713
+ fromVersionId: fromId,
9714
+ toVersionId: toId,
9715
+ changes,
9716
+ stats: {
9717
+ additions: changes.filter((c) => c.type === "addition").length,
9718
+ deletions: changes.filter((c) => c.type === "deletion").length,
9719
+ modifications: changes.filter((c) => c.type === "modification").length
9720
+ }
9721
+ };
9722
+ }, [state.versions]);
9723
+ const startCompare = (0, import_react15.useCallback)((fromId, toId) => {
9724
+ dispatch({ type: "START_COMPARE", fromId, toId });
9725
+ }, []);
9726
+ const stopCompare = (0, import_react15.useCallback)(() => {
9727
+ dispatch({ type: "STOP_COMPARE" });
9728
+ }, []);
9729
+ const togglePanel = (0, import_react15.useCallback)((isOpen) => {
9730
+ dispatch({ type: "TOGGLE_PANEL", isOpen });
9731
+ }, []);
9732
+ const setAutoSave = (0, import_react15.useCallback)((enabled) => {
9733
+ dispatch({ type: "SET_AUTO_SAVE", enabled });
9734
+ emitEvent({ type: "auto-save-toggled", enabled });
9735
+ }, [emitEvent]);
9736
+ const getVersion = (0, import_react15.useCallback)((versionId) => {
9737
+ return state.versions.find((v) => v.id === versionId);
9738
+ }, [state.versions]);
9739
+ const getLatestVersion = (0, import_react15.useCallback)(() => {
9740
+ return state.versions[0];
9741
+ }, [state.versions]);
9742
+ const getPinnedVersions = (0, import_react15.useCallback)(() => {
9743
+ return state.versions.filter((v) => v.isPinned);
9744
+ }, [state.versions]);
9745
+ const subscribe = (0, import_react15.useCallback)((callback) => {
9746
+ subscribersRef.current.add(callback);
9747
+ return () => {
9748
+ subscribersRef.current.delete(callback);
9749
+ };
9750
+ }, []);
9751
+ const value = {
9752
+ state,
9753
+ config: config ?? null,
9754
+ createVersion,
9755
+ deleteVersion,
9756
+ restoreVersion,
9757
+ pinVersion,
9758
+ unpinVersion,
9759
+ renameVersion,
9760
+ viewVersion,
9761
+ getVersionContent,
9762
+ compareVersions,
9763
+ startCompare,
9764
+ stopCompare,
9765
+ togglePanel,
9766
+ setAutoSave,
9767
+ getVersion,
9768
+ getLatestVersion,
9769
+ getPinnedVersions,
9770
+ subscribe,
9771
+ isEnabled: !!config
9772
+ };
9773
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VersionHistoryContext.Provider, { value, children });
9774
+ }
9775
+ function useVersionHistory() {
9776
+ const context = (0, import_react15.useContext)(VersionHistoryContext);
9777
+ if (!context) {
9778
+ throw new Error("useVersionHistory must be used within a VersionHistoryProvider");
9779
+ }
9780
+ return context;
9781
+ }
9782
+ function useVersionHistoryOptional() {
9783
+ return (0, import_react15.useContext)(VersionHistoryContext);
9784
+ }
9785
+
9786
+ // src/history/VersionHistoryPanel.tsx
9787
+ var import_react16 = require("react");
9788
+ var import_jsx_runtime15 = require("react/jsx-runtime");
9789
+ function formatDate(timestamp) {
9790
+ const date = new Date(timestamp);
9791
+ const now = /* @__PURE__ */ new Date();
9792
+ const isToday = date.toDateString() === now.toDateString();
9793
+ if (isToday) {
9794
+ return `Today at ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
9795
+ }
9796
+ const yesterday = new Date(now);
9797
+ yesterday.setDate(yesterday.getDate() - 1);
9798
+ if (date.toDateString() === yesterday.toDateString()) {
9799
+ return `Yesterday at ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
9800
+ }
9801
+ return date.toLocaleDateString([], {
9802
+ month: "short",
9803
+ day: "numeric",
9804
+ hour: "2-digit",
9805
+ minute: "2-digit"
9806
+ });
9807
+ }
9808
+ function VersionItem({
9809
+ version,
9810
+ isViewing,
9811
+ isCompareFrom,
9812
+ isCompareTo,
9813
+ onView,
9814
+ onRestore,
9815
+ onPin,
9816
+ onRename,
9817
+ onDelete,
9818
+ onCompareSelect
9819
+ }) {
9820
+ const [isRenaming, setIsRenaming] = (0, import_react16.useState)(false);
9821
+ const [newTitle, setNewTitle] = (0, import_react16.useState)(version.title || "");
9822
+ const handleRename = () => {
9823
+ onRename(newTitle);
9824
+ setIsRenaming(false);
9825
+ };
9826
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
9827
+ "div",
9828
+ {
9829
+ className: `rte-version-item ${isViewing ? "rte-version-viewing" : ""} ${isCompareFrom ? "rte-version-compare-from" : ""} ${isCompareTo ? "rte-version-compare-to" : ""}`,
9830
+ children: [
9831
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-header", onClick: onView, children: [
9832
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-info", children: [
9833
+ isRenaming ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
9834
+ "input",
9835
+ {
9836
+ type: "text",
9837
+ value: newTitle,
9838
+ onChange: (e) => setNewTitle(e.target.value),
9839
+ onBlur: handleRename,
9840
+ onKeyDown: (e) => e.key === "Enter" && handleRename(),
9841
+ className: "rte-version-rename-input",
9842
+ onClick: (e) => e.stopPropagation(),
9843
+ autoFocus: true
9844
+ }
9845
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "rte-version-title", children: [
9846
+ version.title || `Version ${version.number}`,
9847
+ version.isPinned && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-version-pin-icon", children: "\u{1F4CC}" }),
9848
+ version.isAutoSave && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-version-auto-badge", children: "Auto" })
9849
+ ] }),
9850
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-version-time", children: formatDate(version.createdAt) })
9851
+ ] }),
9852
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-author", children: [
9853
+ version.author.avatar ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("img", { src: version.author.avatar, alt: version.author.name, className: "rte-version-avatar" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rte-version-avatar-placeholder", children: version.author.name.charAt(0).toUpperCase() }),
9854
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: version.author.name })
9855
+ ] })
9856
+ ] }),
9857
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-stats", children: [
9858
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
9859
+ version.wordCount,
9860
+ " words"
9861
+ ] }),
9862
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
9863
+ version.characterCount,
9864
+ " chars"
9865
+ ] })
9866
+ ] }),
9867
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-actions", children: [
9868
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: onRestore, className: "rte-version-btn", title: "Restore this version", children: "\u21BA Restore" }),
9869
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: onCompareSelect, className: "rte-version-btn", title: "Compare with another version", children: "\u21C4 Compare" }),
9870
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: onPin, className: "rte-version-btn", title: version.isPinned ? "Unpin" : "Pin", children: version.isPinned ? "\u{1F4CC} Unpin" : "\u{1F4CC} Pin" }),
9871
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
9872
+ "button",
9873
+ {
9874
+ onClick: () => setIsRenaming(true),
9875
+ className: "rte-version-btn",
9876
+ title: "Rename version",
9877
+ children: "\u270F\uFE0F"
9878
+ }
9879
+ ),
9880
+ !version.isPinned && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: onDelete, className: "rte-version-btn rte-version-btn-delete", title: "Delete", children: "\u{1F5D1}\uFE0F" })
9881
+ ] })
9882
+ ]
9883
+ }
9884
+ );
9885
+ }
9886
+ function ComparisonView({
9887
+ fromVersion,
9888
+ toVersion,
9889
+ onClose
9890
+ }) {
9891
+ const versionHistory = useVersionHistoryOptional();
9892
+ if (!versionHistory) return null;
9893
+ const comparison = versionHistory.compareVersions(fromVersion.id, toVersion.id);
9894
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-comparison", children: [
9895
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-comparison-header", children: [
9896
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h4", { children: "Comparing Versions" }),
9897
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: onClose, className: "rte-version-comparison-close", children: "\xD7" })
9898
+ ] }),
9899
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-comparison-info", children: [
9900
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-comparison-from", children: [
9901
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-version-comparison-label", children: "From:" }),
9902
+ fromVersion.title || `Version ${fromVersion.number}`
9903
+ ] }),
9904
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-version-comparison-arrow", children: "\u2192" }),
9905
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-comparison-to", children: [
9906
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-version-comparison-label", children: "To:" }),
9907
+ toVersion.title || `Version ${toVersion.number}`
9908
+ ] })
9909
+ ] }),
9910
+ comparison && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-comparison-stats", children: [
9911
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "rte-comparison-stat rte-comparison-additions", children: [
9912
+ "+",
9913
+ comparison.stats.additions,
9914
+ " additions"
9915
+ ] }),
9916
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "rte-comparison-stat rte-comparison-deletions", children: [
9917
+ "-",
9918
+ comparison.stats.deletions,
9919
+ " deletions"
9920
+ ] }),
9921
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "rte-comparison-stat rte-comparison-modifications", children: [
9922
+ "~",
9923
+ comparison.stats.modifications,
9924
+ " modifications"
9925
+ ] })
9926
+ ] }),
9927
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rte-version-comparison-diff", children: comparison?.changes.map((change, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `rte-diff-line rte-diff-${change.type}`, children: [
9928
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-diff-indicator", children: change.type === "addition" ? "+" : change.type === "deletion" ? "-" : "~" }),
9929
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rte-diff-content", children: change.content })
9930
+ ] }, index)) })
9931
+ ] });
9932
+ }
9933
+ function VersionHistoryPanel({ position = "right", className = "" }) {
9934
+ const versionHistory = useVersionHistoryOptional();
9935
+ const [compareFromId, setCompareFromId] = (0, import_react16.useState)(null);
9936
+ if (!versionHistory?.isEnabled || !versionHistory.state.isPanelOpen) {
9937
+ return null;
9938
+ }
9939
+ const {
9940
+ state,
9941
+ togglePanel,
9942
+ viewVersion,
9943
+ restoreVersion,
9944
+ pinVersion,
9945
+ unpinVersion,
9946
+ renameVersion,
9947
+ deleteVersion,
9948
+ setAutoSave,
9949
+ getVersion,
9950
+ createVersion,
9951
+ stopCompare
9952
+ } = versionHistory;
9953
+ const handleRestore = (versionId) => {
9954
+ const content = restoreVersion(versionId);
9955
+ if (content) {
9956
+ viewVersion(null);
9957
+ }
9958
+ };
9959
+ const handleCompareSelect = (versionId) => {
9960
+ if (!compareFromId) {
9961
+ setCompareFromId(versionId);
9962
+ } else {
9963
+ versionHistory.startCompare(compareFromId, versionId);
9964
+ setCompareFromId(null);
9965
+ }
9966
+ };
9967
+ const handleCancelCompare = () => {
9968
+ setCompareFromId(null);
9969
+ stopCompare();
9970
+ };
9971
+ const compareFromVersion = state.compareFromId ? getVersion(state.compareFromId) : void 0;
9972
+ const compareToVersion = state.compareToId ? getVersion(state.compareToId) : void 0;
9973
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `rte-version-panel rte-version-panel-${position} ${className}`, children: [
9974
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-panel-header", children: [
9975
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { children: "Version History" }),
9976
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: () => togglePanel(false), className: "rte-version-close-btn", children: "\xD7" })
9977
+ ] }),
9978
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-autosave", children: [
9979
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "rte-version-autosave-label", children: [
9980
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
9981
+ "input",
9982
+ {
9983
+ type: "checkbox",
9984
+ checked: state.autoSaveEnabled,
9985
+ onChange: (e) => setAutoSave(e.target.checked)
9986
+ }
9987
+ ),
9988
+ "Auto-save versions"
9989
+ ] }),
9990
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
9991
+ "button",
9992
+ {
9993
+ onClick: () => createVersion("", void 0, { isAutoSave: false }),
9994
+ className: "rte-version-save-btn",
9995
+ children: "Save Now"
9996
+ }
9997
+ )
9998
+ ] }),
9999
+ compareFromId && !state.isComparing && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-compare-mode", children: [
10000
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Select another version to compare" }),
10001
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: handleCancelCompare, children: "Cancel" })
10002
+ ] }),
10003
+ state.isComparing && compareFromVersion && compareToVersion && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10004
+ ComparisonView,
10005
+ {
10006
+ fromVersion: compareFromVersion,
10007
+ toVersion: compareToVersion,
10008
+ onClose: handleCancelCompare
10009
+ }
10010
+ ),
10011
+ !state.isComparing && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rte-version-list", children: state.versions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rte-version-empty", children: "No versions saved yet. Changes will be auto-saved periodically." }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
10012
+ state.versions.some((v) => v.isPinned) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-section", children: [
10013
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rte-version-section-title", children: "\u{1F4CC} Pinned" }),
10014
+ state.versions.filter((v) => v.isPinned).map((version) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10015
+ VersionItem,
10016
+ {
10017
+ version,
10018
+ isViewing: state.viewingVersionId === version.id,
10019
+ isCompareFrom: compareFromId === version.id,
10020
+ isCompareTo: false,
10021
+ onView: () => viewVersion(version.id),
10022
+ onRestore: () => handleRestore(version.id),
10023
+ onPin: () => unpinVersion(version.id),
10024
+ onRename: (title) => renameVersion(version.id, title),
10025
+ onDelete: () => deleteVersion(version.id),
10026
+ onCompareSelect: () => handleCompareSelect(version.id)
10027
+ },
10028
+ version.id
10029
+ ))
10030
+ ] }),
10031
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-section", children: [
10032
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rte-version-section-title", children: "Recent" }),
10033
+ state.versions.filter((v) => !v.isPinned).map((version) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10034
+ VersionItem,
10035
+ {
10036
+ version,
10037
+ isViewing: state.viewingVersionId === version.id,
10038
+ isCompareFrom: compareFromId === version.id,
10039
+ isCompareTo: false,
10040
+ onView: () => viewVersion(version.id),
10041
+ onRestore: () => handleRestore(version.id),
10042
+ onPin: () => pinVersion(version.id),
10043
+ onRename: (title) => renameVersion(version.id, title),
10044
+ onDelete: () => deleteVersion(version.id),
10045
+ onCompareSelect: () => handleCompareSelect(version.id)
10046
+ },
10047
+ version.id
10048
+ ))
10049
+ ] })
10050
+ ] }) }),
10051
+ state.viewingVersionId && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rte-version-viewing-indicator", children: [
10052
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
10053
+ "Viewing: ",
10054
+ getVersion(state.viewingVersionId)?.title || `Version ${getVersion(state.viewingVersionId)?.number}`
10055
+ ] }),
10056
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: () => viewVersion(null), className: "rte-version-back-btn", children: "\u2190 Back to current" })
10057
+ ] })
10058
+ ] });
10059
+ }
8678
10060
  // Annotate the CommonJS export names for ESM import in node:
8679
10061
  0 && (module.exports = {
10062
+ CollaborationProvider,
10063
+ CommentsPanel,
10064
+ CommentsProvider,
8680
10065
  DEFAULT_FEATURES,
10066
+ DEFAULT_REACTION_EMOJIS,
8681
10067
  EMOJI_CATEGORIES,
8682
10068
  EditorRegistry,
8683
10069
  Emoji,
@@ -8685,12 +10071,15 @@ init_Indent();
8685
10071
  Fullscreen,
8686
10072
  Indent,
8687
10073
  LineHeight,
10074
+ PresenceIndicator,
8688
10075
  Print,
8689
10076
  RichTextEditor,
8690
10077
  TipTapAdapter,
8691
10078
  TipTapEditorComponent,
8692
10079
  TipTapToolbar,
8693
10080
  UnifiedEditor,
10081
+ VersionHistoryPanel,
10082
+ VersionHistoryProvider,
8694
10083
  Video,
8695
10084
  blogToolbar,
8696
10085
  codeToolbar,
@@ -8712,6 +10101,12 @@ init_Indent();
8712
10101
  registerAdapter,
8713
10102
  simpleToolbar,
8714
10103
  toolbarPresets,
8715
- unregisterAdapter
10104
+ unregisterAdapter,
10105
+ useCollaboration,
10106
+ useCollaborationOptional,
10107
+ useComments,
10108
+ useCommentsOptional,
10109
+ useVersionHistory,
10110
+ useVersionHistoryOptional
8716
10111
  });
8717
10112
  //# sourceMappingURL=index.js.map