@graph8/sdk 0.361.0 → 0.370.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 (388) hide show
  1. package/README.md +14 -10
  2. package/blocks/_foundation/README.md +172 -1
  3. package/blocks/_foundation/block.json +234 -13
  4. package/blocks/_foundation/components/api-error.tsx +71 -25
  5. package/blocks/_foundation/components/brand-provider.tsx +23 -0
  6. package/blocks/_foundation/components/confirmed-action.tsx +90 -0
  7. package/blocks/_foundation/components/connect-graph8.tsx +280 -0
  8. package/blocks/_foundation/components/empty-state.tsx +39 -7
  9. package/blocks/_foundation/components/screen/bar-list.tsx +71 -0
  10. package/blocks/_foundation/components/screen/detail-panel.tsx +114 -0
  11. package/blocks/_foundation/components/screen/entity-avatar.tsx +56 -0
  12. package/blocks/_foundation/components/screen/form-sheet.tsx +73 -0
  13. package/blocks/_foundation/components/screen/index.ts +22 -0
  14. package/blocks/_foundation/components/screen/key-value-list.tsx +65 -0
  15. package/blocks/_foundation/components/screen/record-link.tsx +76 -0
  16. package/blocks/_foundation/components/screen/screen-layout.tsx +124 -0
  17. package/blocks/_foundation/components/screen/section-card.tsx +47 -0
  18. package/blocks/_foundation/components/screen/sparkline.tsx +77 -0
  19. package/blocks/_foundation/components/screen/split-view.tsx +125 -0
  20. package/blocks/_foundation/components/screen/stat-strip.tsx +121 -0
  21. package/blocks/_foundation/components/screen/status-pill.tsx +104 -0
  22. package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
  23. package/blocks/_foundation/components/screen/time-series-chart.tsx +32 -0
  24. package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
  25. package/blocks/_foundation/components/screen/toolbar.tsx +144 -0
  26. package/blocks/_foundation/hooks/use-brand.ts +52 -0
  27. package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
  28. package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
  29. package/blocks/_foundation/hooks/use-paginated.ts +120 -0
  30. package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
  31. package/blocks/_foundation/hooks/use-url-state.ts +74 -0
  32. package/blocks/_foundation/lib/g8-cache.ts +454 -0
  33. package/blocks/_foundation/lib/g8-client.ts +1 -1
  34. package/blocks/_foundation/lib/plural.ts +23 -0
  35. package/blocks/_foundation/server/connect-route.ts +15 -0
  36. package/blocks/_foundation/server/connect.ts +127 -0
  37. package/blocks/_foundation/server/local-key.ts +70 -0
  38. package/blocks/_foundation/server/proxy.ts +103 -3
  39. package/blocks/_foundation/server/route.ts +16 -4
  40. package/blocks/agents/README.md +14 -0
  41. package/blocks/agents/block.json +33 -1
  42. package/blocks/agents/components/agent-builder.tsx +13 -8
  43. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -1
  44. package/blocks/agents/components/agent-run-detail.tsx +102 -0
  45. package/blocks/agents/components/agent-runs.tsx +16 -7
  46. package/blocks/agents/components/agents-screen.tsx +184 -0
  47. package/blocks/analytics/README.md +13 -0
  48. package/blocks/analytics/block.json +45 -1
  49. package/blocks/analytics/components/analytics-screen.tsx +209 -0
  50. package/blocks/analytics/components/leaderboard.tsx +14 -7
  51. package/blocks/analytics/components/report-builder.tsx +23 -9
  52. package/blocks/analytics/components/report-detail.tsx +84 -0
  53. package/blocks/analytics/components/trend-chart.tsx +62 -0
  54. package/blocks/appointments/README.md +21 -1
  55. package/blocks/appointments/block.json +43 -2
  56. package/blocks/appointments/components/availability-editor.tsx +17 -5
  57. package/blocks/appointments/components/event-type-editor.tsx +2 -1
  58. package/blocks/appointments/components/event-type-related.tsx +77 -0
  59. package/blocks/appointments/components/meeting-list.tsx +14 -6
  60. package/blocks/appointments/components/meetings-screen.tsx +157 -0
  61. package/blocks/appointments/components/scheduling-screen.tsx +122 -0
  62. package/blocks/appointments/hooks/use-event-types.ts +17 -0
  63. package/blocks/block.schema.json +18 -0
  64. package/blocks/block.types.ts +26 -0
  65. package/blocks/campaigns/README.md +41 -11
  66. package/blocks/campaigns/block.json +105 -42
  67. package/blocks/campaigns/components/campaign-assets.tsx +16 -5
  68. package/blocks/campaigns/components/campaign-launch-step.tsx +102 -19
  69. package/blocks/campaigns/components/campaign-list.tsx +7 -3
  70. package/blocks/campaigns/components/campaign-wizard.tsx +43 -10
  71. package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
  72. package/blocks/campaigns/components/icp-detail.tsx +154 -0
  73. package/blocks/campaigns/components/icp-editor.tsx +15 -15
  74. package/blocks/campaigns/components/persona-detail.tsx +172 -0
  75. package/blocks/campaigns/components/persona-editor.tsx +14 -11
  76. package/blocks/campaigns/lib/campaigns-utils.ts +134 -1
  77. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
  78. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
  79. package/blocks/coach/README.md +10 -0
  80. package/blocks/coach/block.json +16 -1
  81. package/blocks/coach/components/coach-screen.tsx +222 -0
  82. package/blocks/copilot/README.md +13 -0
  83. package/blocks/copilot/block.json +14 -1
  84. package/blocks/copilot/components/copilot-panel.tsx +14 -5
  85. package/blocks/copilot/components/copilot-screen.tsx +148 -0
  86. package/blocks/copilot/components/copilot-team-chat.tsx +7 -5
  87. package/blocks/crm/README.md +28 -0
  88. package/blocks/crm/block.json +58 -2
  89. package/blocks/crm/components/companies-screen.tsx +345 -0
  90. package/blocks/crm/components/company-detail.tsx +8 -6
  91. package/blocks/crm/components/company-table.tsx +11 -4
  92. package/blocks/crm/components/contact-detail.tsx +6 -3
  93. package/blocks/crm/components/contact-table.tsx +10 -3
  94. package/blocks/crm/components/contacts-screen.tsx +359 -0
  95. package/blocks/crm/components/dedupe-review.tsx +7 -5
  96. package/blocks/crm/components/import-wizard.tsx +4 -2
  97. package/blocks/crm/components/notes-panel.tsx +4 -4
  98. package/blocks/crm/components/record-form.tsx +5 -3
  99. package/blocks/crm/components/task-list.tsx +4 -4
  100. package/blocks/crm/components/tasks-screen.tsx +379 -0
  101. package/blocks/crm/hooks/use-custom-fields.ts +6 -1
  102. package/blocks/crm/hooks/use-page-query.ts +7 -1
  103. package/blocks/crm/lib/crm-types.ts +12 -2
  104. package/blocks/crm/lib/screen-parts.tsx +105 -0
  105. package/blocks/deals/README.md +34 -3
  106. package/blocks/deals/block.json +45 -4
  107. package/blocks/deals/components/deal-detail.tsx +27 -13
  108. package/blocks/deals/components/deal-related.tsx +87 -0
  109. package/blocks/deals/components/deals-screen.tsx +298 -0
  110. package/blocks/deals/components/pipeline-board.tsx +4 -2
  111. package/blocks/deals/components/stage-editor.tsx +4 -4
  112. package/blocks/deals/hooks/use-default-owner.ts +60 -0
  113. package/blocks/deals/hooks/use-pipeline-deals.ts +5 -1
  114. package/blocks/deals/hooks/use-pipelines.ts +1 -1
  115. package/blocks/deals/lib/deals-types.ts +14 -0
  116. package/blocks/dialer/README.md +34 -2
  117. package/blocks/dialer/block.json +67 -1
  118. package/blocks/dialer/components/call-contact-button.tsx +252 -0
  119. package/blocks/dialer/components/call-log-screen.tsx +205 -0
  120. package/blocks/dialer/components/call-log.tsx +7 -3
  121. package/blocks/dialer/components/call-queue.tsx +1 -1
  122. package/blocks/dialer/components/dialer-screen.tsx +319 -0
  123. package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
  124. package/blocks/dialer/components/live-calls-table.tsx +7 -3
  125. package/blocks/dialer/components/session-start-form.tsx +4 -2
  126. package/blocks/dialer/components/softphone.tsx +3 -1
  127. package/blocks/enrichment/README.md +14 -2
  128. package/blocks/enrichment/block.json +32 -4
  129. package/blocks/enrichment/components/bulk-enrich-panel.tsx +10 -5
  130. package/blocks/enrichment/components/email-verify-badge.tsx +28 -8
  131. package/blocks/enrichment/components/enrich-button.tsx +32 -9
  132. package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
  133. package/blocks/enrichment/components/enrichment-screen.tsx +288 -0
  134. package/blocks/enrichment/components/waterfall-config.tsx +39 -10
  135. package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
  136. package/blocks/forms/README.md +12 -0
  137. package/blocks/forms/block.json +39 -17
  138. package/blocks/forms/components/form-builder.tsx +3 -1
  139. package/blocks/forms/components/form-embed.tsx +3 -1
  140. package/blocks/forms/components/forms-screen.tsx +353 -0
  141. package/blocks/forms/components/submissions-table.tsx +16 -6
  142. package/blocks/inbox/README.md +27 -3
  143. package/blocks/inbox/block.json +14 -2
  144. package/blocks/inbox/components/inbox-list.tsx +7 -3
  145. package/blocks/inbox/components/inbox-screen.tsx +294 -27
  146. package/blocks/inbox/components/reply-composer.tsx +65 -9
  147. package/blocks/inbox/components/tag-and-assign.tsx +4 -2
  148. package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
  149. package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
  150. package/blocks/inbox/lib/inbox-types.ts +47 -1
  151. package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
  152. package/blocks/integrations/README.md +9 -0
  153. package/blocks/integrations/block.json +46 -1
  154. package/blocks/integrations/components/crm-detail.tsx +100 -0
  155. package/blocks/integrations/components/integration-list.tsx +9 -4
  156. package/blocks/integrations/components/integrations-screen.tsx +135 -0
  157. package/blocks/integrations/components/sync-status.tsx +5 -3
  158. package/blocks/integrations/components/webhook-detail.tsx +160 -0
  159. package/blocks/integrations/components/webhook-manager.tsx +13 -8
  160. package/blocks/integrations/lib/integrations-utils.ts +6 -1
  161. package/blocks/intent/README.md +14 -0
  162. package/blocks/intent/block.json +27 -1
  163. package/blocks/intent/components/intent-feed.tsx +5 -2
  164. package/blocks/intent/components/intent-keyword-manager.tsx +8 -4
  165. package/blocks/intent/components/intent-screen.tsx +265 -0
  166. package/blocks/intent/components/keyword-detail.tsx +124 -0
  167. package/blocks/lifecycle/README.md +16 -0
  168. package/blocks/lifecycle/block.json +34 -7
  169. package/blocks/lifecycle/components/account-related.tsx +66 -0
  170. package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
  171. package/blocks/lifecycle/components/lifecycle-screen.tsx +149 -0
  172. package/blocks/lifecycle/lib/lifecycle.ts +1 -1
  173. package/blocks/lists/README.md +13 -0
  174. package/blocks/lists/block.json +39 -1
  175. package/blocks/lists/components/add-to-list-button.tsx +3 -2
  176. package/blocks/lists/components/list-picker.tsx +5 -1
  177. package/blocks/lists/components/list-table.tsx +11 -5
  178. package/blocks/lists/components/lists-screen.tsx +344 -0
  179. package/blocks/lists/components/suppression-manager.tsx +6 -2
  180. package/blocks/lists/components/suppressions-screen.tsx +255 -0
  181. package/blocks/mailboxes/README.md +17 -0
  182. package/blocks/mailboxes/block.json +65 -17
  183. package/blocks/mailboxes/components/connect-mailbox.tsx +23 -13
  184. package/blocks/mailboxes/components/domain-detail.tsx +142 -0
  185. package/blocks/mailboxes/components/domain-purchase.tsx +6 -3
  186. package/blocks/mailboxes/components/mailbox-detail.tsx +156 -0
  187. package/blocks/mailboxes/components/mailboxes-screen.tsx +223 -0
  188. package/blocks/mailboxes/components/warmup-health.tsx +4 -2
  189. package/blocks/mailboxes/lib/mailboxes-utils.ts +22 -1
  190. package/blocks/newsletter/README.md +13 -0
  191. package/blocks/newsletter/block.json +47 -23
  192. package/blocks/newsletter/components/issue-scheduler.tsx +2 -1
  193. package/blocks/newsletter/components/newsletter-screen.tsx +404 -0
  194. package/blocks/newsletter/components/subscriber-table.tsx +12 -6
  195. package/blocks/nurture/README.md +12 -0
  196. package/blocks/nurture/block.json +35 -13
  197. package/blocks/nurture/components/nurture-builder.tsx +3 -1
  198. package/blocks/nurture/components/nurture-enroll.tsx +6 -3
  199. package/blocks/nurture/components/nurture-form.tsx +23 -3
  200. package/blocks/nurture/components/nurture-screen.tsx +283 -0
  201. package/blocks/nurture/lib/nurture-types.ts +1 -1
  202. package/blocks/objects/README.md +17 -0
  203. package/blocks/objects/block.json +29 -16
  204. package/blocks/objects/components/object-not-found.tsx +3 -1
  205. package/blocks/objects/components/object-record-detail.tsx +2 -1
  206. package/blocks/objects/components/object-schema-editor.tsx +3 -1
  207. package/blocks/objects/components/object-table.tsx +13 -3
  208. package/blocks/objects/components/objects-screen.tsx +147 -0
  209. package/blocks/objects/lib/object-types.ts +1 -1
  210. package/blocks/pages/README.md +12 -0
  211. package/blocks/pages/block.json +35 -22
  212. package/blocks/pages/components/brand-kit-editor.tsx +5 -2
  213. package/blocks/pages/components/content-library.tsx +46 -11
  214. package/blocks/pages/components/page-list.tsx +6 -3
  215. package/blocks/pages/components/pages-screen.tsx +154 -0
  216. package/blocks/quotes/README.md +17 -0
  217. package/blocks/quotes/block.json +38 -9
  218. package/blocks/quotes/components/product-picker.tsx +3 -1
  219. package/blocks/quotes/components/quote-list.tsx +7 -3
  220. package/blocks/quotes/components/quote-related.tsx +123 -0
  221. package/blocks/quotes/components/quotes-screen.tsx +169 -0
  222. package/blocks/radar/README.md +10 -0
  223. package/blocks/radar/block.json +41 -1
  224. package/blocks/radar/components/competitor-detail.tsx +117 -0
  225. package/blocks/radar/components/competitor-tracker.tsx +3 -1
  226. package/blocks/radar/components/radar-report.tsx +4 -2
  227. package/blocks/radar/components/radar-screen.tsx +214 -0
  228. package/blocks/radar/components/report-detail.tsx +104 -0
  229. package/blocks/search/README.md +10 -0
  230. package/blocks/search/block.json +16 -1
  231. package/blocks/search/components/filter-builder.tsx +3 -3
  232. package/blocks/search/components/prospect-results.tsx +5 -3
  233. package/blocks/search/components/prospect-search-screen.tsx +235 -0
  234. package/blocks/search/components/saved-searches.tsx +4 -1
  235. package/blocks/search/hooks/use-filter-options.ts +20 -6
  236. package/blocks/search/lib/search-types.ts +2 -0
  237. package/blocks/sequencer/README.md +49 -9
  238. package/blocks/sequencer/block.json +99 -19
  239. package/blocks/sequencer/components/enroll-dialog.tsx +9 -3
  240. package/blocks/sequencer/components/sequence-builder.tsx +211 -46
  241. package/blocks/sequencer/components/sequence-detail.tsx +259 -0
  242. package/blocks/sequencer/components/sequence-enrollments.tsx +114 -0
  243. package/blocks/sequencer/components/sequence-list.tsx +33 -8
  244. package/blocks/sequencer/components/sequence-stats.tsx +3 -2
  245. package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
  246. package/blocks/sequencer/components/sequences-screen.tsx +159 -0
  247. package/blocks/sequencer/components/step-editor.tsx +3 -1
  248. package/blocks/sequencer/lib/sequencer-utils.ts +128 -5
  249. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
  250. package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
  251. package/blocks/social-ads/README.md +11 -0
  252. package/blocks/social-ads/block.json +101 -2
  253. package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
  254. package/blocks/social-ads/components/ads-screen.tsx +152 -0
  255. package/blocks/social-ads/components/audience-sync-detail.tsx +188 -0
  256. package/blocks/social-ads/components/audience-sync.tsx +11 -7
  257. package/blocks/social-ads/components/campaign-detail.tsx +133 -0
  258. package/blocks/social-ads/components/creative-detail.tsx +150 -0
  259. package/blocks/social-ads/components/creative-gallery.tsx +15 -5
  260. package/blocks/social-ads/components/post-composer.tsx +3 -1
  261. package/blocks/social-ads/components/post-detail.tsx +160 -0
  262. package/blocks/social-ads/components/social-post-list.tsx +11 -3
  263. package/blocks/social-ads/components/social-screen.tsx +160 -0
  264. package/blocks/visitors/README.md +10 -0
  265. package/blocks/visitors/block.json +25 -1
  266. package/blocks/visitors/components/snippet-install.tsx +3 -1
  267. package/blocks/visitors/components/visitor-company-card.tsx +2 -2
  268. package/blocks/visitors/components/visitor-detail.tsx +105 -0
  269. package/blocks/visitors/components/visitor-feed.tsx +11 -2
  270. package/blocks/visitors/components/visitors-screen.tsx +166 -0
  271. package/blocks/webchat/README.md +10 -0
  272. package/blocks/webchat/block.json +21 -7
  273. package/blocks/webchat/components/chat-widget-config.tsx +7 -4
  274. package/blocks/webchat/components/conversation-list.tsx +7 -3
  275. package/blocks/webchat/components/webchat-screen.tsx +245 -0
  276. package/blocks/workbench/README.md +11 -0
  277. package/blocks/workbench/block.json +48 -1
  278. package/blocks/workbench/components/column-recipe-editor.tsx +4 -2
  279. package/blocks/workbench/components/workbench-grid.tsx +9 -3
  280. package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
  281. package/blocks/workbench/components/workbench-screen.tsx +192 -0
  282. package/blocks/workbench/components/workbench-sources.tsx +194 -0
  283. package/blocks/workflows/README.md +13 -0
  284. package/blocks/workflows/block.json +28 -9
  285. package/blocks/workflows/components/run-history.tsx +31 -9
  286. package/blocks/workflows/components/trigger-config.tsx +3 -1
  287. package/blocks/workflows/components/workflow-editor.tsx +3 -1
  288. package/blocks/workflows/components/workflow-list.tsx +13 -6
  289. package/blocks/workflows/components/workflows-screen.tsx +193 -0
  290. package/dist/cli.js +2785 -1637
  291. package/dist/errors.d.mts +34 -0
  292. package/dist/errors.d.ts +34 -0
  293. package/dist/errors.js +46 -0
  294. package/dist/errors.js.map +1 -0
  295. package/dist/errors.mjs +21 -0
  296. package/dist/errors.mjs.map +1 -0
  297. package/dist/index.browser.js +11 -5
  298. package/dist/index.browser.js.map +1 -1
  299. package/dist/index.browser.mjs +11 -5
  300. package/dist/index.browser.mjs.map +1 -1
  301. package/dist/index.d.mts +164 -25
  302. package/dist/index.d.ts +164 -25
  303. package/dist/index.js +11 -5
  304. package/dist/index.js.map +1 -1
  305. package/dist/index.mjs +11 -5
  306. package/dist/index.mjs.map +1 -1
  307. package/dist/react.browser.js +10 -4
  308. package/dist/react.browser.js.map +1 -1
  309. package/dist/react.browser.mjs +10 -4
  310. package/dist/react.browser.mjs.map +1 -1
  311. package/dist/react.d.mts +142 -5
  312. package/dist/react.d.ts +142 -5
  313. package/dist/react.js +10 -4
  314. package/dist/react.js.map +1 -1
  315. package/dist/react.mjs +10 -4
  316. package/dist/react.mjs.map +1 -1
  317. package/dist/types/blocks/add.d.ts +11 -4
  318. package/dist/types/blocks/brand.d.ts +113 -0
  319. package/dist/types/blocks/compose.d.ts +53 -0
  320. package/dist/types/blocks/create.d.ts +25 -8
  321. package/dist/types/blocks/doctor.d.ts +6 -1
  322. package/dist/types/blocks/flows.d.ts +20 -0
  323. package/dist/types/blocks/navfile.d.ts +35 -0
  324. package/dist/types/blocks/paths.d.ts +4 -0
  325. package/dist/types/blocks/prefixes.d.ts +37 -0
  326. package/dist/types/blocks/registry.d.ts +32 -0
  327. package/dist/types/blocks/screens.d.ts +53 -0
  328. package/dist/types/cli.d.ts +2 -0
  329. package/dist/types/errors.d.ts +33 -0
  330. package/dist/types/http.d.ts +2 -18
  331. package/dist/types/version.d.ts +2 -2
  332. package/docs/agents.md +15 -4
  333. package/docs/api/README.md +2 -2
  334. package/docs/api/by-path.md +1 -0
  335. package/docs/api/desk.d.ts +98 -2
  336. package/docs/api/desk.md +123 -1
  337. package/docs/api/inbox.d.ts +10 -2
  338. package/docs/api/inbox.md +10 -2
  339. package/docs/api/seo.d.ts +19 -1
  340. package/docs/api/seo.md +24 -1
  341. package/package.json +16 -2
  342. package/stacks/6sense.json +41 -16
  343. package/stacks/apollo/pages/companies.tsx.tmpl +50 -0
  344. package/stacks/apollo/pages/contacts.tsx.tmpl +117 -0
  345. package/stacks/apollo.json +118 -36
  346. package/stacks/calendly.json +60 -0
  347. package/stacks/clay/pages/companies.tsx.tmpl +15 -0
  348. package/stacks/clay/pages/contacts.tsx.tmpl +19 -0
  349. package/stacks/clay.json +59 -26
  350. package/stacks/gong.json +81 -35
  351. package/stacks/hootsuite.json +99 -0
  352. package/stacks/hubspot/pages/companies.tsx.tmpl +47 -0
  353. package/stacks/hubspot/pages/contacts.tsx.tmpl +111 -0
  354. package/stacks/hubspot/pages/deals.tsx.tmpl +11 -0
  355. package/stacks/hubspot/pages/inbox.tsx.tmpl +92 -0
  356. package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
  357. package/stacks/hubspot.json +167 -66
  358. package/stacks/intercom.json +48 -20
  359. package/stacks/mailchimp.json +52 -33
  360. package/stacks/outreach/pages/contacts.tsx.tmpl +85 -0
  361. package/stacks/outreach.json +88 -25
  362. package/stacks/pipedrive/pages/companies.tsx.tmpl +47 -0
  363. package/stacks/pipedrive/pages/contacts.tsx.tmpl +47 -0
  364. package/stacks/pipedrive/pages/deals.tsx.tmpl +11 -0
  365. package/stacks/pipedrive/pages/inbox.tsx.tmpl +92 -0
  366. package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
  367. package/stacks/pipedrive.json +115 -0
  368. package/stacks/salesforce/pages/companies.tsx.tmpl +47 -0
  369. package/stacks/salesforce/pages/contacts.tsx.tmpl +47 -0
  370. package/stacks/salesforce/pages/deals.tsx.tmpl +11 -0
  371. package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
  372. package/stacks/salesforce.json +81 -31
  373. package/stacks/stack.schema.json +6 -1
  374. package/stacks/zoominfo/pages/companies.tsx.tmpl +15 -0
  375. package/stacks/zoominfo/pages/contacts.tsx.tmpl +19 -0
  376. package/stacks/zoominfo.json +102 -0
  377. package/styles/styles.css +1 -1
  378. package/templates/next-app/README.md.tmpl +55 -8
  379. package/templates/next-app/app/globals.css.tmpl +105 -0
  380. package/templates/next-app/app/layout.tsx.tmpl +18 -4
  381. package/templates/next-app/components/app-shell.tsx.tmpl +248 -23
  382. package/templates/next-app/components/shell/connection-status.tsx.tmpl +109 -0
  383. package/templates/next-app/components/shell/nav-model.ts.tmpl +65 -0
  384. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +131 -0
  385. package/templates/next-app/components/shell/theme-toggle.tsx.tmpl +40 -0
  386. package/templates/next-app/dot-gitignore.tmpl +2 -0
  387. package/templates/next-app/lib/nav.ts.tmpl +1 -1
  388. package/templates/next-app/package.json.tmpl +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@ The most comprehensive GTM SDK. One `npm install`. Every graph8 capability.
8
8
  > operation in [`docs/api/`](docs/api/README.md). It ships with the package, so it matches this version.
9
9
  > Run `npx @graph8/sdk init-agent` to point Claude Code, Codex and Cursor in your project at it automatically.
10
10
  >
11
- > Blocks (copy-in components and hooks per feature): `npx @graph8/sdk list`, `search <text>`, `add <block>[/<Component>]`, `create <stack> <dir>` (a whole Next.js app: `npx @graph8/sdk create starter my-app`), and `doctor` to check your key, scopes and subscription. Each takes `--help`.
11
+ > Blocks (copy-in components and hooks per feature): `npx @graph8/sdk list`, `search <text>`, `add <block>[/<Component>]` (`--routes` also wires its screens as routes and nav entries in a created app), `create <stack> <dir>` (a whole Next.js app: `npx @graph8/sdk create starter my-app`), and `doctor` to check your key, scopes and subscription. Each takes `--help`.
12
12
 
13
13
  ## Install
14
14
 
@@ -496,15 +496,19 @@ npx @graph8/sdk create mailchimp my-app # then: cd my-app && cp .env.local.exa
496
496
  | Stack | Screens |
497
497
  |---|---|
498
498
  | `starter` | An empty app: proxy, shell and one landing page |
499
- | `mailchimp` | Contacts, companies, forms, submissions, lists, suppressions, newsletter, subscribers, nurture |
500
- | `clay` | Contacts, companies, lists, suppressions, prospect search, bulk enrichment, workbench |
501
- | `6sense` | Contacts, companies, intent feed, website visitors, competitor tracker |
502
- | `intercom` | Contacts, companies, web chat conversations, mailboxes, AI inbox, AI agents |
503
- | `outreach` | Contacts, companies, lists, mailboxes, sequences, inbox, dialer, call log, meetings, analytics |
504
- | `apollo` | Outreach's screens plus prospect search, enrichment, intent, visitors, deals and integrations |
505
- | `gong` | Contacts, companies, lists, dialer, call log, meetings, coaching feed, analytics |
506
- | `salesforce` | Contacts, companies, custom objects, lists, lifecycle, deals, quotes, analytics, workflows, copilot, integrations |
507
- | `hubspot` | The full CRM and marketing suite: forms, newsletter, nurture, web chat, sequences, inbox, pages, lifecycle, deals, quotes, workflows |
499
+ | `mailchimp` | Newsletter, nurture, forms, contacts, companies, tasks, lists, suppressions |
500
+ | `clay` | Prospect search, workbench, enrichment, lists, contacts, companies, tasks, suppressions |
501
+ | `6sense` | Intent, visitors, competitors, companies, contacts, tasks |
502
+ | `intercom` | Inbox, web chat conversations, contacts, companies, tasks, AI agents, mailboxes |
503
+ | `calendly` | Meetings and scheduling (event types, availability, booking pages) over contacts, companies and tasks |
504
+ | `pipedrive` | Analytics, deals, quotes, contacts, companies, inbox, meetings, scheduling, tasks and mailboxes; deals and quotes open each other, replies can start a deal |
505
+ | `outreach` | Analytics, contacts, companies, lists, sequences, campaigns, inbox, dialer, call log, meetings, scheduling, tasks, mailboxes, suppressions |
506
+ | `apollo` | Outreach's screens plus prospect search, enrichment, intent, visitors, deals and integrations; contacts get Enrich, Add to sequence and a Deals tab |
507
+ | `gong` | Analytics, call log, coaching, dialer, meetings, scheduling, contacts, companies, tasks, lists, suppressions |
508
+ | `salesforce` | Analytics, contacts, companies, lists, deals, quotes, lifecycle, tasks, workflows, copilot, custom objects, suppressions, integrations |
509
+ | `hubspot` | The full CRM and marketing suite: inbox, web chat, sequences, campaigns, newsletter, nurture, forms, pages, social, ads, deals, quotes, lifecycle, workflows |
510
+ | `zoominfo` | Prospect search, enrichment, intent and visitors over contacts, companies and lists |
511
+ | `hootsuite` | Social posts, ads, landing pages and analytics over your contacts and lists |
508
512
 
509
513
  Every available stack is proven by `npm run build && npm run e2e:create -- <stack>`, which packs the SDK, scaffolds the app with the built CLI, installs it and requires `next build` to pass. More stacks appear here as their blocks land; `create` refuses a stack that is not available yet.
510
514
 
@@ -8,7 +8,9 @@ Shared base every other block depends on. Install it first (the CLI does it for
8
8
  |---|---|
9
9
  | `lib/g8-client.ts` | Browser caller: `g8Get/g8Post/g8Put/g8Patch/g8Delete` -> your own `/api/g8/...` proxy. No API key. Returns the API envelope `{ data, pagination? }`, throws `G8Error`. |
10
10
  | `server/proxy.ts` | `createG8Proxy(config)`: the logic of the proxy (forwarding, allowlist, `authorize` hook). |
11
- | `server/route.ts` | Installs as `app/api/g8/[...path]/route.ts` and mounts the proxy. |
11
+ | `server/route.ts` | Installs as `app/api/g8/[...path]/route.ts` and mounts the proxy. `allowedPrefixes` is generated from the installed blocks' operations; local preview is on. |
12
+ | `server/local-key.ts`, `server/connect.ts`, `server/connect-route.ts` | Local preview key store: `app/api/g8-connect/route.ts` checks a pasted key (GET /me) and keeps it in `.g8/local-key` on the server. 404 in production. |
13
+ | `components/connect-graph8.tsx` | `ConnectGraph8Panel` + `G8ConnectGate`: the first-run Connect graph8 panel in `next dev`; a no-op in production. |
12
14
  | `hooks/use-g8-query.ts` | `useG8Query(fetcher, deps)` -> `{ data, error, loading, refetch }`. Latest request wins. |
13
15
  | `hooks/use-g8-mutation.ts` | `useG8Mutation(fn)` -> `{ mutate, mutateAsync, data, error, loading, reset }`. |
14
16
  | `hooks/use-paginated.ts` | `usePaginated(fetchPage, deps)` -> `{ items, hasMore, loadMore, reload, loading, loadingMore, error }`. Follows `pagination.next_cursor`. |
@@ -16,6 +18,9 @@ Shared base every other block depends on. Install it first (the CLI does it for
16
18
  | `components/confirm-spend-dialog.tsx` | `ConfirmSpendDialog`: shows tier, estimated credits, recipients, before a billable/external/destructive call. |
17
19
  | `components/api-error.tsx` | `ApiError`: shows `detail`, 422 field errors, request id, retry. |
18
20
  | `components/empty-state.tsx` | `EmptyState`: empty list with a first step. |
21
+ | `components/screen/*.tsx` | The **screen kit** (below): `ScreenLayout`, `StatStrip`, `SplitView`, `DetailPanel`, `Toolbar`, `StatusPill`, `EntityAvatar`, `SectionCard`, `FormSheet`, `KeyValueList`, `Sparkline`, `BarList`, `TimeSeriesChart`. Barrel: `@/g8/components/screen`. |
22
+ | `hooks/use-url-state.ts` | `useUrlState(key, default = "")` -> `[value, set]`. One string in the URL query as state (replaceState + popstate, SSR-safe). |
23
+ | `lib/g8-cache.ts` | The shared query cache behind the hooks' optional `key`: dedupe, stale-while-revalidate, focus/reconnect refresh, `invalidate(prefix)`, `setQueryData`, bounded LRU. See "Query cache" below. |
19
24
 
20
25
  ## Flow of every call
21
26
 
@@ -28,6 +33,48 @@ Shared base every other block depends on. Install it first (the CLI does it for
28
33
  Ids that pass between calls always come from a previous response (a list's `id` into the next call's
29
34
  path). Never invent one.
30
35
 
36
+ ## Query cache (opt in with a `key`)
37
+
38
+ Every hook works exactly as before without a key. Pass one and the data lives in one app-wide cache
39
+ (`lib/g8-cache.ts`, no dependencies):
40
+
41
+ ```ts
42
+ const contacts = useG8Query(
43
+ (signal) => g8Get("/contacts", { page, limit: 25, q }, { signal }),
44
+ [page, q],
45
+ { key: ["contacts", "list", { page, q }] }, // name EVERY input the fetcher reads
46
+ );
47
+ const contact = useG8Query((s) => g8Get(`/contacts/${id}`, {}, { signal: s }), [id], { key: ["contacts", "detail", id] });
48
+ const save = useG8Mutation((v: Patch) => g8Patch(`/contacts/${v.id}`, v), { invalidates: ["contacts"] });
49
+ ```
50
+
51
+ - **Dedupe**: components asking for the same key share one request and one answer.
52
+ - **Stale-while-revalidate**: a cached key renders instantly on mount (no skeleton) and refreshes in the
53
+ background once older than `staleTime` (default 0). Use `staleTime: 60_000` for data that rarely changes
54
+ (custom-field schemas, pipelines, dispositions).
55
+ - **Focus/reconnect**: watched keys refresh when the window regains focus or the network returns, at most
56
+ once per 5 s per key (`refetchOnFocus: false` opts out; `usePaginated` defaults to false so a scroll is kept).
57
+ - **Invalidation**: `invalidate("contacts")` (or a mutation's `invalidates: ["contacts"]`) marks every key
58
+ starting with `"contacts"` stale; keys on screen refetch now, a request started before the write is
59
+ superseded, the rest refetch when next shown. Prefixes match whole parts (`"deals"` never matches `"deals-x"`).
60
+ - **Writes into the cache**: `setQueryData(key, value | old => new)`; `useG8Mutation(fn, { optimistic: { prefix, update } })`
61
+ changes cached data before the request and puts it back if the write fails.
62
+ - **Memory**: at most 200 keys (LRU); a key nobody watches is dropped after 5 minutes; requests abort when
63
+ their LAST subscriber unmounts.
64
+ - **`placeholderData`**: show a list row while the full record loads (`isPlaceholderData: true`). Never seed
65
+ an edit form from it (edit-before-load PATCHes blanks over real values): enable Edit only when
66
+ `!isPlaceholderData && !loading`.
67
+ - **SSR**: nothing is cached on the server; hooks render their initial state and fetch after hydration.
68
+ - **Keys are JSON-like**: strings, numbers, booleans, null, arrays, plain objects and Dates. A `Map`, `Set`,
69
+ `URLSearchParams` or class instance throws (it would otherwise collide with every other one).
70
+ - **Sign-out and org switch**: the cache is one per page and is not keyed by user or org. Call
71
+ `g8QueryCache.clear()` on sign-out or org switch, and add the org to your keys if you pass `targetOrgId`,
72
+ so one org's cached rows are never shown to another.
73
+
74
+ Key convention used by the blocks: `[area, "list" | "detail" | <sub-resource>, ...inputs]`, area = the API
75
+ path's first segment (`contacts`, `companies`, `deals`, `sequences`, `lists`, `inbox`, `tasks`...), so a write
76
+ to an area invalidates that area with one prefix.
77
+
31
78
  ## Tiers: when to confirm
32
79
 
33
80
  Every operation has a tier (`read`, `write`, `billable`, `external`, `destructive`); each component's
@@ -60,6 +107,21 @@ The proxy therefore **refuses every request (500) until you configure it**:
60
107
  5. Local prototypes only: `dangerouslyAllowUnauthenticated: true` and `dangerouslyAllowAllPaths: true`
61
108
  switch the two gates off. Never ship them.
62
109
 
110
+ ### Local preview (apps made by `graph8-sdk create`)
111
+
112
+ Created apps run before they have sign-in: the route sets `localPreview: true`. While `authorize` is unset the
113
+ proxy answers a request only if NODE_ENV is not `production` AND it comes from this machine: Host is
114
+ `localhost`, `127.0.0.1` or `[::1]` (blocks DNS rebinding), the browser's `Sec-Fetch-Site`/`Origin` say it is
115
+ the app's own page (blocks other sites in the same browser), and no forwarding proxy names a remote client
116
+ (blocks tunnels). `npm run dev` listens on 127.0.0.1, because a machine on your network could forge those
117
+ headers. Other hosts get 403; a production build gets 500 until `authorize` is set. Once `authorize` is set,
118
+ `localPreview` does nothing.
119
+
120
+ The key comes from `G8_API_KEY` (env, `.env.local`) or, in local preview only, the key pasted in the
121
+ Connect graph8 panel, which the server checks against `GET /me` and stores in `.g8/local-key` (0600, git-ignored).
122
+ `npx @graph8/sdk doctor --production` fails until the route is safe to deploy;
123
+ `npx @graph8/sdk doctor --fix-prefixes` regenerates `allowedPrefixes` after you add or remove blocks.
124
+
63
125
  What the proxy does on its own: rejects `..`, encoded slashes and empty segments (400); forwards only
64
126
  `content-type`, `accept`, `accept-language` and `idempotency-key` (never the browser's cookies or
65
127
  `Authorization`); strips hop-by-hop headers, `set-cookie` and `location` from the response; does not follow
@@ -67,6 +129,115 @@ redirects (an upstream 3xx becomes a 502); caps the request body at 5 MiB (413),
67
129
  an oversized upload is never held whole in memory; returns 500 with a clear message if `G8_API_KEY` is unset
68
130
  and a generic 502 if graph8 is unreachable. The key is never logged, echoed or included in any response.
69
131
 
132
+ ## Screen kit
133
+
134
+ Finished screens (`<Name>Screen` in every block) are composed from these parts, so an Apollo clone and a
135
+ HubSpot clone look like one product. Import each part from `@/g8/components/screen/<file>` (or everything
136
+ from `@/g8/components/screen`). The look follows graph8's rules: ink on white, calm density, colour only
137
+ for meaning, no entrance animations, light and dark through the tokens.
138
+
139
+ | Part | File | Use it for |
140
+ |---|---|---|
141
+ | `ScreenLayout` | `screen-layout` | The frame: sticky header (icon, title, description, actions, underline `tabs`), `stats`, `toolbar`, content. One per route. |
142
+ | `StatStrip` | `stat-strip` | KPI tiles. `StatItem = { label, value, delta?, hint?, tone?, sparkline?, footer?, onClick? }`. Numbers are ink; a delta takes colour only through `tone`. |
143
+ | `SplitView` | `split-view` | `list` beside the selected record (`detail`), `detailWidth` md, lg, xl. Under 1024px the detail is a right sheet. `expanded` + `onExpandedChange` make it a full-page record screen with a Back affordance. |
144
+ | `DetailPanel` | `detail-panel` | The record: sticky header (avatar, title, subtitle, badges, actions, close) + `tabs: { id, label, content, count? }[]` or children. `onExpand` shows "Open full page"; `onCollapse` shows "Back to split view". |
145
+ | `Toolbar` | `toolbar` | Debounced `search` (Enter flushes, Escape clears), `filters`, right-aligned `actions`, `status` (polite result count), a chips row as children. |
146
+ | `StatusPill` | `status-pill` | `tone`: neutral, info, positive, warning, negative. `toneForStatus(raw)` maps API states; unknown is warning, never positive. `humanizeStatus`. |
147
+ | `EntityAvatar` | `entity-avatar` | `kind` person (circle, initials) or company (rounded square, letter); `size` sm, md, lg; `imageUrl`. |
148
+ | `SectionCard` | `section-card` | A flat, hairlined section: title, description, actions, body (`flush` for tables), footer. |
149
+ | `FormSheet` | `form-sheet` | Create/edit sheet: `title`, `description`, fields as children, `footer` (Cancel, then the primary), `onSubmit` wraps the body in a form. |
150
+ | `KeyValueList` | `key-value-list` | Facts about a record, `columns` 1 or 2, `inline` for dense rows. A missing value is a faint dash. |
151
+ | `Sparkline` | `sparkline` | Tiny SVG trend for tiles and cells. `data: number[]`, optional `tone`. |
152
+ | `BarList` | `bar-list` | Ranked rows with a proportional ink bar; `href` makes a row a link. |
153
+ | `TimeSeriesChart` | `time-series-chart` | `data`, `xKey`, `series: { key, label?, tone? }[]`, `kind` line, area, bar. Needs the `recharts` peer. `tone: "prior"` is the dashed comparison. |
154
+ | `useRowActivation` | `hooks/use-row-activation` | `<TableRow {...rowActivationProps(() => open(row.id))}>` (inside `.map()`; `useRowActivation` is the hook form): the whole row is focusable, Enter/Space/click open it, nested checkboxes, buttons and links keep their own behaviour. Pass `{ role: "button" }` for a non-table row. |
155
+ | `RecordLink` | `components/screen/record-link` | A record name as a real `<a href="?id=X">`: plain click opens in place (URL state), Cmd/Ctrl/middle-click opens a new tab on the same record. `full` adds `view=full`. |
156
+ | `plural`, `pluralWord` | `lib/plural` | `plural(1, "contact")` -> "1 contact", `plural(3, "company", "companies")` -> "3 companies". Never hand-roll `=== 1 ? "" : "s"`. |
157
+ | `useUrlState` | `hooks/use-url-state` | `const [id, setId] = useUrlState("id")`; `setId(null)` removes it; `{ push: true }` adds a history entry. |
158
+
159
+ A finished list screen, start to end:
160
+
161
+ ```tsx
162
+ "use client";
163
+ import { Users, Plus } from "lucide-react";
164
+ import { Button } from "@graph8/sdk/ui";
165
+ import { ScreenLayout } from "@/g8/components/screen/screen-layout";
166
+ import { Toolbar } from "@/g8/components/screen/toolbar";
167
+ import { SplitView } from "@/g8/components/screen/split-view";
168
+ import { DetailPanel } from "@/g8/components/screen/detail-panel";
169
+ import { EntityAvatar } from "@/g8/components/screen/entity-avatar";
170
+ import { StatusPill, toneForStatus, humanizeStatus } from "@/g8/components/screen/status-pill";
171
+ import { KeyValueList } from "@/g8/components/screen/key-value-list";
172
+ import { SectionCard } from "@/g8/components/screen/section-card";
173
+ import { FormSheet } from "@/g8/components/screen/form-sheet";
174
+ import { useUrlState } from "@/g8/hooks/use-url-state";
175
+
176
+ export function ContactsScreen() {
177
+ const [q, setQ] = useUrlState("q");
178
+ const [id, setId] = useUrlState("id");
179
+ const [creating, setCreating] = useUrlState("new");
180
+ const [view, setView] = useUrlState("view"); // "full" = the record as a full page (?id=X&view=full)
181
+ const contacts = useContacts(q); // your block's hook; { data, loading, error, refetch }
182
+ const open = contacts.data?.find((c) => c.id === id);
183
+
184
+ return (
185
+ <ScreenLayout
186
+ title="Contacts"
187
+ description="Everyone your team is working"
188
+ icon={<Users />}
189
+ stats={[{ label: "Contacts", value: contacts.total ?? "-" }, { label: "Replied this week", value: 12, delta: 8, tone: "positive" }]}
190
+ actions={<Button onClick={() => setCreating("1")}><Plus />Add contact</Button>}
191
+ toolbar={<Toolbar search={{ value: q, onChange: setQ, placeholder: "Search contacts" }} status={contacts.total != null ? `${contacts.total} contacts` : undefined} />}
192
+ fill
193
+ >
194
+ <SplitView
195
+ detailOpen={!!open}
196
+ onCloseDetail={() => { setId(null); setView(null); }}
197
+ expanded={view === "full"}
198
+ onExpandedChange={(full) => setView(full ? "full" : null)}
199
+ list={<ContactTable rows={contacts.data} selectedId={id} onOpen={(c) => setId(c.id)} />}
200
+ detail={open ? (
201
+ <DetailPanel
202
+ title={open.name}
203
+ subtitle={open.title}
204
+ avatar={<EntityAvatar name={open.name} imageUrl={open.avatar_url} size="lg" />}
205
+ badges={<StatusPill tone={toneForStatus(open.status)} dot>{humanizeStatus(open.status)}</StatusPill>}
206
+ onClose={() => { setId(null); setView(null); }}
207
+ onExpand={view === "full" ? undefined : () => setView("full")}
208
+ onCollapse={view === "full" ? () => setView(null) : undefined}
209
+ tabs={[
210
+ { id: "overview", label: "Overview", content: (
211
+ <SectionCard title="Details">
212
+ <KeyValueList columns={2} items={[{ label: "Email", value: open.email }, { label: "Phone", value: open.phone }]} />
213
+ </SectionCard>
214
+ ) },
215
+ { id: "activity", label: "Activity", content: <ActivityFeed contactId={open.id} /> },
216
+ ]}
217
+ />
218
+ ) : null}
219
+ />
220
+ <FormSheet open={!!creating} onOpenChange={(o) => setCreating(o ? "1" : null)} title="Add contact" onSubmit={submit}
221
+ footer={<><Button variant="outline" type="button" onClick={() => setCreating(null)}>Cancel</Button><Button type="submit">Add contact</Button></>}>
222
+ {/* fields */}
223
+ </FormSheet>
224
+ </ScreenLayout>
225
+ );
226
+ }
227
+ ```
228
+
229
+ Rules the kit encodes, so screens stay consistent:
230
+
231
+ - The URL owns the open record, the tab and the search (`useUrlState`): reload and Back work, links are shareable.
232
+ - Every record has a full-page view, not only a side panel: `?id=X&view=full` opens it (SplitView `expanded`), and
233
+ the panel's "Open full page" button gets there. Back returns to the split view with the list.
234
+ - One primary action per screen, last on the right. Bulk actions appear on selection. Edits autosave where possible.
235
+ - Colour means good or bad for the user: `tone` on deltas, pills and series; everything else is ink.
236
+ - An unknown status is a warning pill with the humanized value, never a green "Active".
237
+ - A failed load is `ApiError` with Retry, never an empty list; an empty list is `EmptyState` with the first step.
238
+ - Loading is visible and distinct from empty: `StatStrip loading`, `Skeleton` rows.
239
+ - Dates show short text; the full timestamp is the `title` of a `<time dateTime>`.
240
+
70
241
  ## Prerequisites
71
242
 
72
243
  - `G8_API_KEY` in the server env; verify with `recipes/verify-proxy-key.ts`.
@@ -3,9 +3,18 @@
3
3
  "id": "foundation",
4
4
  "title": "Foundation",
5
5
  "summary": "Shared base for every block: browser client, data hooks, Next.js proxy route, confirm/error/empty components.",
6
- "features": ["browser-safe API calls via a server proxy", "query, mutation, pagination and job-polling hooks", "spend/send/delete confirmation dialog"],
6
+ "features": [
7
+ "browser-safe API calls via a server proxy",
8
+ "query, mutation, pagination and job-polling hooks",
9
+ "spend/send/delete confirmation dialog",
10
+ "screen kit: layout, stats, split view, detail panel, toolbar, status pills, avatars, cards, form sheet, charts",
11
+ "local preview: a Connect graph8 screen stores the server's key for `next dev`; generated allowedPrefixes",
12
+ "white-label: BrandProvider + useBrand give every block's copy and link-outs your platform name and app URL (default graph8)"
13
+ ],
7
14
  "dependsOn": [],
8
- "operations": [],
15
+ "operations": [
16
+ "describe_current_key_me_get"
17
+ ],
9
18
  "components": [
10
19
  {
11
20
  "name": "ConfirmSpendDialog",
@@ -14,6 +23,13 @@
14
23
  "operations": [],
15
24
  "tiers": []
16
25
  },
26
+ {
27
+ "name": "AskButton",
28
+ "file": "components/confirmed-action.tsx",
29
+ "purpose": "useConfirmAction + AskButton: a button asks, one ConfirmSpendDialog per screen confirms, then the write runs (Cancel is ignored while it runs).",
30
+ "operations": [],
31
+ "tiers": []
32
+ },
17
33
  {
18
34
  "name": "ApiError",
19
35
  "file": "components/api-error.tsx",
@@ -27,20 +43,209 @@
27
43
  "purpose": "Empty-list placeholder with a title, hint and an optional first-step action.",
28
44
  "operations": [],
29
45
  "tiers": []
46
+ },
47
+ {
48
+ "name": "ScreenLayout",
49
+ "file": "components/screen/screen-layout.tsx",
50
+ "purpose": "Frame of a finished screen: sticky header (icon, title, description, actions, underline tabs), StatStrip, toolbar, content.",
51
+ "operations": [],
52
+ "tiers": []
53
+ },
54
+ {
55
+ "name": "StatStrip",
56
+ "file": "components/screen/stat-strip.tsx",
57
+ "purpose": "Row of KPI tiles: label, value, delta (coloured only through tone), hint tooltip, sparkline, skeleton while loading.",
58
+ "operations": [],
59
+ "tiers": []
60
+ },
61
+ {
62
+ "name": "SplitView",
63
+ "file": "components/screen/split-view.tsx",
64
+ "purpose": "List beside the selected record; the detail pane becomes a right Sheet under 1024px.",
65
+ "operations": [],
66
+ "tiers": []
67
+ },
68
+ {
69
+ "name": "DetailPanel",
70
+ "file": "components/screen/detail-panel.tsx",
71
+ "purpose": "The selected record: sticky header (avatar, title, subtitle, badges, actions, close) and underline tabs or plain children.",
72
+ "operations": [],
73
+ "tiers": []
74
+ },
75
+ {
76
+ "name": "Toolbar",
77
+ "file": "components/screen/toolbar.tsx",
78
+ "purpose": "The row above a list: debounced search (Enter flushes, Escape clears), filters, right-aligned actions, an optional chips row.",
79
+ "operations": [],
80
+ "tiers": []
81
+ },
82
+ {
83
+ "name": "StatusPill",
84
+ "file": "components/screen/status-pill.tsx",
85
+ "purpose": "State badge with a tone (neutral, info, positive, warning, negative); toneForStatus maps raw API states, unknown ones to warning.",
86
+ "operations": [],
87
+ "tiers": []
88
+ },
89
+ {
90
+ "name": "EntityAvatar",
91
+ "file": "components/screen/entity-avatar.tsx",
92
+ "purpose": "Person (circle, initials) or company (rounded square, first letter) avatar in three sizes, with an image when there is one.",
93
+ "operations": [],
94
+ "tiers": []
95
+ },
96
+ {
97
+ "name": "SectionCard",
98
+ "file": "components/screen/section-card.tsx",
99
+ "purpose": "Flat hairlined section with a title row (title, description, actions) and a body; cards cast no shadow.",
100
+ "operations": [],
101
+ "tiers": []
102
+ },
103
+ {
104
+ "name": "FormSheet",
105
+ "file": "components/screen/form-sheet.tsx",
106
+ "purpose": "Create/edit side sheet: header, scrolling fields, sticky footer; optional onSubmit wraps the body in a form.",
107
+ "operations": [],
108
+ "tiers": []
109
+ },
110
+ {
111
+ "name": "KeyValueList",
112
+ "file": "components/screen/key-value-list.tsx",
113
+ "purpose": "Labelled facts about a record in one or two columns; a missing value is a faint dash.",
114
+ "operations": [],
115
+ "tiers": []
116
+ },
117
+ {
118
+ "name": "Sparkline",
119
+ "file": "components/screen/sparkline.tsx",
120
+ "purpose": "Tiny inline SVG trend (ink line, 6% area) for stat tiles and cells; no chart library.",
121
+ "operations": [],
122
+ "tiers": []
123
+ },
124
+ {
125
+ "name": "BarList",
126
+ "file": "components/screen/bar-list.tsx",
127
+ "purpose": "Ranked rows with a proportional ink bar and a right-aligned value; rows can be links.",
128
+ "operations": [],
129
+ "tiers": []
130
+ },
131
+ {
132
+ "name": "TimeSeriesChart",
133
+ "file": "components/screen/time-series-chart.tsx",
134
+ "purpose": "Line, area or bar chart over time on recharts: ink series, tones only for meaning, dashed prior period, hairline grid.",
135
+ "operations": [],
136
+ "tiers": []
137
+ },
138
+ {
139
+ "name": "RecordLink",
140
+ "file": "components/screen/record-link.tsx",
141
+ "purpose": "A record's name as a real <a href=\"?id=X\">: a plain click opens it in place (URL state), Cmd/Ctrl/middle-click opens a new tab on the same record.",
142
+ "operations": [],
143
+ "tiers": []
144
+ },
145
+ {
146
+ "name": "ConnectGraph8Panel",
147
+ "file": "components/connect-graph8.tsx",
148
+ "purpose": "Local preview only: paste a graph8 API key, the server checks it (GET /me) and stores it in .g8/local-key, never in the browser; replace or remove it. G8ConnectGate shows it until a key is connected and is a no-op in production.",
149
+ "operations": [],
150
+ "tiers": []
151
+ },
152
+ {
153
+ "name": "BrandProvider",
154
+ "file": "components/brand-provider.tsx",
155
+ "purpose": "White-label context: the app's name, logo, and what the platform is called and where it lives, for every block's copy and link-outs. Fields left out stay graph8's.",
156
+ "operations": [],
157
+ "tiers": []
30
158
  }
31
159
  ],
32
160
  "hooks": [
33
- { "name": "useG8Query", "file": "hooks/use-g8-query.ts", "purpose": "Load data with loading/error/data, an out-of-order guard and refetch." },
34
- { "name": "useG8Mutation", "file": "hooks/use-g8-mutation.ts", "purpose": "Run a write with loading/error state and success/error callbacks." },
35
- { "name": "usePaginated", "file": "hooks/use-paginated.ts", "purpose": "Cursor pagination over the API envelope (pagination.next_cursor) with load-more and reload." },
36
- { "name": "useJobPoll", "file": "hooks/use-job-poll.ts", "purpose": "Poll a job-status fetcher with backoff until a terminal state (the API has no streams)." }
161
+ {
162
+ "name": "useG8Query",
163
+ "file": "hooks/use-g8-query.ts",
164
+ "purpose": "Load data with loading/error/data, an out-of-order guard and refetch. Optional `key` joins the shared cache (dedupe, stale-while-revalidate, focus refresh, invalidation)."
165
+ },
166
+ {
167
+ "name": "useG8Mutation",
168
+ "file": "hooks/use-g8-mutation.ts",
169
+ "purpose": "Run a write with loading/error state and success/error callbacks. Optional `invalidates` refreshes cached keys; optional `optimistic` update with rollback."
170
+ },
171
+ {
172
+ "name": "usePaginated",
173
+ "file": "hooks/use-paginated.ts",
174
+ "purpose": "Cursor pagination over the API envelope (pagination.next_cursor) with load-more and reload. Optional `key` caches the loaded pages."
175
+ },
176
+ {
177
+ "name": "useJobPoll",
178
+ "file": "hooks/use-job-poll.ts",
179
+ "purpose": "Poll a job-status fetcher with backoff until a terminal state (the API has no streams)."
180
+ },
181
+ {
182
+ "name": "useUrlState",
183
+ "file": "hooks/use-url-state.ts",
184
+ "purpose": "One string in the URL query as state (replaceState + popstate, SSR-safe, no next/navigation): the open record, tab or search survives reload and Back."
185
+ },
186
+ {
187
+ "name": "useRowActivation",
188
+ "file": "hooks/use-row-activation.ts",
189
+ "purpose": "Props that make a whole list row focusable and openable with Enter/Space and click, leaving nested checkboxes, buttons and links alone. rowActivationProps is the same as a plain function for use inside .map()."
190
+ },
191
+ {
192
+ "name": "useBrand",
193
+ "file": "hooks/use-brand.ts",
194
+ "purpose": "The current brand ({ name, appUrl, linkLabel, logo }) from the nearest BrandProvider, else graph8's defaults; brandUrl(brand, path) builds a link-out."
195
+ }
37
196
  ],
38
197
  "lib": [
39
- { "file": "lib/g8-client.ts", "purpose": "Browser caller for the app's own /api/g8 proxy. Holds no key, returns the SDK envelope, throws G8Error." }
198
+ {
199
+ "file": "lib/g8-client.ts",
200
+ "purpose": "Browser caller for the app's own /api/g8 proxy. Holds no key, returns the SDK envelope, throws G8Error."
201
+ },
202
+ {
203
+ "file": "components/screen/time-series-chart-recharts.tsx",
204
+ "purpose": "The recharts implementation of TimeSeriesChart, code-split: TimeSeriesChart loads it on first render, so pages without a chart never download recharts."
205
+ },
206
+ {
207
+ "file": "components/screen/time-series-format.ts",
208
+ "purpose": "TimeSeriesChart props, series type and axis formatters (defaultFormatX/defaultFormatY), with no recharts import."
209
+ },
210
+ {
211
+ "file": "lib/g8-cache.ts",
212
+ "purpose": "Shared query cache behind the hooks' optional `key`: one in-flight request per key, stale-while-revalidate, focus/reconnect refresh, invalidate(prefix), setQueryData, optimistic writes, bounded LRU (200) with GC."
213
+ },
214
+ {
215
+ "file": "components/screen/index.ts",
216
+ "purpose": "Barrel for the screen kit: import every part from @/g8/components/screen."
217
+ },
218
+ {
219
+ "file": "lib/plural.ts",
220
+ "purpose": "plural(n, 'contact') -> '1 contact' / '2 contacts' (and pluralWord): one correct plural helper for titles, counts and bulk dialogs."
221
+ }
40
222
  ],
41
223
  "server": [
42
- { "file": "server/proxy.ts", "purpose": "createG8Proxy(): forwards browser calls to the graph8 API with the server-side key, a required path allowlist and authorize hook (it refuses to run without them) and a server-side org hook.", "target": "g8/server/proxy.ts" },
43
- { "file": "server/route.ts", "purpose": "The Next.js App Router catch-all route that mounts the proxy.", "target": "app/api/g8/[...path]/route.ts" }
224
+ {
225
+ "file": "server/proxy.ts",
226
+ "purpose": "createG8Proxy(): forwards browser calls to the graph8 API with the server-side key, a required path allowlist and authorize hook (it refuses to run without them) and a server-side org hook.",
227
+ "target": "g8/server/proxy.ts"
228
+ },
229
+ {
230
+ "file": "server/local-key.ts",
231
+ "purpose": "Node only: the local-preview key file (.g8/local-key, 0600, self-gitignored) and resolveApiKey() (environment first, then the file outside production).",
232
+ "target": "g8/server/local-key.ts"
233
+ },
234
+ {
235
+ "file": "server/connect.ts",
236
+ "purpose": "createG8ConnectRoute(): GET status, POST a key (checked with GET /me), DELETE it; answers only next dev requests from this machine, 404 in production.",
237
+ "target": "g8/server/connect.ts"
238
+ },
239
+ {
240
+ "file": "server/route.ts",
241
+ "purpose": "The Next.js App Router catch-all route that mounts the proxy.",
242
+ "target": "app/api/g8/[...path]/route.ts"
243
+ },
244
+ {
245
+ "file": "server/connect-route.ts",
246
+ "purpose": "The Next.js route behind the Connect graph8 screen.",
247
+ "target": "app/api/g8-connect/route.ts"
248
+ }
44
249
  ],
45
250
  "recipes": [
46
251
  {
@@ -71,9 +276,25 @@
71
276
  ],
72
277
  "scopes": [],
73
278
  "defaults": [
74
- { "decision": "Which API paths the browser may call through the proxy", "default": "Only the areas the installed blocks use (allowedPrefixes, required), not all of /api/v1", "askHuman": false },
75
- { "decision": "How the proxy knows the caller is a signed-in user of the app", "default": "None: the route refuses every request until `authorize` is set", "askHuman": true },
76
- { "decision": "Styling", "default": "Tailwind utility classes on plain elements; overlays (ConfirmSpendDialog) and later blocks use @graph8/sdk/ui, so import its CSS", "askHuman": false }
279
+ {
280
+ "decision": "Which API paths the browser may call through the proxy",
281
+ "default": "Only the areas the installed blocks use (allowedPrefixes, required), not all of /api/v1",
282
+ "askHuman": false
283
+ },
284
+ {
285
+ "decision": "How the proxy knows the caller is a signed-in user of the app",
286
+ "default": "Local preview: until `authorize` is set the route answers only `next dev` requests from this machine; a production build refuses every request until `authorize` is set",
287
+ "askHuman": true
288
+ },
289
+ {
290
+ "decision": "Styling",
291
+ "default": "Tailwind utility classes on plain elements; overlays (ConfirmSpendDialog) and later blocks use @graph8/sdk/ui, so import its CSS",
292
+ "askHuman": false
293
+ }
77
294
  ],
78
- "peerDependencies": { "react": ">=18" }
295
+ "peerDependencies": {
296
+ "react": ">=18",
297
+ "lucide-react": ">=0.400.0",
298
+ "recharts": "^2.15.0 || ^3.0.0"
299
+ }
79
300
  }