@adminide-stack/yantra-mobile 12.0.141-alpha.1 → 12.0.142-alpha.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.
Files changed (227) 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/dictate/DictateExperience.js +59 -7
  12. package/lib/features/dictate/DictateExperience.js.map +1 -1
  13. package/lib/features/dictate/components/NoteView.js +227 -12
  14. package/lib/features/dictate/components/NoteView.js.map +1 -1
  15. package/lib/features/dictate/dictateClient.js +93 -23
  16. package/lib/features/dictate/dictateClient.js.map +1 -1
  17. package/lib/features/dictate/dictatePreference.js +19 -0
  18. package/lib/features/dictate/dictatePreference.js.map +1 -0
  19. package/lib/features/dictate/noteText.js +17 -2
  20. package/lib/features/dictate/noteText.js.map +1 -1
  21. package/lib/features/dictate/privacy.js +96 -0
  22. package/lib/features/dictate/privacy.js.map +1 -0
  23. package/lib/features/dictate/shownNote.js +24 -0
  24. package/lib/features/dictate/shownNote.js.map +1 -0
  25. package/lib/features/discover/DiscoverArticle.js +105 -92
  26. package/lib/features/discover/DiscoverArticle.js.map +1 -1
  27. package/lib/features/discover/DiscoverFeed.js +97 -48
  28. package/lib/features/discover/DiscoverFeed.js.map +1 -1
  29. package/lib/features/discover/DiscoverFollowUp.js +135 -0
  30. package/lib/features/discover/DiscoverFollowUp.js.map +1 -0
  31. package/lib/features/discover/DiscoverSidebar.js +555 -0
  32. package/lib/features/discover/DiscoverSidebar.js.map +1 -0
  33. package/lib/features/discover/DiscoverSkeletons.js +123 -0
  34. package/lib/features/discover/DiscoverSkeletons.js.map +1 -0
  35. package/lib/features/discover/DiscoverStoryCards.js +193 -87
  36. package/lib/features/discover/DiscoverStoryCards.js.map +1 -1
  37. package/lib/features/discover/followUpQuery.js +42 -0
  38. package/lib/features/discover/followUpQuery.js.map +1 -0
  39. package/lib/features/discover/markets.js +37 -0
  40. package/lib/features/discover/markets.js.map +1 -0
  41. package/lib/features/discover/useDiscoverSidebar.js +148 -0
  42. package/lib/features/discover/useDiscoverSidebar.js.map +1 -0
  43. package/lib/features/discover/weather.js +42 -0
  44. package/lib/features/discover/weather.js.map +1 -0
  45. package/lib/features/expenses/ExpensesAgentChatPane.js +1 -1
  46. package/lib/features/expenses/ExpensesAgentChatPane.js.map +1 -1
  47. package/lib/features/inbox/InboxAgentChatPane.js +1 -1
  48. package/lib/features/inbox/InboxAgentChatPane.js.map +1 -1
  49. package/lib/features/job-search/JobSearchAgentChatPane.js +1 -0
  50. package/lib/features/job-search/JobSearchAgentChatPane.js.map +1 -1
  51. package/lib/features/local-services/LocalServicesAgentChatPane.js +1 -1
  52. package/lib/features/local-services/LocalServicesAgentChatPane.js.map +1 -1
  53. package/lib/features/movies/MoviesAgentChatPane.js +1 -1
  54. package/lib/features/movies/MoviesAgentChatPane.js.map +1 -1
  55. package/lib/features/notion/NotionBlockSheets.js +206 -0
  56. package/lib/features/notion/NotionBlockSheets.js.map +1 -0
  57. package/lib/features/notion/NotionBlockView.js +36 -0
  58. package/lib/features/notion/NotionBlockView.js.map +1 -0
  59. package/lib/features/notion/NotionCommentsSheet.js +461 -0
  60. package/lib/features/notion/NotionCommentsSheet.js.map +1 -0
  61. package/lib/features/notion/NotionConnectionsSheet.js +244 -0
  62. package/lib/features/notion/NotionConnectionsSheet.js.map +1 -0
  63. package/lib/features/notion/NotionHistorySheet.js +439 -0
  64. package/lib/features/notion/NotionHistorySheet.js.map +1 -0
  65. package/lib/features/notion/NotionMediaBlocks.js +803 -0
  66. package/lib/features/notion/NotionMediaBlocks.js.map +1 -0
  67. package/lib/features/notion/NotionMentionSheet.js +279 -0
  68. package/lib/features/notion/NotionMentionSheet.js.map +1 -0
  69. package/lib/features/notion/NotionPageMenu.js +585 -0
  70. package/lib/features/notion/NotionPageMenu.js.map +1 -0
  71. package/lib/features/notion/NotionPageTree.js +220 -0
  72. package/lib/features/notion/NotionPageTree.js.map +1 -0
  73. package/lib/features/notion/NotionPageView.js +1253 -152
  74. package/lib/features/notion/NotionPageView.js.map +1 -1
  75. package/lib/features/notion/NotionShareSheet.js +606 -0
  76. package/lib/features/notion/NotionShareSheet.js.map +1 -0
  77. package/lib/features/notion/NotionSheets.js +87 -15
  78. package/lib/features/notion/NotionSheets.js.map +1 -1
  79. package/lib/features/notion/NotionSidebarSections.js +492 -0
  80. package/lib/features/notion/NotionSidebarSections.js.map +1 -0
  81. package/lib/features/notion/NotionWorkspaceAdmin.js +659 -0
  82. package/lib/features/notion/NotionWorkspaceAdmin.js.map +1 -0
  83. package/lib/features/notion/NotionWorkspaceView.js +257 -45
  84. package/lib/features/notion/NotionWorkspaceView.js.map +1 -1
  85. package/lib/features/notion/database/DatabaseCells.js +554 -0
  86. package/lib/features/notion/database/DatabaseCells.js.map +1 -0
  87. package/lib/features/notion/database/DatabaseDateLayouts.js +792 -0
  88. package/lib/features/notion/database/DatabaseDateLayouts.js.map +1 -0
  89. package/lib/features/notion/database/DatabaseLayouts.js +461 -0
  90. package/lib/features/notion/database/DatabaseLayouts.js.map +1 -0
  91. package/lib/features/notion/database/DatabaseSheets.js +791 -0
  92. package/lib/features/notion/database/DatabaseSheets.js.map +1 -0
  93. package/lib/features/notion/database/NotionDatabaseView.js +817 -0
  94. package/lib/features/notion/database/NotionDatabaseView.js.map +1 -0
  95. package/lib/features/notion/database/RowPropertiesPanel.js +162 -0
  96. package/lib/features/notion/database/RowPropertiesPanel.js.map +1 -0
  97. package/lib/features/notion/database/chartData.js +89 -0
  98. package/lib/features/notion/database/chartData.js.map +1 -0
  99. package/lib/features/notion/database/chartView.js +112 -0
  100. package/lib/features/notion/database/chartView.js.map +1 -0
  101. package/lib/features/notion/database/connectorButton.js +33 -0
  102. package/lib/features/notion/database/connectorButton.js.map +1 -0
  103. package/lib/features/notion/database/databaseCalc.js +208 -0
  104. package/lib/features/notion/database/databaseCalc.js.map +1 -0
  105. package/lib/features/notion/database/databaseCalendar.js +157 -0
  106. package/lib/features/notion/database/databaseCalendar.js.map +1 -0
  107. package/lib/features/notion/database/databaseCsv.js +98 -0
  108. package/lib/features/notion/database/databaseCsv.js.map +1 -0
  109. package/lib/features/notion/database/databaseCsvActions.js +58 -0
  110. package/lib/features/notion/database/databaseCsvActions.js.map +1 -0
  111. package/lib/features/notion/database/databaseFilter.js +473 -0
  112. package/lib/features/notion/database/databaseFilter.js.map +1 -0
  113. package/lib/features/notion/database/databaseFormula.js +1072 -0
  114. package/lib/features/notion/database/databaseFormula.js.map +1 -0
  115. package/lib/features/notion/database/databaseGroup.js +151 -0
  116. package/lib/features/notion/database/databaseGroup.js.map +1 -0
  117. package/lib/features/notion/database/databaseRollup.js +58 -0
  118. package/lib/features/notion/database/databaseRollup.js.map +1 -0
  119. package/lib/features/notion/database/databaseTimeline.js +156 -0
  120. package/lib/features/notion/database/databaseTimeline.js.map +1 -0
  121. package/lib/features/notion/database/databaseView.js +1188 -0
  122. package/lib/features/notion/database/databaseView.js.map +1 -0
  123. package/lib/features/notion/database/databaseViews.js +310 -0
  124. package/lib/features/notion/database/databaseViews.js.map +1 -0
  125. package/lib/features/notion/database/relationSource.js +92 -0
  126. package/lib/features/notion/database/relationSource.js.map +1 -0
  127. package/lib/features/notion/database/uniqueId.js +17 -0
  128. package/lib/features/notion/database/uniqueId.js.map +1 -0
  129. package/lib/features/notion/database/useNotionDatabase.js +617 -0
  130. package/lib/features/notion/database/useNotionDatabase.js.map +1 -0
  131. package/lib/features/notion/notionAgentRunStatus.js +39 -0
  132. package/lib/features/notion/notionAgentRunStatus.js.map +1 -0
  133. package/lib/features/notion/notionCodeLanguages.js +112 -0
  134. package/lib/features/notion/notionCodeLanguages.js.map +1 -0
  135. package/lib/features/notion/notionComments.js +86 -0
  136. package/lib/features/notion/notionComments.js.map +1 -0
  137. package/lib/features/notion/notionConnections.js +74 -0
  138. package/lib/features/notion/notionConnections.js.map +1 -0
  139. package/lib/features/notion/notionDocuments.js +453 -1
  140. package/lib/features/notion/notionDocuments.js.map +1 -1
  141. package/lib/features/notion/notionEditorModel.js +142 -7
  142. package/lib/features/notion/notionEditorModel.js.map +1 -1
  143. package/lib/features/notion/notionMarkdownShortcuts.js +51 -0
  144. package/lib/features/notion/notionMarkdownShortcuts.js.map +1 -0
  145. package/lib/features/notion/notionMedia.js +119 -0
  146. package/lib/features/notion/notionMedia.js.map +1 -0
  147. package/lib/features/notion/notionMentionDate.js +32 -0
  148. package/lib/features/notion/notionMentionDate.js.map +1 -0
  149. package/lib/features/notion/notionMentionOptions.js +162 -0
  150. package/lib/features/notion/notionMentionOptions.js.map +1 -0
  151. package/lib/features/notion/notionNotesAgent.js +445 -0
  152. package/lib/features/notion/notionNotesAgent.js.map +1 -0
  153. package/lib/features/notion/notionPageAccess.js +168 -0
  154. package/lib/features/notion/notionPageAccess.js.map +1 -0
  155. package/lib/features/notion/notionPageActions.js +138 -0
  156. package/lib/features/notion/notionPageActions.js.map +1 -0
  157. package/lib/features/notion/notionPageHistory.js +154 -0
  158. package/lib/features/notion/notionPageHistory.js.map +1 -0
  159. package/lib/features/notion/notionPageKinds.js +200 -0
  160. package/lib/features/notion/notionPageKinds.js.map +1 -0
  161. package/lib/features/notion/notionRecents.js +48 -0
  162. package/lib/features/notion/notionRecents.js.map +1 -0
  163. package/lib/features/notion/notionRichText.js +177 -0
  164. package/lib/features/notion/notionRichText.js.map +1 -0
  165. package/lib/features/notion/notionRichTitle.js +336 -0
  166. package/lib/features/notion/notionRichTitle.js.map +1 -0
  167. package/lib/features/notion/useNotionWorkspace.js +272 -13
  168. package/lib/features/notion/useNotionWorkspace.js.map +1 -1
  169. package/lib/features/notion/usePageActions.js +155 -0
  170. package/lib/features/notion/usePageActions.js.map +1 -0
  171. package/lib/features/resume/ResumeAgentChatPane.js +1 -1
  172. package/lib/features/resume/ResumeAgentChatPane.js.map +1 -1
  173. package/lib/features/shopping/ShoppingAgentChatPane.js +1 -1
  174. package/lib/features/shopping/ShoppingAgentChatPane.js.map +1 -1
  175. package/lib/features/study/StudyAgentChatPane.js +1 -1
  176. package/lib/features/study/StudyAgentChatPane.js.map +1 -1
  177. package/lib/features/travel/TravelAgentChatPane.js +1 -1
  178. package/lib/features/travel/TravelAgentChatPane.js.map +1 -1
  179. package/lib/features/webbuilder/WebBuilderExperience.js +40 -8
  180. package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
  181. package/lib/features/webbuilder/buildFragments.js +1 -1
  182. package/lib/features/webbuilder/buildFragments.js.map +1 -1
  183. package/lib/features/webbuilder/codeFiles.js +302 -0
  184. package/lib/features/webbuilder/codeFiles.js.map +1 -0
  185. package/lib/features/webbuilder/codeHighlight.js +209 -0
  186. package/lib/features/webbuilder/codeHighlight.js.map +1 -0
  187. package/lib/features/webbuilder/components/CodeView.js +682 -52
  188. package/lib/features/webbuilder/components/CodeView.js.map +1 -1
  189. package/lib/features/webbuilder/components/PreviewScreen.js +59 -38
  190. package/lib/features/webbuilder/components/PreviewScreen.js.map +1 -1
  191. package/lib/features/webbuilder/components/ProjectChat.js +3 -3
  192. package/lib/features/webbuilder/components/ProjectChat.js.map +1 -1
  193. package/lib/features/webbuilder/stepLabels.js +68 -0
  194. package/lib/features/webbuilder/stepLabels.js.map +1 -0
  195. package/lib/hooks/useHostChatSteering.js +45 -0
  196. package/lib/hooks/useHostChatSteering.js.map +1 -0
  197. package/lib/screens/Chat/index.js +2 -12
  198. package/lib/screens/Chat/index.js.map +1 -1
  199. package/lib/screens/ExpensesNative/useExpensesHostChat.js +25 -5
  200. package/lib/screens/ExpensesNative/useExpensesHostChat.js.map +1 -1
  201. package/lib/screens/InboxNative/useInboxHostChat.js +25 -5
  202. package/lib/screens/InboxNative/useInboxHostChat.js.map +1 -1
  203. package/lib/screens/JobSearchNative/useJobSearchHostChat.js +25 -5
  204. package/lib/screens/JobSearchNative/useJobSearchHostChat.js.map +1 -1
  205. package/lib/screens/LocalServicesNative/useLocalServicesHostChat.js +25 -5
  206. package/lib/screens/LocalServicesNative/useLocalServicesHostChat.js.map +1 -1
  207. package/lib/screens/MoviesNative/useMoviesHostChat.js +25 -5
  208. package/lib/screens/MoviesNative/useMoviesHostChat.js.map +1 -1
  209. package/lib/screens/NotionPage/index.js +2 -2
  210. package/lib/screens/NotionPage/index.js.map +1 -1
  211. package/lib/screens/ResumeNative/useResumeHostChat.js +25 -5
  212. package/lib/screens/ResumeNative/useResumeHostChat.js.map +1 -1
  213. package/lib/screens/ShoppingNative/useShoppingHostChat.js +25 -5
  214. package/lib/screens/ShoppingNative/useShoppingHostChat.js.map +1 -1
  215. package/lib/screens/StudyNative/useStudyHostChat.js +25 -5
  216. package/lib/screens/StudyNative/useStudyHostChat.js.map +1 -1
  217. package/lib/screens/TravelNative/useTravelHostChat.js +25 -5
  218. package/lib/screens/TravelNative/useTravelHostChat.js.map +1 -1
  219. package/lib/screens/WebBuilderNative/codeWorkspaceApi.js +222 -0
  220. package/lib/screens/WebBuilderNative/codeWorkspaceApi.js.map +1 -0
  221. package/lib/screens/WebBuilderNative/index.js +1 -1
  222. package/lib/screens/WebBuilderNative/index.js.map +1 -1
  223. package/lib/screens/WebBuilderNative/sandboxProxyApi.js +126 -4
  224. package/lib/screens/WebBuilderNative/sandboxProxyApi.js.map +1 -1
  225. package/lib/screens/WebBuilderNative/useWebBuilderHost.js +120 -11
  226. package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
  227. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notionNotesAgent.js","sources":["../../../src/features/notion/notionNotesAgent.ts"],"sourcesContent":["// Ported from forms-stack packages/notion-api/browser/src/utils/notesAgent.ts (develop); import paths changed and\n// the database schema type (web utils/databaseView) inlined as a local type.\n/* eslint-disable */\n/**\n * notesAgent — pure helpers for agent pages (PLAN §4). An agent is a page with\n * `properties.kind === 'agent'`; its configuration lives in `properties.agent` (this file's\n * {@link NotesAgent}) and its page body is the instructions.\n *\n * - config: normalise whatever is stored, merge patches, trigger / tool / scope edits;\n * - schedule: a friendly picker value ⇄ a 5-field cron + label (\"Every weekday at 9:00\");\n * - templates: Blank, Project Updater, Task Router, IT Answer Bot;\n * - runs: the prompt sent for \"Run now\", the reply → status + summary, and the hidden Runs\n * database (schema, row properties, row parsing).\n *\n * Framework-free (tested in notesAgent.test.ts). The GraphQL side is components/api/agentRuns.ts,\n * the UI components/AgentPageHeader.tsx.\n */\nimport { HIDDEN_FLAG, isHiddenBlock, newAgentProperties } from './notionPageKinds';\nimport {\n AGENT_RUN_STATUSES,\n RUN_STATUS_OPTION,\n effectiveRunStatus,\n isAgentRunStatus,\n type AgentRunStatus,\n} from './notionAgentRunStatus';\nimport { textTitle, titlePlainText } from './notionRichTitle';\ntype DatabaseSchema = Record<\n string,\n { type: string; name?: string; options?: Array<Record<string, unknown>>; [k: string]: unknown }\n>;\n\n// Run statuses and the hidden-block flag have one home each (utils/agentRunStatus, utils/pageKinds).\nexport type { AgentRunStatus };\nexport { AGENT_RUN_STATUSES, RUN_STATUS_OPTION, HIDDEN_FLAG, isHiddenBlock };\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport type AgentTrigger =\n | { type: 'manual' }\n | { type: 'schedule'; cron: string; timezone?: string; label?: string }\n | {\n type: 'database_row';\n databaseId: string;\n on: 'created' | 'updated';\n where?: Record<string, unknown>;\n /** Display only (the database's title when it was picked). */\n databaseTitle?: string;\n }\n | { type: 'comment_mention' }\n | { type: 'meeting_ended' };\n\nexport type AgentTriggerType = AgentTrigger['type'];\n\nexport interface AgentTool {\n kind: 'yantra-notion' | 'connector';\n connector: string;\n actions?: string[];\n /** Display only (catalogue label when it was picked). */\n label?: string;\n}\n\nexport interface AgentScope {\n pageIds: string[];\n databaseIds: string[];\n teamspace?: string;\n /** Display only: titles of the picked pages / databases, by id. */\n titles?: Record<string, string>;\n}\n\nexport interface AgentLastRun {\n runId: string;\n at: string;\n status: AgentRunStatus;\n summary?: string;\n sessionId?: string;\n}\n\n/** `page.properties.agent` (PLAN §4 `NotesAgent`, plus where the Runs database lives). */\nexport interface NotesAgent {\n version: 1;\n enabled: boolean;\n color?: string;\n triggers: AgentTrigger[];\n tools: AgentTool[];\n scope: AgentScope;\n model?: string;\n requireApproval?: boolean;\n lastRun?: AgentLastRun;\n /** The hidden Runs database (child of the agent page), created on the first run. */\n runsDatabaseId?: string;\n /** The template the agent was created from (informational). */\n template?: AgentTemplateId;\n}\n\nexport const YANTRA_NOTION_TOOL: AgentTool = { kind: 'yantra-notion', connector: 'yantra-notion' };\n\n// ============================================================================\n// CONFIG\n// ============================================================================\n\nconst str = (v: unknown): string | undefined => (typeof v === 'string' && v.trim() ? v.trim() : undefined);\nconst strList = (v: unknown): string[] =>\n Array.isArray(v) ? [...new Set(v.filter((x): x is string => typeof x === 'string' && !!x.trim()))] : [];\n\nfunction normalizeTrigger(raw: unknown): AgentTrigger | null {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n switch (r.type) {\n case 'manual':\n return { type: 'manual' };\n case 'schedule': {\n const cron = str(r.cron);\n if (!cron) return null;\n return {\n type: 'schedule',\n cron,\n ...(str(r.timezone) ? { timezone: str(r.timezone) } : {}),\n ...(str(r.label) ? { label: str(r.label) } : {}),\n };\n }\n case 'database_row': {\n const databaseId = str(r.databaseId);\n if (!databaseId) return null;\n return {\n type: 'database_row',\n databaseId,\n on: r.on === 'updated' ? 'updated' : 'created',\n ...(r.where && typeof r.where === 'object' ? { where: r.where as Record<string, unknown> } : {}),\n ...(str(r.databaseTitle) ? { databaseTitle: str(r.databaseTitle) } : {}),\n };\n }\n case 'comment_mention':\n return { type: 'comment_mention' };\n case 'meeting_ended':\n return { type: 'meeting_ended' };\n default:\n return null;\n }\n}\n\n/** Key that makes two triggers \"the same slot\" (one schedule, one per database + event, …). */\nexport function triggerKey(t: AgentTrigger): string {\n return t.type === 'database_row' ? `database_row:${t.databaseId}:${t.on}` : t.type;\n}\n\n/** Whatever is stored → a complete config (manual trigger and yantra-notion tool always present). */\nexport function normalizeAgentConfig(raw: unknown): NotesAgent {\n const r = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n const triggers: AgentTrigger[] = [{ type: 'manual' }];\n const seen = new Set(['manual']);\n for (const t of Array.isArray(r.triggers) ? r.triggers : []) {\n const n = normalizeTrigger(t);\n if (!n || seen.has(triggerKey(n))) continue;\n seen.add(triggerKey(n));\n triggers.push(n);\n }\n const tools: AgentTool[] = [{ ...YANTRA_NOTION_TOOL }];\n const toolSeen = new Set(['yantra-notion']);\n for (const t of Array.isArray(r.tools) ? r.tools : []) {\n if (!t || typeof t !== 'object') continue;\n const tt = t as Record<string, unknown>;\n const connector = str(tt.connector);\n if (!connector || toolSeen.has(connector) || tt.kind === 'yantra-notion') continue;\n toolSeen.add(connector);\n tools.push({\n kind: 'connector',\n connector,\n ...(Array.isArray(tt.actions) ? { actions: strList(tt.actions) } : {}),\n ...(str(tt.label) ? { label: str(tt.label) } : {}),\n });\n }\n const s = (r.scope && typeof r.scope === 'object' ? r.scope : {}) as Record<string, unknown>;\n const titles =\n s.titles && typeof s.titles === 'object'\n ? Object.fromEntries(\n Object.entries(s.titles as Record<string, unknown>).filter(\n (e): e is [string, string] => typeof e[1] === 'string',\n ),\n )\n : undefined;\n const scope: AgentScope = {\n pageIds: strList(s.pageIds),\n databaseIds: strList(s.databaseIds),\n ...(str(s.teamspace) ? { teamspace: str(s.teamspace) } : {}),\n ...(titles && Object.keys(titles).length ? { titles } : {}),\n };\n const lr = r.lastRun as Record<string, unknown> | undefined;\n const lastRun: AgentLastRun | undefined =\n lr && typeof lr === 'object' && isAgentRunStatus(lr.status)\n ? {\n runId: str(lr.runId) ?? '',\n at: str(lr.at) ?? '',\n status: lr.status as AgentRunStatus,\n ...(str(lr.summary) ? { summary: str(lr.summary) } : {}),\n ...(str(lr.sessionId) ? { sessionId: str(lr.sessionId) } : {}),\n }\n : undefined;\n return {\n version: 1,\n enabled: r.enabled === true,\n ...(str(r.color) ? { color: str(r.color) } : {}),\n triggers,\n tools,\n scope,\n ...(str(r.model) ? { model: str(r.model) } : {}),\n ...(typeof r.requireApproval === 'boolean' ? { requireApproval: r.requireApproval } : {}),\n ...(lastRun ? { lastRun } : {}),\n ...(str(r.runsDatabaseId) ? { runsDatabaseId: str(r.runsDatabaseId) } : {}),\n ...(isAgentTemplateId(r.template) ? { template: r.template } : {}),\n };\n}\n\nexport type AgentConfigPatch = Partial<Omit<NotesAgent, 'version' | 'scope'>> & { scope?: Partial<AgentScope> };\n\n/**\n * Merge a patch into the stored config. `updateBlock` merges `properties` shallowly, so the\n * WHOLE `agent` object is written each time — this computes it. `undefined` values remove a key\n * (e.g. `{ model: undefined }` clears the model).\n */\nexport function mergeAgentConfig(current: unknown, patch: AgentConfigPatch): NotesAgent {\n const base = normalizeAgentConfig(current);\n const next: Record<string, unknown> = { ...base };\n for (const [k, v] of Object.entries(patch)) {\n if (k === 'scope') continue;\n if (v === undefined) delete next[k];\n else next[k] = v;\n }\n if (patch.scope) next.scope = { ...base.scope, ...patch.scope };\n return normalizeAgentConfig(next);\n}\n\n/**\n * The `properties` patch for `updateBlock`: the merged `agent`, plus the page-level `color`\n * (the sidebar reads `properties.color` first) when the colour changes.\n */\nexport function agentPropertiesPatch(\n properties: Record<string, unknown> | null | undefined,\n patch: AgentConfigPatch,\n): { agent: NotesAgent; color?: string } {\n const agent = mergeAgentConfig(properties?.agent, patch);\n return patch.color ? { agent, color: patch.color } : { agent };\n}\n\n/** Put a trigger in its slot (replacing the same slot). `manual` is always kept. */\nexport function upsertTrigger(list: readonly AgentTrigger[], trigger: AgentTrigger): AgentTrigger[] {\n const key = triggerKey(trigger);\n const idx = list.findIndex((t) => triggerKey(t) === key);\n if (idx === -1) return [...list, trigger];\n const next = [...list];\n next[idx] = trigger;\n return next;\n}\n\n/** Remove a trigger by slot key (`manual` can't be removed). */\nexport function removeTrigger(list: readonly AgentTrigger[], key: string): AgentTrigger[] {\n if (key === 'manual') return [...list];\n return list.filter((t) => triggerKey(t) !== key);\n}\n\n/** Add / remove a connector tool (yantra-notion is always on). */\nexport function toggleConnectorTool(list: readonly AgentTool[], connector: string, label?: string): AgentTool[] {\n if (connector === 'yantra-notion') return [...list];\n if (list.some((t) => t.connector === connector)) return list.filter((t) => t.connector !== connector);\n return [...list, { kind: 'connector', connector, ...(label ? { label } : {}) }];\n}\n\n/** Add / remove a page or database from the scope (titles kept for display). */\nexport function toggleScopeItem(\n scope: AgentScope,\n item: { id: string; type: 'page' | 'database'; title?: string },\n): AgentScope {\n const key = item.type === 'database' ? 'databaseIds' : 'pageIds';\n const has = scope[key].includes(item.id);\n const titles = { ...(scope.titles ?? {}) };\n if (has) delete titles[item.id];\n else if (item.title) titles[item.id] = item.title;\n return {\n ...scope,\n [key]: has ? scope[key].filter((id) => id !== item.id) : [...scope[key], item.id],\n titles,\n };\n}\n\n// ============================================================================\n// SCHEDULE (friendly picker ⇄ cron)\n// ============================================================================\n\nexport type ScheduleFrequency = 'hourly' | 'daily' | 'weekdays' | 'weekly' | 'monthly';\n\nexport interface FriendlySchedule {\n frequency: ScheduleFrequency;\n /** 0–23 (ignored for hourly). */\n hour: number;\n /** 0–59. */\n minute: number;\n /** 0 = Sunday … 6 = Saturday (weekly). */\n weekday?: number;\n /** 1–{@link MAX_MONTH_DAY} (monthly). */\n dayOfMonth?: number;\n}\n\nexport const WEEKDAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\n\nexport const SCHEDULE_FREQUENCIES: Array<{ id: ScheduleFrequency; label: string }> = [\n { id: 'hourly', label: 'Every hour' },\n { id: 'daily', label: 'Every day' },\n { id: 'weekdays', label: 'Every weekday' },\n { id: 'weekly', label: 'Every week' },\n { id: 'monthly', label: 'Every month' },\n];\n\nexport const DEFAULT_SCHEDULE: FriendlySchedule = { frequency: 'weekdays', hour: 9, minute: 0 };\n\n/**\n * Last day of month the picker offers (every month has it). A cron on day 29–31 — which standard\n * cron skips in shorter months — is not picker-representable: cronToSchedule returns null, so it\n * stays a raw cron (labelled as such) and is never silently rewritten.\n */\nexport const MAX_MONTH_DAY = 28;\n\nconst clamp = (n: unknown, lo: number, hi: number, dflt: number) => {\n const v = typeof n === 'number' && Number.isFinite(n) ? Math.round(n) : dflt;\n return Math.min(hi, Math.max(lo, v));\n};\n\nexport function scheduleToCron(s: FriendlySchedule): string {\n const m = clamp(s.minute, 0, 59, 0);\n const h = clamp(s.hour, 0, 23, 9);\n switch (s.frequency) {\n case 'hourly':\n return `${m} * * * *`;\n case 'daily':\n return `${m} ${h} * * *`;\n case 'weekdays':\n return `${m} ${h} * * 1-5`;\n case 'weekly':\n return `${m} ${h} * * ${clamp(s.weekday, 0, 6, 1)}`;\n case 'monthly':\n return `${m} ${h} ${clamp(s.dayOfMonth, 1, MAX_MONTH_DAY, 1)} * *`;\n }\n}\n\nconst INT = /^\\d{1,2}$/;\n\n/** A cron the picker can show, or null (anything else is kept as a raw cron). */\nexport function cronToSchedule(cron: string): FriendlySchedule | null {\n const parts = cron.trim().split(/\\s+/);\n if (parts.length !== 5) return null;\n const [mi, ho, dom, mon, dow] = parts;\n if (!INT.test(mi) || Number(mi) > 59 || mon !== '*') return null;\n const minute = Number(mi);\n if (ho === '*' && dom === '*' && dow === '*') return { frequency: 'hourly', hour: 0, minute };\n if (!INT.test(ho) || Number(ho) > 23) return null;\n const hour = Number(ho);\n if (dom === '*' && dow === '*') return { frequency: 'daily', hour, minute };\n if (dom === '*' && dow === '1-5') return { frequency: 'weekdays', hour, minute };\n if (dom === '*' && /^[0-7]$/.test(dow)) return { frequency: 'weekly', hour, minute, weekday: Number(dow) % 7 };\n if (dow === '*' && INT.test(dom) && Number(dom) >= 1 && Number(dom) <= MAX_MONTH_DAY) {\n return { frequency: 'monthly', hour, minute, dayOfMonth: Number(dom) };\n }\n return null;\n}\n\nconst pad2 = (n: number) => String(n).padStart(2, '0');\n\n/** \"Every weekday at 9:00\", \"Every Monday at 14:30\", \"Every hour at :15\", \"Monthly on day 1 at 9:00\". */\nexport function scheduleLabel(s: FriendlySchedule): string {\n const time = `${clamp(s.hour, 0, 23, 9)}:${pad2(clamp(s.minute, 0, 59, 0))}`;\n switch (s.frequency) {\n case 'hourly':\n return s.minute ? `Every hour at :${pad2(clamp(s.minute, 0, 59, 0))}` : 'Every hour';\n case 'daily':\n return `Every day at ${time}`;\n case 'weekdays':\n return `Every weekday at ${time}`;\n case 'weekly':\n return `Every ${WEEKDAY_NAMES[clamp(s.weekday, 0, 6, 1)]} at ${time}`;\n case 'monthly':\n return `Monthly on day ${clamp(s.dayOfMonth, 1, MAX_MONTH_DAY, 1)} at ${time}`;\n }\n}\n\n/** A schedule trigger from the picker value. */\nexport function scheduleTrigger(s: FriendlySchedule, timezone?: string): Extract<AgentTrigger, { type: 'schedule' }> {\n return {\n type: 'schedule',\n cron: scheduleToCron(s),\n label: scheduleLabel(s),\n ...(timezone ? { timezone } : {}),\n };\n}\n\n/** One line per trigger, for chips and the runs log. */\nexport function triggerLabel(t: AgentTrigger): string {\n switch (t.type) {\n case 'manual':\n return 'Manual';\n case 'schedule': {\n const friendly = cronToSchedule(t.cron);\n return t.label || (friendly ? scheduleLabel(friendly) : `Cron ${t.cron}`);\n }\n case 'database_row':\n return `When a row is ${t.on} in ${t.databaseTitle ? `“${t.databaseTitle}”` : 'a database'}`;\n case 'comment_mention':\n return 'When @mentioned in a comment';\n case 'meeting_ended':\n return 'When a meeting ends';\n }\n}\n\n/** The Runs database's Trigger select value. */\nexport const TRIGGER_KIND_LABEL: Record<AgentTriggerType, string> = {\n manual: 'Manual',\n schedule: 'Schedule',\n database_row: 'Database row',\n comment_mention: 'Comment',\n meeting_ended: 'Meeting ended',\n};\n\n// ============================================================================\n// TEMPLATES\n// ============================================================================\n\nexport type AgentTemplateId = 'blank' | 'project-updater' | 'task-router' | 'it-answer-bot';\n\nexport interface AgentInstructionLine {\n type: 'heading_2' | 'paragraph' | 'bulleted_list' | 'numbered_list';\n text: string;\n}\n\nexport interface AgentTemplate {\n id: AgentTemplateId;\n name: string;\n description: string;\n color: string;\n instructions: AgentInstructionLine[];\n triggers: AgentTrigger[];\n tools: AgentTool[];\n /** Shown on the template card (\"needs comments\"). */\n note?: string;\n}\n\nexport const AGENT_TEMPLATES: readonly AgentTemplate[] = [\n {\n id: 'blank',\n name: 'Blank agent',\n description: 'Start from scratch: write what it should do in the page.',\n color: 'gray',\n instructions: [\n { type: 'heading_2', text: 'Instructions' },\n {\n type: 'paragraph',\n text: 'Describe the job in plain words. The agent reads this page every time it runs.',\n },\n ],\n triggers: [{ type: 'manual' }],\n tools: [{ ...YANTRA_NOTION_TOOL }],\n },\n {\n id: 'project-updater',\n name: 'Project Updater',\n description: 'Reads what changed in your projects and posts a weekly update page.',\n color: 'blue',\n instructions: [\n { type: 'heading_2', text: 'Goal' },\n {\n type: 'paragraph',\n text: 'Keep the team up to date without anyone writing a status report by hand.',\n },\n { type: 'heading_2', text: 'Steps' },\n {\n type: 'numbered_list',\n text: 'Look at the pages and databases in scope that changed in the last 7 days.',\n },\n {\n type: 'numbered_list',\n text: 'Group the changes by project: shipped, in progress, blocked, and what is due next week.',\n },\n {\n type: 'numbered_list',\n text: 'Create a new page titled “Update — <today’s date>” with a short summary and a bulleted list per group.',\n },\n { type: 'numbered_list', text: 'Link every item back to its page or row.' },\n { type: 'heading_2', text: 'Style' },\n { type: 'bulleted_list', text: 'Short, factual bullets. No filler.' },\n { type: 'bulleted_list', text: 'Call out anything overdue or blocked first.' },\n ],\n triggers: [{ type: 'manual' }, scheduleTrigger({ frequency: 'weekly', hour: 9, minute: 0, weekday: 5 })],\n tools: [{ ...YANTRA_NOTION_TOOL }],\n },\n {\n id: 'task-router',\n name: 'Task Router',\n description: 'Finds unassigned tasks and sets an owner and a priority.',\n color: 'orange',\n instructions: [\n { type: 'heading_2', text: 'Goal' },\n { type: 'paragraph', text: 'No task sits in the tracker without an owner or a priority.' },\n { type: 'heading_2', text: 'Steps' },\n { type: 'numbered_list', text: 'Query the task databases in scope for rows with no “Assigned to”.' },\n {\n type: 'numbered_list',\n text: 'Pick the owner from who already works on similar tasks; spread the load evenly.',\n },\n {\n type: 'numbered_list',\n text: 'Set Priority from the due date and wording: Urgent (due in 2 days or “outage”), High, Medium, Low.',\n },\n { type: 'numbered_list', text: 'Reply with the rows you changed and why.' },\n { type: 'heading_2', text: 'Rules' },\n { type: 'bulleted_list', text: 'Never change a row that already has an owner.' },\n { type: 'bulleted_list', text: 'If you are unsure, leave the row and list it in the summary.' },\n ],\n triggers: [{ type: 'manual' }, scheduleTrigger({ frequency: 'weekdays', hour: 9, minute: 0 })],\n tools: [{ ...YANTRA_NOTION_TOOL }],\n },\n {\n id: 'it-answer-bot',\n name: 'IT Answer Bot',\n description: 'Answers IT questions in comment threads from your IT docs.',\n color: 'purple',\n note: 'Replies in comment threads when comments are available.',\n instructions: [\n { type: 'heading_2', text: 'Goal' },\n {\n type: 'paragraph',\n text: 'Answer IT questions (laptops, accounts, VPN, access requests) where people ask them.',\n },\n { type: 'heading_2', text: 'Steps' },\n { type: 'numbered_list', text: 'Read the comment that mentioned you and the page it is on.' },\n { type: 'numbered_list', text: 'Search the IT pages in scope for the answer.' },\n {\n type: 'numbered_list',\n text: 'Reply in the same thread with a short answer and a link to the source page.',\n },\n { type: 'heading_2', text: 'Rules' },\n { type: 'bulleted_list', text: 'If the docs don’t cover it, say so and suggest who to ask.' },\n { type: 'bulleted_list', text: 'Never share passwords or secrets.' },\n ],\n triggers: [{ type: 'manual' }, { type: 'comment_mention' }],\n tools: [{ ...YANTRA_NOTION_TOOL }],\n },\n];\n\nexport function isAgentTemplateId(v: unknown): v is AgentTemplateId {\n return typeof v === 'string' && AGENT_TEMPLATES.some((t) => t.id === v);\n}\n\nexport function agentTemplate(id: AgentTemplateId | null | undefined): AgentTemplate {\n return AGENT_TEMPLATES.find((t) => t.id === id) ?? AGENT_TEMPLATES[0];\n}\n\n/** The template's instructions as page blocks (appendChildren input). */\nexport function templateInstructionBlocks(\n t: AgentTemplate,\n): Array<{ type: string; properties: Record<string, unknown> }> {\n return t.instructions.map((l) => ({ type: l.type, properties: { title: textTitle(l.text) } }));\n}\n\n// ============================================================================\n// INSTRUCTIONS (page blocks → text)\n// ============================================================================\n\nexport interface InstructionBlock {\n id?: string;\n type?: string;\n properties?: Record<string, unknown> | null;\n}\n\nconst TEXT_PREFIX: Record<string, (i: number) => string> = {\n heading_1: () => '# ',\n heading_2: () => '## ',\n heading_3: () => '### ',\n bulleted_list: () => '- ',\n numbered_list: (i) => `${i}. `,\n quote: () => '> ',\n callout: () => '> ',\n toggle: () => '- ',\n paragraph: () => '',\n text: () => '',\n};\n\n/** The page body (top-level blocks) as markdown-ish text: what the agent is told to do. */\nexport function blocksToInstructions(blocks: readonly InstructionBlock[] | null | undefined): string {\n const out: string[] = [];\n let n = 0;\n for (const b of blocks ?? []) {\n if (!b || b.properties?.hidden === true) continue;\n const type = String(b.type ?? '');\n const text = titlePlainText(b.properties).trim();\n n = type === 'numbered_list' ? n + 1 : 0;\n if (type === 'todo') {\n if (text) out.push(`- [${b.properties?.checked ? 'x' : ' '}] ${text}`);\n continue;\n }\n if (type === 'code' && text) {\n out.push('```\\n' + text + '\\n```');\n continue;\n }\n const prefix = TEXT_PREFIX[type];\n if (!prefix || !text) continue;\n out.push(prefix(n) + text);\n }\n return out.join('\\n');\n}\n\n// ============================================================================\n// RUN PROMPT + RESULT\n// ============================================================================\n\n/** Marks a chat turn as an agent run (the mock agent and transcripts key off it). */\nexport const AGENT_RUN_MARKER = '[Agent run]';\n\n/** What the browser hands the runner (context/NotesAgentRunnerContext). */\nexport interface AgentRunInput {\n agentId: string;\n workspaceId?: string | null;\n name: string;\n /** The page body as text (blocksToInstructions). */\n instructions: string;\n config: NotesAgent;\n trigger: AgentTrigger;\n /** The run row id in the Runs database (when logging worked). */\n runId?: string;\n}\n\nexport interface AgentRunResult {\n status: Exclude<AgentRunStatus, 'queued' | 'running'>;\n summary: string;\n sessionId?: string;\n}\n\nfunction scopeLine(scope: AgentScope): string {\n const name = (id: string) => (scope.titles?.[id] ? `${id} (“${scope.titles[id]}”)` : id);\n const parts: string[] = [];\n if (scope.pageIds.length) parts.push(`pages ${scope.pageIds.map(name).join(', ')}`);\n if (scope.databaseIds.length) parts.push(`databases ${scope.databaseIds.map(name).join(', ')}`);\n if (scope.teamspace) parts.push(`teamspace “${scope.teamspace}”`);\n return parts.length ? parts.join('; ') : 'the whole workspace';\n}\n\n/** The structured prompt for one run (sent as the first turn of a NEW chat session). */\nexport function buildAgentRunPrompt(input: AgentRunInput): string {\n const { config } = input;\n const tools = config.tools.map((t) => (t.kind === 'yantra-notion' ? 'yantra-notion' : t.connector));\n const lines = [\n `${AGENT_RUN_MARKER} You are the agent “${input.name || 'Untitled agent'}” (agent page ${input.agentId}${\n input.runId ? `, run ${input.runId}` : ''\n }).`,\n `Trigger: ${triggerLabel(input.trigger)}.`,\n '',\n 'Instructions:',\n input.instructions.trim() || '(The agent page has no instructions yet — say so and stop.)',\n '',\n `Scope: ${scopeLine(config.scope)}. Only read and change things in scope.`,\n `Tools: ${tools.join(', ')}. Use the yantra-notion skill for every workspace read and write.`,\n ];\n if (config.requireApproval) {\n lines.push(\n 'Approval required: do NOT change anything yet. List the exact changes you would make and ask for approval.',\n );\n } else {\n lines.push('Do the task now, without asking questions unless you cannot continue.');\n }\n lines.push('When you are done, reply with a one-paragraph summary of what you did (or found).');\n return lines.join('\\n');\n}\n\nconst MARKER_BLOCK = /\\[([A-Z_]+)\\][\\s\\S]*?\\[\\/\\1\\]/g;\n\n/** The agent's reply → run status + a short summary for the log. */\nexport function classifyRunReply(\n text: string | null | undefined,\n opts: { requireApproval?: boolean } = {},\n): AgentRunResult {\n const raw = (text ?? '').trim();\n if (!raw) return { status: 'failed', summary: 'The agent did not reply.' };\n const asks = /\\[(CDECLI_ASK_USER_QUESTION|CONNECTOR_CONNECT|FORM_REQUEST)\\]/.test(raw);\n const plain = raw\n .replace(MARKER_BLOCK, ' ')\n .replace(/[ \\t]+\\n/g, '\\n')\n .trim();\n const paras = plain\n .split(/\\n\\s*\\n/)\n .map((p) =>\n p\n .replace(/[#*_`>]+/g, '')\n .replace(/\\s+/g, ' ')\n .trim(),\n )\n .filter(Boolean);\n // The prompt asks for the summary LAST (agents often narrate first): the last real paragraph,\n // else the first one.\n const para = [...paras].reverse().find((p) => p.length >= 30 && !/^[-•\\d]/.test(p)) ?? paras[0] ?? '';\n const summary = (para.length > 280 ? `${para.slice(0, 279)}…` : para) || 'The agent needs input.';\n if (asks) return { status: 'needs_input', summary };\n if (opts.requireApproval && /\\b(approve|approval|confirm)\\b/i.test(plain))\n return { status: 'needs_input', summary };\n return { status: 'succeeded', summary };\n}\n\n// ============================================================================\n// RUNS DATABASE (hidden child of the agent page)\n// ============================================================================\n\nexport const RUNS_DATABASE_SCHEMA: DatabaseSchema = {\n title: { type: 'title' },\n Status: {\n type: 'status',\n options: AGENT_RUN_STATUSES.map((s) => ({ ...RUN_STATUS_OPTION[s] })) as DatabaseSchema[string]['options'],\n },\n Started: { type: 'date' },\n Trigger: {\n type: 'select',\n options: (Object.keys(TRIGGER_KIND_LABEL) as AgentTriggerType[]).map((k, i) => ({\n name: TRIGGER_KIND_LABEL[k],\n color: ['gray', 'blue', 'purple', 'orange', 'green'][i] ?? 'gray',\n })),\n },\n Summary: { type: 'text' },\n Session: { type: 'url' },\n};\n\nexport function runsDatabaseProperties(agentId: string, agentName: string): Record<string, unknown> {\n return {\n title: textTitle(`Runs — ${agentName || 'Agent'}`),\n icon: { type: 'emoji', emoji: '🧾' },\n schema: RUNS_DATABASE_SCHEMA,\n [HIDDEN_FLAG]: true,\n agentRunsOf: agentId,\n };\n}\n\n/** `yantra-session:<id>` — what the Session url cell stores (the runner can reopen it). */\nexport const SESSION_URL_PREFIX = 'yantra-session:';\n\nexport function sessionUrl(sessionId: string | undefined): string {\n return sessionId ? `${SESSION_URL_PREFIX}${sessionId}` : '';\n}\n\nexport function runRowTitle(at: Date): string {\n const d = at.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n const t = at.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });\n return `Run — ${d}, ${t}`;\n}\n\n/** Properties of a new run row (written when the run starts). */\nexport function runRowProperties(opts: { at: Date; trigger: AgentTrigger; status?: AgentRunStatus }) {\n return {\n title: textTitle(runRowTitle(opts.at)),\n Status: { name: RUN_STATUS_OPTION[opts.status ?? 'running'].name },\n Started: opts.at.toISOString(),\n Trigger: { name: TRIGGER_KIND_LABEL[opts.trigger.type] },\n Summary: '',\n Session: '',\n };\n}\n\n/** The patch written when the run finishes. */\nexport function runRowFinishPatch(result: AgentRunResult) {\n return {\n Status: { name: RUN_STATUS_OPTION[result.status].name },\n Summary: result.summary,\n Session: sessionUrl(result.sessionId),\n };\n}\n\nexport interface AgentRunRow {\n id: string;\n status: AgentRunStatus;\n /** ms since epoch (0 when unknown). */\n startedAt: number;\n trigger: string;\n summary: string;\n sessionId?: string;\n}\n\nconst optName = (v: unknown): string =>\n typeof v === 'string' ? v : v && typeof v === 'object' ? String((v as { name?: unknown }).name ?? '') : '';\n\nfunction statusFromName(name: string): AgentRunStatus {\n const hit = AGENT_RUN_STATUSES.find((s) => RUN_STATUS_OPTION[s].name.toLowerCase() === name.trim().toLowerCase());\n return hit ?? 'queued';\n}\n\n/** A Runs database row → what the runs list shows. */\nexport function parseRunRow(row: {\n id?: unknown;\n properties?: Record<string, unknown> | null;\n createdTime?: string | null;\n}): AgentRunRow | null {\n if (!row?.id) return null;\n const p = row.properties ?? {};\n const started = p.Started;\n const startedRaw =\n typeof started === 'string' ? started : started && typeof started === 'object' ? (started as any).start : '';\n const summary = typeof p.Summary === 'string' ? p.Summary : titlePlainText({ title: p.Summary });\n const session = typeof p.Session === 'string' ? p.Session : '';\n return {\n id: String(row.id),\n status: statusFromName(optName(p.Status)),\n startedAt: Date.parse(startedRaw || '') || Date.parse(row.createdTime ?? '') || 0,\n trigger: optName(p.Trigger) || 'Manual',\n summary,\n ...(session.startsWith(SESSION_URL_PREFIX) ? { sessionId: session.slice(SESSION_URL_PREFIX.length) } : {}),\n };\n}\n\n/** Newest first, capped. */\nexport function latestRuns(rows: readonly AgentRunRow[], limit = 10): AgentRunRow[] {\n return [...rows].sort((a, b) => b.startedAt - a.startedAt).slice(0, limit);\n}\n\n// ============================================================================\n// NEW AGENT FROM A TEMPLATE\n// ============================================================================\n\n/** Properties of a new agent page made from a template (kind 'agent' + a full `agent` config). */\nexport function templateAgentProperties(\n id: AgentTemplateId | null | undefined,\n opts: { teamspace?: string | null } = {},\n) {\n const t = agentTemplate(id);\n const blank = t.id === 'blank';\n const base = newAgentProperties({\n ...(blank ? {} : { name: t.name, color: t.color }),\n teamspace: opts.teamspace ?? null,\n });\n const agent = normalizeAgentConfig({\n ...base.agent,\n triggers: t.triggers,\n tools: t.tools,\n template: t.id,\n });\n return { ...base, agent };\n}\n\n/** The sidebar dot: the last run's EFFECTIVE status (utils/agentRunStatus: a stale \"running\" is failed). */\nexport function agentRunStatusOf(\n properties: Record<string, unknown> | null | undefined,\n now: number = Date.now(),\n): AgentRunStatus | undefined {\n const lr = (properties?.agent as { lastRun?: { status?: unknown; at?: unknown } } | undefined)?.lastRun;\n if (!lr || !isAgentRunStatus(lr.status)) return undefined;\n return effectiveRunStatus(lr.status, typeof lr.at === 'string' ? lr.at : undefined, now);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,168 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ const ACCESS_LEVELS = [{
21
+ value: "full_access",
22
+ label: "Full access"
23
+ }, {
24
+ value: "edit",
25
+ label: "Can edit"
26
+ }, {
27
+ value: "comment",
28
+ label: "Can comment"
29
+ }, {
30
+ value: "read",
31
+ label: "Can view"
32
+ }];
33
+ const EMPTY_ACCESS = {
34
+ people: [],
35
+ publicLevel: null
36
+ };
37
+ function isAccessLevel(v) {
38
+ return v === "full_access" || v === "edit" || v === "comment" || v === "read";
39
+ }
40
+ function levelLabel(level) {
41
+ var _a, _b;
42
+ return (_b = (_a = ACCESS_LEVELS.find((l) => l.value === level)) == null ? void 0 : _a.label) != null ? _b : "Can view";
43
+ }
44
+ function coerceLevel(v) {
45
+ return isAccessLevel(v) ? v : "read";
46
+ }
47
+ function isEmailLike(id) {
48
+ return id.includes("@");
49
+ }
50
+ function canonicalId(id) {
51
+ const t = id.trim();
52
+ return isEmailLike(t) ? t.toLowerCase() : t;
53
+ }
54
+ function personKey(type, id) {
55
+ return `${type}:${canonicalId(id)}`;
56
+ }
57
+ function normalizePermissions(entries) {
58
+ var _a;
59
+ const people = [];
60
+ const seen = /* @__PURE__ */ new Set();
61
+ let publicLevel = null;
62
+ for (const e of entries != null ? entries : []) {
63
+ if (!e) continue;
64
+ if (e.type === "public") {
65
+ if (publicLevel === null) publicLevel = coerceLevel(e.permission);
66
+ continue;
67
+ }
68
+ if (e.type !== "user" && e.type !== "group") continue;
69
+ const raw = (_a = e.type === "user" ? e.userId : e.groupId) != null ? _a : "";
70
+ const id = canonicalId(raw);
71
+ if (!id) continue;
72
+ const key = personKey(e.type, id);
73
+ if (seen.has(key)) continue;
74
+ seen.add(key);
75
+ people.push({
76
+ key,
77
+ type: e.type,
78
+ id,
79
+ level: coerceLevel(e.permission)
80
+ });
81
+ }
82
+ return {
83
+ people,
84
+ publicLevel
85
+ };
86
+ }
87
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
88
+ function validateInvitee(raw) {
89
+ const t = (raw != null ? raw : "").trim();
90
+ if (!t) return {
91
+ ok: false,
92
+ error: "Enter an email or user id"
93
+ };
94
+ if (/\s/.test(t)) return {
95
+ ok: false,
96
+ error: "Emails and user ids cannot contain spaces"
97
+ };
98
+ if (isEmailLike(t) && !EMAIL_RE.test(t)) return {
99
+ ok: false,
100
+ error: "That email address doesn\u2019t look right"
101
+ };
102
+ return {
103
+ ok: true,
104
+ id: canonicalId(t)
105
+ };
106
+ }
107
+ function addPerson(state, id, level, type = "user") {
108
+ const cid = canonicalId(id);
109
+ if (!cid) return state;
110
+ const key = personKey(type, cid);
111
+ const existing = state.people.find((p) => p.key === key);
112
+ if (existing) {
113
+ if (existing.level === level) return state;
114
+ return __spreadProps(__spreadValues({}, state), {
115
+ people: state.people.map((p) => p.key === key ? __spreadProps(__spreadValues({}, p), {
116
+ level
117
+ }) : p)
118
+ });
119
+ }
120
+ return __spreadProps(__spreadValues({}, state), {
121
+ people: [...state.people, {
122
+ key,
123
+ type,
124
+ id: cid,
125
+ level
126
+ }]
127
+ });
128
+ }
129
+ function updatePersonLevel(state, key, level) {
130
+ if (!state.people.some((p) => p.key === key && p.level !== level)) return state;
131
+ return __spreadProps(__spreadValues({}, state), {
132
+ people: state.people.map((p) => p.key === key ? __spreadProps(__spreadValues({}, p), {
133
+ level
134
+ }) : p)
135
+ });
136
+ }
137
+ function removePerson(state, key) {
138
+ if (!state.people.some((p) => p.key === key)) return state;
139
+ return __spreadProps(__spreadValues({}, state), {
140
+ people: state.people.filter((p) => p.key !== key)
141
+ });
142
+ }
143
+ function generalAccessOf(state) {
144
+ return state.publicLevel ? "link" : "restricted";
145
+ }
146
+ function setGeneralAccess(state, general) {
147
+ if (general === generalAccessOf(state)) return state;
148
+ return __spreadProps(__spreadValues({}, state), {
149
+ publicLevel: general === "link" ? "read" : null
150
+ });
151
+ }
152
+ function toPermissionInputs(state) {
153
+ const out = state.people.map((p) => ({
154
+ type: p.type,
155
+ id: p.id,
156
+ permission: p.level
157
+ }));
158
+ if (state.publicLevel) out.push({
159
+ type: "public",
160
+ id: null,
161
+ permission: state.publicLevel
162
+ });
163
+ return out;
164
+ }
165
+ function personInitial(id) {
166
+ const c = id.trim().replace(/^[^a-z0-9]+/i, "").charAt(0);
167
+ return (c || "?").toUpperCase();
168
+ }export{ACCESS_LEVELS,EMPTY_ACCESS,addPerson,canonicalId,generalAccessOf,isAccessLevel,isEmailLike,levelLabel,normalizePermissions,personInitial,personKey,removePerson,setGeneralAccess,toPermissionInputs,updatePersonLevel,validateInvitee};//# sourceMappingURL=notionPageAccess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notionPageAccess.js","sources":["../../../src/features/notion/notionPageAccess.ts"],"sourcesContent":["// Ported verbatim from forms-stack packages/notion-api/browser/src/utils/pageAccess.ts (develop).\n/* eslint-disable */\n/**\n * Pure helpers for the page share / access dialog.\n *\n * Server model (`block.permissions`): a flat list of `{ type, userId?, groupId?, permission }`\n * where `type` is 'user' | 'group' | 'public'. `updateBlockPermissions` REPLACES the whole list,\n * so the UI keeps a normalized state and serializes it back in full on every change.\n *\n * NOTE: these rules are recorded but not enforced server-side yet (no resolver calls\n * `checkPermission`), so the UI must not promise that uninvited members are blocked.\n */\n\nexport type AccessLevel = 'full_access' | 'edit' | 'comment' | 'read';\n\nexport const ACCESS_LEVELS: ReadonlyArray<{ value: AccessLevel; label: string }> = [\n { value: 'full_access', label: 'Full access' },\n { value: 'edit', label: 'Can edit' },\n { value: 'comment', label: 'Can comment' },\n { value: 'read', label: 'Can view' },\n];\n\nexport type GeneralAccess = 'restricted' | 'link';\n\nexport const GENERAL_ACCESS_OPTIONS: ReadonlyArray<{ value: GeneralAccess; label: string }> = [\n { value: 'restricted', label: 'Only people invited' },\n { value: 'link', label: 'Anyone with the link can view' },\n];\n\n/** Server-shaped permission entry (subset of the generated GraphQL type). */\nexport interface PermissionEntryLike {\n type: string;\n userId?: string | null;\n groupId?: string | null;\n permission: string;\n}\n\n/** Input shape for `updateBlockPermissions`. */\nexport interface PermissionInputLike {\n type: string;\n id?: string | null;\n permission: AccessLevel;\n}\n\nexport interface AccessPerson {\n /** Stable de-dupe key (`user:<id>`; emails lower-cased). */\n key: string;\n type: 'user' | 'group';\n id: string;\n level: AccessLevel;\n}\n\nexport interface PageAccessState {\n people: AccessPerson[];\n /**\n * Level of the `public` entry, or null when access is restricted to invited people.\n * Usually 'read' (the only option the UI offers) but other levels round-trip unchanged.\n */\n publicLevel: AccessLevel | null;\n}\n\nexport const EMPTY_ACCESS: PageAccessState = { people: [], publicLevel: null };\n\nexport function isAccessLevel(v: unknown): v is AccessLevel {\n return v === 'full_access' || v === 'edit' || v === 'comment' || v === 'read';\n}\n\nexport function levelLabel(level: string | null | undefined): string {\n return ACCESS_LEVELS.find((l) => l.value === level)?.label ?? 'Can view';\n}\n\nfunction coerceLevel(v: unknown): AccessLevel {\n return isAccessLevel(v) ? v : 'read';\n}\n\nexport function isEmailLike(id: string): boolean {\n return id.includes('@');\n}\n\n/** Canonical form of a user/group id: trimmed; emails lower-cased. */\nexport function canonicalId(id: string): string {\n const t = id.trim();\n return isEmailLike(t) ? t.toLowerCase() : t;\n}\n\nexport function personKey(type: 'user' | 'group', id: string): string {\n return `${type}:${canonicalId(id)}`;\n}\n\n/** Server entries → UI state (unknown types dropped, duplicates collapsed — first wins). */\nexport function normalizePermissions(entries: ReadonlyArray<PermissionEntryLike> | null | undefined): PageAccessState {\n const people: AccessPerson[] = [];\n const seen = new Set<string>();\n let publicLevel: AccessLevel | null = null;\n for (const e of entries ?? []) {\n if (!e) continue;\n if (e.type === 'public') {\n if (publicLevel === null) publicLevel = coerceLevel(e.permission);\n continue;\n }\n if (e.type !== 'user' && e.type !== 'group') continue;\n const raw = (e.type === 'user' ? e.userId : e.groupId) ?? '';\n const id = canonicalId(raw);\n if (!id) continue;\n const key = personKey(e.type, id);\n if (seen.has(key)) continue;\n seen.add(key);\n people.push({ key, type: e.type, id, level: coerceLevel(e.permission) });\n }\n return { people, publicLevel };\n}\n\nexport type InviteValidation = { ok: true; id: string } | { ok: false; error: string };\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Validate the free-text invite field (email or user id). */\nexport function validateInvitee(raw: string): InviteValidation {\n const t = (raw ?? '').trim();\n if (!t) return { ok: false, error: 'Enter an email or user id' };\n if (/\\s/.test(t)) return { ok: false, error: 'Emails and user ids cannot contain spaces' };\n if (isEmailLike(t) && !EMAIL_RE.test(t)) return { ok: false, error: 'That email address doesn’t look right' };\n return { ok: true, id: canonicalId(t) };\n}\n\n/** Add a user, or update their level if already present (case-insensitive for emails). */\nexport function addPerson(\n state: PageAccessState,\n id: string,\n level: AccessLevel,\n type: 'user' | 'group' = 'user',\n): PageAccessState {\n const cid = canonicalId(id);\n if (!cid) return state;\n const key = personKey(type, cid);\n const existing = state.people.find((p) => p.key === key);\n if (existing) {\n if (existing.level === level) return state;\n return { ...state, people: state.people.map((p) => (p.key === key ? { ...p, level } : p)) };\n }\n return { ...state, people: [...state.people, { key, type, id: cid, level }] };\n}\n\nexport function updatePersonLevel(state: PageAccessState, key: string, level: AccessLevel): PageAccessState {\n if (!state.people.some((p) => p.key === key && p.level !== level)) return state;\n return { ...state, people: state.people.map((p) => (p.key === key ? { ...p, level } : p)) };\n}\n\nexport function removePerson(state: PageAccessState, key: string): PageAccessState {\n if (!state.people.some((p) => p.key === key)) return state;\n return { ...state, people: state.people.filter((p) => p.key !== key) };\n}\n\nexport function generalAccessOf(state: PageAccessState): GeneralAccess {\n return state.publicLevel ? 'link' : 'restricted';\n}\n\n/** Toggle \"Anyone with the link can view\" (adds/removes the public:read entry). */\nexport function setGeneralAccess(state: PageAccessState, general: GeneralAccess): PageAccessState {\n if (general === generalAccessOf(state)) return state;\n return { ...state, publicLevel: general === 'link' ? 'read' : null };\n}\n\n/** UI state → full replacement list for `updateBlockPermissions`. */\nexport function toPermissionInputs(state: PageAccessState): PermissionInputLike[] {\n const out: PermissionInputLike[] = state.people.map((p) => ({ type: p.type, id: p.id, permission: p.level }));\n if (state.publicLevel) out.push({ type: 'public', id: null, permission: state.publicLevel });\n return out;\n}\n\nexport function sameAccess(a: PageAccessState, b: PageAccessState): boolean {\n return JSON.stringify(toPermissionInputs(a)) === JSON.stringify(toPermissionInputs(b));\n}\n\n/** Initial for the avatar bubble. */\nexport function personInitial(id: string): string {\n const c = id\n .trim()\n .replace(/^[^a-z0-9]+/i, '')\n .charAt(0);\n return (c || '?').toUpperCase();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ const PAGE_SETTINGS_KEY = "pageSettings";
21
+ const PAGE_FONTS = [{
22
+ value: "default",
23
+ label: "Default"
24
+ }, {
25
+ value: "serif",
26
+ label: "Serif"
27
+ }, {
28
+ value: "mono",
29
+ label: "Mono"
30
+ }];
31
+ const DEFAULT_PAGE_SETTINGS = {
32
+ fullWidth: false,
33
+ smallText: false,
34
+ locked: false,
35
+ font: "default"
36
+ };
37
+ function readPageSettings(properties) {
38
+ const raw = properties == null ? void 0 : properties[PAGE_SETTINGS_KEY];
39
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return __spreadValues({}, DEFAULT_PAGE_SETTINGS);
40
+ const r = raw;
41
+ return {
42
+ fullWidth: r.fullWidth === true,
43
+ smallText: r.smallText === true,
44
+ locked: r.locked === true,
45
+ font: r.font === "serif" || r.font === "mono" ? r.font : "default"
46
+ };
47
+ }
48
+ function pageSettingsPatch(properties, key, value) {
49
+ const current = readPageSettings(properties);
50
+ return {
51
+ [PAGE_SETTINGS_KEY]: __spreadProps(__spreadValues({}, current), {
52
+ [key]: value != null ? value : !current[key]
53
+ })
54
+ };
55
+ }
56
+ function pageFontPatch(properties, font) {
57
+ return {
58
+ [PAGE_SETTINGS_KEY]: __spreadProps(__spreadValues({}, readPageSettings(properties)), {
59
+ font
60
+ })
61
+ };
62
+ }
63
+ function wordCount(title, model) {
64
+ var _a;
65
+ const texts = [title, ...Object.values((_a = model == null ? void 0 : model.blocks) != null ? _a : {}).map((b) => b.text)];
66
+ return texts.reduce((n, t) => n + (t.trim() ? t.trim().split(/\s+/).length : 0), 0);
67
+ }
68
+ function pageLinkUrl(origin, pageId) {
69
+ return `${origin.replace(/\/+$/, "")}/notion/page/${encodeURIComponent(pageId)}`;
70
+ }
71
+ function toggleFavorite(ids, id) {
72
+ return ids.includes(id) ? ids.filter((x) => x !== id) : [...ids, id];
73
+ }
74
+ const WORKSPACE_ROLES = ["owner", "admin", "member", "guest"];
75
+ const ROLE_LABEL = {
76
+ owner: "Owner",
77
+ admin: "Admin",
78
+ member: "Member",
79
+ guest: "Guest"
80
+ };
81
+ function viewerWorkspaceRole(ws, viewer) {
82
+ var _a;
83
+ if (!ws || !viewer) return null;
84
+ const ids = [viewer.id, viewer.email].filter((x) => typeof x === "string" && !!x.trim()).map((x) => x.trim().toLowerCase());
85
+ if (!ids.length) return null;
86
+ const same = (u) => !!u && ids.includes(u.trim().toLowerCase());
87
+ const m = ((_a = ws.members) != null ? _a : []).find((x) => same(x == null ? void 0 : x.userId));
88
+ if ((m == null ? void 0 : m.role) && WORKSPACE_ROLES.includes(m.role)) return m.role;
89
+ if (same(ws.ownerId)) return "owner";
90
+ return null;
91
+ }
92
+ function canManageWorkspace(role) {
93
+ return role === null || role === "owner" || role === "admin";
94
+ }
95
+ function workspaceIconInput(text) {
96
+ const t = text.trim();
97
+ if (!t) return null;
98
+ if (/^https?:\/\//i.test(t)) return {
99
+ type: "external",
100
+ url: t
101
+ };
102
+ return {
103
+ type: "emoji",
104
+ emoji: t
105
+ };
106
+ }
107
+ function serverErrorText(err) {
108
+ var _a, _b, _c, _d;
109
+ const e = err;
110
+ const msg = (_d = (_c = (_b = (_a = e == null ? void 0 : e.graphQLErrors) == null ? void 0 : _a[0]) == null ? void 0 : _b.message) != null ? _c : e == null ? void 0 : e.message) != null ? _d : "Something went wrong";
111
+ return String(msg).replace(/^GraphQL error:\s*/i, "");
112
+ }
113
+ const SEARCH_FILTERS = [{
114
+ id: "all",
115
+ label: "All"
116
+ }, {
117
+ id: "pages",
118
+ label: "Pages",
119
+ types: ["page"]
120
+ }, {
121
+ id: "databases",
122
+ label: "Databases",
123
+ types: ["database"]
124
+ }, {
125
+ id: "todos",
126
+ label: "To-dos",
127
+ types: ["todo"]
128
+ }, {
129
+ id: "code",
130
+ label: "Code",
131
+ types: ["code"]
132
+ }];
133
+ function hitBreadcrumb(path) {
134
+ return (path != null ? path : []).map((p) => {
135
+ var _a;
136
+ return ((_a = p == null ? void 0 : p.title) != null ? _a : "").trim() || "Untitled";
137
+ }).join(" / ");
138
+ }export{DEFAULT_PAGE_SETTINGS,PAGE_FONTS,PAGE_SETTINGS_KEY,ROLE_LABEL,SEARCH_FILTERS,WORKSPACE_ROLES,canManageWorkspace,hitBreadcrumb,pageFontPatch,pageLinkUrl,pageSettingsPatch,readPageSettings,serverErrorText,toggleFavorite,viewerWorkspaceRole,wordCount,workspaceIconInput};//# sourceMappingURL=notionPageActions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notionPageActions.js","sources":["../../../src/features/notion/notionPageActions.ts"],"sourcesContent":["/**\n * Page-level settings and workspace rules shared with the web workspace, so both\n * clients read and write the same data:\n * - pageSettings (web utils/pageSettings): Full width, Small text, Lock page, Font,\n * stored as `properties.pageSettings` (updateBlock merges top-level keys);\n * - workspace roles (web components/api/workspaceAdmin): who may manage a workspace;\n * - favorites, word count, and the web page link (web utils/pageCommands).\n *\n * Pure: no RN imports, so it runs under jest as-is.\n */\nimport type { EditorModel } from './notionEditorModel';\n\n// ---- Page settings ---------------------------------------------------------\n\nexport type PageFont = 'default' | 'serif' | 'mono';\n\nexport interface PageSettings {\n fullWidth: boolean;\n smallText: boolean;\n locked: boolean;\n font: PageFont;\n}\n\nexport type PageSettingKey = 'fullWidth' | 'smallText' | 'locked';\n\nexport const PAGE_SETTINGS_KEY = 'pageSettings';\n\nexport const PAGE_FONTS: Array<{ value: PageFont; label: string }> = [\n { value: 'default', label: 'Default' },\n { value: 'serif', label: 'Serif' },\n { value: 'mono', label: 'Mono' },\n];\n\nexport const DEFAULT_PAGE_SETTINGS: PageSettings = {\n fullWidth: false,\n smallText: false,\n locked: false,\n font: 'default',\n};\n\nexport function readPageSettings(properties: Record<string, unknown> | null | undefined): PageSettings {\n const raw = properties?.[PAGE_SETTINGS_KEY];\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return { ...DEFAULT_PAGE_SETTINGS };\n const r = raw as Record<string, unknown>;\n return {\n fullWidth: r.fullWidth === true,\n smallText: r.smallText === true,\n locked: r.locked === true,\n font: r.font === 'serif' || r.font === 'mono' ? r.font : 'default',\n };\n}\n\n/** The `properties` patch that flips (or sets) one setting. */\nexport function pageSettingsPatch(\n properties: Record<string, unknown> | null | undefined,\n key: PageSettingKey,\n value?: boolean,\n): Record<string, PageSettings> {\n const current = readPageSettings(properties);\n return { [PAGE_SETTINGS_KEY]: { ...current, [key]: value ?? !current[key] } };\n}\n\nexport function pageFontPatch(\n properties: Record<string, unknown> | null | undefined,\n font: PageFont,\n): Record<string, PageSettings> {\n return { [PAGE_SETTINGS_KEY]: { ...readPageSettings(properties), font } };\n}\n\n// ---- Words / links -----------------------------------------------------------\n\n/** Words in the page title and every text block (the web ••• footer count). */\nexport function wordCount(title: string, model: EditorModel | null): number {\n const texts = [title, ...Object.values(model?.blocks ?? {}).map((b) => b.text)];\n return texts.reduce((n, t) => n + (t.trim() ? t.trim().split(/\\s+/).length : 0), 0);\n}\n\n/** The web link of a page: `<origin>/notion/page/<id>` (web pageLinkUrl with the global base). */\nexport function pageLinkUrl(origin: string, pageId: string): string {\n return `${origin.replace(/\\/+$/, '')}/notion/page/${encodeURIComponent(pageId)}`;\n}\n\n// ---- Favorites ---------------------------------------------------------------\n\nexport function toggleFavorite(ids: readonly string[], id: string): string[] {\n return ids.includes(id) ? ids.filter((x) => x !== id) : [...ids, id];\n}\n\n// ---- Workspace roles -------------------------------------------------------\n\nexport type WorkspaceRole = 'owner' | 'admin' | 'member' | 'guest';\nexport const WORKSPACE_ROLES: WorkspaceRole[] = ['owner', 'admin', 'member', 'guest'];\nexport const ROLE_LABEL: Record<WorkspaceRole, string> = {\n owner: 'Owner',\n admin: 'Admin',\n member: 'Member',\n guest: 'Guest',\n};\n\nexport interface WorkspaceMember {\n userId: string;\n role: string;\n joinedAt?: string | null;\n}\n\n/** The viewer's role from the workspace's members / ownerId (null = can't tell). */\nexport function viewerWorkspaceRole(\n ws: { ownerId?: string | null; members?: Array<{ userId?: string | null; role?: string | null }> | null } | null,\n viewer: { id?: string | null; email?: string | null } | null,\n): WorkspaceRole | null {\n if (!ws || !viewer) return null;\n const ids = [viewer.id, viewer.email]\n .filter((x): x is string => typeof x === 'string' && !!x.trim())\n .map((x) => x.trim().toLowerCase());\n if (!ids.length) return null;\n const same = (u?: string | null) => !!u && ids.includes(u.trim().toLowerCase());\n const m = (ws.members ?? []).find((x) => same(x?.userId));\n if (m?.role && (WORKSPACE_ROLES as string[]).includes(m.role)) return m.role as WorkspaceRole;\n if (same(ws.ownerId)) return 'owner';\n return null;\n}\n\n/** Owner / admin manage the workspace; unknown is allowed and the server decides (web rule). */\nexport function canManageWorkspace(role: WorkspaceRole | null): boolean {\n return role === null || role === 'owner' || role === 'admin';\n}\n\n/** Workspace icon input from what was typed: an http(s) URL or an emoji. */\nexport function workspaceIconInput(text: string): { type: 'emoji' | 'external'; emoji?: string; url?: string } | null {\n const t = text.trim();\n if (!t) return null;\n if (/^https?:\\/\\//i.test(t)) return { type: 'external', url: t };\n return { type: 'emoji', emoji: t };\n}\n\n/** Server error text without the \"GraphQL error:\" prefix (web serverErrorText). */\nexport function serverErrorText(err: unknown): string {\n const e = err as { graphQLErrors?: Array<{ message?: string }>; message?: string } | null;\n const msg = e?.graphQLErrors?.[0]?.message ?? e?.message ?? 'Something went wrong';\n return String(msg).replace(/^GraphQL error:\\s*/i, '');\n}\n\n// ---- Search -------------------------------------------------------------------\n\n/** Web SearchView type filter chips. */\nexport const SEARCH_FILTERS: Array<{ id: string; label: string; types?: string[] }> = [\n { id: 'all', label: 'All' },\n { id: 'pages', label: 'Pages', types: ['page'] },\n { id: 'databases', label: 'Databases', types: ['database'] },\n { id: 'todos', label: 'To-dos', types: ['todo'] },\n { id: 'code', label: 'Code', types: ['code'] },\n];\n\n/** Breadcrumb for a search hit: \"Parent / Child\". */\nexport function hitBreadcrumb(path: ReadonlyArray<{ title?: string | null }> | null | undefined): string {\n return (path ?? []).map((p) => (p?.title ?? '').trim() || 'Untitled').join(' / ');\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ const COLLAPSE_WINDOW_MS = 10 * 60 * 1e3;
18
+ const CREATE_DEDUPE_MS = 5 * 1e3;
19
+ function toDate(v) {
20
+ if (v == null || v === "") return null;
21
+ const d = v instanceof Date ? new Date(v.getTime()) : new Date(v);
22
+ return Number.isNaN(d.getTime()) ? null : d;
23
+ }
24
+ function pad(n) {
25
+ return n < 10 ? `0${n}` : String(n);
26
+ }
27
+ function dayKey(d) {
28
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
29
+ }
30
+ function startOfDay(d) {
31
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
32
+ }
33
+ function dayLabel(d, now = /* @__PURE__ */ new Date(), locale) {
34
+ const diffDays = Math.round((startOfDay(now).getTime() - startOfDay(d).getTime()) / 864e5);
35
+ if (diffDays === 0) return "Today";
36
+ if (diffDays === 1) return "Yesterday";
37
+ return d.toLocaleDateString(locale, __spreadValues({
38
+ weekday: "long",
39
+ month: "long",
40
+ day: "numeric"
41
+ }, d.getFullYear() !== now.getFullYear() ? {
42
+ year: "numeric"
43
+ } : {}));
44
+ }
45
+ function formatItemTime(d, now = /* @__PURE__ */ new Date(), locale) {
46
+ const ms = Math.max(0, now.getTime() - d.getTime());
47
+ const min = Math.floor(ms / 6e4);
48
+ if (min < 1) return "Just now";
49
+ if (min < 60) return `${min} min ago`;
50
+ return d.toLocaleTimeString(locale, {
51
+ hour: "numeric",
52
+ minute: "2-digit"
53
+ });
54
+ }
55
+ function describeItem(item) {
56
+ if (item.kind === "created") return "Created the page";
57
+ return item.count > 1 ? `Edited ${item.count} times` : "Edited the page";
58
+ }
59
+ function buildHistoryGroups(entries, opts = {}) {
60
+ var _a, _b, _c, _d;
61
+ const now = (_a = opts.now) != null ? _a : /* @__PURE__ */ new Date();
62
+ const windowMs = (_b = opts.collapseWindowMs) != null ? _b : COLLAPSE_WINDOW_MS;
63
+ const created = toDate((_c = opts.createdTime) != null ? _c : null);
64
+ const parsed = (entries != null ? entries : []).map((e) => {
65
+ var _a2;
66
+ return {
67
+ at: toDate(e == null ? void 0 : e.timestamp),
68
+ userId: ((_a2 = e == null ? void 0 : e.userId) != null ? _a2 : "").trim(),
69
+ version: typeof (e == null ? void 0 : e.version) === "number" ? e.version : void 0
70
+ };
71
+ }).filter((e) => e.at !== null).filter((e) => !created || Math.abs(e.at.getTime() - created.getTime()) > CREATE_DEDUPE_MS).sort((a, b) => b.at.getTime() - a.at.getTime());
72
+ const items = [];
73
+ for (const e of parsed) {
74
+ const prev = items[items.length - 1];
75
+ if (prev && prev.kind === "edit" && prev.userId === e.userId && dayKey(prev.earliest) === dayKey(e.at) && prev.earliest.getTime() - e.at.getTime() <= windowMs) {
76
+ prev.count += 1;
77
+ prev.earliest = e.at;
78
+ continue;
79
+ }
80
+ items.push(__spreadValues({
81
+ key: `edit-${e.at.getTime()}-${e.userId}-${items.length}`,
82
+ kind: "edit",
83
+ userId: e.userId,
84
+ count: 1,
85
+ latest: e.at,
86
+ earliest: e.at
87
+ }, e.version !== void 0 ? {
88
+ version: e.version
89
+ } : {}));
90
+ }
91
+ if (created) {
92
+ items.push({
93
+ key: `created-${created.getTime()}`,
94
+ kind: "created",
95
+ userId: ((_d = opts.createdBy) != null ? _d : "").trim(),
96
+ count: 1,
97
+ latest: created,
98
+ earliest: created
99
+ });
100
+ }
101
+ const groups = [];
102
+ for (const item of items) {
103
+ const k = dayKey(item.latest);
104
+ const last = groups[groups.length - 1];
105
+ if (last && dayKey(last.items[0].latest) === k) last.items.push(item);
106
+ else {
107
+ const key = groups.some((g) => g.key === k) ? `${k}~${groups.length}` : k;
108
+ groups.push({
109
+ key,
110
+ label: dayLabel(item.latest, now, opts.locale),
111
+ items: [item]
112
+ });
113
+ }
114
+ }
115
+ return groups;
116
+ }
117
+ function canRestoreItem(item, newestVersion) {
118
+ if (item.kind !== "edit" || typeof item.version !== "number") return false;
119
+ return newestVersion === null || item.version < newestVersion;
120
+ }
121
+ function outlineOf(blocks) {
122
+ const byId = new Map(blocks.map((b) => [b.id, b]));
123
+ const root = blocks[0];
124
+ if (!root) return [];
125
+ const plain = (b) => {
126
+ var _a;
127
+ const t = (_a = b.properties) == null ? void 0 : _a.title;
128
+ if (Array.isArray(t)) return t.map((s) => {
129
+ var _a2, _b, _c, _d;
130
+ return (_d = (_c = (_b = s == null ? void 0 : s.plainText) != null ? _b : (_a2 = s == null ? void 0 : s.text) == null ? void 0 : _a2.content) != null ? _c : s == null ? void 0 : s.content) != null ? _d : "";
131
+ }).join("");
132
+ return typeof t === "string" ? t : "";
133
+ };
134
+ const out = [];
135
+ const seen = /* @__PURE__ */ new Set();
136
+ const walk = (b, depth) => {
137
+ var _a;
138
+ if (seen.has(b.id)) return;
139
+ seen.add(b.id);
140
+ out.push({
141
+ id: b.id,
142
+ type: b.type,
143
+ depth,
144
+ text: plain(b)
145
+ });
146
+ if (depth > 0 && (b.type === "page" || b.type === "database" || b.type === "database_row")) return;
147
+ for (const id of (_a = b.content) != null ? _a : []) {
148
+ const c = byId.get(id);
149
+ if (c) walk(c, depth + 1);
150
+ }
151
+ };
152
+ walk(root, 0);
153
+ return out;
154
+ }export{COLLAPSE_WINDOW_MS,buildHistoryGroups,canRestoreItem,dayKey,dayLabel,describeItem,formatItemTime,outlineOf,toDate};//# sourceMappingURL=notionPageHistory.js.map