@brainfish-ai/devdoc 0.1.21

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 (268) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +415 -0
  3. package/bin/devdoc.js +13 -0
  4. package/dist/cli/commands/build.d.ts +5 -0
  5. package/dist/cli/commands/build.js +87 -0
  6. package/dist/cli/commands/check.d.ts +1 -0
  7. package/dist/cli/commands/check.js +143 -0
  8. package/dist/cli/commands/create.d.ts +24 -0
  9. package/dist/cli/commands/create.js +387 -0
  10. package/dist/cli/commands/deploy.d.ts +9 -0
  11. package/dist/cli/commands/deploy.js +433 -0
  12. package/dist/cli/commands/dev.d.ts +6 -0
  13. package/dist/cli/commands/dev.js +139 -0
  14. package/dist/cli/commands/init.d.ts +11 -0
  15. package/dist/cli/commands/init.js +238 -0
  16. package/dist/cli/commands/keys.d.ts +12 -0
  17. package/dist/cli/commands/keys.js +165 -0
  18. package/dist/cli/commands/start.d.ts +5 -0
  19. package/dist/cli/commands/start.js +56 -0
  20. package/dist/cli/commands/upload.d.ts +13 -0
  21. package/dist/cli/commands/upload.js +238 -0
  22. package/dist/cli/commands/whoami.d.ts +8 -0
  23. package/dist/cli/commands/whoami.js +91 -0
  24. package/dist/cli/index.d.ts +1 -0
  25. package/dist/cli/index.js +106 -0
  26. package/dist/config/index.d.ts +80 -0
  27. package/dist/config/index.js +133 -0
  28. package/dist/constants.d.ts +9 -0
  29. package/dist/constants.js +13 -0
  30. package/dist/index.d.ts +7 -0
  31. package/dist/index.js +12 -0
  32. package/dist/utils/logger.d.ts +16 -0
  33. package/dist/utils/logger.js +61 -0
  34. package/dist/utils/paths.d.ts +16 -0
  35. package/dist/utils/paths.js +50 -0
  36. package/package.json +51 -0
  37. package/renderer/app/api/assets/[...path]/route.ts +123 -0
  38. package/renderer/app/api/assets/route.ts +124 -0
  39. package/renderer/app/api/assets/upload/route.ts +177 -0
  40. package/renderer/app/api/auth-schemes/route.ts +77 -0
  41. package/renderer/app/api/chat/route.ts +858 -0
  42. package/renderer/app/api/codegen/route.ts +72 -0
  43. package/renderer/app/api/collections/route.ts +1016 -0
  44. package/renderer/app/api/debug/route.ts +53 -0
  45. package/renderer/app/api/deploy/route.ts +234 -0
  46. package/renderer/app/api/device/route.ts +42 -0
  47. package/renderer/app/api/docs/route.ts +187 -0
  48. package/renderer/app/api/keys/regenerate/route.ts +80 -0
  49. package/renderer/app/api/openapi-spec/route.ts +151 -0
  50. package/renderer/app/api/projects/[slug]/route.ts +153 -0
  51. package/renderer/app/api/projects/[slug]/stats/route.ts +96 -0
  52. package/renderer/app/api/projects/register/route.ts +152 -0
  53. package/renderer/app/api/proxy/route.ts +149 -0
  54. package/renderer/app/api/proxy-stream/route.ts +168 -0
  55. package/renderer/app/api/redirects/route.ts +47 -0
  56. package/renderer/app/api/schema/route.ts +65 -0
  57. package/renderer/app/api/subdomains/check/route.ts +172 -0
  58. package/renderer/app/api/suggestions/route.ts +144 -0
  59. package/renderer/app/favicon.ico +0 -0
  60. package/renderer/app/globals.css +1103 -0
  61. package/renderer/app/layout.tsx +47 -0
  62. package/renderer/app/llms-full.txt/route.ts +346 -0
  63. package/renderer/app/llms.txt/route.ts +279 -0
  64. package/renderer/app/page.tsx +14 -0
  65. package/renderer/app/robots.txt/route.ts +84 -0
  66. package/renderer/app/sitemap.xml/route.ts +199 -0
  67. package/renderer/components/docs/index.ts +12 -0
  68. package/renderer/components/docs/mdx/accordion.tsx +169 -0
  69. package/renderer/components/docs/mdx/badge.tsx +132 -0
  70. package/renderer/components/docs/mdx/callouts.tsx +154 -0
  71. package/renderer/components/docs/mdx/cards.tsx +213 -0
  72. package/renderer/components/docs/mdx/changelog.tsx +120 -0
  73. package/renderer/components/docs/mdx/code-block.tsx +186 -0
  74. package/renderer/components/docs/mdx/code-group.tsx +421 -0
  75. package/renderer/components/docs/mdx/file-embeds.tsx +105 -0
  76. package/renderer/components/docs/mdx/frame.tsx +112 -0
  77. package/renderer/components/docs/mdx/highlight.tsx +151 -0
  78. package/renderer/components/docs/mdx/iframe.tsx +134 -0
  79. package/renderer/components/docs/mdx/image.tsx +235 -0
  80. package/renderer/components/docs/mdx/index.ts +204 -0
  81. package/renderer/components/docs/mdx/mermaid.tsx +240 -0
  82. package/renderer/components/docs/mdx/param-field.tsx +200 -0
  83. package/renderer/components/docs/mdx/steps.tsx +113 -0
  84. package/renderer/components/docs/mdx/tabs.tsx +86 -0
  85. package/renderer/components/docs/mdx-renderer.tsx +100 -0
  86. package/renderer/components/docs/navigation/breadcrumbs.tsx +76 -0
  87. package/renderer/components/docs/navigation/index.ts +8 -0
  88. package/renderer/components/docs/navigation/page-nav.tsx +64 -0
  89. package/renderer/components/docs/navigation/sidebar.tsx +515 -0
  90. package/renderer/components/docs/navigation/toc.tsx +113 -0
  91. package/renderer/components/docs/notice.tsx +105 -0
  92. package/renderer/components/docs-header.tsx +274 -0
  93. package/renderer/components/docs-viewer/agent/agent-chat.tsx +2076 -0
  94. package/renderer/components/docs-viewer/agent/cards/debug-context-card.tsx +90 -0
  95. package/renderer/components/docs-viewer/agent/cards/endpoint-context-card.tsx +49 -0
  96. package/renderer/components/docs-viewer/agent/cards/index.tsx +50 -0
  97. package/renderer/components/docs-viewer/agent/cards/response-options-card.tsx +212 -0
  98. package/renderer/components/docs-viewer/agent/cards/types.ts +84 -0
  99. package/renderer/components/docs-viewer/agent/chat-message.tsx +17 -0
  100. package/renderer/components/docs-viewer/agent/index.tsx +6 -0
  101. package/renderer/components/docs-viewer/agent/messages/assistant-message.tsx +119 -0
  102. package/renderer/components/docs-viewer/agent/messages/chat-message.tsx +46 -0
  103. package/renderer/components/docs-viewer/agent/messages/index.ts +17 -0
  104. package/renderer/components/docs-viewer/agent/messages/tool-call-display.tsx +721 -0
  105. package/renderer/components/docs-viewer/agent/messages/types.ts +61 -0
  106. package/renderer/components/docs-viewer/agent/messages/typing-indicator.tsx +24 -0
  107. package/renderer/components/docs-viewer/agent/messages/user-message.tsx +51 -0
  108. package/renderer/components/docs-viewer/code-editor/index.tsx +2 -0
  109. package/renderer/components/docs-viewer/code-editor/notes-mode.tsx +1283 -0
  110. package/renderer/components/docs-viewer/content/changelog-page.tsx +331 -0
  111. package/renderer/components/docs-viewer/content/doc-page.tsx +285 -0
  112. package/renderer/components/docs-viewer/content/documentation-viewer.tsx +17 -0
  113. package/renderer/components/docs-viewer/content/index.tsx +29 -0
  114. package/renderer/components/docs-viewer/content/introduction.tsx +21 -0
  115. package/renderer/components/docs-viewer/content/request-details.tsx +330 -0
  116. package/renderer/components/docs-viewer/content/sections/auth.tsx +69 -0
  117. package/renderer/components/docs-viewer/content/sections/body.tsx +66 -0
  118. package/renderer/components/docs-viewer/content/sections/headers.tsx +43 -0
  119. package/renderer/components/docs-viewer/content/sections/overview.tsx +40 -0
  120. package/renderer/components/docs-viewer/content/sections/parameters.tsx +43 -0
  121. package/renderer/components/docs-viewer/content/sections/responses.tsx +87 -0
  122. package/renderer/components/docs-viewer/global-auth-modal.tsx +352 -0
  123. package/renderer/components/docs-viewer/index.tsx +1466 -0
  124. package/renderer/components/docs-viewer/playground/auth-editor.tsx +280 -0
  125. package/renderer/components/docs-viewer/playground/body-editor.tsx +221 -0
  126. package/renderer/components/docs-viewer/playground/code-editor.tsx +224 -0
  127. package/renderer/components/docs-viewer/playground/code-snippet.tsx +387 -0
  128. package/renderer/components/docs-viewer/playground/graphql-playground.tsx +745 -0
  129. package/renderer/components/docs-viewer/playground/index.tsx +671 -0
  130. package/renderer/components/docs-viewer/playground/key-value-editor.tsx +261 -0
  131. package/renderer/components/docs-viewer/playground/method-selector.tsx +60 -0
  132. package/renderer/components/docs-viewer/playground/request-builder.tsx +179 -0
  133. package/renderer/components/docs-viewer/playground/request-tabs.tsx +237 -0
  134. package/renderer/components/docs-viewer/playground/response-cards/idle-card.tsx +21 -0
  135. package/renderer/components/docs-viewer/playground/response-cards/index.tsx +93 -0
  136. package/renderer/components/docs-viewer/playground/response-cards/loading-card.tsx +16 -0
  137. package/renderer/components/docs-viewer/playground/response-cards/network-error-card.tsx +23 -0
  138. package/renderer/components/docs-viewer/playground/response-cards/response-body-card.tsx +268 -0
  139. package/renderer/components/docs-viewer/playground/response-cards/types.ts +82 -0
  140. package/renderer/components/docs-viewer/playground/response-viewer.tsx +43 -0
  141. package/renderer/components/docs-viewer/search/index.ts +2 -0
  142. package/renderer/components/docs-viewer/search/search-dialog.tsx +331 -0
  143. package/renderer/components/docs-viewer/search/use-search.ts +117 -0
  144. package/renderer/components/docs-viewer/shared/markdown-renderer.tsx +431 -0
  145. package/renderer/components/docs-viewer/shared/method-badge.tsx +41 -0
  146. package/renderer/components/docs-viewer/shared/schema-viewer.tsx +349 -0
  147. package/renderer/components/docs-viewer/sidebar/collection-tree.tsx +239 -0
  148. package/renderer/components/docs-viewer/sidebar/endpoint-options.tsx +316 -0
  149. package/renderer/components/docs-viewer/sidebar/index.tsx +343 -0
  150. package/renderer/components/docs-viewer/sidebar/right-sidebar.tsx +202 -0
  151. package/renderer/components/docs-viewer/sidebar/sidebar-group.tsx +118 -0
  152. package/renderer/components/docs-viewer/sidebar/sidebar-item.tsx +226 -0
  153. package/renderer/components/docs-viewer/sidebar/sidebar-section.tsx +52 -0
  154. package/renderer/components/theme-provider.tsx +11 -0
  155. package/renderer/components/theme-toggle.tsx +76 -0
  156. package/renderer/components/ui/badge.tsx +46 -0
  157. package/renderer/components/ui/button.tsx +59 -0
  158. package/renderer/components/ui/dialog.tsx +118 -0
  159. package/renderer/components/ui/dropdown-menu.tsx +257 -0
  160. package/renderer/components/ui/input.tsx +21 -0
  161. package/renderer/components/ui/label.tsx +24 -0
  162. package/renderer/components/ui/navigation-menu.tsx +168 -0
  163. package/renderer/components/ui/select.tsx +190 -0
  164. package/renderer/components/ui/spinner.tsx +114 -0
  165. package/renderer/components/ui/tabs.tsx +66 -0
  166. package/renderer/components/ui/tooltip.tsx +61 -0
  167. package/renderer/hooks/use-code-copy.ts +88 -0
  168. package/renderer/hooks/use-openapi-title.ts +44 -0
  169. package/renderer/lib/api-docs/agent/index.ts +6 -0
  170. package/renderer/lib/api-docs/agent/indexer.ts +323 -0
  171. package/renderer/lib/api-docs/agent/spec-summary.ts +335 -0
  172. package/renderer/lib/api-docs/agent/types.ts +116 -0
  173. package/renderer/lib/api-docs/auth/auth-context.tsx +225 -0
  174. package/renderer/lib/api-docs/auth/auth-storage.ts +87 -0
  175. package/renderer/lib/api-docs/auth/crypto.ts +89 -0
  176. package/renderer/lib/api-docs/auth/index.ts +4 -0
  177. package/renderer/lib/api-docs/code-editor/db.ts +164 -0
  178. package/renderer/lib/api-docs/code-editor/hooks.ts +266 -0
  179. package/renderer/lib/api-docs/code-editor/index.ts +6 -0
  180. package/renderer/lib/api-docs/code-editor/mode-context.tsx +207 -0
  181. package/renderer/lib/api-docs/code-editor/types.ts +105 -0
  182. package/renderer/lib/api-docs/codegen/definitions.ts +297 -0
  183. package/renderer/lib/api-docs/codegen/har.ts +251 -0
  184. package/renderer/lib/api-docs/codegen/index.ts +159 -0
  185. package/renderer/lib/api-docs/factories.ts +151 -0
  186. package/renderer/lib/api-docs/index.ts +17 -0
  187. package/renderer/lib/api-docs/mobile-context.tsx +112 -0
  188. package/renderer/lib/api-docs/navigation-context.tsx +88 -0
  189. package/renderer/lib/api-docs/parsers/graphql/README.md +129 -0
  190. package/renderer/lib/api-docs/parsers/graphql/index.ts +91 -0
  191. package/renderer/lib/api-docs/parsers/graphql/parser.ts +491 -0
  192. package/renderer/lib/api-docs/parsers/graphql/transformer.ts +246 -0
  193. package/renderer/lib/api-docs/parsers/graphql/types.ts +283 -0
  194. package/renderer/lib/api-docs/parsers/openapi/README.md +32 -0
  195. package/renderer/lib/api-docs/parsers/openapi/dereferencer.ts +60 -0
  196. package/renderer/lib/api-docs/parsers/openapi/extractors/auth.ts +574 -0
  197. package/renderer/lib/api-docs/parsers/openapi/extractors/body.ts +403 -0
  198. package/renderer/lib/api-docs/parsers/openapi/extractors/index.ts +232 -0
  199. package/renderer/lib/api-docs/parsers/openapi/index.ts +171 -0
  200. package/renderer/lib/api-docs/parsers/openapi/transformer.ts +277 -0
  201. package/renderer/lib/api-docs/parsers/openapi/validator.ts +31 -0
  202. package/renderer/lib/api-docs/playground/context.tsx +107 -0
  203. package/renderer/lib/api-docs/playground/navigation-context.tsx +124 -0
  204. package/renderer/lib/api-docs/playground/request-builder.ts +223 -0
  205. package/renderer/lib/api-docs/playground/request-runner.ts +282 -0
  206. package/renderer/lib/api-docs/playground/types.ts +35 -0
  207. package/renderer/lib/api-docs/types.ts +269 -0
  208. package/renderer/lib/api-docs/utils.ts +311 -0
  209. package/renderer/lib/cache.ts +193 -0
  210. package/renderer/lib/docs/config/index.ts +29 -0
  211. package/renderer/lib/docs/config/loader.ts +142 -0
  212. package/renderer/lib/docs/config/schema.ts +298 -0
  213. package/renderer/lib/docs/index.ts +12 -0
  214. package/renderer/lib/docs/mdx/compiler.ts +176 -0
  215. package/renderer/lib/docs/mdx/frontmatter.ts +80 -0
  216. package/renderer/lib/docs/mdx/index.ts +26 -0
  217. package/renderer/lib/docs/navigation/generator.ts +348 -0
  218. package/renderer/lib/docs/navigation/index.ts +12 -0
  219. package/renderer/lib/docs/navigation/types.ts +123 -0
  220. package/renderer/lib/docs-navigation-context.tsx +80 -0
  221. package/renderer/lib/multi-tenant/context.ts +105 -0
  222. package/renderer/lib/storage/blob.ts +845 -0
  223. package/renderer/lib/utils.ts +6 -0
  224. package/renderer/next.config.ts +76 -0
  225. package/renderer/package.json +66 -0
  226. package/renderer/postcss.config.mjs +5 -0
  227. package/renderer/public/assets/images/screenshot.png +0 -0
  228. package/renderer/public/assets/logo/dark.svg +9 -0
  229. package/renderer/public/assets/logo/light.svg +9 -0
  230. package/renderer/public/assets/logo.svg +9 -0
  231. package/renderer/public/file.svg +1 -0
  232. package/renderer/public/globe.svg +1 -0
  233. package/renderer/public/icon.png +0 -0
  234. package/renderer/public/logo.svg +9 -0
  235. package/renderer/public/window.svg +1 -0
  236. package/renderer/tsconfig.json +28 -0
  237. package/templates/basic/README.md +139 -0
  238. package/templates/basic/assets/favicon.svg +4 -0
  239. package/templates/basic/assets/logo.svg +9 -0
  240. package/templates/basic/docs.json +47 -0
  241. package/templates/basic/guides/configuration.mdx +149 -0
  242. package/templates/basic/guides/overview.mdx +96 -0
  243. package/templates/basic/index.mdx +39 -0
  244. package/templates/basic/package.json +14 -0
  245. package/templates/basic/quickstart.mdx +92 -0
  246. package/templates/basic/vercel.json +6 -0
  247. package/templates/graphql/README.md +139 -0
  248. package/templates/graphql/api-reference/schema.graphql +305 -0
  249. package/templates/graphql/assets/favicon.svg +4 -0
  250. package/templates/graphql/assets/logo.svg +9 -0
  251. package/templates/graphql/docs.json +54 -0
  252. package/templates/graphql/guides/configuration.mdx +149 -0
  253. package/templates/graphql/guides/overview.mdx +96 -0
  254. package/templates/graphql/index.mdx +39 -0
  255. package/templates/graphql/package.json +14 -0
  256. package/templates/graphql/quickstart.mdx +92 -0
  257. package/templates/graphql/vercel.json +6 -0
  258. package/templates/openapi/README.md +139 -0
  259. package/templates/openapi/api-reference/openapi.json +419 -0
  260. package/templates/openapi/assets/favicon.svg +4 -0
  261. package/templates/openapi/assets/logo.svg +9 -0
  262. package/templates/openapi/docs.json +61 -0
  263. package/templates/openapi/guides/configuration.mdx +149 -0
  264. package/templates/openapi/guides/overview.mdx +96 -0
  265. package/templates/openapi/index.mdx +39 -0
  266. package/templates/openapi/package.json +14 -0
  267. package/templates/openapi/quickstart.mdx +92 -0
  268. package/templates/openapi/vercel.json +6 -0
@@ -0,0 +1,266 @@
1
+ 'use client'
2
+
3
+ import { useState, useEffect, useCallback } from 'react'
4
+ import { dbOperations } from './db'
5
+ import type { Workspace, NoteFile, EditorState } from './types'
6
+
7
+ /**
8
+ * Hook for managing the workspace (auto-created per API)
9
+ */
10
+ export function useWorkspace(apiSpecUrl: string | null, apiName: string = 'API Notes') {
11
+ const [workspace, setWorkspace] = useState<Workspace | null>(null)
12
+ const [loading, setLoading] = useState(true)
13
+ const [error, setError] = useState<Error | null>(null)
14
+
15
+ useEffect(() => {
16
+ if (!apiSpecUrl) {
17
+ setWorkspace(null)
18
+ setLoading(false)
19
+ return
20
+ }
21
+
22
+ const loadWorkspace = async () => {
23
+ try {
24
+ setLoading(true)
25
+ const ws = await dbOperations.getOrCreateWorkspace(apiSpecUrl, `${apiName} Notes`)
26
+ setWorkspace(ws)
27
+ setError(null)
28
+ } catch (err) {
29
+ setError(err instanceof Error ? err : new Error('Failed to load workspace'))
30
+ } finally {
31
+ setLoading(false)
32
+ }
33
+ }
34
+
35
+ loadWorkspace()
36
+ }, [apiSpecUrl, apiName])
37
+
38
+ const clearWorkspace = useCallback(async () => {
39
+ if (!workspace) return
40
+ await dbOperations.clearWorkspace(workspace.id)
41
+ }, [workspace])
42
+
43
+ return {
44
+ workspace,
45
+ loading,
46
+ error,
47
+ clearWorkspace,
48
+ workspaceId: workspace?.id || null,
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Hook for managing notes/files in the workspace
54
+ */
55
+ export function useNotes(workspaceId: string | null) {
56
+ const [notes, setNotes] = useState<NoteFile[]>([])
57
+ const [loading, setLoading] = useState(true)
58
+ const [error, setError] = useState<Error | null>(null)
59
+
60
+ const loadNotes = useCallback(async () => {
61
+ if (!workspaceId) {
62
+ setNotes([])
63
+ setLoading(false)
64
+ return
65
+ }
66
+
67
+ try {
68
+ setLoading(true)
69
+ const list = await dbOperations.listNotes(workspaceId)
70
+ setNotes(list)
71
+ setError(null)
72
+ } catch (err) {
73
+ setError(err instanceof Error ? err : new Error('Failed to load notes'))
74
+ } finally {
75
+ setLoading(false)
76
+ }
77
+ }, [workspaceId])
78
+
79
+ useEffect(() => {
80
+ loadNotes()
81
+ }, [loadNotes])
82
+
83
+ const createNote = useCallback(async (path: string, content: string = ''): Promise<NoteFile | null> => {
84
+ if (!workspaceId) return null
85
+
86
+ const note = await dbOperations.createNote(workspaceId, path, content)
87
+ setNotes(prev => [...prev, note])
88
+ return note
89
+ }, [workspaceId])
90
+
91
+ const readNote = useCallback(async (path: string): Promise<string> => {
92
+ if (!workspaceId) return ''
93
+
94
+ const note = await dbOperations.getNote(workspaceId, path)
95
+ return note?.content || ''
96
+ }, [workspaceId])
97
+
98
+ const updateNote = useCallback(async (path: string, content: string): Promise<void> => {
99
+ if (!workspaceId) return
100
+
101
+ await dbOperations.updateNote(workspaceId, path, content)
102
+ setNotes(prev => prev.map(n =>
103
+ n.path === path
104
+ ? { ...n, content, updatedAt: new Date() }
105
+ : n
106
+ ))
107
+ }, [workspaceId])
108
+
109
+ const deleteNote = useCallback(async (path: string): Promise<void> => {
110
+ if (!workspaceId) return
111
+
112
+ await dbOperations.deleteNote(workspaceId, path)
113
+ setNotes(prev => prev.filter(n => n.path !== path))
114
+ }, [workspaceId])
115
+
116
+ const renameNote = useCallback(async (oldPath: string, newPath: string): Promise<void> => {
117
+ if (!workspaceId) return
118
+
119
+ await dbOperations.renameNote(workspaceId, oldPath, newPath)
120
+ setNotes(prev => prev.map(n =>
121
+ n.path === oldPath
122
+ ? { ...n, path: newPath, updatedAt: new Date() }
123
+ : n
124
+ ))
125
+ }, [workspaceId])
126
+
127
+ const deleteAllNotes = useCallback(async (): Promise<void> => {
128
+ if (!workspaceId) return
129
+
130
+ await dbOperations.clearWorkspace(workspaceId)
131
+ setNotes([])
132
+ }, [workspaceId])
133
+
134
+ return {
135
+ notes,
136
+ loading,
137
+ error,
138
+ createNote,
139
+ readNote,
140
+ updateNote,
141
+ deleteNote,
142
+ deleteAllNotes,
143
+ renameNote,
144
+ refresh: loadNotes,
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Hook for managing editor state (tabs, active file, etc.)
150
+ */
151
+ export function useEditorState(workspaceId: string | null) {
152
+ const [state, setState] = useState<EditorState | null>(null)
153
+
154
+ // Load saved state
155
+ useEffect(() => {
156
+ if (!workspaceId) {
157
+ setState(null)
158
+ return
159
+ }
160
+
161
+ const loadState = async () => {
162
+ const saved = await dbOperations.getEditorState(workspaceId)
163
+ if (saved) {
164
+ setState(saved)
165
+ } else {
166
+ // Initialize new state
167
+ setState({
168
+ workspaceId,
169
+ openTabs: [],
170
+ activeTab: null,
171
+ cursorPositions: {},
172
+ scrollPositions: {},
173
+ })
174
+ }
175
+ }
176
+
177
+ loadState()
178
+ }, [workspaceId])
179
+
180
+ // Save state when it changes
181
+ useEffect(() => {
182
+ if (state && workspaceId) {
183
+ dbOperations.saveEditorState(state)
184
+ }
185
+ }, [state, workspaceId])
186
+
187
+ const openTab = useCallback(async (path: string) => {
188
+ setState(prev => {
189
+ if (!prev) return prev
190
+
191
+ const newTabs = prev.openTabs.includes(path)
192
+ ? prev.openTabs
193
+ : [...prev.openTabs, path]
194
+
195
+ return {
196
+ ...prev,
197
+ openTabs: newTabs,
198
+ activeTab: path,
199
+ }
200
+ })
201
+ }, [])
202
+
203
+ const closeTab = useCallback((path: string) => {
204
+ setState(prev => {
205
+ if (!prev) return prev
206
+
207
+ const newTabs = prev.openTabs.filter(t => t !== path)
208
+ const newActive = prev.activeTab === path
209
+ ? newTabs[newTabs.length - 1] || null
210
+ : prev.activeTab
211
+
212
+ return {
213
+ ...prev,
214
+ openTabs: newTabs,
215
+ activeTab: newActive,
216
+ }
217
+ })
218
+ }, [])
219
+
220
+ const setActiveTab = useCallback((path: string) => {
221
+ setState(prev => {
222
+ if (!prev) return prev
223
+ return { ...prev, activeTab: path }
224
+ })
225
+ }, [])
226
+
227
+ const saveCursorPosition = useCallback((path: string, line: number, column: number) => {
228
+ setState(prev => {
229
+ if (!prev) return prev
230
+ return {
231
+ ...prev,
232
+ cursorPositions: {
233
+ ...prev.cursorPositions,
234
+ [path]: { line, column },
235
+ },
236
+ }
237
+ })
238
+ }, [])
239
+
240
+ const clearAllTabs = useCallback(() => {
241
+ setState(prev => {
242
+ if (!prev) return prev
243
+ return {
244
+ ...prev,
245
+ openTabs: [],
246
+ activeTab: null,
247
+ cursorPositions: {},
248
+ scrollPositions: {},
249
+ }
250
+ })
251
+ }, [])
252
+
253
+ return {
254
+ state,
255
+ openTab,
256
+ closeTab,
257
+ setActiveTab,
258
+ saveCursorPosition,
259
+ clearAllTabs,
260
+ }
261
+ }
262
+
263
+ // Re-export types and helpers
264
+ export type { Workspace, NoteFile, EditorState } from './types'
265
+ export { getFileType, getMonacoLanguage, supportsPreview } from './types'
266
+ export { getWorkspaceId } from './db'
@@ -0,0 +1,6 @@
1
+ // Notes workspace - simplified code editor for brainstorming
2
+ // Not for building apps, just for organizing ideas around APIs
3
+
4
+ export * from './types'
5
+ export * from './hooks'
6
+ export { useModeContext, ModeProvider } from './mode-context'
@@ -0,0 +1,207 @@
1
+ 'use client'
2
+
3
+ import React, { createContext, useContext, useState, useCallback, useEffect, useRef, type ReactNode } from 'react'
4
+ import type { PlaygroundMode } from './types'
5
+
6
+ /**
7
+ * Streaming content for real-time code display
8
+ */
9
+ interface StreamingContent {
10
+ path: string
11
+ content: string
12
+ isStreaming: boolean
13
+ }
14
+
15
+ /**
16
+ * Simplified mode context - toggles between Docs, API Client, and Notes
17
+ * Uses URL hash for persistence: #notes or #notes/path/to/file.py
18
+ */
19
+ interface ModeContextValue {
20
+ // Current mode
21
+ mode: PlaygroundMode
22
+
23
+ // Active file in notes mode (for navigation from agent)
24
+ activeFilePath: string | null
25
+ setActiveFilePath: (path: string | null) => void
26
+
27
+ // Streaming content for real-time display
28
+ streamingContent: StreamingContent | null
29
+ setStreamingContent: (content: StreamingContent | null) => void
30
+ appendStreamingContent: (delta: string) => void
31
+
32
+ // Mode switching
33
+ switchToDocs: () => void
34
+ switchToNotes: (openFile?: string) => void
35
+ switchToApiClient: () => void
36
+
37
+ // Notes refresh trigger - increment to signal notes-mode to refresh
38
+ notesRefreshTrigger: number
39
+ triggerNotesRefresh: () => void
40
+ }
41
+
42
+ const ModeContext = createContext<ModeContextValue | null>(null)
43
+
44
+ interface ModeProviderProps {
45
+ children: ReactNode
46
+ defaultMode?: PlaygroundMode
47
+ }
48
+
49
+ // Helper to parse notes path from URL hash
50
+ function parseNotesHash(): { isNotes: boolean; filePath: string | null } {
51
+ if (typeof window === 'undefined') return { isNotes: false, filePath: null }
52
+
53
+ const hash = window.location.hash.slice(1) // Remove #
54
+ if (hash === 'notes') {
55
+ return { isNotes: true, filePath: null }
56
+ }
57
+ if (hash.startsWith('notes/')) {
58
+ // Decode any encoded characters but path should mostly be plain text
59
+ const filePath = decodeURIComponent(hash.slice(6)) // Remove 'notes/'
60
+ return { isNotes: true, filePath: filePath || null }
61
+ }
62
+ return { isNotes: false, filePath: null }
63
+ }
64
+
65
+ // Helper to update URL hash for notes
66
+ // Only encode special chars that would break the URL, keep slashes readable
67
+ function updateNotesHash(filePath: string | null): void {
68
+ if (typeof window === 'undefined') return
69
+
70
+ const newHash = filePath
71
+ ? `#notes/${filePath.replace(/#/g, '%23').replace(/\?/g, '%3F')}`
72
+ : '#notes'
73
+
74
+ window.history.pushState(null, '', newHash)
75
+ }
76
+
77
+ export function ModeProvider({ children, defaultMode = 'docs' }: ModeProviderProps) {
78
+ const [mode, setMode] = useState<PlaygroundMode>(defaultMode)
79
+ const [activeFilePath, setActiveFilePathState] = useState<string | null>(null)
80
+ const [streamingContent, setStreamingContent] = useState<StreamingContent | null>(null)
81
+ const [notesRefreshTrigger, setNotesRefreshTrigger] = useState(0)
82
+
83
+ // Use ref to track mode for callbacks (avoids stale closure issues)
84
+ const modeRef = useRef<PlaygroundMode>(defaultMode)
85
+
86
+ // Keep ref in sync with state
87
+ useEffect(() => {
88
+ modeRef.current = mode
89
+ }, [mode])
90
+
91
+ // Initialize from URL hash on mount
92
+ useEffect(() => {
93
+ const { isNotes, filePath } = parseNotesHash()
94
+
95
+ if (isNotes) {
96
+ setMode('notes')
97
+ modeRef.current = 'notes'
98
+ setActiveFilePathState(filePath)
99
+ }
100
+ }, [])
101
+
102
+ // Handle browser back/forward navigation
103
+ useEffect(() => {
104
+ const handlePopState = () => {
105
+ const { isNotes, filePath } = parseNotesHash()
106
+
107
+ if (isNotes) {
108
+ setMode('notes')
109
+ modeRef.current = 'notes'
110
+ setActiveFilePathState(filePath)
111
+ } else {
112
+ // Default to docs mode when no notes hash
113
+ setMode('docs')
114
+ modeRef.current = 'docs'
115
+ }
116
+ }
117
+
118
+ window.addEventListener('popstate', handlePopState)
119
+ return () => window.removeEventListener('popstate', handlePopState)
120
+ }, [])
121
+
122
+ // Wrapper for setActiveFilePath that also updates URL
123
+ const setActiveFilePath = useCallback((path: string | null) => {
124
+ // Handle the ?t= timestamp suffix for forcing reloads
125
+ const cleanPath = path?.split('?')[0] || null
126
+ setActiveFilePathState(cleanPath)
127
+
128
+ // Update URL if we're in notes mode (use ref to avoid stale closure)
129
+ if (modeRef.current === 'notes') {
130
+ updateNotesHash(cleanPath)
131
+ }
132
+ }, []) // No dependencies - uses ref
133
+
134
+ const switchToNotes = useCallback((openFile?: string) => {
135
+ const filePath = openFile || null
136
+ setActiveFilePathState(filePath)
137
+ setMode('notes')
138
+ modeRef.current = 'notes'
139
+ updateNotesHash(filePath)
140
+ }, [])
141
+
142
+ const switchToApiClient = useCallback(() => {
143
+ setMode('api_client')
144
+ modeRef.current = 'api_client'
145
+ // Don't update URL here - let the main API docs component handle it
146
+ }, [])
147
+
148
+ const switchToDocs = useCallback(() => {
149
+ setMode('docs')
150
+ modeRef.current = 'docs'
151
+ // Only clear the hash if it's a notes hash, preserve doc/endpoint hashes
152
+ if (typeof window !== 'undefined') {
153
+ const hash = window.location.hash.slice(1)
154
+ if (hash === 'notes' || hash.startsWith('notes/')) {
155
+ window.history.pushState(null, '', window.location.pathname + window.location.search)
156
+ }
157
+ }
158
+ }, [])
159
+
160
+ const appendStreamingContent = useCallback((delta: string) => {
161
+ setStreamingContent(prev => {
162
+ if (!prev) return prev
163
+ return {
164
+ ...prev,
165
+ content: prev.content + delta,
166
+ }
167
+ })
168
+ }, [])
169
+
170
+ const triggerNotesRefresh = useCallback(() => {
171
+ setNotesRefreshTrigger(prev => prev + 1)
172
+ }, [])
173
+
174
+ const value: ModeContextValue = {
175
+ mode,
176
+ activeFilePath,
177
+ setActiveFilePath,
178
+ streamingContent,
179
+ setStreamingContent,
180
+ appendStreamingContent,
181
+ switchToDocs,
182
+ switchToNotes,
183
+ switchToApiClient,
184
+ notesRefreshTrigger,
185
+ triggerNotesRefresh,
186
+ }
187
+
188
+ return (
189
+ <ModeContext.Provider value={value}>
190
+ {children}
191
+ </ModeContext.Provider>
192
+ )
193
+ }
194
+
195
+ export function useModeContext() {
196
+ const context = useContext(ModeContext)
197
+ if (!context) {
198
+ throw new Error('useModeContext must be used within a ModeProvider')
199
+ }
200
+ return context
201
+ }
202
+
203
+ // Hook for checking if we're in notes mode
204
+ export function useIsNotesMode() {
205
+ const { mode } = useModeContext()
206
+ return mode === 'notes'
207
+ }
@@ -0,0 +1,105 @@
1
+ // Types for the Notes/Scratchpad feature
2
+ // Simplified from full project management to a brainstorming workspace
3
+
4
+ export type PlaygroundMode = 'docs' | 'api_client' | 'notes'
5
+
6
+ // File types supported in the notes workspace
7
+ export type NoteFileType =
8
+ | 'javascript'
9
+ | 'typescript'
10
+ | 'python'
11
+ | 'go'
12
+ | 'ruby'
13
+ | 'php'
14
+ | 'shell' // curl, bash
15
+ | 'markdown'
16
+ | 'mermaid' // Diagrams
17
+ | 'json'
18
+ | 'yaml'
19
+ | 'text'
20
+
21
+ // Single workspace per API (not multiple projects)
22
+ export interface Workspace {
23
+ id: string // Hash of API spec URL
24
+ name: string // e.g., "Vrio API Notes"
25
+ apiSpecUrl?: string
26
+ createdAt: Date
27
+ updatedAt: Date
28
+ }
29
+
30
+ // A note/file in the workspace
31
+ export interface NoteFile {
32
+ id: string
33
+ workspaceId: string
34
+ path: string // e.g., "auth/oauth-example.py"
35
+ content: string
36
+ createdAt: Date
37
+ updatedAt: Date
38
+ }
39
+
40
+ // Editor state for the workspace
41
+ export interface EditorState {
42
+ workspaceId: string
43
+ openTabs: string[] // File paths
44
+ activeTab: string | null
45
+ cursorPositions: Record<string, { line: number; column: number }>
46
+ scrollPositions: Record<string, number>
47
+ }
48
+
49
+ // Helper to detect file type from path
50
+ export function getFileType(path: string): NoteFileType {
51
+ const ext = path.split('.').pop()?.toLowerCase() || ''
52
+
53
+ const typeMap: Record<string, NoteFileType> = {
54
+ 'js': 'javascript',
55
+ 'mjs': 'javascript',
56
+ 'jsx': 'javascript',
57
+ 'ts': 'typescript',
58
+ 'tsx': 'typescript',
59
+ 'py': 'python',
60
+ 'go': 'go',
61
+ 'rb': 'ruby',
62
+ 'php': 'php',
63
+ 'sh': 'shell',
64
+ 'bash': 'shell',
65
+ 'curl': 'shell',
66
+ 'md': 'markdown',
67
+ 'mdx': 'markdown',
68
+ 'mmd': 'mermaid',
69
+ 'mermaid': 'mermaid',
70
+ 'json': 'json',
71
+ 'yaml': 'yaml',
72
+ 'yml': 'yaml',
73
+ 'txt': 'text',
74
+ }
75
+
76
+ return typeMap[ext] || 'text'
77
+ }
78
+
79
+ // Helper to get Monaco language from file type
80
+ export function getMonacoLanguage(path: string): string {
81
+ const type = getFileType(path)
82
+
83
+ const languageMap: Record<NoteFileType, string> = {
84
+ 'javascript': 'javascript',
85
+ 'typescript': 'typescript',
86
+ 'python': 'python',
87
+ 'go': 'go',
88
+ 'ruby': 'ruby',
89
+ 'php': 'php',
90
+ 'shell': 'shell',
91
+ 'markdown': 'markdown',
92
+ 'mermaid': 'markdown', // Use markdown for mermaid editing
93
+ 'json': 'json',
94
+ 'yaml': 'yaml',
95
+ 'text': 'plaintext',
96
+ }
97
+
98
+ return languageMap[type]
99
+ }
100
+
101
+ // Check if file type supports preview
102
+ export function supportsPreview(path: string): boolean {
103
+ const type = getFileType(path)
104
+ return type === 'markdown' || type === 'mermaid'
105
+ }