@codella-software/react 2.1.0 → 2.2.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.cjs CHANGED
@@ -1631,6 +1631,782 @@ const useLiveUpdates = (eventName) => {
1631
1631
  }, [eventName, service]);
1632
1632
  return { data, error };
1633
1633
  };
1634
+ class ContentModel {
1635
+ constructor(doc) {
1636
+ this.document = doc || this.createEmptyDocument();
1637
+ }
1638
+ /**
1639
+ * Create empty document with single empty paragraph
1640
+ */
1641
+ static createEmpty() {
1642
+ return {
1643
+ type: "document",
1644
+ version: "1.0",
1645
+ children: [
1646
+ {
1647
+ type: "paragraph",
1648
+ children: [{ type: "text", text: "" }]
1649
+ }
1650
+ ]
1651
+ };
1652
+ }
1653
+ /**
1654
+ * Get the current document
1655
+ */
1656
+ getDocument() {
1657
+ return JSON.parse(JSON.stringify(this.document));
1658
+ }
1659
+ /**
1660
+ * Replace entire document
1661
+ */
1662
+ setDocument(doc) {
1663
+ this.document = JSON.parse(JSON.stringify(doc));
1664
+ }
1665
+ /**
1666
+ * Get all text content as plain string
1667
+ */
1668
+ getPlainText() {
1669
+ let text = "";
1670
+ const traverse = (node) => {
1671
+ if (node.type === "text") {
1672
+ text += node.text;
1673
+ } else if ("children" in node) {
1674
+ node.children.forEach(traverse);
1675
+ } else if (node.type === "image") {
1676
+ text += "[Image]";
1677
+ } else if (node.type === "mention") {
1678
+ text += `@${node.label}`;
1679
+ }
1680
+ };
1681
+ this.document.children.forEach(traverse);
1682
+ return text;
1683
+ }
1684
+ /**
1685
+ * Insert text at selection/cursor position
1686
+ */
1687
+ insertText(text, marks) {
1688
+ const doc = this.cloneDocument();
1689
+ const firstPara = this.getFirstParagraph(doc);
1690
+ if (firstPara && firstPara.children.length > 0) {
1691
+ const firstChild = firstPara.children[0];
1692
+ if (firstChild.type === "text") {
1693
+ firstChild.text += text;
1694
+ if (marks) {
1695
+ firstChild.marks = marks;
1696
+ }
1697
+ }
1698
+ }
1699
+ return doc;
1700
+ }
1701
+ /**
1702
+ * Insert a paragraph at the end
1703
+ */
1704
+ insertParagraph(content) {
1705
+ const doc = this.cloneDocument();
1706
+ doc.children.push({
1707
+ type: "paragraph",
1708
+ children: content || [{ type: "text", text: "" }]
1709
+ });
1710
+ return doc;
1711
+ }
1712
+ /**
1713
+ * Insert heading
1714
+ */
1715
+ insertHeading(level, content) {
1716
+ const doc = this.cloneDocument();
1717
+ doc.children.push({
1718
+ type: "heading",
1719
+ level,
1720
+ children: content || [{ type: "text", text: "" }]
1721
+ });
1722
+ return doc;
1723
+ }
1724
+ /**
1725
+ * Insert image
1726
+ */
1727
+ insertImage(url, attrs) {
1728
+ const doc = this.cloneDocument();
1729
+ const lastBlock = doc.children[doc.children.length - 1];
1730
+ if ((attrs == null ? void 0 : attrs.display) === "block") {
1731
+ doc.children.push({
1732
+ type: "paragraph",
1733
+ children: [
1734
+ {
1735
+ type: "image",
1736
+ url,
1737
+ attrs: attrs || {}
1738
+ }
1739
+ ]
1740
+ });
1741
+ } else {
1742
+ if (lastBlock && lastBlock.type === "paragraph") {
1743
+ lastBlock.children.push({
1744
+ type: "image",
1745
+ url,
1746
+ attrs: attrs || {}
1747
+ });
1748
+ } else {
1749
+ doc.children.push({
1750
+ type: "paragraph",
1751
+ children: [
1752
+ {
1753
+ type: "image",
1754
+ url,
1755
+ attrs: attrs || {}
1756
+ }
1757
+ ]
1758
+ });
1759
+ }
1760
+ }
1761
+ return doc;
1762
+ }
1763
+ /**
1764
+ * Insert mention
1765
+ */
1766
+ insertMention(id, label, meta) {
1767
+ const doc = this.cloneDocument();
1768
+ const lastBlock = doc.children[doc.children.length - 1];
1769
+ if (lastBlock && lastBlock.type === "paragraph") {
1770
+ lastBlock.children.push({
1771
+ type: "mention",
1772
+ id,
1773
+ label,
1774
+ meta
1775
+ });
1776
+ }
1777
+ return doc;
1778
+ }
1779
+ /**
1780
+ * Insert list
1781
+ */
1782
+ insertList(listType, items = [[]]) {
1783
+ const doc = this.cloneDocument();
1784
+ const children = items.map((item) => ({
1785
+ type: "list-item",
1786
+ children: item || [{ type: "text", text: "" }],
1787
+ depth: 0
1788
+ }));
1789
+ doc.children.push({
1790
+ type: "list",
1791
+ listType,
1792
+ children
1793
+ });
1794
+ return doc;
1795
+ }
1796
+ /**
1797
+ * Toggle a mark type on text in a range
1798
+ */
1799
+ toggleMark(mark, selection) {
1800
+ var _a;
1801
+ const doc = this.cloneDocument();
1802
+ const firstPara = this.getFirstParagraph(doc);
1803
+ if (firstPara && ((_a = firstPara.children[0]) == null ? void 0 : _a.type) === "text") {
1804
+ const textNode = firstPara.children[0];
1805
+ if (!textNode.marks) {
1806
+ textNode.marks = [];
1807
+ }
1808
+ const existingMark = textNode.marks.findIndex((m) => m.type === mark);
1809
+ if (existingMark >= 0) {
1810
+ textNode.marks.splice(existingMark, 1);
1811
+ } else {
1812
+ textNode.marks.push({ type: mark });
1813
+ }
1814
+ }
1815
+ return doc;
1816
+ }
1817
+ /**
1818
+ * Check if a mark type is active at cursor
1819
+ */
1820
+ isMarkActive(mark) {
1821
+ var _a, _b;
1822
+ const firstPara = this.getFirstParagraph(this.document);
1823
+ if (((_a = firstPara == null ? void 0 : firstPara.children[0]) == null ? void 0 : _a.type) === "text") {
1824
+ const textNode = firstPara.children[0];
1825
+ return ((_b = textNode.marks) == null ? void 0 : _b.some((m) => m.type === mark)) || false;
1826
+ }
1827
+ return false;
1828
+ }
1829
+ /**
1830
+ * Get active marks at cursor
1831
+ */
1832
+ getActiveMarks() {
1833
+ var _a, _b;
1834
+ const firstPara = this.getFirstParagraph(this.document);
1835
+ if (((_a = firstPara == null ? void 0 : firstPara.children[0]) == null ? void 0 : _a.type) === "text") {
1836
+ const textNode = firstPara.children[0];
1837
+ return ((_b = textNode.marks) == null ? void 0 : _b.map((m) => m.type)) || [];
1838
+ }
1839
+ return [];
1840
+ }
1841
+ /**
1842
+ * Delete content (simplified - deletes last block)
1843
+ */
1844
+ deleteContent(selection) {
1845
+ const doc = this.cloneDocument();
1846
+ if (doc.children.length > 1) {
1847
+ doc.children.pop();
1848
+ } else if (doc.children.length === 1 && doc.children[0].type === "paragraph") {
1849
+ doc.children[0].children = [{ type: "text", text: "" }];
1850
+ }
1851
+ return doc;
1852
+ }
1853
+ /**
1854
+ * Replace entire content
1855
+ */
1856
+ replaceContent(nodes) {
1857
+ const doc = this.cloneDocument();
1858
+ doc.children = nodes;
1859
+ return doc;
1860
+ }
1861
+ /**
1862
+ * Clone document (deep copy)
1863
+ */
1864
+ cloneDocument() {
1865
+ return JSON.parse(JSON.stringify(this.document));
1866
+ }
1867
+ /**
1868
+ * Get first paragraph in document
1869
+ */
1870
+ getFirstParagraph(doc) {
1871
+ for (const block of doc.children) {
1872
+ if (block.type === "paragraph") {
1873
+ return block;
1874
+ }
1875
+ }
1876
+ return null;
1877
+ }
1878
+ /**
1879
+ * Find node by predicate
1880
+ */
1881
+ findNode(predicate) {
1882
+ const search = (node) => {
1883
+ if (predicate(node)) {
1884
+ return node;
1885
+ }
1886
+ if ("children" in node) {
1887
+ for (const child of node.children) {
1888
+ const result = search(child);
1889
+ if (result) return result;
1890
+ }
1891
+ }
1892
+ return null;
1893
+ };
1894
+ for (const child of this.document.children) {
1895
+ const result = search(child);
1896
+ if (result) return result;
1897
+ }
1898
+ return null;
1899
+ }
1900
+ /**
1901
+ * Count all nodes of a type
1902
+ */
1903
+ countNodes(type) {
1904
+ let count = 0;
1905
+ const traverse = (node) => {
1906
+ if (node.type === type) count++;
1907
+ if ("children" in node) {
1908
+ node.children.forEach(traverse);
1909
+ }
1910
+ };
1911
+ this.document.children.forEach(traverse);
1912
+ return count;
1913
+ }
1914
+ createEmptyDocument() {
1915
+ return ContentModel.createEmpty();
1916
+ }
1917
+ }
1918
+ class RichContentService {
1919
+ constructor(config2 = {}) {
1920
+ this.history = [];
1921
+ this.historyIndex = -1;
1922
+ this.adapter = null;
1923
+ this.defaultImageHandler = async (file) => {
1924
+ return new Promise((resolve) => {
1925
+ const reader = new FileReader();
1926
+ reader.onload = (e) => {
1927
+ var _a;
1928
+ resolve({ url: (_a = e.target) == null ? void 0 : _a.result });
1929
+ };
1930
+ reader.readAsDataURL(file);
1931
+ });
1932
+ };
1933
+ this.config = {
1934
+ initialContent: config2.initialContent || ContentModel.createEmpty(),
1935
+ allowedMarks: config2.allowedMarks || [
1936
+ "bold",
1937
+ "italic",
1938
+ "underline",
1939
+ "strikethrough",
1940
+ "code"
1941
+ ],
1942
+ allowedBlocks: config2.allowedBlocks || [
1943
+ "document",
1944
+ "paragraph",
1945
+ "heading",
1946
+ "list",
1947
+ "blockquote",
1948
+ "code-block",
1949
+ "horizontal-rule"
1950
+ ],
1951
+ maxListDepth: config2.maxListDepth ?? 3,
1952
+ imageUploadHandler: config2.imageUploadHandler || this.defaultImageHandler,
1953
+ mentionProvider: config2.mentionProvider || (() => Promise.resolve([])),
1954
+ middleware: config2.middleware || {},
1955
+ enableHistory: config2.enableHistory ?? true,
1956
+ maxHistorySteps: config2.maxHistorySteps ?? 100,
1957
+ placeholder: config2.placeholder || "",
1958
+ readOnly: config2.readOnly ?? false
1959
+ };
1960
+ this.middleware = this.config.middleware;
1961
+ this.contentModel = new ContentModel(this.config.initialContent);
1962
+ this.contentSubject = new rxjs.BehaviorSubject(
1963
+ this.contentModel.getDocument()
1964
+ );
1965
+ this.commandSubject = new rxjs.Subject();
1966
+ this.mentionSubject = new rxjs.Subject();
1967
+ this.stateSubject = new rxjs.BehaviorSubject({
1968
+ content: this.contentModel.getDocument(),
1969
+ selection: null,
1970
+ canUndo: false,
1971
+ canRedo: false,
1972
+ isFocused: false,
1973
+ isDirty: false,
1974
+ mentions: {
1975
+ isActive: false,
1976
+ query: "",
1977
+ position: { x: 0, y: 0 }
1978
+ },
1979
+ selectedFormats: /* @__PURE__ */ new Set()
1980
+ });
1981
+ if (this.config.enableHistory) {
1982
+ this.history.push({
1983
+ content: this.contentModel.getDocument(),
1984
+ timestamp: Date.now()
1985
+ });
1986
+ this.historyIndex = 0;
1987
+ }
1988
+ this.commandSubject.subscribe((cmd) => this.executeCommand(cmd));
1989
+ }
1990
+ /**
1991
+ * Create service from empty state
1992
+ */
1993
+ static create(config2) {
1994
+ return new RichContentService(config2);
1995
+ }
1996
+ // =========================================================================
1997
+ // PUBLIC OBSERVABLES
1998
+ // =========================================================================
1999
+ /**
2000
+ * Get content$ observable
2001
+ */
2002
+ getContent$() {
2003
+ return this.contentSubject.asObservable();
2004
+ }
2005
+ /**
2006
+ * Get full state$ observable
2007
+ */
2008
+ getState$() {
2009
+ return this.stateSubject.asObservable();
2010
+ }
2011
+ /**
2012
+ * Get canUndo$ observable
2013
+ */
2014
+ getCanUndo$() {
2015
+ return this.stateSubject.pipe(
2016
+ operators.map((s) => s.canUndo),
2017
+ operators.distinctUntilChanged()
2018
+ );
2019
+ }
2020
+ /**
2021
+ * Get canRedo$ observable
2022
+ */
2023
+ getCanRedo$() {
2024
+ return this.stateSubject.pipe(
2025
+ operators.map((s) => s.canRedo),
2026
+ operators.distinctUntilChanged()
2027
+ );
2028
+ }
2029
+ /**
2030
+ * Get isFocused$ observable
2031
+ */
2032
+ getIsFocused$() {
2033
+ return this.stateSubject.pipe(
2034
+ operators.map((s) => s.isFocused),
2035
+ operators.distinctUntilChanged()
2036
+ );
2037
+ }
2038
+ /**
2039
+ * Get mentions$ observable - for autocomplete dropdown
2040
+ */
2041
+ getMentions$() {
2042
+ return this.mentionSubject.asObservable();
2043
+ }
2044
+ /**
2045
+ * Get selectedFormats$ observable
2046
+ */
2047
+ getSelectedFormats$() {
2048
+ return this.stateSubject.pipe(
2049
+ operators.map((s) => s.selectedFormats || /* @__PURE__ */ new Set()),
2050
+ operators.distinctUntilChanged()
2051
+ );
2052
+ }
2053
+ // =========================================================================
2054
+ // STATE GETTERS
2055
+ // =========================================================================
2056
+ getContent() {
2057
+ return this.contentSubject.getValue();
2058
+ }
2059
+ getState() {
2060
+ return this.stateSubject.getValue();
2061
+ }
2062
+ getPlainText() {
2063
+ return this.contentModel.getPlainText();
2064
+ }
2065
+ canUndo() {
2066
+ return this.historyIndex > 0;
2067
+ }
2068
+ canRedo() {
2069
+ return this.historyIndex < this.history.length - 1;
2070
+ }
2071
+ isFocused() {
2072
+ return this.getState().isFocused;
2073
+ }
2074
+ // =========================================================================
2075
+ // ADAPTER MANAGEMENT
2076
+ // =========================================================================
2077
+ /**
2078
+ * Attach DOM adapter for contentEditable syncing
2079
+ */
2080
+ attachAdapter(adapter) {
2081
+ this.adapter = adapter;
2082
+ const docFromDom = adapter.syncFromDOM();
2083
+ this.setContent(docFromDom);
2084
+ }
2085
+ /**
2086
+ * Detach adapter
2087
+ */
2088
+ detachAdapter() {
2089
+ if (this.adapter) {
2090
+ this.adapter.unmount();
2091
+ this.adapter = null;
2092
+ }
2093
+ }
2094
+ /**
2095
+ * Get attached adapter
2096
+ */
2097
+ getAdapter() {
2098
+ return this.adapter;
2099
+ }
2100
+ // =========================================================================
2101
+ // COMMAND EXECUTION
2102
+ // =========================================================================
2103
+ /**
2104
+ * Execute a command
2105
+ */
2106
+ execute(command, payload) {
2107
+ const cmd = typeof command === "string" ? { type: command, payload } : command;
2108
+ this.commandSubject.next(cmd);
2109
+ }
2110
+ /**
2111
+ * Execute text insertion
2112
+ */
2113
+ insertText(text, marks) {
2114
+ this.execute("insertText", { text, marks });
2115
+ }
2116
+ /**
2117
+ * Execute paragraph insertion
2118
+ */
2119
+ insertParagraph() {
2120
+ this.execute("insertParagraph");
2121
+ }
2122
+ /**
2123
+ * Execute heading insertion
2124
+ */
2125
+ insertHeading(level) {
2126
+ this.execute("insertHeading", { level });
2127
+ }
2128
+ /**
2129
+ * Execute image insertion
2130
+ */
2131
+ insertImage(url, attrs) {
2132
+ this.execute("insertImage", { url, attrs });
2133
+ }
2134
+ /**
2135
+ * Execute image upload
2136
+ */
2137
+ async uploadImage(file) {
2138
+ try {
2139
+ const result = await this.config.imageUploadHandler(file);
2140
+ this.insertImage(result.url, result.attrs);
2141
+ } catch (error) {
2142
+ console.error("Image upload failed:", error);
2143
+ throw error;
2144
+ }
2145
+ }
2146
+ /**
2147
+ * Execute mention insertion
2148
+ */
2149
+ insertMention(id, label, meta) {
2150
+ this.execute("insertMention", { id, label, meta });
2151
+ }
2152
+ /**
2153
+ * Execute list insertion
2154
+ */
2155
+ insertList(listType) {
2156
+ this.execute("insertList", { listType });
2157
+ }
2158
+ /**
2159
+ * Execute mark toggle (bold, italic, etc.)
2160
+ */
2161
+ toggleMark(mark) {
2162
+ this.execute("toggleMark", { mark });
2163
+ }
2164
+ /**
2165
+ * Delete content
2166
+ */
2167
+ deleteContent() {
2168
+ this.execute("deleteContent");
2169
+ }
2170
+ /**
2171
+ * Set focus state
2172
+ */
2173
+ setFocus(focused) {
2174
+ const state = this.getState();
2175
+ state.isFocused = focused;
2176
+ this.stateSubject.next(state);
2177
+ }
2178
+ /**
2179
+ * Set selection
2180
+ */
2181
+ setSelection(selection) {
2182
+ const state = this.getState();
2183
+ state.selection = selection;
2184
+ this.stateSubject.next(state);
2185
+ }
2186
+ /**
2187
+ * Set mention query (triggers autocomplete)
2188
+ */
2189
+ setMentionQuery(query, position) {
2190
+ if (query.length > 0) {
2191
+ this.mentionSubject.next({ query, position });
2192
+ }
2193
+ }
2194
+ // =========================================================================
2195
+ // HISTORY (UNDO/REDO)
2196
+ // =========================================================================
2197
+ /**
2198
+ * Undo to previous state
2199
+ */
2200
+ undo() {
2201
+ if (this.canUndo()) {
2202
+ this.historyIndex--;
2203
+ this.applyHistoryState();
2204
+ }
2205
+ }
2206
+ /**
2207
+ * Redo to next state
2208
+ */
2209
+ redo() {
2210
+ if (this.canRedo()) {
2211
+ this.historyIndex++;
2212
+ this.applyHistoryState();
2213
+ }
2214
+ }
2215
+ /**
2216
+ * Clear history
2217
+ */
2218
+ clearHistory() {
2219
+ this.history = [{ content: this.getContent(), timestamp: Date.now() }];
2220
+ this.historyIndex = 0;
2221
+ this.updateHistoryState();
2222
+ }
2223
+ // =========================================================================
2224
+ // PRIVATE METHODS
2225
+ // =========================================================================
2226
+ executeCommand(cmd) {
2227
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
2228
+ if (this.config.readOnly) {
2229
+ console.warn("Editor is in read-only mode");
2230
+ return;
2231
+ }
2232
+ this.getContent();
2233
+ let newContent;
2234
+ switch (cmd.type) {
2235
+ case "insertText":
2236
+ newContent = this.contentModel.insertText((_a = cmd.payload) == null ? void 0 : _a.text, (_b = cmd.payload) == null ? void 0 : _b.marks);
2237
+ break;
2238
+ case "insertParagraph":
2239
+ newContent = this.contentModel.insertParagraph((_c = cmd.payload) == null ? void 0 : _c.children);
2240
+ break;
2241
+ case "insertHeading":
2242
+ newContent = this.contentModel.insertHeading(
2243
+ ((_d = cmd.payload) == null ? void 0 : _d.level) || 1,
2244
+ (_e = cmd.payload) == null ? void 0 : _e.children
2245
+ );
2246
+ break;
2247
+ case "insertImage":
2248
+ newContent = this.contentModel.insertImage((_f = cmd.payload) == null ? void 0 : _f.url, (_g = cmd.payload) == null ? void 0 : _g.attrs);
2249
+ break;
2250
+ case "insertMention":
2251
+ newContent = this.contentModel.insertMention(
2252
+ (_h = cmd.payload) == null ? void 0 : _h.id,
2253
+ (_i = cmd.payload) == null ? void 0 : _i.label,
2254
+ (_j = cmd.payload) == null ? void 0 : _j.meta
2255
+ );
2256
+ break;
2257
+ case "insertList":
2258
+ newContent = this.contentModel.insertList((_k = cmd.payload) == null ? void 0 : _k.listType, (_l = cmd.payload) == null ? void 0 : _l.items);
2259
+ break;
2260
+ case "toggleMark":
2261
+ newContent = this.contentModel.toggleMark((_m = cmd.payload) == null ? void 0 : _m.mark, (_n = cmd.payload) == null ? void 0 : _n.selection);
2262
+ break;
2263
+ case "deleteContent":
2264
+ newContent = this.contentModel.deleteContent((_o = cmd.payload) == null ? void 0 : _o.selection);
2265
+ break;
2266
+ case "replaceContent":
2267
+ newContent = this.contentModel.replaceContent((_p = cmd.payload) == null ? void 0 : _p.nodes);
2268
+ break;
2269
+ default:
2270
+ console.warn(`Unknown command type: ${cmd.type}`);
2271
+ return;
2272
+ }
2273
+ if (this.config.enableHistory) {
2274
+ this.history = this.history.slice(0, this.historyIndex + 1);
2275
+ this.history.push({
2276
+ content: newContent,
2277
+ timestamp: Date.now()
2278
+ });
2279
+ if (this.history.length > this.config.maxHistorySteps) {
2280
+ this.history.shift();
2281
+ } else {
2282
+ this.historyIndex++;
2283
+ }
2284
+ }
2285
+ this.setContent(newContent);
2286
+ }
2287
+ setContent(doc) {
2288
+ this.contentModel.setDocument(doc);
2289
+ this.contentSubject.next(doc);
2290
+ const state = this.getState();
2291
+ state.content = doc;
2292
+ state.isDirty = true;
2293
+ state.selectedFormats = new Set(this.contentModel.getActiveMarks());
2294
+ this.stateSubject.next(state);
2295
+ if (this.adapter) {
2296
+ this.adapter.syncToDOM(doc);
2297
+ }
2298
+ this.updateHistoryState();
2299
+ }
2300
+ applyHistoryState() {
2301
+ const entry = this.history[this.historyIndex];
2302
+ this.contentModel.setDocument(entry.content);
2303
+ this.contentSubject.next(entry.content);
2304
+ const state = this.getState();
2305
+ state.content = entry.content;
2306
+ this.stateSubject.next(state);
2307
+ if (this.adapter) {
2308
+ this.adapter.syncToDOM(entry.content);
2309
+ }
2310
+ this.updateHistoryState();
2311
+ }
2312
+ updateHistoryState() {
2313
+ const state = this.getState();
2314
+ state.canUndo = this.canUndo();
2315
+ state.canRedo = this.canRedo();
2316
+ this.stateSubject.next(state);
2317
+ }
2318
+ }
2319
+ function useRichContent(options = {}) {
2320
+ const service = react.useMemo(() => RichContentService.create(options), []);
2321
+ const [content, setContent] = react.useState(service.getContent());
2322
+ const [state, setState] = react.useState(service.getState());
2323
+ const [isFocused, setIsFocused] = react.useState(false);
2324
+ const [canUndo, setCanUndo] = react.useState(false);
2325
+ const [canRedo, setCanRedo] = react.useState(false);
2326
+ const [selectedFormats, setSelectedFormats] = react.useState(/* @__PURE__ */ new Set());
2327
+ react.useEffect(() => {
2328
+ const contentSub = service.getContent$().subscribe(setContent);
2329
+ const stateSub = service.getState$().subscribe((newState) => {
2330
+ setState(newState);
2331
+ setIsFocused(newState.isFocused);
2332
+ setCanUndo(newState.canUndo);
2333
+ setCanRedo(newState.canRedo);
2334
+ setSelectedFormats(newState.selectedFormats || /* @__PURE__ */ new Set());
2335
+ });
2336
+ return () => {
2337
+ contentSub.unsubscribe();
2338
+ stateSub.unsubscribe();
2339
+ };
2340
+ }, [service]);
2341
+ react.useEffect(() => {
2342
+ if (options.adapter) {
2343
+ service.attachAdapter(options.adapter);
2344
+ return () => service.detachAdapter();
2345
+ }
2346
+ }, [options.adapter, service]);
2347
+ const defaultAdapterRef = react.useRef(null);
2348
+ react.useEffect(() => {
2349
+ var _a;
2350
+ if (((_a = options.editorRef) == null ? void 0 : _a.current) && !options.adapter && !defaultAdapterRef.current) {
2351
+ const { DefaultContentEditableAdapter } = require("@codella/core/rich-content");
2352
+ const adapter = new DefaultContentEditableAdapter();
2353
+ adapter.mount(options.editorRef.current);
2354
+ service.attachAdapter(adapter);
2355
+ defaultAdapterRef.current = adapter;
2356
+ return () => {
2357
+ adapter.unmount();
2358
+ service.detachAdapter();
2359
+ defaultAdapterRef.current = null;
2360
+ };
2361
+ }
2362
+ }, [options.editorRef, options.adapter, service]);
2363
+ const insertText = react.useCallback((text) => service.insertText(text), [service]);
2364
+ const insertParagraph = react.useCallback(() => service.insertParagraph(), [service]);
2365
+ const insertHeading = react.useCallback((level) => service.insertHeading(level), [service]);
2366
+ const insertImage = react.useCallback((url) => service.insertImage(url), [service]);
2367
+ const uploadImage = react.useCallback((file) => service.uploadImage(file), [service]);
2368
+ const insertMention = react.useCallback((id, label) => service.insertMention(id, label), [service]);
2369
+ const insertList = react.useCallback((type) => service.insertList(type), [service]);
2370
+ const toggleMark = react.useCallback((mark) => service.toggleMark(mark), [service]);
2371
+ const deleteContent = react.useCallback(() => service.deleteContent(), [service]);
2372
+ const undo = react.useCallback(() => service.undo(), [service]);
2373
+ const redo = react.useCallback(() => service.redo(), [service]);
2374
+ const clearHistory = react.useCallback(() => service.clearHistory(), [service]);
2375
+ const focus = react.useCallback(() => {
2376
+ var _a;
2377
+ return (_a = service.getAdapter()) == null ? void 0 : _a.focus();
2378
+ }, [service]);
2379
+ const setFocus = react.useCallback((focused) => service.setFocus(focused), [service]);
2380
+ const getPlainText = react.useCallback(() => service.getPlainText(), [service]);
2381
+ const setSelection = react.useCallback((sel) => service.setSelection(sel), [service]);
2382
+ return {
2383
+ service,
2384
+ content,
2385
+ state,
2386
+ isFocused,
2387
+ canUndo,
2388
+ canRedo,
2389
+ selectedFormats,
2390
+ selection: state.selection || null,
2391
+ isDirty: state.isDirty,
2392
+ insertText,
2393
+ insertParagraph,
2394
+ insertHeading,
2395
+ insertImage,
2396
+ uploadImage,
2397
+ insertMention,
2398
+ insertList,
2399
+ toggleMark,
2400
+ deleteContent,
2401
+ undo,
2402
+ redo,
2403
+ clearHistory,
2404
+ focus,
2405
+ setFocus,
2406
+ getPlainText,
2407
+ setSelection
2408
+ };
2409
+ }
1634
2410
  function useTableService(options) {
1635
2411
  const { config: config2, data } = options;
1636
2412
  const builder = react.useMemo(() => {
@@ -1993,6 +2769,7 @@ exports.useLiveRequest = useLiveRequest;
1993
2769
  exports.useLiveUpdateContext = useLiveUpdateContext;
1994
2770
  exports.useLiveUpdateListener = useLiveUpdateListener;
1995
2771
  exports.useLiveUpdates = useLiveUpdates;
2772
+ exports.useRichContent = useRichContent;
1996
2773
  exports.useSetActiveTab = useSetActiveTab;
1997
2774
  exports.useTabChange = useTabChange;
1998
2775
  exports.useTableService = useTableService;