@adminide-stack/yantra-mobile 12.0.141-alpha.1 → 12.0.141

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.
Files changed (220) hide show
  1. package/lib/features/agent-chat/NativeAgentChat.js +21 -4
  2. package/lib/features/agent-chat/NativeAgentChat.js.map +1 -1
  3. package/lib/features/chat/ApprovalCard.js +353 -0
  4. package/lib/features/chat/ApprovalCard.js.map +1 -0
  5. package/lib/features/chat/AskUserCard.js +9 -2
  6. package/lib/features/chat/AskUserCard.js.map +1 -1
  7. package/lib/features/chat/askUser.js +25 -3
  8. package/lib/features/chat/askUser.js.map +1 -1
  9. package/lib/features/chat/queuedFollowUpRows.js +13 -0
  10. package/lib/features/chat/queuedFollowUpRows.js.map +1 -0
  11. package/lib/features/devices/bodyComposition.js +116 -0
  12. package/lib/features/devices/bodyComposition.js.map +1 -0
  13. package/lib/features/devices/readings.js.map +1 -1
  14. package/lib/features/devices/session.js +22 -2
  15. package/lib/features/devices/session.js.map +1 -1
  16. package/lib/features/discover/DiscoverArticle.js +105 -92
  17. package/lib/features/discover/DiscoverArticle.js.map +1 -1
  18. package/lib/features/discover/DiscoverFeed.js +97 -48
  19. package/lib/features/discover/DiscoverFeed.js.map +1 -1
  20. package/lib/features/discover/DiscoverFollowUp.js +135 -0
  21. package/lib/features/discover/DiscoverFollowUp.js.map +1 -0
  22. package/lib/features/discover/DiscoverSidebar.js +555 -0
  23. package/lib/features/discover/DiscoverSidebar.js.map +1 -0
  24. package/lib/features/discover/DiscoverSkeletons.js +123 -0
  25. package/lib/features/discover/DiscoverSkeletons.js.map +1 -0
  26. package/lib/features/discover/DiscoverStoryCards.js +193 -87
  27. package/lib/features/discover/DiscoverStoryCards.js.map +1 -1
  28. package/lib/features/discover/followUpQuery.js +42 -0
  29. package/lib/features/discover/followUpQuery.js.map +1 -0
  30. package/lib/features/discover/markets.js +37 -0
  31. package/lib/features/discover/markets.js.map +1 -0
  32. package/lib/features/discover/useDiscoverSidebar.js +148 -0
  33. package/lib/features/discover/useDiscoverSidebar.js.map +1 -0
  34. package/lib/features/discover/weather.js +42 -0
  35. package/lib/features/discover/weather.js.map +1 -0
  36. package/lib/features/expenses/ExpensesAgentChatPane.js +1 -1
  37. package/lib/features/expenses/ExpensesAgentChatPane.js.map +1 -1
  38. package/lib/features/inbox/InboxAgentChatPane.js +1 -1
  39. package/lib/features/inbox/InboxAgentChatPane.js.map +1 -1
  40. package/lib/features/job-search/JobSearchAgentChatPane.js +1 -0
  41. package/lib/features/job-search/JobSearchAgentChatPane.js.map +1 -1
  42. package/lib/features/local-services/LocalServicesAgentChatPane.js +1 -1
  43. package/lib/features/local-services/LocalServicesAgentChatPane.js.map +1 -1
  44. package/lib/features/movies/MoviesAgentChatPane.js +1 -1
  45. package/lib/features/movies/MoviesAgentChatPane.js.map +1 -1
  46. package/lib/features/notion/NotionBlockSheets.js +206 -0
  47. package/lib/features/notion/NotionBlockSheets.js.map +1 -0
  48. package/lib/features/notion/NotionBlockView.js +36 -0
  49. package/lib/features/notion/NotionBlockView.js.map +1 -0
  50. package/lib/features/notion/NotionCommentsSheet.js +461 -0
  51. package/lib/features/notion/NotionCommentsSheet.js.map +1 -0
  52. package/lib/features/notion/NotionConnectionsSheet.js +244 -0
  53. package/lib/features/notion/NotionConnectionsSheet.js.map +1 -0
  54. package/lib/features/notion/NotionHistorySheet.js +439 -0
  55. package/lib/features/notion/NotionHistorySheet.js.map +1 -0
  56. package/lib/features/notion/NotionMediaBlocks.js +803 -0
  57. package/lib/features/notion/NotionMediaBlocks.js.map +1 -0
  58. package/lib/features/notion/NotionMentionSheet.js +279 -0
  59. package/lib/features/notion/NotionMentionSheet.js.map +1 -0
  60. package/lib/features/notion/NotionPageMenu.js +585 -0
  61. package/lib/features/notion/NotionPageMenu.js.map +1 -0
  62. package/lib/features/notion/NotionPageTree.js +220 -0
  63. package/lib/features/notion/NotionPageTree.js.map +1 -0
  64. package/lib/features/notion/NotionPageView.js +1253 -152
  65. package/lib/features/notion/NotionPageView.js.map +1 -1
  66. package/lib/features/notion/NotionShareSheet.js +606 -0
  67. package/lib/features/notion/NotionShareSheet.js.map +1 -0
  68. package/lib/features/notion/NotionSheets.js +87 -15
  69. package/lib/features/notion/NotionSheets.js.map +1 -1
  70. package/lib/features/notion/NotionSidebarSections.js +492 -0
  71. package/lib/features/notion/NotionSidebarSections.js.map +1 -0
  72. package/lib/features/notion/NotionWorkspaceAdmin.js +659 -0
  73. package/lib/features/notion/NotionWorkspaceAdmin.js.map +1 -0
  74. package/lib/features/notion/NotionWorkspaceView.js +257 -45
  75. package/lib/features/notion/NotionWorkspaceView.js.map +1 -1
  76. package/lib/features/notion/database/DatabaseCells.js +554 -0
  77. package/lib/features/notion/database/DatabaseCells.js.map +1 -0
  78. package/lib/features/notion/database/DatabaseDateLayouts.js +792 -0
  79. package/lib/features/notion/database/DatabaseDateLayouts.js.map +1 -0
  80. package/lib/features/notion/database/DatabaseLayouts.js +461 -0
  81. package/lib/features/notion/database/DatabaseLayouts.js.map +1 -0
  82. package/lib/features/notion/database/DatabaseSheets.js +791 -0
  83. package/lib/features/notion/database/DatabaseSheets.js.map +1 -0
  84. package/lib/features/notion/database/NotionDatabaseView.js +817 -0
  85. package/lib/features/notion/database/NotionDatabaseView.js.map +1 -0
  86. package/lib/features/notion/database/RowPropertiesPanel.js +162 -0
  87. package/lib/features/notion/database/RowPropertiesPanel.js.map +1 -0
  88. package/lib/features/notion/database/chartData.js +89 -0
  89. package/lib/features/notion/database/chartData.js.map +1 -0
  90. package/lib/features/notion/database/chartView.js +112 -0
  91. package/lib/features/notion/database/chartView.js.map +1 -0
  92. package/lib/features/notion/database/connectorButton.js +33 -0
  93. package/lib/features/notion/database/connectorButton.js.map +1 -0
  94. package/lib/features/notion/database/databaseCalc.js +208 -0
  95. package/lib/features/notion/database/databaseCalc.js.map +1 -0
  96. package/lib/features/notion/database/databaseCalendar.js +157 -0
  97. package/lib/features/notion/database/databaseCalendar.js.map +1 -0
  98. package/lib/features/notion/database/databaseCsv.js +98 -0
  99. package/lib/features/notion/database/databaseCsv.js.map +1 -0
  100. package/lib/features/notion/database/databaseCsvActions.js +58 -0
  101. package/lib/features/notion/database/databaseCsvActions.js.map +1 -0
  102. package/lib/features/notion/database/databaseFilter.js +473 -0
  103. package/lib/features/notion/database/databaseFilter.js.map +1 -0
  104. package/lib/features/notion/database/databaseFormula.js +1072 -0
  105. package/lib/features/notion/database/databaseFormula.js.map +1 -0
  106. package/lib/features/notion/database/databaseGroup.js +151 -0
  107. package/lib/features/notion/database/databaseGroup.js.map +1 -0
  108. package/lib/features/notion/database/databaseRollup.js +58 -0
  109. package/lib/features/notion/database/databaseRollup.js.map +1 -0
  110. package/lib/features/notion/database/databaseTimeline.js +156 -0
  111. package/lib/features/notion/database/databaseTimeline.js.map +1 -0
  112. package/lib/features/notion/database/databaseView.js +1188 -0
  113. package/lib/features/notion/database/databaseView.js.map +1 -0
  114. package/lib/features/notion/database/databaseViews.js +310 -0
  115. package/lib/features/notion/database/databaseViews.js.map +1 -0
  116. package/lib/features/notion/database/relationSource.js +92 -0
  117. package/lib/features/notion/database/relationSource.js.map +1 -0
  118. package/lib/features/notion/database/uniqueId.js +17 -0
  119. package/lib/features/notion/database/uniqueId.js.map +1 -0
  120. package/lib/features/notion/database/useNotionDatabase.js +617 -0
  121. package/lib/features/notion/database/useNotionDatabase.js.map +1 -0
  122. package/lib/features/notion/notionAgentRunStatus.js +39 -0
  123. package/lib/features/notion/notionAgentRunStatus.js.map +1 -0
  124. package/lib/features/notion/notionCodeLanguages.js +112 -0
  125. package/lib/features/notion/notionCodeLanguages.js.map +1 -0
  126. package/lib/features/notion/notionComments.js +86 -0
  127. package/lib/features/notion/notionComments.js.map +1 -0
  128. package/lib/features/notion/notionConnections.js +74 -0
  129. package/lib/features/notion/notionConnections.js.map +1 -0
  130. package/lib/features/notion/notionDocuments.js +453 -1
  131. package/lib/features/notion/notionDocuments.js.map +1 -1
  132. package/lib/features/notion/notionEditorModel.js +142 -7
  133. package/lib/features/notion/notionEditorModel.js.map +1 -1
  134. package/lib/features/notion/notionMarkdownShortcuts.js +51 -0
  135. package/lib/features/notion/notionMarkdownShortcuts.js.map +1 -0
  136. package/lib/features/notion/notionMedia.js +119 -0
  137. package/lib/features/notion/notionMedia.js.map +1 -0
  138. package/lib/features/notion/notionMentionDate.js +32 -0
  139. package/lib/features/notion/notionMentionDate.js.map +1 -0
  140. package/lib/features/notion/notionMentionOptions.js +162 -0
  141. package/lib/features/notion/notionMentionOptions.js.map +1 -0
  142. package/lib/features/notion/notionNotesAgent.js +445 -0
  143. package/lib/features/notion/notionNotesAgent.js.map +1 -0
  144. package/lib/features/notion/notionPageAccess.js +168 -0
  145. package/lib/features/notion/notionPageAccess.js.map +1 -0
  146. package/lib/features/notion/notionPageActions.js +138 -0
  147. package/lib/features/notion/notionPageActions.js.map +1 -0
  148. package/lib/features/notion/notionPageHistory.js +154 -0
  149. package/lib/features/notion/notionPageHistory.js.map +1 -0
  150. package/lib/features/notion/notionPageKinds.js +200 -0
  151. package/lib/features/notion/notionPageKinds.js.map +1 -0
  152. package/lib/features/notion/notionRecents.js +48 -0
  153. package/lib/features/notion/notionRecents.js.map +1 -0
  154. package/lib/features/notion/notionRichText.js +177 -0
  155. package/lib/features/notion/notionRichText.js.map +1 -0
  156. package/lib/features/notion/notionRichTitle.js +336 -0
  157. package/lib/features/notion/notionRichTitle.js.map +1 -0
  158. package/lib/features/notion/useNotionWorkspace.js +272 -13
  159. package/lib/features/notion/useNotionWorkspace.js.map +1 -1
  160. package/lib/features/notion/usePageActions.js +155 -0
  161. package/lib/features/notion/usePageActions.js.map +1 -0
  162. package/lib/features/resume/ResumeAgentChatPane.js +1 -1
  163. package/lib/features/resume/ResumeAgentChatPane.js.map +1 -1
  164. package/lib/features/shopping/ShoppingAgentChatPane.js +1 -1
  165. package/lib/features/shopping/ShoppingAgentChatPane.js.map +1 -1
  166. package/lib/features/study/StudyAgentChatPane.js +1 -1
  167. package/lib/features/study/StudyAgentChatPane.js.map +1 -1
  168. package/lib/features/travel/TravelAgentChatPane.js +1 -1
  169. package/lib/features/travel/TravelAgentChatPane.js.map +1 -1
  170. package/lib/features/webbuilder/WebBuilderExperience.js +40 -8
  171. package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
  172. package/lib/features/webbuilder/buildFragments.js +1 -1
  173. package/lib/features/webbuilder/buildFragments.js.map +1 -1
  174. package/lib/features/webbuilder/codeFiles.js +302 -0
  175. package/lib/features/webbuilder/codeFiles.js.map +1 -0
  176. package/lib/features/webbuilder/codeHighlight.js +209 -0
  177. package/lib/features/webbuilder/codeHighlight.js.map +1 -0
  178. package/lib/features/webbuilder/components/CodeView.js +682 -52
  179. package/lib/features/webbuilder/components/CodeView.js.map +1 -1
  180. package/lib/features/webbuilder/components/PreviewScreen.js +59 -38
  181. package/lib/features/webbuilder/components/PreviewScreen.js.map +1 -1
  182. package/lib/features/webbuilder/components/ProjectChat.js +3 -3
  183. package/lib/features/webbuilder/components/ProjectChat.js.map +1 -1
  184. package/lib/features/webbuilder/stepLabels.js +68 -0
  185. package/lib/features/webbuilder/stepLabels.js.map +1 -0
  186. package/lib/hooks/useHostChatSteering.js +45 -0
  187. package/lib/hooks/useHostChatSteering.js.map +1 -0
  188. package/lib/screens/Chat/index.js +2 -12
  189. package/lib/screens/Chat/index.js.map +1 -1
  190. package/lib/screens/Devices/index.js +205 -3
  191. package/lib/screens/Devices/index.js.map +1 -1
  192. package/lib/screens/ExpensesNative/useExpensesHostChat.js +25 -5
  193. package/lib/screens/ExpensesNative/useExpensesHostChat.js.map +1 -1
  194. package/lib/screens/InboxNative/useInboxHostChat.js +25 -5
  195. package/lib/screens/InboxNative/useInboxHostChat.js.map +1 -1
  196. package/lib/screens/JobSearchNative/useJobSearchHostChat.js +25 -5
  197. package/lib/screens/JobSearchNative/useJobSearchHostChat.js.map +1 -1
  198. package/lib/screens/LocalServicesNative/useLocalServicesHostChat.js +25 -5
  199. package/lib/screens/LocalServicesNative/useLocalServicesHostChat.js.map +1 -1
  200. package/lib/screens/MoviesNative/useMoviesHostChat.js +25 -5
  201. package/lib/screens/MoviesNative/useMoviesHostChat.js.map +1 -1
  202. package/lib/screens/NotionPage/index.js +2 -2
  203. package/lib/screens/NotionPage/index.js.map +1 -1
  204. package/lib/screens/ResumeNative/useResumeHostChat.js +25 -5
  205. package/lib/screens/ResumeNative/useResumeHostChat.js.map +1 -1
  206. package/lib/screens/ShoppingNative/useShoppingHostChat.js +25 -5
  207. package/lib/screens/ShoppingNative/useShoppingHostChat.js.map +1 -1
  208. package/lib/screens/StudyNative/useStudyHostChat.js +25 -5
  209. package/lib/screens/StudyNative/useStudyHostChat.js.map +1 -1
  210. package/lib/screens/TravelNative/useTravelHostChat.js +25 -5
  211. package/lib/screens/TravelNative/useTravelHostChat.js.map +1 -1
  212. package/lib/screens/WebBuilderNative/codeWorkspaceApi.js +222 -0
  213. package/lib/screens/WebBuilderNative/codeWorkspaceApi.js.map +1 -0
  214. package/lib/screens/WebBuilderNative/index.js +1 -1
  215. package/lib/screens/WebBuilderNative/index.js.map +1 -1
  216. package/lib/screens/WebBuilderNative/sandboxProxyApi.js +126 -4
  217. package/lib/screens/WebBuilderNative/sandboxProxyApi.js.map +1 -1
  218. package/lib/screens/WebBuilderNative/useWebBuilderHost.js +120 -11
  219. package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
  220. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useState,useRef,useCallback,useEffect,useMemo}from'react';import {View,StyleSheet,ActivityIndicator,Text,Pressable,KeyboardAvoidingView,ScrollView,TextInput,RefreshControl,Keyboard,Platform,Alert}from'react-native';import {Ionicons}from'@expo/vector-icons';import {useNavigation,StackActions}from'@react-navigation/native';import {useApolloClient,useQuery}from'@apollo/client/index.js';import {useSafeAreaInsets}from'react-native-safe-area-context';import {mobileTokens}from'../../theme/mobileTokens.js';import {notionPalette}from'./notionTheme.js';import {NATIVE_NOTION_PAGE_TREE,NATIVE_NOTION_BLOCK,NATIVE_NOTION_UPDATE_BLOCK,NATIVE_NOTION_APPEND_CHILDREN,NATIVE_NOTION_TRANSFORM_BLOCK,NATIVE_NOTION_DELETE_BLOCK}from'./notionDocuments.js';import {blockTitle,blockIcon,toTitleProperty,editedLabel,isPageBlock,pageTitle,richTextToPlain}from'./notionBlocks.js';import {modelFromTree,newBlockId,isTextType,insertAfter,patchBlock,previousLineId,removeBlock,flattenModel,nextTypeAfter,TURN_INTO}from'./notionEditorModel.js';var __defProp = Object.defineProperty;
1
+ import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useState,useEffect,useRef,useCallback,useMemo}from'react';import {Keyboard,Platform,View,StyleSheet,ActivityIndicator,Text,Pressable,KeyboardAvoidingView,ScrollView,TextInput,RefreshControl,Share,Clipboard}from'react-native';import {Ionicons}from'@expo/vector-icons';import {useNavigation,StackActions}from'@react-navigation/native';import {useApolloClient,useQuery}from'@apollo/client/index.js';import {useSafeAreaInsets}from'react-native-safe-area-context';import {mobileTokens}from'../../theme/mobileTokens.js';import {RowPropertiesPanel}from'./database/RowPropertiesPanel.js';import {NEW_DATABASE_PROPERTIES}from'./database/useNotionDatabase.js';import {notionPalette}from'./notionTheme.js';import {NATIVE_NOTION_PAGE_TREE,NATIVE_NOTION_BLOCK,NATIVE_NOTION_UPDATE_BLOCK,NATIVE_NOTION_DELETE_BLOCK,NATIVE_NOTION_RESTORE_BLOCK,NATIVE_NOTION_APPEND_CHILDREN,NATIVE_NOTION_TRANSFORM_BLOCK,NATIVE_NOTION_BACKLINKS,NATIVE_NOTION_EXPORT_MARKDOWN,NATIVE_NOTION_IMPORT_MARKDOWN,NATIVE_NOTION_DUPLICATE_BLOCK,NATIVE_NOTION_REORDER_CHILDREN}from'./notionDocuments.js';import {blockTitle,blockIcon,toTitleProperty,editedLabel,pageTitle,isPageBlock,richTextToPlain}from'./notionBlocks.js';import {modelFromTree,patchBlock,snapshotSubtree,removeBlock,restoreSubtree,newBlockId,isTextType,insertAfter,previousLineId,flattenModel,emptyBlock,nextTypeAfter,TURN_INTO,toEditorBlock,moveAmongSiblings,columnLayout,insertTree,treeInput,firstTextId}from'./notionEditorModel.js';import {runsToTitleProperty,runsText,applyPlainTextEdit,applyInlineMarkdown,linkAt,rangeHasFormat,applyFormatRange,applyFormatToAll,replaceRange}from'./notionRichTitle.js';import {markdownShortcutFor}from'./notionMarkdownShortcuts.js';import {blockColorParts,textColorOf,washColorOf}from'./notionRichText.js';import {languageLabel}from'./notionCodeLanguages.js';import {ColorSheet,LanguageSheet}from'./NotionBlockSheets.js';import {fontFamilyOf,PageActionsSheet,TextPromptDialog}from'./NotionPageMenu.js';import {readPageSettings,serverErrorText,wordCount,pageFontPatch,pageSettingsPatch,pageLinkUrl}from'./notionPageActions.js';import {usePageActions,webOrigin}from'./usePageActions.js';import {MEDIA_TYPES,MediaBlock}from'./NotionMediaBlocks.js';import {MentionSheet}from'./NotionMentionSheet.js';import {recordRecent}from'./notionRecents.js';import {usePageComments,CommentsSheet}from'./NotionCommentsSheet.js';import {ShareSheet}from'./NotionShareSheet.js';import {HistorySheet}from'./NotionHistorySheet.js';import {useNotionWorkspace}from'./useNotionWorkspace.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -17,6 +17,15 @@ var __spreadValues = (a, b) => {
17
17
  return a;
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ function scaled(style, small) {
21
+ if (!small) return style;
22
+ const s = style;
23
+ return __spreadValues(__spreadValues(__spreadValues({}, style), s.fontSize ? {
24
+ fontSize: Math.round(s.fontSize * 0.875)
25
+ } : {}), s.lineHeight ? {
26
+ lineHeight: Math.round(s.lineHeight * 0.875)
27
+ } : {});
28
+ }
20
29
  const SAVE_DELAY_MS = 500;
21
30
  const PAGE_EMOJIS = ["\u{1F4C4}", "\u{1F4DD}", "\u{1F4CC}", "\u{1F4A1}", "\u{1F3AF}", "\u{1F680}", "\u{1F4DA}", "\u2705", "\u{1F5D3}\uFE0F", "\u{1F4BC}", "\u{1F3E0}", "\u2764\uFE0F", "\u2B50", "\u{1F525}", "\u{1F9E0}", "\u{1F3A8}"];
22
31
  const PLACEHOLDER = {
@@ -59,7 +68,7 @@ function NotionPageView({
59
68
  blockId,
60
69
  isDark
61
70
  }) {
62
- var _a, _b, _c, _d;
71
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
63
72
  const palette = notionPalette(isDark);
64
73
  const navigation = useNavigation();
65
74
  const client = useApolloClient();
@@ -99,6 +108,24 @@ function NotionPageView({
99
108
  const [save, setSave] = useState("idle");
100
109
  const [iconPicker, setIconPicker] = useState(false);
101
110
  const [refreshing, setRefreshing] = useState(false);
111
+ const [keyboardShown, setKeyboardShown] = useState(false);
112
+ useEffect(() => {
113
+ const show = Keyboard.addListener(Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow", () => setKeyboardShown(true));
114
+ const hide = Keyboard.addListener(Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide", () => setKeyboardShown(false));
115
+ return () => {
116
+ show.remove();
117
+ hide.remove();
118
+ };
119
+ }, []);
120
+ const [pageProps, setPageProps] = useState({});
121
+ const [importOpen, setImportOpen] = useState(false);
122
+ const [showBacklinks, setShowBacklinks] = useState(false);
123
+ const settings = readPageSettings(pageProps);
124
+ const look = {
125
+ fontFamily: fontFamilyOf(settings.font),
126
+ small: settings.smallText,
127
+ locked: settings.locked
128
+ };
102
129
  const modelRef = useRef(null);
103
130
  modelRef.current = model;
104
131
  const inputs = useRef(/* @__PURE__ */ new Map());
@@ -107,24 +134,32 @@ function NotionPageView({
107
134
  const savedTitles = useRef(/* @__PURE__ */ new Map());
108
135
  const inFlight = useRef(0);
109
136
  const applyTree = useCallback((next, pageBlock) => {
110
- var _a2;
111
- setModel(modelFromTree(next));
137
+ var _a2, _b2;
138
+ const seededModel = modelFromTree(next, pageBlock.id);
139
+ setModel(seededModel);
140
+ setCollapsed(new Set(Object.values(seededModel.blocks).filter((b) => b.type === "toggle").map((b) => b.id)));
112
141
  const t = blockTitle(pageBlock);
113
142
  setTitle(t);
114
143
  setIcon(blockIcon(pageBlock));
144
+ setPageProps((_a2 = pageBlock.properties) != null ? _a2 : {});
115
145
  savedTitles.current.set(pageBlock.id, t);
116
146
  const remember = (node) => {
117
147
  var _a3;
118
148
  savedTitles.current.set(node.block.id, blockTitle(node.block));
119
149
  ((_a3 = node.children) != null ? _a3 : []).forEach(remember);
120
150
  };
121
- ((_a2 = next.children) != null ? _a2 : []).forEach(remember);
151
+ ((_b2 = next.children) != null ? _b2 : []).forEach(remember);
122
152
  }, []);
123
153
  const seeded = useRef(false);
124
154
  useEffect(() => {
125
155
  if (!tree || !page || seeded.current) return;
126
156
  seeded.current = true;
127
157
  applyTree(tree, page);
158
+ recordRecent(page.workspaceId, {
159
+ id: page.id,
160
+ title: blockTitle(page),
161
+ icon: blockIcon(page)
162
+ });
128
163
  }, [tree, page, applyTree]);
129
164
  useEffect(() => {
130
165
  if (!pendingFocus) return void 0;
@@ -145,6 +180,8 @@ function NotionPageView({
145
180
  if (!res.data) throw new Error("The server returned no data");
146
181
  return res;
147
182
  }, [client]);
183
+ const failed = useRef([]);
184
+ const [saveError, setSaveError] = useState(null);
148
185
  const enqueue = useCallback((id, op) => {
149
186
  var _a2;
150
187
  inFlight.current += 1;
@@ -152,39 +189,54 @@ function NotionPageView({
152
189
  const prev = (_a2 = chains.current.get(id)) != null ? _a2 : Promise.resolve();
153
190
  const next = prev.catch(() => void 0).then(op).then(() => {
154
191
  inFlight.current -= 1;
155
- if (!inFlight.current && !pending.current.size) setSave("saved");
192
+ if (!inFlight.current && !pending.current.size && !failed.current.length) setSave("saved");
156
193
  }, (err) => {
157
194
  inFlight.current -= 1;
195
+ failed.current.push({
196
+ id,
197
+ op
198
+ });
199
+ setSaveError(serverErrorText(err));
158
200
  setSave("error");
159
- if (__DEV__) console.warn("[NotionPage] save failed", err);
201
+ if (__DEV__) console.warn("[NotionPage] save failed", serverErrorText(err), err);
160
202
  });
161
203
  chains.current.set(id, next);
162
204
  return next;
163
205
  }, []);
164
- const writeTitle = useCallback((id, text) => {
206
+ const retryFailed = useCallback(() => {
207
+ const again = failed.current;
208
+ failed.current = [];
209
+ setSaveError(null);
210
+ again.forEach(({
211
+ id,
212
+ op
213
+ }) => enqueue(id, op));
214
+ }, [enqueue]);
215
+ const writeTitle = useCallback((id, text, runs) => {
165
216
  var _a2;
166
217
  const baseTitle = (_a2 = savedTitles.current.get(id)) != null ? _a2 : "";
167
218
  savedTitles.current.set(id, text);
219
+ const title2 = runs ? runsToTitleProperty(runs) : toTitleProperty(text);
168
220
  return enqueue(id, () => write({
169
221
  mutation: NATIVE_NOTION_UPDATE_BLOCK,
170
222
  variables: {
171
223
  input: {
172
224
  id,
173
225
  properties: {
174
- title: toTitleProperty(text)
226
+ title: title2
175
227
  },
176
228
  baseTitle
177
229
  }
178
230
  }
179
231
  }));
180
232
  }, [write, enqueue]);
181
- const scheduleTitle = useCallback((id, text) => {
233
+ const scheduleTitle = useCallback((id, text, runs) => {
182
234
  var _a2;
183
235
  (_a2 = pending.current.get(id)) == null ? void 0 : _a2.cancel();
184
236
  setSave("saving");
185
237
  const handle = setTimeout(() => {
186
238
  pending.current.delete(id);
187
- writeTitle(id, text);
239
+ writeTitle(id, text, runs);
188
240
  }, SAVE_DELAY_MS);
189
241
  const cancel = () => {
190
242
  clearTimeout(handle);
@@ -194,13 +246,78 @@ function NotionPageView({
194
246
  cancel,
195
247
  flush: () => {
196
248
  cancel();
197
- writeTitle(id, text);
249
+ writeTitle(id, text, runs);
198
250
  }
199
251
  });
200
252
  }, [writeTitle]);
201
- useEffect(() => () => {
202
- [...pending.current.values()].forEach((p) => p.flush());
253
+ const undoStack = useRef([]);
254
+ const redoStack = useRef([]);
255
+ const [, setHistoryTick] = useState(0);
256
+ const record = useCallback((entry) => {
257
+ const top = undoStack.current[undoStack.current.length - 1];
258
+ if (entry.key && (top == null ? void 0 : top.key) === entry.key && Date.now() - top.at < 1500) {
259
+ top.redo = entry.redo;
260
+ top.at = Date.now();
261
+ } else {
262
+ undoStack.current.push(__spreadProps(__spreadValues({}, entry), {
263
+ at: Date.now()
264
+ }));
265
+ if (undoStack.current.length > 100) undoStack.current.shift();
266
+ }
267
+ redoStack.current = [];
268
+ setHistoryTick((t) => t + 1);
203
269
  }, []);
270
+ const undo = () => {
271
+ const entry = undoStack.current.pop();
272
+ if (!entry) return;
273
+ entry.undo();
274
+ redoStack.current.push(entry);
275
+ setHistoryTick((t) => t + 1);
276
+ };
277
+ const redo = () => {
278
+ const entry = redoStack.current.pop();
279
+ if (!entry) return;
280
+ entry.redo();
281
+ undoStack.current.push(__spreadProps(__spreadValues({}, entry), {
282
+ at: 0
283
+ }));
284
+ setHistoryTick((t) => t + 1);
285
+ };
286
+ const setRuns = useCallback((id, runs) => {
287
+ const text = runsText(runs);
288
+ setModel((m) => m ? patchBlock(m, id, {
289
+ runs,
290
+ text
291
+ }) : m);
292
+ scheduleTitle(id, text, runs);
293
+ }, [scheduleTitle]);
294
+ const commitRuns = useCallback((id, before, after, key) => {
295
+ setRuns(id, after);
296
+ record({
297
+ key,
298
+ undo: () => setRuns(id, before),
299
+ redo: () => setRuns(id, after)
300
+ });
301
+ }, [record, setRuns]);
302
+ const latest = useRef({
303
+ title: "",
304
+ icon: null,
305
+ workspaceId: null
306
+ });
307
+ useEffect(
308
+ () => () => {
309
+ [...pending.current.values()].forEach((p) => p.flush());
310
+ if (blockId && latest.current.workspaceId) {
311
+ recordRecent(latest.current.workspaceId, {
312
+ id: blockId,
313
+ title: latest.current.title,
314
+ icon: latest.current.icon
315
+ });
316
+ }
317
+ },
318
+ // eslint-disable-next-line react-hooks/exhaustive-deps
319
+ []
320
+ );
204
321
  const updateProps = useCallback((id, properties) => enqueue(id, () => write({
205
322
  mutation: NATIVE_NOTION_UPDATE_BLOCK,
206
323
  variables: {
@@ -210,20 +327,44 @@ function NotionPageView({
210
327
  }
211
328
  }
212
329
  })), [write, enqueue]);
330
+ const removeLocal = useCallback((id) => {
331
+ var _a2;
332
+ const m = modelRef.current;
333
+ if (!m) return null;
334
+ const snap = snapshotSubtree(m, id);
335
+ (_a2 = pending.current.get(id)) == null ? void 0 : _a2.cancel();
336
+ setModel((cur) => cur ? removeBlock(cur, id) : cur);
337
+ enqueue(id, () => write({
338
+ mutation: NATIVE_NOTION_DELETE_BLOCK,
339
+ variables: {
340
+ blockId: id
341
+ }
342
+ }));
343
+ return snap;
344
+ }, [write, enqueue]);
345
+ const bringBack = useCallback((snap) => {
346
+ if (!snap) return;
347
+ setModel((cur) => cur ? restoreSubtree(cur, snap) : cur);
348
+ enqueue(snap.id, () => write({
349
+ mutation: NATIVE_NOTION_RESTORE_BLOCK,
350
+ variables: {
351
+ blockId: snap.id
352
+ }
353
+ }));
354
+ }, [write, enqueue]);
213
355
  const addBlock = useCallback((parentId, afterId, type, extra = {}) => {
356
+ if (!parentId) {
357
+ setSaveError("This page has no id yet. Pull down to reload it.");
358
+ setSave("error");
359
+ return "";
360
+ }
214
361
  const id = newBlockId();
215
362
  const properties = __spreadValues(__spreadValues(__spreadValues({}, isTextType(type) || type === "page" ? {
216
363
  title: []
217
364
  } : {}), type === "todo" ? {
218
365
  checked: false
219
366
  } : {}), extra);
220
- const block = {
221
- id,
222
- type,
223
- parentId,
224
- text: "",
225
- properties
226
- };
367
+ const block = emptyBlock(id, type, parentId, properties);
227
368
  setModel((m) => m ? insertAfter(m, block, afterId) : m);
228
369
  savedTitles.current.set(id, "");
229
370
  if (isTextType(type)) setPendingFocus(id);
@@ -247,54 +388,74 @@ function NotionPageView({
247
388
  })
248
389
  }
249
390
  }));
391
+ let snap = null;
392
+ record({
393
+ undo: () => {
394
+ snap = removeLocal(id);
395
+ },
396
+ redo: () => bringBack(snap)
397
+ });
250
398
  return id;
251
- }, [write, enqueue]);
252
- const turnInto = useCallback((id, type) => {
253
- var _a2;
254
- const block = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id];
255
- if (!block || block.type === type) return;
256
- setModel((cur) => cur ? patchBlock(cur, id, {
257
- type
258
- }) : cur);
259
- enqueue(id, async () => {
260
- await write({
261
- mutation: NATIVE_NOTION_TRANSFORM_BLOCK,
262
- variables: {
263
- blockId: id,
264
- newType: type
265
- }
266
- });
267
- if (type === "todo") {
399
+ }, [write, enqueue, record, removeLocal, bringBack]);
400
+ const turnInto = useCallback(
401
+ (id, type, extraProps, opts = {}) => {
402
+ var _a2;
403
+ const block = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id];
404
+ if (!block || block.type === type) return;
405
+ const fromType = block.type;
406
+ const props = __spreadValues(__spreadValues({}, type === "todo" ? {
407
+ checked: false
408
+ } : {}), extraProps != null ? extraProps : {});
409
+ setModel((cur) => cur ? patchBlock(cur, id, {
410
+ type,
411
+ properties: __spreadValues(__spreadValues({}, block.properties), props)
412
+ }) : cur);
413
+ enqueue(id, async () => {
268
414
  await write({
269
- mutation: NATIVE_NOTION_UPDATE_BLOCK,
415
+ mutation: NATIVE_NOTION_TRANSFORM_BLOCK,
270
416
  variables: {
271
- input: {
272
- id,
273
- properties: {
274
- checked: false
417
+ blockId: id,
418
+ newType: type
419
+ }
420
+ });
421
+ if (Object.keys(props).length) {
422
+ await write({
423
+ mutation: NATIVE_NOTION_UPDATE_BLOCK,
424
+ variables: {
425
+ input: {
426
+ id,
427
+ properties: props
275
428
  }
276
429
  }
277
- }
430
+ });
431
+ }
432
+ });
433
+ if (opts.record !== false) {
434
+ record({
435
+ undo: () => turnInto(id, fromType, void 0, {
436
+ record: false
437
+ }),
438
+ redo: () => turnInto(id, type, extraProps, {
439
+ record: false
440
+ })
278
441
  });
279
442
  }
280
- });
281
- }, [write, enqueue]);
443
+ },
444
+ // eslint-disable-next-line react-hooks/exhaustive-deps
445
+ [write, enqueue, record]
446
+ );
282
447
  const deleteLine = useCallback((id) => {
283
- var _a2;
284
448
  const m = modelRef.current;
285
449
  if (!m) return;
286
450
  const prev = previousLineId(m, id);
287
- (_a2 = pending.current.get(id)) == null ? void 0 : _a2.cancel();
288
- setModel(removeBlock(m, id));
451
+ const snap = removeLocal(id);
289
452
  if (prev) setPendingFocus(prev);
290
453
  else setFocusedId(null);
291
- enqueue(id, () => write({
292
- mutation: NATIVE_NOTION_DELETE_BLOCK,
293
- variables: {
294
- blockId: id
295
- }
296
- }));
297
- }, [write, enqueue]);
454
+ record({
455
+ undo: () => bringBack(snap),
456
+ redo: () => removeLocal(id)
457
+ });
458
+ }, [record, removeLocal, bringBack]);
298
459
  const openPage = useCallback((id) => navigation.dispatch(StackActions.push("MainStack.NotionPage", {
299
460
  orgName,
300
461
  blockId: id
@@ -313,42 +474,594 @@ function NotionPageView({
313
474
  } : null
314
475
  });
315
476
  };
316
- const trashPage = () => {
477
+ const notion = useNotionWorkspace();
478
+ const menuExtras = useRef(() => ({}));
479
+ const pageActions = usePageActions({
480
+ notion,
481
+ palette,
482
+ onOpenPage: openPage,
483
+ onTrashed: () => navigation.goBack(),
484
+ extrasFor: () => menuExtras.current(),
485
+ onRenamed: (id, newTitle) => {
486
+ var _a2;
487
+ if (id !== blockId) return;
488
+ (_a2 = pending.current.get(id)) == null ? void 0 : _a2.cancel();
489
+ savedTitles.current.set(id, newTitle);
490
+ setTitle(newTitle);
491
+ }
492
+ });
493
+ const backlinksQuery = useQuery(NATIVE_NOTION_BACKLINKS, {
494
+ variables: {
495
+ blockId: blockId != null ? blockId : "",
496
+ limit: 50
497
+ },
498
+ skip: !blockId,
499
+ fetchPolicy: "cache-and-network"
500
+ });
501
+ const backlinks = ((_e = (_d = backlinksQuery.data) == null ? void 0 : _d.getBacklinks) != null ? _e : []).filter((b) => !b.inTrash && b.id !== blockId);
502
+ const pagePropsRef = useRef(pageProps);
503
+ pagePropsRef.current = pageProps;
504
+ const patchPage = (patch) => {
505
+ if (!blockId) return;
506
+ pagePropsRef.current = __spreadValues(__spreadValues({}, pagePropsRef.current), patch);
507
+ setPageProps(pagePropsRef.current);
508
+ updateProps(blockId, patch);
509
+ };
510
+ const exportMarkdown = async () => {
511
+ var _a2, _b2;
317
512
  if (!blockId) return;
318
- Alert.alert("Move to Trash?", "You can restore it from Trash.", [{
319
- text: "Cancel",
320
- style: "cancel"
321
- }, {
322
- text: "Move to Trash",
323
- style: "destructive",
324
- onPress: async () => {
325
- try {
326
- await write({
327
- mutation: NATIVE_NOTION_DELETE_BLOCK,
328
- variables: {
329
- blockId
330
- }
331
- });
332
- client.refetchQueries({
333
- include: ["NativeNotionChildren"]
334
- }).catch(() => void 0);
335
- navigation.goBack();
336
- } catch (err) {
337
- Alert.alert("Couldn't move to Trash", err instanceof Error ? err.message : void 0);
513
+ try {
514
+ const res = await client.query({
515
+ query: NATIVE_NOTION_EXPORT_MARKDOWN,
516
+ variables: {
517
+ blockId
518
+ },
519
+ fetchPolicy: "network-only"
520
+ });
521
+ const md = (_b2 = (_a2 = res.data) == null ? void 0 : _a2.exportNotionPageMarkdown) != null ? _b2 : "";
522
+ await Share.share({
523
+ title: title.trim() || "Untitled",
524
+ message: md || `# ${title.trim() || "Untitled"}`
525
+ });
526
+ } catch (err) {
527
+ pageActions.showToast({
528
+ message: `Couldn't export. ${err instanceof Error ? err.message : ""}`.trim()
529
+ });
530
+ }
531
+ };
532
+ const importMarkdown = async (markdown) => {
533
+ var _a2, _b2;
534
+ if (!blockId) return;
535
+ await write({
536
+ mutation: NATIVE_NOTION_IMPORT_MARKDOWN,
537
+ variables: {
538
+ parentBlockId: blockId,
539
+ markdown
540
+ }
541
+ });
542
+ const [res, pageRes] = await Promise.all([refetch(), pageQuery.refetch()]);
543
+ const next = (_a2 = res.data) == null ? void 0 : _a2.getBlockTree;
544
+ const nextPage = (_b2 = pageRes.data) == null ? void 0 : _b2.getBlock;
545
+ if (next && nextPage && !pending.current.size && !inFlight.current) applyTree(next, nextPage);
546
+ pageActions.showToast({
547
+ message: "Imported"
548
+ });
549
+ };
550
+ latest.current = {
551
+ title,
552
+ icon,
553
+ workspaceId: page == null ? void 0 : page.workspaceId
554
+ };
555
+ menuExtras.current = () => {
556
+ const words = wordCount(title, modelRef.current);
557
+ return {
558
+ subtitle: page ? editedLabel(page.lastEditedTime) || void 0 : void 0,
559
+ pageSettings: {
560
+ settings,
561
+ onToggle: (key) => patchPage(pageSettingsPatch(pagePropsRef.current, key)),
562
+ onFont: (font) => patchPage(pageFontPatch(pagePropsRef.current, font)),
563
+ footer: `${words} ${words === 1 ? "word" : "words"}`
564
+ },
565
+ groups: [[{
566
+ key: "export",
567
+ label: "Export as Markdown\u2026",
568
+ icon: "share-outline",
569
+ onPress: exportMarkdown
570
+ }, {
571
+ key: "history",
572
+ label: "Page history",
573
+ icon: "time-outline",
574
+ onPress: () => setHistoryOpen(true)
575
+ }, {
576
+ key: "import",
577
+ label: "Import Markdown\u2026",
578
+ icon: "download-outline",
579
+ onPress: () => setImportOpen(true)
580
+ }]]
581
+ };
582
+ };
583
+ const openPageMenu = () => {
584
+ if (!page) return;
585
+ Keyboard.dismiss();
586
+ pageActions.open(__spreadProps(__spreadValues({}, page), {
587
+ properties: __spreadProps(__spreadValues({}, pageProps), {
588
+ title: toTitleProperty(title)
589
+ })
590
+ }));
591
+ };
592
+ const selections = useRef(/* @__PURE__ */ new Map());
593
+ const onChangeText = (id, text) => {
594
+ var _a2;
595
+ const block = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id];
596
+ if (!block) return;
597
+ const before = block.runs;
598
+ if (text === "/" && !block.text && block.type !== "code") {
599
+ setInsertFor({
600
+ mode: "replace",
601
+ id
602
+ });
603
+ return;
604
+ }
605
+ if (block.type !== "code" && text.length === block.text.length + 1) {
606
+ let at = 0;
607
+ while (at < block.text.length && text[at] === block.text[at]) at += 1;
608
+ if (text[at] === "@" && (at === 0 || /\s/.test(text[at - 1])) && text.slice(at + 1) === block.text.slice(at)) {
609
+ commitRuns(id, before, applyPlainTextEdit(before, text), `text:${id}`);
610
+ Keyboard.dismiss();
611
+ setMentionFor({
612
+ id,
613
+ at
614
+ });
615
+ return;
616
+ }
617
+ }
618
+ if (block.type !== "code") {
619
+ const sp = text.indexOf(" ");
620
+ if (sp > 0 && text.length === block.text.length + 1 && text.slice(0, sp) === block.text.slice(0, sp) && text.slice(sp + 1) === block.text.slice(sp)) {
621
+ const shortcut = markdownShortcutFor(text.slice(0, sp), block.type);
622
+ if (shortcut) {
623
+ setRuns(id, applyPlainTextEdit(before, text.slice(sp + 1)));
624
+ turnInto(id, shortcut.type, shortcut.properties);
625
+ return;
338
626
  }
339
627
  }
628
+ if (text === "```") {
629
+ setRuns(id, []);
630
+ turnInto(id, "code");
631
+ return;
632
+ }
633
+ if ((text === "---" || text === "***" || text === "___") && (block.type === "paragraph" || block.type === "text")) {
634
+ setRuns(id, []);
635
+ turnInto(id, "divider");
636
+ addBlock(block.parentId, id, "paragraph");
637
+ return;
638
+ }
639
+ }
640
+ let runs = applyPlainTextEdit(before, text);
641
+ if (block.type !== "code") {
642
+ let suffix = 0;
643
+ const old = block.text;
644
+ while (suffix < old.length && suffix < text.length && old[old.length - 1 - suffix] === text[text.length - 1 - suffix]) {
645
+ suffix += 1;
646
+ }
647
+ const md = applyInlineMarkdown(runs, text.length - suffix);
648
+ if (md) runs = md.runs;
649
+ }
650
+ commitRuns(id, before, runs, `text:${id}`);
651
+ };
652
+ const [colorFor, setColorFor] = useState(null);
653
+ const [linkFor, setLinkFor] = useState(null);
654
+ const [languageFor, setLanguageFor] = useState(null);
655
+ const [blockMenuFor, setBlockMenuFor] = useState(null);
656
+ const [insertFor, setInsertFor] = useState(null);
657
+ const [mentionFor, setMentionFor] = useState(null);
658
+ const [captionFor, setCaptionFor] = useState(null);
659
+ const [commentsFor, setCommentsFor] = useState(null);
660
+ const [shareOpen, setShareOpen] = useState(false);
661
+ const [historyOpen, setHistoryOpen] = useState(false);
662
+ const comments = usePageComments(blockId);
663
+ const openComments = [...comments.openCounts.values()].reduce((n, c) => n + c, 0);
664
+ const addTree = (tree2, parentId, afterId) => {
665
+ setModel((m) => m ? insertTree(m, tree2, parentId, afterId) : m);
666
+ enqueue(tree2.id, () => write({
667
+ mutation: NATIVE_NOTION_APPEND_CHILDREN,
668
+ variables: {
669
+ input: __spreadProps(__spreadValues({
670
+ parentId
671
+ }, afterId ? {
672
+ after: afterId
673
+ } : {}), {
674
+ children: [treeInput(tree2, parentId)]
675
+ })
676
+ }
677
+ }));
678
+ const focus = firstTextId(tree2);
679
+ if (focus) setPendingFocus(focus);
680
+ let snap = null;
681
+ record({
682
+ undo: () => {
683
+ snap = removeLocal(tree2.id);
684
+ },
685
+ redo: () => bringBack(snap)
686
+ });
687
+ };
688
+ const insertGroups = (target) => {
689
+ var _a2;
690
+ const m = modelRef.current;
691
+ if (!m) return [];
692
+ const anchor = target.id ? m.blocks[target.id] : null;
693
+ const parentId = (_a2 = anchor == null ? void 0 : anchor.parentId) != null ? _a2 : m.rootId;
694
+ const put = (type, props = {}) => () => {
695
+ var _a3;
696
+ if (type === "page") {
697
+ addSubPage();
698
+ return;
699
+ }
700
+ if (target.mode === "replace" && anchor && !anchor.text && type !== "database") {
701
+ setRuns(anchor.id, []);
702
+ turnInto(anchor.id, type, props);
703
+ if (!isTextType(type)) setFocusedId(null);
704
+ return;
705
+ }
706
+ addBlock(parentId, (_a3 = anchor == null ? void 0 : anchor.id) != null ? _a3 : lastTopId(m), type, props);
707
+ };
708
+ const columns = (n) => () => {
709
+ var _a3;
710
+ return addTree(columnLayout(n), parentId, (_a3 = anchor == null ? void 0 : anchor.id) != null ? _a3 : lastTopId(m));
711
+ };
712
+ return [[{
713
+ key: "image",
714
+ label: "Image",
715
+ icon: "image-outline",
716
+ onPress: put("image")
717
+ }, {
718
+ key: "video",
719
+ label: "Video",
720
+ icon: "videocam-outline",
721
+ onPress: put("video")
722
+ }, {
723
+ key: "audio",
724
+ label: "Audio",
725
+ icon: "musical-notes-outline",
726
+ onPress: put("audio")
727
+ }, {
728
+ key: "file",
729
+ label: "File",
730
+ icon: "document-attach-outline",
731
+ onPress: put("file")
732
+ }, {
733
+ key: "bookmark",
734
+ label: "Bookmark",
735
+ icon: "bookmark-outline",
736
+ onPress: put("bookmark")
737
+ }, {
738
+ key: "embed",
739
+ label: "Embed",
740
+ icon: "globe-outline",
741
+ onPress: put("embed", {
742
+ embedType: "url"
743
+ })
744
+ }], [{
745
+ key: "table",
746
+ label: "Table",
747
+ icon: "grid-outline",
748
+ onPress: put("table", {
749
+ rows: [["", ""], ["", ""]],
750
+ hasHeaderRow: false
751
+ })
752
+ }, {
753
+ key: "col2",
754
+ label: "2 columns",
755
+ icon: "pause-outline",
756
+ onPress: columns(2)
757
+ }, {
758
+ key: "col3",
759
+ label: "3 columns",
760
+ icon: "reorder-three-outline",
761
+ onPress: columns(3)
762
+ }, {
763
+ key: "equation",
764
+ label: "Block equation",
765
+ icon: "calculator-outline",
766
+ onPress: put("equation", {
767
+ expression: ""
768
+ })
769
+ }, {
770
+ key: "toc",
771
+ label: "Table of contents",
772
+ icon: "list-outline",
773
+ onPress: put("table_of_contents")
774
+ }, {
775
+ key: "divider",
776
+ label: "Divider",
777
+ icon: "remove-outline",
778
+ onPress: put("divider")
779
+ }, {
780
+ key: "page",
781
+ label: "Sub-page",
782
+ icon: "document-text-outline",
783
+ onPress: put("page")
784
+ }, {
785
+ key: "database",
786
+ label: "Database",
787
+ icon: "server-outline",
788
+ onPress: put("database", NEW_DATABASE_PROPERTIES)
789
+ }], [{
790
+ key: "todo",
791
+ label: "To-do list",
792
+ icon: "checkbox-outline",
793
+ onPress: put("todo")
794
+ }, {
795
+ key: "toggle",
796
+ label: "Toggle list",
797
+ icon: "caret-forward-outline",
798
+ onPress: put("toggle")
799
+ }, {
800
+ key: "callout",
801
+ label: "Callout",
802
+ icon: "bulb-outline",
803
+ onPress: put("callout", {
804
+ icon: {
805
+ emoji: "\u{1F4A1}"
806
+ }
807
+ })
808
+ }, {
809
+ key: "quote",
810
+ label: "Quote",
811
+ icon: "chatbox-ellipses-outline",
812
+ onPress: put("quote")
813
+ }, {
814
+ key: "code",
815
+ label: "Code",
816
+ icon: "code-slash-outline",
817
+ onPress: put("code")
818
+ }]];
819
+ };
820
+ const insertMention = (run) => {
821
+ var _a2;
822
+ const target = mentionFor;
823
+ const block = target ? (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[target.id] : null;
824
+ if (!target || !block) return;
825
+ const runs = replaceRange(block.runs, target.at, target.at + 1, [run, {
826
+ content: " "
340
827
  }]);
828
+ commitRuns(target.id, block.runs, runs);
829
+ setPendingFocus(target.id);
341
830
  };
342
- const onChangeText = (id, text) => {
831
+ const mentionedPeople = () => {
832
+ var _a2, _b2;
833
+ const out = /* @__PURE__ */ new Set();
834
+ Object.values((_b2 = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks) != null ? _b2 : {}).forEach((b) => b.runs.forEach((r) => {
835
+ var _a3;
836
+ if (((_a3 = r.mention) == null ? void 0 : _a3.type) === "user" && r.mention.name) out.add(r.mention.name);
837
+ }));
838
+ return [...out];
839
+ };
840
+ const rangeOf = (id, block) => {
841
+ const sel = selections.current.get(id);
842
+ const ranged = !!sel && sel.end > sel.start;
843
+ return ranged ? {
844
+ start: sel.start,
845
+ end: sel.end,
846
+ ranged
847
+ } : {
848
+ start: 0,
849
+ end: block.text.length,
850
+ ranged
851
+ };
852
+ };
853
+ const toggleMark = (key) => {
854
+ var _a2;
855
+ const id = focusedId;
856
+ const block = id ? (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id] : null;
857
+ if (!id || !block || !block.text) return;
858
+ const r = rangeOf(id, block);
859
+ const on = !rangeHasFormat(block.runs, r.start, r.end, key);
860
+ const runs = r.ranged ? applyFormatRange(block.runs, r.start, r.end, {
861
+ [key]: on
862
+ }) : applyFormatToAll(block.runs, {
863
+ [key]: on
864
+ });
865
+ commitRuns(id, block.runs, runs);
866
+ };
867
+ const pickColor = (choice) => {
868
+ var _a2;
869
+ const target = colorFor;
870
+ const block = target ? (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[target.id] : null;
871
+ if (!target || !block) return;
872
+ if (target.mode === "inline") {
873
+ const r = rangeOf(target.id, block);
874
+ const update = choice.kind === "text" ? {
875
+ color: choice.value
876
+ } : {
877
+ highlight: choice.value
878
+ };
879
+ const runs = r.ranged ? applyFormatRange(block.runs, r.start, r.end, update) : applyFormatToAll(block.runs, update);
880
+ commitRuns(target.id, block.runs, runs);
881
+ return;
882
+ }
883
+ const value = choice.value ? choice.kind === "background" ? `${choice.value}_background` : choice.value : null;
884
+ setBlockProps(target.id, {
885
+ color: value
886
+ });
887
+ };
888
+ const setBlockProps = (id, patch, opts = {}) => {
889
+ var _a2;
890
+ const block = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id];
891
+ if (!block) return;
892
+ const beforeProps = {};
893
+ Object.keys(patch).forEach((k) => {
894
+ var _a3;
895
+ beforeProps[k] = (_a3 = block.properties[k]) != null ? _a3 : null;
896
+ });
343
897
  setModel((m) => m ? patchBlock(m, id, {
344
- text
898
+ properties: __spreadValues(__spreadValues({}, block.properties), patch)
345
899
  }) : m);
346
- scheduleTitle(id, text);
900
+ updateProps(id, patch);
901
+ if (opts.record !== false) {
902
+ record({
903
+ undo: () => setBlockProps(id, beforeProps, {
904
+ record: false
905
+ }),
906
+ redo: () => setBlockProps(id, patch, {
907
+ record: false
908
+ })
909
+ });
910
+ }
911
+ };
912
+ const openLink = () => {
913
+ var _a2, _b2;
914
+ const id = focusedId;
915
+ const block = id ? (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id] : null;
916
+ if (!id || !block || !block.text) return;
917
+ const r = rangeOf(id, block);
918
+ setLinkFor({
919
+ id,
920
+ start: r.start,
921
+ end: r.end,
922
+ current: (_b2 = linkAt(block.runs, r.start, r.end)) != null ? _b2 : ""
923
+ });
924
+ };
925
+ const applyLink = (url) => {
926
+ var _a2;
927
+ const target = linkFor;
928
+ const block = target ? (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[target.id] : null;
929
+ if (!target || !block) return;
930
+ const href = url.trim() ? /^[a-z]+:/i.test(url.trim()) ? url.trim() : `https://${url.trim()}` : null;
931
+ const runs = target.end > target.start ? applyFormatRange(block.runs, target.start, target.end, {
932
+ href
933
+ }) : applyFormatToAll(block.runs, {
934
+ href
935
+ });
936
+ commitRuns(target.id, block.runs, runs);
937
+ };
938
+ const duplicateLine = (id) => {
939
+ var _a2;
940
+ const block = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id];
941
+ if (!block) return;
942
+ enqueue(id, async () => {
943
+ var _a3;
944
+ const res = await write({
945
+ mutation: NATIVE_NOTION_DUPLICATE_BLOCK,
946
+ variables: {
947
+ blockId: id,
948
+ includeChildren: true
949
+ }
950
+ });
951
+ const copy = (_a3 = res.data) == null ? void 0 : _a3.duplicateBlock;
952
+ if (!(copy == null ? void 0 : copy.id)) return;
953
+ setModel((m) => m ? insertAfter(m, toEditorBlock(copy, block.parentId), id) : m);
954
+ savedTitles.current.set(copy.id, blockTitle(copy));
955
+ let snap = null;
956
+ record({
957
+ undo: () => {
958
+ snap = removeLocal(copy.id);
959
+ },
960
+ redo: () => bringBack(snap)
961
+ });
962
+ });
963
+ };
964
+ const moveLine = (id, direction, opts = {}) => {
965
+ const m = modelRef.current;
966
+ if (!m) return;
967
+ const moved = moveAmongSiblings(m, id, direction);
968
+ if (!moved) return;
969
+ setModel(moved.model);
970
+ enqueue(moved.parentId, () => write({
971
+ mutation: NATIVE_NOTION_REORDER_CHILDREN,
972
+ variables: {
973
+ blockId: moved.parentId,
974
+ childIds: moved.order
975
+ }
976
+ }));
977
+ if (opts.record !== false) {
978
+ record({
979
+ undo: () => moveLine(id, direction === 1 ? -1 : 1, {
980
+ record: false
981
+ }),
982
+ redo: () => moveLine(id, direction, {
983
+ record: false
984
+ })
985
+ });
986
+ }
987
+ };
988
+ const blockMenuGroups = (id) => {
989
+ var _a2;
990
+ const block = (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[id];
991
+ if (!block) return [];
992
+ const colorable = isTextType(block.type) && block.type !== "code";
993
+ return [[...colorable ? [{
994
+ key: "color",
995
+ label: "Color\u2026",
996
+ icon: "color-palette-outline",
997
+ onPress: () => setColorFor({
998
+ mode: "block",
999
+ id
1000
+ })
1001
+ }] : [], ...block.type === "code" ? [{
1002
+ key: "language",
1003
+ label: "Language\u2026",
1004
+ icon: "code-slash-outline",
1005
+ detail: languageLabel(block.properties.language),
1006
+ onPress: () => setLanguageFor(id)
1007
+ }] : [], {
1008
+ key: "comment",
1009
+ label: "Comment",
1010
+ icon: "chatbubble-outline",
1011
+ onPress: () => setCommentsFor({
1012
+ blockId: id
1013
+ })
1014
+ }, {
1015
+ key: "duplicate",
1016
+ label: "Duplicate",
1017
+ icon: "copy-outline",
1018
+ onPress: () => duplicateLine(id)
1019
+ }, {
1020
+ key: "up",
1021
+ label: "Move up",
1022
+ icon: "arrow-up",
1023
+ onPress: () => moveLine(id, -1)
1024
+ }, {
1025
+ key: "down",
1026
+ label: "Move down",
1027
+ icon: "arrow-down",
1028
+ onPress: () => moveLine(id, 1)
1029
+ }, {
1030
+ key: "link",
1031
+ label: "Copy link to block",
1032
+ icon: "link-outline",
1033
+ onPress: () => {
1034
+ if (!blockId) return;
1035
+ Clipboard.setString(`${pageLinkUrl(webOrigin(), blockId)}?block=${encodeURIComponent(id)}`);
1036
+ pageActions.showToast({
1037
+ message: "Link copied"
1038
+ });
1039
+ }
1040
+ }], [{
1041
+ key: "delete",
1042
+ label: "Delete",
1043
+ icon: "trash-outline",
1044
+ destructive: true,
1045
+ onPress: () => deleteLine(id)
1046
+ }]];
347
1047
  };
1048
+ const [collapsed, setCollapsed] = useState(/* @__PURE__ */ new Set());
1049
+ const toggleCollapsed = (id) => setCollapsed((cur) => {
1050
+ const next = new Set(cur);
1051
+ if (next.has(id)) next.delete(id);
1052
+ else next.add(id);
1053
+ return next;
1054
+ });
348
1055
  const onSubmit = (line) => {
1056
+ var _a2;
349
1057
  const {
350
1058
  block
351
1059
  } = line;
1060
+ const m = modelRef.current;
1061
+ if (block.type === "toggle" && m && !collapsed.has(block.id) && !((_a2 = m.children[block.id]) != null ? _a2 : []).length && block.text) {
1062
+ addBlock(block.id, null, "paragraph");
1063
+ return;
1064
+ }
352
1065
  if (!block.text && block.type !== "paragraph" && nextTypeAfter(block.type) === block.type) {
353
1066
  turnInto(block.id, "paragraph");
354
1067
  return;
@@ -363,17 +1076,9 @@ function NotionPageView({
363
1076
  if (block.type !== "paragraph" && block.type !== "text") turnInto(block.id, "paragraph");
364
1077
  else deleteLine(block.id);
365
1078
  };
366
- const toggleTodo = (block) => {
367
- const checked = block.properties.checked !== true;
368
- setModel((m) => m ? patchBlock(m, block.id, {
369
- properties: __spreadProps(__spreadValues({}, block.properties), {
370
- checked
371
- })
372
- }) : m);
373
- updateProps(block.id, {
374
- checked
375
- });
376
- };
1079
+ const toggleTodo = (block) => setBlockProps(block.id, {
1080
+ checked: block.properties.checked !== true
1081
+ });
377
1082
  const addSubPage = () => {
378
1083
  var _a2, _b2, _c2;
379
1084
  const m = modelRef.current;
@@ -392,8 +1097,13 @@ function NotionPageView({
392
1097
  if (last && isTextType(last.type) && !last.text) setPendingFocus(last.id);
393
1098
  else addBlock(m.rootId, null, "paragraph");
394
1099
  };
395
- const lines = useMemo(() => model ? flattenModel(model) : [], [model]);
396
- const focused = focusedId && model ? (_d = model.blocks[focusedId]) != null ? _d : null : null;
1100
+ const lines = useMemo(() => model ? flattenModel(model, collapsed) : [], [model, collapsed]);
1101
+ const headings = useMemo(() => lines.filter((l) => l.block.type.startsWith("heading_") && l.block.text.trim()).map((l) => ({
1102
+ id: l.block.id,
1103
+ text: l.block.text.trim(),
1104
+ level: Number(l.block.type.slice(-1))
1105
+ })), [lines]);
1106
+ const focused = focusedId && model ? (_f = model.blocks[focusedId]) != null ? _f : null : null;
397
1107
  if (!model || !tree || !page) {
398
1108
  return /* @__PURE__ */ jsx(View, { style: [styles.center, {
399
1109
  backgroundColor: palette.card
@@ -428,7 +1138,7 @@ function NotionPageView({
428
1138
  }
429
1139
  } }), children: [
430
1140
  /* @__PURE__ */ jsxs(View, { style: styles.pageActions, children: [
431
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: icon ? "Change icon" : "Add icon", onPress: () => setIconPicker((v) => !v), style: ({
1141
+ /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: icon ? "Change icon" : "Add icon", onPress: () => !settings.locked && setIconPicker((v) => !v), style: ({
432
1142
  pressed
433
1143
  }) => [styles.pageAction, pressed && {
434
1144
  backgroundColor: palette.pressed
@@ -439,12 +1149,55 @@ function NotionPageView({
439
1149
  }], children: "Add icon" })
440
1150
  ] }) }),
441
1151
  /* @__PURE__ */ jsx(View, { style: styles.flex }),
442
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Move page to Trash", onPress: trashPage, hitSlop: 8, style: ({
1152
+ /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Share", onPress: () => {
1153
+ Keyboard.dismiss();
1154
+ setShareOpen(true);
1155
+ }, hitSlop: 8, style: ({
1156
+ pressed
1157
+ }) => [styles.pageAction, pressed && {
1158
+ backgroundColor: palette.pressed
1159
+ }], children: /* @__PURE__ */ jsx(Text, { style: [styles.shareLabel, {
1160
+ color: palette.text
1161
+ }], children: "Share" }) }),
1162
+ /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: openComments ? `Comments, ${openComments} open` : "Comments", onPress: () => {
1163
+ Keyboard.dismiss();
1164
+ setCommentsFor({
1165
+ blockId: null
1166
+ });
1167
+ }, hitSlop: 8, style: ({
1168
+ pressed
1169
+ }) => [styles.pageAction, pressed && {
1170
+ backgroundColor: palette.pressed
1171
+ }], children: [
1172
+ /* @__PURE__ */ jsx(Ionicons, { name: "chatbubble-outline", size: 18, color: palette.textMuted }),
1173
+ openComments ? /* @__PURE__ */ jsx(Text, { style: [styles.commentCount, {
1174
+ color: palette.textMuted
1175
+ }], children: openComments }) : null
1176
+ ] }),
1177
+ blockId ? /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: notion.favoriteIds.includes(blockId) ? "Remove from Favorites" : "Add to Favorites", onPress: () => notion.toggleFavoritePage(blockId).catch(() => pageActions.showToast({
1178
+ message: "Couldn't update Favorites"
1179
+ })), hitSlop: 8, style: ({
1180
+ pressed
1181
+ }) => [styles.pageAction, pressed && {
1182
+ backgroundColor: palette.pressed
1183
+ }], children: /* @__PURE__ */ jsx(Ionicons, { name: notion.favoriteIds.includes(blockId) ? "star" : "star-outline", size: 19, color: notion.favoriteIds.includes(blockId) ? "#eab308" : palette.textMuted }) }) : null,
1184
+ /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Page actions", onPress: openPageMenu, hitSlop: 8, style: ({
443
1185
  pressed
444
1186
  }) => [styles.pageAction, pressed && {
445
1187
  backgroundColor: palette.pressed
446
- }], children: /* @__PURE__ */ jsx(Ionicons, { name: "trash-outline", size: 18, color: palette.textMuted }) })
1188
+ }], children: /* @__PURE__ */ jsx(Ionicons, { name: "ellipsis-horizontal", size: 20, color: palette.textMuted }) })
447
1189
  ] }),
1190
+ settings.locked ? /* @__PURE__ */ jsxs(View, { style: [styles.lockBanner, {
1191
+ backgroundColor: palette.fill
1192
+ }], children: [
1193
+ /* @__PURE__ */ jsx(Ionicons, { name: "lock-closed", size: 15, color: palette.textMuted }),
1194
+ /* @__PURE__ */ jsx(Text, { style: [styles.lockText, {
1195
+ color: palette.textMuted
1196
+ }], children: "This page is locked" }),
1197
+ /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", onPress: () => patchPage(pageSettingsPatch(pageProps, "locked", false)), hitSlop: 8, children: /* @__PURE__ */ jsx(Text, { style: [styles.lockUnlock, {
1198
+ color: palette.primary
1199
+ }], children: "Unlock" }) })
1200
+ ] }) : null,
448
1201
  iconPicker ? /* @__PURE__ */ jsxs(View, { style: [styles.emojiGrid, {
449
1202
  backgroundColor: palette.fill
450
1203
  }], children: [
@@ -458,37 +1211,178 @@ function NotionPageView({
458
1211
  const first = lines.find((l) => isTextType(l.block.type));
459
1212
  if (first) setPendingFocus(first.block.id);
460
1213
  else addBlock(model.rootId, null, "paragraph");
461
- }, onFocus: () => setFocusedId(null), style: [styles.title, {
462
- color: palette.text
1214
+ }, onFocus: () => setFocusedId(null), editable: !settings.locked, style: [scaled(styles.title, look.small), {
1215
+ color: palette.text,
1216
+ fontFamily: look.fontFamily
463
1217
  }] }),
464
1218
  /* @__PURE__ */ jsx(Text, { style: [styles.meta, {
465
1219
  color: save === "error" ? palette.danger : palette.textMuted
466
- }], children: saveLabelOf(save, page.lastEditedTime) }),
467
- /* @__PURE__ */ jsx(View, { style: styles.body, children: lines.map((line) => /* @__PURE__ */ jsx(BlockLine, { line, palette, focused: line.block.id === focusedId, registerInput: (input) => inputs.current.set(line.block.id, input), onFocus: () => setFocusedId(line.block.id), onChangeText: (t) => onChangeText(line.block.id, t), onSubmit: () => onSubmit(line), onBackspace: () => onBackspace(line), onToggleTodo: () => toggleTodo(line.block), onOpenPage: openPage, onDelete: () => deleteLine(line.block.id) }, line.block.id)) }),
468
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Add a block", onPress: appendAtEnd, style: styles.appendArea, children: lines.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles.startHint, {
1220
+ }], children: save === "error" && saveError ? `Not saved \u2014 ${saveError}` : saveLabelOf(save, page.lastEditedTime) }),
1221
+ save === "error" ? /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", onPress: retryFailed, hitSlop: 8, style: styles.retryRow, children: [
1222
+ /* @__PURE__ */ jsx(Ionicons, { name: "refresh", size: 14, color: palette.primary }),
1223
+ /* @__PURE__ */ jsx(Text, { style: [styles.retryLabel, {
1224
+ color: palette.primary
1225
+ }], children: "Retry" })
1226
+ ] }) : null,
1227
+ page.type === "database_row" && ((_g = page.parent) == null ? void 0 : _g.type) === "database" && page.parent.id ? /* @__PURE__ */ jsx(RowPropertiesPanel, { rowId: page.id, databaseId: page.parent.id, properties: pageProps, meta: {
1228
+ createdTime: page.createdTime,
1229
+ lastEditedTime: page.lastEditedTime
1230
+ }, isDark, palette, onError: (message) => pageActions.showToast({
1231
+ message: `Couldn't save. ${message}`
1232
+ }) }) : null,
1233
+ backlinks.length ? /* @__PURE__ */ jsxs(View, { style: styles.backlinks, children: [
1234
+ /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityState: {
1235
+ expanded: showBacklinks
1236
+ }, onPress: () => setShowBacklinks((v) => !v), hitSlop: 6, style: styles.backlinksToggle, children: [
1237
+ /* @__PURE__ */ jsx(Ionicons, { name: "link-outline", size: 14, color: palette.textMuted }),
1238
+ /* @__PURE__ */ jsx(Text, { style: [styles.backlinksLabel, {
1239
+ color: palette.textMuted
1240
+ }], children: `${backlinks.length} ${backlinks.length === 1 ? "backlink" : "backlinks"}` }),
1241
+ /* @__PURE__ */ jsx(Ionicons, { name: showBacklinks ? "chevron-up" : "chevron-down", size: 13, color: palette.textMuted })
1242
+ ] }),
1243
+ showBacklinks ? backlinks.map((b) => {
1244
+ var _a2;
1245
+ return /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "link", onPress: () => openPage(b.rootPageId || b.id), style: styles.backlinkRow, children: [
1246
+ /* @__PURE__ */ jsx(Text, { style: styles.inlineEmoji, children: (_a2 = blockIcon(b)) != null ? _a2 : "\u{1F4C4}" }),
1247
+ /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: [styles.text, styles.link, {
1248
+ color: palette.text,
1249
+ borderBottomColor: palette.hairline
1250
+ }], children: pageTitle(b) })
1251
+ ] }, b.id);
1252
+ }) : null
1253
+ ] }) : null,
1254
+ /* @__PURE__ */ jsx(View, { style: styles.body, children: lines.map((line) => {
1255
+ var _a2;
1256
+ return /* @__PURE__ */ jsx(BlockLine, { line, palette, focused: line.block.id === focusedId, registerInput: (input) => inputs.current.set(line.block.id, input), onFocus: () => setFocusedId(line.block.id), onChangeText: (t) => onChangeText(line.block.id, t), onSubmit: () => onSubmit(line), onBackspace: () => onBackspace(line), onToggleTodo: () => toggleTodo(line.block), onOpenPage: openPage, onDelete: () => deleteLine(line.block.id), onSelection: (sel) => selections.current.set(line.block.id, sel), onMenu: () => {
1257
+ Keyboard.dismiss();
1258
+ setBlockMenuFor(line.block.id);
1259
+ }, collapsed: collapsed.has(line.block.id), onToggleCollapsed: () => toggleCollapsed(line.block.id), onLanguage: () => setLanguageFor(line.block.id), onPatch: (patch) => setBlockProps(line.block.id, patch), onCaption: () => setCaptionFor(line.block.id), headings, onJump: (id) => setPendingFocus(id), commentCount: (_a2 = comments.openCounts.get(line.block.id)) != null ? _a2 : 0, onComments: () => setCommentsFor({
1260
+ blockId: line.block.id
1261
+ }), isDark, look }, line.block.id);
1262
+ }) }),
1263
+ /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Add a block", onPress: settings.locked ? void 0 : appendAtEnd, disabled: settings.locked, style: styles.appendArea, children: lines.length === 0 && !settings.locked ? /* @__PURE__ */ jsx(Text, { style: [styles.startHint, {
469
1264
  color: palette.textFaint
470
1265
  }], children: "Tap here to start writing\u2026" }) : null })
471
1266
  ] }),
472
- focused ? /* @__PURE__ */ jsx(EditorToolbar, { palette, block: focused, onTurnInto: (type) => turnInto(focused.id, type), onDivider: () => {
1267
+ focused && !settings.locked ? /* @__PURE__ */ jsx(EditorToolbar, { palette, bottomInset: keyboardShown ? 0 : insets.bottom, block: focused, onTurnInto: (type) => turnInto(focused.id, type), onDivider: () => {
473
1268
  addBlock(focused.parentId, focused.id, "divider");
474
- }, onSubPage: addSubPage, onDelete: () => deleteLine(focused.id), onDone: () => {
1269
+ }, onSubPage: addSubPage, onDelete: () => deleteLine(focused.id), onMark: toggleMark, onColor: () => setColorFor({
1270
+ mode: "inline",
1271
+ id: focused.id
1272
+ }), onLink: openLink, onMenu: () => setBlockMenuFor(focused.id), onInsert: () => {
1273
+ Keyboard.dismiss();
1274
+ setInsertFor({
1275
+ mode: "after",
1276
+ id: focused.id
1277
+ });
1278
+ }, canUndo: undoStack.current.length > 0, canRedo: redoStack.current.length > 0, onUndo: undo, onRedo: redo, onDone: () => {
475
1279
  Keyboard.dismiss();
476
1280
  setFocusedId(null);
477
- } }) : null
1281
+ } }) : null,
1282
+ pageActions.element,
1283
+ /* @__PURE__ */ jsx(PageActionsSheet, { visible: Boolean(blockMenuFor), palette, title: blockMenuFor ? blockMenuTitle(model.blocks[blockMenuFor]) : "", groups: blockMenuFor ? blockMenuGroups(blockMenuFor) : [], onClose: () => setBlockMenuFor(null) }),
1284
+ blockId ? /* @__PURE__ */ jsx(HistorySheet, { visible: historyOpen, palette, pageId: blockId, createdTime: page.createdTime, locked: settings.locked, onRestored: async () => {
1285
+ var _a2, _b2;
1286
+ const [res, pageRes] = await Promise.all([refetch(), pageQuery.refetch()]);
1287
+ const next = (_a2 = res.data) == null ? void 0 : _a2.getBlockTree;
1288
+ const nextPage = (_b2 = pageRes.data) == null ? void 0 : _b2.getBlock;
1289
+ if (next && nextPage && !pending.current.size && !inFlight.current) applyTree(next, nextPage);
1290
+ }, onClose: () => setHistoryOpen(false) }) : null,
1291
+ blockId ? /* @__PURE__ */ jsx(ShareSheet, { visible: shareOpen, palette, pageId: blockId, pageTitle: title.trim() || "Untitled", onClose: () => setShareOpen(false) }) : null,
1292
+ blockId ? /* @__PURE__ */ jsx(CommentsSheet, { visible: Boolean(commentsFor), palette, pageId: blockId, blockId: (_h = commentsFor == null ? void 0 : commentsFor.blockId) != null ? _h : null, blockLabel: (commentsFor == null ? void 0 : commentsFor.blockId) ? (_i = model.blocks[commentsFor.blockId]) == null ? void 0 : _i.text.slice(0, 120) : void 0, onClose: () => {
1293
+ setCommentsFor(null);
1294
+ comments.refetch().catch(() => void 0);
1295
+ } }) : null,
1296
+ /* @__PURE__ */ jsx(PageActionsSheet, { visible: Boolean(insertFor), palette, title: "Insert", subtitle: "Text styles are on the toolbar's second row", groups: insertFor ? insertGroups(insertFor) : [], onClose: () => setInsertFor(null) }),
1297
+ /* @__PURE__ */ jsx(MentionSheet, { visible: Boolean(mentionFor), palette, pages: notion.pages, workspaceId: (_k = (_j = notion.workspace) == null ? void 0 : _j.id) != null ? _k : null, people: mentionFor ? mentionedPeople() : [], onPick: insertMention, onClose: () => setMentionFor(null) }),
1298
+ /* @__PURE__ */ jsx(TextPromptDialog, { visible: Boolean(captionFor), palette, title: "Caption", initial: captionFor ? (_m = (_l = model.blocks[captionFor]) == null ? void 0 : _l.text) != null ? _m : "" : "", placeholder: "Write a caption", confirmLabel: "Save", allowEmpty: true, onClose: () => setCaptionFor(null), onConfirm: (caption) => {
1299
+ var _a2;
1300
+ const block = captionFor ? (_a2 = modelRef.current) == null ? void 0 : _a2.blocks[captionFor] : null;
1301
+ if (block) commitRuns(block.id, block.runs, caption.trim() ? [{
1302
+ content: caption.trim()
1303
+ }] : []);
1304
+ } }),
1305
+ /* @__PURE__ */ jsx(ColorSheet, { visible: Boolean(colorFor), palette, isDark, title: (colorFor == null ? void 0 : colorFor.mode) === "inline" ? "Text color" : "Block color", currentText: colorCurrent(colorFor, model).text, currentBackground: colorCurrent(colorFor, model).background, onPick: pickColor, onClose: () => setColorFor(null) }),
1306
+ /* @__PURE__ */ jsx(LanguageSheet, { visible: Boolean(languageFor), palette, current: languageFor ? (_n = model.blocks[languageFor]) == null ? void 0 : _n.properties.language : void 0, onPick: (language) => languageFor && setBlockProps(languageFor, {
1307
+ language
1308
+ }), onClose: () => setLanguageFor(null) }),
1309
+ /* @__PURE__ */ jsx(TextPromptDialog, { visible: Boolean(linkFor), palette, title: (linkFor == null ? void 0 : linkFor.current) ? "Edit link" : "Add link", message: (linkFor == null ? void 0 : linkFor.current) ? "Leave empty to remove the link." : void 0, initial: (_o = linkFor == null ? void 0 : linkFor.current) != null ? _o : "", placeholder: "https://", confirmLabel: "Save", allowEmpty: true, onClose: () => setLinkFor(null), onConfirm: applyLink }),
1310
+ /* @__PURE__ */ jsx(TextPromptDialog, { visible: importOpen, palette, title: "Import Markdown", message: "Paste Markdown. Headings, lists, to-dos, quotes and code become blocks at the end of this page.", placeholder: "# Heading\n- item\n- [ ] to-do", confirmLabel: "Import", multiline: true, onClose: () => setImportOpen(false), onConfirm: importMarkdown })
478
1311
  ] });
479
1312
  }
1313
+ function lastTopId(model) {
1314
+ var _a;
1315
+ const top = (_a = model.children[model.rootId]) != null ? _a : [];
1316
+ return top.length ? top[top.length - 1] : null;
1317
+ }
1318
+ function blockMenuTitle(block) {
1319
+ var _a, _b;
1320
+ if (!block) return "";
1321
+ const label = (_b = (_a = TURN_INTO.find((t) => t.type === block.type)) == null ? void 0 : _a.label) != null ? _b : block.type.replace(/_/g, " ");
1322
+ return block.text.trim() ? `${label} \xB7 ${block.text.trim().slice(0, 40)}` : label;
1323
+ }
1324
+ function colorCurrent(target, model) {
1325
+ var _a, _b;
1326
+ const block = target ? model.blocks[target.id] : null;
1327
+ if (!target || !block) return {};
1328
+ if (target.mode === "block") return blockColorParts(block.properties.color);
1329
+ const first = (_b = (_a = block.runs[0]) == null ? void 0 : _a.annotations) != null ? _b : {};
1330
+ return {
1331
+ text: first.color || "",
1332
+ background: first.highlight || ""
1333
+ };
1334
+ }
1335
+ function runStyle(run, palette, isDark, mono) {
1336
+ var _a;
1337
+ const a = (_a = run.annotations) != null ? _a : {};
1338
+ const lines = [a.underline || run.href ? "underline" : "", a.strikethrough ? "line-through" : ""].filter(Boolean).join(" ");
1339
+ return __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, a.bold ? {
1340
+ fontWeight: "700"
1341
+ } : {}), a.italic ? {
1342
+ fontStyle: "italic"
1343
+ } : {}), lines ? {
1344
+ textDecorationLine: lines
1345
+ } : {}), a.code ? {
1346
+ fontFamily: mono,
1347
+ backgroundColor: palette.fill,
1348
+ color: palette.danger
1349
+ } : {}), textColorOf(a.color, isDark) ? {
1350
+ color: textColorOf(a.color, isDark)
1351
+ } : {}), washColorOf(a.highlight, isDark) ? {
1352
+ backgroundColor: washColorOf(a.highlight, isDark)
1353
+ } : {}), run.href ? {
1354
+ color: palette.primary
1355
+ } : {}), run.mention ? {
1356
+ color: palette.primary,
1357
+ fontWeight: "600"
1358
+ } : {});
1359
+ }
1360
+ const MONO = Platform.OS === "ios" ? "Menlo" : "monospace";
480
1361
  function BlockLine({
481
1362
  line,
482
1363
  palette,
1364
+ isDark,
483
1365
  focused,
1366
+ collapsed,
484
1367
  registerInput,
485
1368
  onFocus,
486
1369
  onChangeText,
487
1370
  onSubmit,
488
1371
  onBackspace,
489
1372
  onToggleTodo,
1373
+ onToggleCollapsed,
490
1374
  onOpenPage,
491
- onDelete
1375
+ onDelete,
1376
+ onSelection,
1377
+ onMenu,
1378
+ onLanguage,
1379
+ onPatch,
1380
+ onCaption,
1381
+ headings,
1382
+ onJump,
1383
+ commentCount,
1384
+ onComments,
1385
+ look
492
1386
  }) {
493
1387
  var _a, _b, _c;
494
1388
  const {
@@ -499,17 +1393,10 @@ function BlockLine({
499
1393
  const indent = {
500
1394
  marginLeft: depth * 22
501
1395
  };
502
- const confirmDelete = () => Alert.alert("Delete block?", void 0, [{
503
- text: "Cancel",
504
- style: "cancel"
505
- }, {
506
- text: "Delete",
507
- style: "destructive",
508
- onPress: onDelete
509
- }]);
1396
+ const menu = look.locked ? void 0 : onMenu;
510
1397
  if (isPageBlock(block)) {
511
1398
  const emoji = blockIcon(block);
512
- return /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "link", onPress: () => onOpenPage(block.id), onLongPress: confirmDelete, style: ({
1399
+ return /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "link", onPress: () => onOpenPage(block.id), onLongPress: menu, style: ({
513
1400
  pressed
514
1401
  }) => [styles.row, styles.linkRow, indent, pressed && {
515
1402
  backgroundColor: palette.pressed
@@ -524,13 +1411,26 @@ function BlockLine({
524
1411
  ] });
525
1412
  }
526
1413
  if (block.type === "divider") {
527
- return /* @__PURE__ */ jsx(Pressable, { onLongPress: confirmDelete, style: [styles.dividerHit, indent], children: /* @__PURE__ */ jsx(View, { style: [styles.divider, {
1414
+ return /* @__PURE__ */ jsx(Pressable, { onLongPress: menu, style: [styles.dividerHit, indent], children: /* @__PURE__ */ jsx(View, { style: [styles.divider, {
528
1415
  backgroundColor: palette.hairline
529
1416
  }] }) });
530
1417
  }
1418
+ if (MEDIA_TYPES.includes(block.type)) {
1419
+ return /* @__PURE__ */ jsx(View, { style: [indent, styles.mediaRow], children: /* @__PURE__ */ jsx(MediaBlock, { block, palette, locked: look.locked, onPatch, onCaption, onMenu: menu }) });
1420
+ }
1421
+ if (block.type === "table_of_contents") {
1422
+ return /* @__PURE__ */ jsx(Pressable, { onLongPress: menu, style: [indent, styles.toc, {
1423
+ borderLeftColor: palette.hairline
1424
+ }], children: headings.length ? headings.map((h) => /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "link", onPress: () => onJump(h.id), hitSlop: 4, children: /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: [styles.tocItem, {
1425
+ color: palette.textMuted,
1426
+ marginLeft: (h.level - 1) * 14
1427
+ }], children: h.text }) }, h.id)) : /* @__PURE__ */ jsx(Text, { style: [styles.tocItem, {
1428
+ color: palette.textFaint
1429
+ }], children: "Add headings to build a table of contents" }) });
1430
+ }
531
1431
  if (!isTextType(block.type)) {
532
1432
  const caption = block.text || richTextToPlain(block.properties.caption) || block.type.replace(/_/g, " ");
533
- return /* @__PURE__ */ jsxs(Pressable, { onLongPress: confirmDelete, style: [styles.row, styles.unsupported, {
1433
+ return /* @__PURE__ */ jsxs(Pressable, { onLongPress: menu != null ? menu : look.locked ? void 0 : onDelete, style: [styles.row, styles.unsupported, {
534
1434
  borderColor: palette.hairline
535
1435
  }, indent], children: [
536
1436
  /* @__PURE__ */ jsx(Ionicons, { name: "cube-outline", size: 16, color: palette.textMuted }),
@@ -543,6 +1443,9 @@ function BlockLine({
543
1443
  ] });
544
1444
  }
545
1445
  const checked = block.properties.checked === true;
1446
+ const colors = blockColorParts(block.properties.color);
1447
+ const blockText = textColorOf(colors.text, isDark);
1448
+ const blockWash = washColorOf(colors.background, isDark);
546
1449
  let gutter = null;
547
1450
  if (block.type === "bulleted_list") {
548
1451
  gutter = /* @__PURE__ */ jsx(Text, { style: [styles.text, styles.marker, {
@@ -555,66 +1458,150 @@ function BlockLine({
555
1458
  } else if (block.type === "todo") {
556
1459
  gutter = /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "checkbox", accessibilityState: {
557
1460
  checked
558
- }, onPress: onToggleTodo, hitSlop: 8, style: styles.check, children: /* @__PURE__ */ jsx(Ionicons, { name: checked ? "checkbox" : "square-outline", size: 21, color: checked ? palette.primary : palette.textMuted }) });
1461
+ }, onPress: look.locked ? void 0 : onToggleTodo, hitSlop: 8, style: styles.check, children: /* @__PURE__ */ jsx(Ionicons, { name: checked ? "checkbox" : "square-outline", size: 21, color: checked ? palette.primary : palette.textMuted }) });
559
1462
  } else if (block.type === "toggle") {
560
- gutter = /* @__PURE__ */ jsx(Ionicons, { name: "caret-down", size: 14, color: palette.textMuted, style: styles.caret });
1463
+ gutter = /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: collapsed ? "Expand toggle" : "Collapse toggle", accessibilityState: {
1464
+ expanded: !collapsed
1465
+ }, onPress: onToggleCollapsed, hitSlop: 10, style: styles.caretHit, children: /* @__PURE__ */ jsx(Ionicons, { name: collapsed ? "caret-forward" : "caret-down", size: 14, color: palette.textMuted }) });
561
1466
  } else if (block.type === "callout") {
562
1467
  gutter = /* @__PURE__ */ jsx(Text, { style: styles.inlineEmoji, children: (_a = blockIcon({
563
1468
  properties: block.properties
564
1469
  })) != null ? _a : "\u{1F4A1}" });
565
1470
  }
566
- return /* @__PURE__ */ jsxs(View, { style: [styles.row, indent, block.type === "quote" && [styles.quote, {
1471
+ const baseText = [styles.input, scaled((_b = TEXT_STYLE[block.type]) != null ? _b : styles.text, look.small), {
1472
+ color: blockText != null ? blockText : palette.text,
1473
+ fontFamily: look.fontFamily
1474
+ }, block.type === "todo" && checked && styles.done, block.type === "todo" && checked && {
1475
+ color: palette.textMuted
1476
+ }, block.type === "code" && styles.codeText];
1477
+ return /* @__PURE__ */ jsxs(Pressable, { onLongPress: menu, delayLongPress: 350, style: [styles.row, indent, block.type === "quote" && [styles.quote, {
567
1478
  borderLeftColor: palette.text
568
1479
  }], block.type === "callout" && [styles.callout, {
569
- backgroundColor: palette.fill
1480
+ backgroundColor: blockWash != null ? blockWash : palette.fill
570
1481
  }], block.type === "code" && [styles.code, {
571
1482
  backgroundColor: palette.fill
572
- }], block.type.startsWith("heading") && styles.headingRow], children: [
1483
+ }], block.type.startsWith("heading") && styles.headingRow, blockWash && block.type !== "callout" ? [styles.wash, {
1484
+ backgroundColor: blockWash
1485
+ }] : null], children: [
573
1486
  gutter,
574
- /* @__PURE__ */ jsx(TextInput, { ref: registerInput, value: block.text, onChangeText, onFocus, multiline: true, scrollEnabled: false, submitBehavior: block.type === "code" ? "newline" : "submit", onSubmitEditing: block.type === "code" ? void 0 : onSubmit, onKeyPress: (e) => {
575
- if (e.nativeEvent.key === "Backspace") onBackspace();
576
- }, autoCorrect: block.type !== "code", autoCapitalize: block.type === "code" ? "none" : "sentences", placeholder: focused ? (_b = PLACEHOLDER[block.type]) != null ? _b : "Type something\u2026" : PLACEHOLDER[block.type], placeholderTextColor: palette.textFaint, style: [styles.input, (_c = TEXT_STYLE[block.type]) != null ? _c : styles.text, {
577
- color: palette.text
578
- }, block.type === "todo" && checked && styles.done, block.type === "todo" && checked && {
579
- color: palette.textMuted
580
- }, block.type === "code" && styles.codeText], textAlignVertical: "top" })
1487
+ /* @__PURE__ */ jsxs(View, { style: styles.flex, children: [
1488
+ block.type === "code" ? /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: "Code language", onPress: look.locked ? void 0 : onLanguage, hitSlop: 6, style: [styles.langPill, {
1489
+ backgroundColor: palette.card
1490
+ }], children: [
1491
+ /* @__PURE__ */ jsx(Text, { style: [styles.langPillText, {
1492
+ color: palette.textMuted
1493
+ }], children: languageLabel(block.properties.language) }),
1494
+ !look.locked ? /* @__PURE__ */ jsx(Ionicons, { name: "chevron-down", size: 11, color: palette.textMuted }) : null
1495
+ ] }) : null,
1496
+ /* @__PURE__ */ jsx(TextInput, { ref: registerInput, onChangeText, onFocus, onSelectionChange: (e) => onSelection(e.nativeEvent.selection), editable: !look.locked, multiline: true, scrollEnabled: false, submitBehavior: block.type === "code" ? "newline" : "submit", onSubmitEditing: block.type === "code" ? void 0 : onSubmit, onKeyPress: (e) => {
1497
+ if (e.nativeEvent.key === "Backspace") onBackspace();
1498
+ }, autoCorrect: block.type !== "code", autoCapitalize: block.type === "code" ? "none" : "sentences", placeholder: focused ? (_c = PLACEHOLDER[block.type]) != null ? _c : "Type something\u2026" : PLACEHOLDER[block.type], placeholderTextColor: palette.textFaint, style: baseText, textAlignVertical: "top", children: block.runs.map((run, i) => (
1499
+ // eslint-disable-next-line react/no-array-index-key
1500
+ /* @__PURE__ */ jsx(Text, { style: runStyle(run, palette, isDark, MONO), children: run.content }, i)
1501
+ )) })
1502
+ ] }),
1503
+ commentCount ? /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: `${commentCount} comments`, onPress: onComments, hitSlop: 8, style: [styles.commentBubble, {
1504
+ backgroundColor: palette.fill
1505
+ }], children: [
1506
+ /* @__PURE__ */ jsx(Ionicons, { name: "chatbubble", size: 11, color: palette.textMuted }),
1507
+ /* @__PURE__ */ jsx(Text, { style: [styles.commentBubbleText, {
1508
+ color: palette.textMuted
1509
+ }], children: commentCount })
1510
+ ] }) : null
581
1511
  ] });
582
1512
  }
583
1513
  function EditorToolbar({
1514
+ bottomInset,
584
1515
  palette,
585
1516
  block,
586
1517
  onTurnInto,
587
1518
  onDivider,
588
1519
  onSubPage,
589
1520
  onDelete,
590
- onDone
1521
+ onDone,
1522
+ onMark,
1523
+ onColor,
1524
+ onLink,
1525
+ onMenu,
1526
+ onInsert,
1527
+ canUndo,
1528
+ canRedo,
1529
+ onUndo,
1530
+ onRedo
591
1531
  }) {
1532
+ const marks = [{
1533
+ key: "bold",
1534
+ label: "B",
1535
+ style: {
1536
+ fontWeight: "800"
1537
+ }
1538
+ }, {
1539
+ key: "italic",
1540
+ label: "I",
1541
+ style: {
1542
+ fontStyle: "italic",
1543
+ fontWeight: "600"
1544
+ }
1545
+ }, {
1546
+ key: "underline",
1547
+ label: "U",
1548
+ style: {
1549
+ textDecorationLine: "underline",
1550
+ fontWeight: "600"
1551
+ }
1552
+ }, {
1553
+ key: "strikethrough",
1554
+ label: "S",
1555
+ style: {
1556
+ textDecorationLine: "line-through",
1557
+ fontWeight: "600"
1558
+ }
1559
+ }, {
1560
+ key: "code",
1561
+ label: "</>",
1562
+ style: {
1563
+ fontFamily: MONO,
1564
+ fontSize: 13
1565
+ }
1566
+ }];
1567
+ const formattable = block.type !== "code";
1568
+ const tool = (key, label, onPress, child, disabled) => /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: label, onPress, disabled, style: [styles.tool, {
1569
+ backgroundColor: palette.fill
1570
+ }, disabled && styles.toolDisabled], children: child }, key);
592
1571
  return /* @__PURE__ */ jsxs(View, { style: [styles.toolbar, {
593
1572
  backgroundColor: palette.card,
594
- borderTopColor: palette.hairline
1573
+ borderTopColor: palette.hairline,
1574
+ paddingBottom: 6 + bottomInset
595
1575
  }], children: [
596
- /* @__PURE__ */ jsxs(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, keyboardShouldPersistTaps: "always", contentContainerStyle: styles.toolbarContent, children: [
597
- TURN_INTO.map((t) => {
598
- const active = t.type === block.type || t.type === "paragraph" && block.type === "text";
599
- return /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: `Turn into ${t.label}`, accessibilityState: {
600
- selected: active
601
- }, onPress: () => onTurnInto(t.type), style: [styles.tool, {
602
- backgroundColor: active ? palette.primarySoft : palette.fill
603
- }], children: /* @__PURE__ */ jsx(Text, { style: [styles.toolLabel, {
604
- color: active ? palette.primary : palette.text
605
- }], children: t.short }) }, t.type);
606
- }),
607
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Insert divider", onPress: onDivider, style: [styles.tool, {
608
- backgroundColor: palette.fill
609
- }], children: /* @__PURE__ */ jsx(Ionicons, { name: "remove-outline", size: 18, color: palette.text }) }),
610
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Insert sub-page", onPress: onSubPage, style: [styles.tool, {
611
- backgroundColor: palette.fill
612
- }], children: /* @__PURE__ */ jsx(Ionicons, { name: "document-text-outline", size: 17, color: palette.text }) }),
613
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Delete block", onPress: onDelete, style: [styles.tool, {
614
- backgroundColor: palette.fill
615
- }], children: /* @__PURE__ */ jsx(Ionicons, { name: "trash-outline", size: 17, color: palette.danger }) })
616
- ] }),
617
- /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Done editing", onPress: onDone, style: styles.toolDone, hitSlop: 6, children: /* @__PURE__ */ jsx(Ionicons, { name: "chevron-down", size: 22, color: palette.text }) })
1576
+ /* @__PURE__ */ jsx(View, { style: styles.toolbarRow, children: /* @__PURE__ */ jsxs(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, keyboardShouldPersistTaps: "always", contentContainerStyle: styles.toolbarContent, children: [
1577
+ tool("insert", "Insert block", onInsert, /* @__PURE__ */ jsx(Ionicons, { name: "add", size: 20, color: palette.primary })),
1578
+ tool("undo", "Undo", onUndo, /* @__PURE__ */ jsx(Ionicons, { name: "arrow-undo", size: 17, color: palette.text }), !canUndo),
1579
+ tool("redo", "Redo", onRedo, /* @__PURE__ */ jsx(Ionicons, { name: "arrow-redo", size: 17, color: palette.text }), !canRedo),
1580
+ formattable ? marks.map((m) => tool(m.key, m.key, () => onMark(m.key), /* @__PURE__ */ jsx(Text, { style: [styles.toolLabel, {
1581
+ color: palette.text
1582
+ }, m.style], children: m.label }))) : null,
1583
+ formattable ? tool("color", "Text color", onColor, /* @__PURE__ */ jsx(Ionicons, { name: "color-palette-outline", size: 17, color: palette.text })) : null,
1584
+ formattable ? tool("link", "Link", onLink, /* @__PURE__ */ jsx(Ionicons, { name: "link", size: 17, color: palette.text })) : null,
1585
+ tool("menu", "Block actions", onMenu, /* @__PURE__ */ jsx(Ionicons, { name: "ellipsis-horizontal", size: 17, color: palette.text }))
1586
+ ] }) }),
1587
+ /* @__PURE__ */ jsxs(View, { style: styles.toolbarRow, children: [
1588
+ /* @__PURE__ */ jsxs(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, keyboardShouldPersistTaps: "always", contentContainerStyle: styles.toolbarContent, children: [
1589
+ TURN_INTO.map((t) => {
1590
+ const active = t.type === block.type || t.type === "paragraph" && block.type === "text";
1591
+ return /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: `Turn into ${t.label}`, accessibilityState: {
1592
+ selected: active
1593
+ }, onPress: () => onTurnInto(t.type), style: [styles.tool, {
1594
+ backgroundColor: active ? palette.primarySoft : palette.fill
1595
+ }], children: /* @__PURE__ */ jsx(Text, { style: [styles.toolLabel, {
1596
+ color: active ? palette.primary : palette.text
1597
+ }], children: t.short }) }, t.type);
1598
+ }),
1599
+ tool("divider", "Insert divider", onDivider, /* @__PURE__ */ jsx(Ionicons, { name: "remove-outline", size: 18, color: palette.text })),
1600
+ tool("subpage", "Insert sub-page", onSubPage, /* @__PURE__ */ jsx(Ionicons, { name: "document-text-outline", size: 17, color: palette.text })),
1601
+ tool("delete", "Delete block", onDelete, /* @__PURE__ */ jsx(Ionicons, { name: "trash-outline", size: 17, color: palette.danger }))
1602
+ ] }),
1603
+ /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: "Done editing", onPress: onDone, style: styles.toolDone, hitSlop: 6, children: /* @__PURE__ */ jsx(Ionicons, { name: "chevron-down", size: 22, color: palette.text }) })
1604
+ ] })
618
1605
  ] });
619
1606
  }
620
1607
  const styles = StyleSheet.create({
@@ -781,6 +1768,56 @@ const styles = StyleSheet.create({
781
1768
  fontSize: 12,
782
1769
  fontWeight: "600"
783
1770
  },
1771
+ retryRow: {
1772
+ flexDirection: "row",
1773
+ alignItems: "center",
1774
+ gap: 4,
1775
+ alignSelf: "flex-start",
1776
+ marginTop: 4
1777
+ },
1778
+ retryLabel: {
1779
+ fontSize: 13,
1780
+ fontWeight: "700"
1781
+ },
1782
+ lockBanner: {
1783
+ flexDirection: "row",
1784
+ alignItems: "center",
1785
+ gap: 8,
1786
+ paddingHorizontal: 12,
1787
+ paddingVertical: 10,
1788
+ borderRadius: 12,
1789
+ marginBottom: 10
1790
+ },
1791
+ lockText: {
1792
+ flex: 1,
1793
+ fontSize: 14,
1794
+ fontWeight: "500"
1795
+ },
1796
+ lockUnlock: {
1797
+ fontSize: 14,
1798
+ fontWeight: "700"
1799
+ },
1800
+ backlinks: {
1801
+ marginTop: 8
1802
+ },
1803
+ backlinksToggle: {
1804
+ flexDirection: "row",
1805
+ alignItems: "center",
1806
+ gap: 5,
1807
+ alignSelf: "flex-start",
1808
+ paddingVertical: 4
1809
+ },
1810
+ backlinksLabel: {
1811
+ fontSize: 13,
1812
+ fontWeight: "600"
1813
+ },
1814
+ backlinkRow: {
1815
+ flexDirection: "row",
1816
+ alignItems: "center",
1817
+ gap: 8,
1818
+ paddingVertical: 4,
1819
+ paddingLeft: 4
1820
+ },
784
1821
  appendArea: {
785
1822
  minHeight: 160,
786
1823
  paddingTop: 8
@@ -788,6 +1825,70 @@ const styles = StyleSheet.create({
788
1825
  startHint: {
789
1826
  fontSize: 16
790
1827
  },
1828
+ commentCount: {
1829
+ fontSize: 13,
1830
+ fontWeight: "600"
1831
+ },
1832
+ shareLabel: {
1833
+ fontSize: 15,
1834
+ fontWeight: "600"
1835
+ },
1836
+ commentBubble: {
1837
+ flexDirection: "row",
1838
+ alignItems: "center",
1839
+ gap: 3,
1840
+ paddingHorizontal: 6,
1841
+ paddingVertical: 2,
1842
+ borderRadius: 999,
1843
+ marginTop: 4
1844
+ },
1845
+ commentBubbleText: {
1846
+ fontSize: 11,
1847
+ fontWeight: "700"
1848
+ },
1849
+ mediaRow: {
1850
+ marginVertical: 4
1851
+ },
1852
+ toc: {
1853
+ borderLeftWidth: 2,
1854
+ paddingLeft: 12,
1855
+ paddingVertical: 4,
1856
+ gap: 6
1857
+ },
1858
+ tocItem: {
1859
+ fontSize: 14,
1860
+ textDecorationLine: "underline"
1861
+ },
1862
+ toolbarRow: {
1863
+ flexDirection: "row",
1864
+ alignItems: "center"
1865
+ },
1866
+ toolDisabled: {
1867
+ opacity: 0.35
1868
+ },
1869
+ caretHit: {
1870
+ paddingTop: 8,
1871
+ width: 18,
1872
+ alignItems: "center"
1873
+ },
1874
+ wash: {
1875
+ paddingHorizontal: 6,
1876
+ borderRadius: 6
1877
+ },
1878
+ langPill: {
1879
+ flexDirection: "row",
1880
+ alignItems: "center",
1881
+ gap: 3,
1882
+ alignSelf: "flex-end",
1883
+ paddingHorizontal: 8,
1884
+ paddingVertical: 3,
1885
+ borderRadius: 999,
1886
+ marginBottom: 6
1887
+ },
1888
+ langPillText: {
1889
+ fontSize: 11,
1890
+ fontWeight: "700"
1891
+ },
791
1892
  toolbar: {
792
1893
  flexDirection: "row",
793
1894
  alignItems: "center",