dotdo 0.0.2 → 0.1.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 (313) hide show
  1. package/cli/README.md +238 -0
  2. package/cli/agent.ts +72 -0
  3. package/cli/bin.js +44 -0
  4. package/cli/bin.ts +38 -0
  5. package/cli/build.ts +157 -0
  6. package/cli/commands/auth/login.ts +14 -0
  7. package/cli/commands/auth/logout.ts +6 -0
  8. package/cli/commands/auth/whoami.ts +16 -0
  9. package/cli/commands/deploy-multi.ts +245 -0
  10. package/cli/commands/dev/deploy.ts +100 -0
  11. package/cli/commands/dev/dev.ts +95 -0
  12. package/cli/commands/dev/logs.ts +91 -0
  13. package/cli/commands/dev-local.ts +88 -0
  14. package/cli/commands/do-ops.ts +314 -0
  15. package/cli/commands/index.ts +100 -0
  16. package/cli/commands/init.ts +247 -0
  17. package/cli/commands/introspect/emitter.ts +315 -0
  18. package/cli/commands/introspect/index.ts +193 -0
  19. package/cli/commands/link.ts +598 -0
  20. package/cli/commands/snippets.ts +415 -0
  21. package/cli/commands/tunnel.ts +239 -0
  22. package/cli/device-auth.ts +289 -0
  23. package/cli/fallback.ts +12 -0
  24. package/cli/index.ts +121 -0
  25. package/cli/main.ts +246 -0
  26. package/cli/mcp-stdio.ts +790 -0
  27. package/cli/package.json +62 -0
  28. package/cli/runtime/do-registry.ts +193 -0
  29. package/cli/runtime/embedded-db.ts +344 -0
  30. package/cli/runtime/index.ts +9 -0
  31. package/cli/runtime/miniflare-adapter.ts +162 -0
  32. package/cli/sandbox.ts +82 -0
  33. package/cli/src/args.ts +174 -0
  34. package/cli/src/auth.ts +55 -0
  35. package/cli/src/commands/call.ts +84 -0
  36. package/cli/src/commands/charge.ts +96 -0
  37. package/cli/src/commands/config.ts +115 -0
  38. package/cli/src/commands/email.ts +112 -0
  39. package/cli/src/commands/llm.ts +115 -0
  40. package/cli/src/commands/queue.ts +134 -0
  41. package/cli/src/commands/text.ts +86 -0
  42. package/cli/src/config.ts +185 -0
  43. package/cli/src/output.ts +246 -0
  44. package/cli/src/rpc.ts +192 -0
  45. package/cli/utils/config.ts +282 -0
  46. package/cli/utils/detect.ts +73 -0
  47. package/cli/utils/index.ts +15 -0
  48. package/cli/utils/logger.ts +232 -0
  49. package/dist/ai/template-literals.js +2 -2
  50. package/dist/ai/template-literals.js.map +1 -1
  51. package/dist/api/middleware/auth.js +3 -2
  52. package/dist/api/middleware/auth.js.map +1 -1
  53. package/dist/db/iceberg/inverted-index.js +1 -1
  54. package/dist/db/iceberg/inverted-index.js.map +1 -1
  55. package/dist/db/iceberg/puffin.js.map +1 -1
  56. package/dist/db/json-indexes.js.map +1 -1
  57. package/dist/db/objects.js.map +1 -1
  58. package/dist/db/primitives/dag-scheduler/index.js +1 -1
  59. package/dist/db/primitives/dag-scheduler/index.js.map +1 -1
  60. package/dist/db/primitives/observability.js.map +1 -1
  61. package/dist/db/primitives/schema-evolution.js.map +1 -1
  62. package/dist/db/primitives/temporal-store.js.map +1 -1
  63. package/dist/db/primitives/typed-column-store.js.map +1 -1
  64. package/dist/db/primitives/utils/duration.js.map +1 -1
  65. package/dist/db/primitives/utils/murmur3.js +12 -14
  66. package/dist/db/primitives/utils/murmur3.js.map +1 -1
  67. package/dist/db/primitives/window-manager.js.map +1 -1
  68. package/dist/db/stores.js.map +1 -1
  69. package/dist/db/things.js.map +1 -1
  70. package/dist/lib/DODispatcher.js +2 -2
  71. package/dist/lib/DODispatcher.js.map +1 -1
  72. package/dist/lib/auto-wiring.js.map +1 -1
  73. package/dist/lib/channels/email.js +1 -1
  74. package/dist/lib/channels/email.js.map +1 -1
  75. package/dist/lib/channels/slack-blockkit.js.map +1 -1
  76. package/dist/lib/cloudflare/ai.js +1 -1
  77. package/dist/lib/cloudflare/ai.js.map +1 -1
  78. package/dist/lib/cloudflare/kv.js +1 -1
  79. package/dist/lib/cloudflare/kv.js.map +1 -1
  80. package/dist/lib/cloudflare/r2.js +3 -3
  81. package/dist/lib/cloudflare/r2.js.map +1 -1
  82. package/dist/lib/cloudflare/vectorize.js.map +1 -1
  83. package/dist/lib/cloudflare/workflows.js.map +1 -1
  84. package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -1
  85. package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -1
  86. package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -1
  87. package/dist/lib/executors/HumanFunctionExecutor.js +1 -1
  88. package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -1
  89. package/dist/lib/executors/ParallelStepExecutor.js.map +1 -1
  90. package/dist/lib/experiments.js.map +1 -1
  91. package/dist/lib/flags/store.js.map +1 -1
  92. package/dist/lib/functions/FunctionComposition.js.map +1 -1
  93. package/dist/lib/functions/FunctionMiddleware.js.map +1 -1
  94. package/dist/lib/functions/FunctionRegistry.js.map +1 -1
  95. package/dist/lib/humans/templates.js.map +1 -1
  96. package/dist/lib/identity.js +2 -2
  97. package/dist/lib/identity.js.map +1 -1
  98. package/dist/lib/logging/index.js.map +1 -1
  99. package/dist/lib/mixins/bash.js +1 -73
  100. package/dist/lib/mixins/bash.js.map +1 -1
  101. package/dist/lib/mixins/git.js +0 -5
  102. package/dist/lib/mixins/git.js.map +1 -1
  103. package/dist/lib/mixins/npm.js.map +1 -1
  104. package/dist/lib/noun-id.js.map +1 -1
  105. package/dist/lib/rate-limit/sliding-window.js.map +1 -1
  106. package/dist/lib/rpc/bindings.js.map +1 -1
  107. package/dist/lib/safe-stringify.js.map +1 -1
  108. package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -1
  109. package/dist/lib/sqids.js.map +1 -1
  110. package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -1
  111. package/dist/lib/sql/adapters/pgsql-parser.js +19 -18
  112. package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -1
  113. package/dist/metrics/hunch.js.map +1 -1
  114. package/dist/objects/API.js +1 -1
  115. package/dist/objects/API.js.map +1 -1
  116. package/dist/objects/Agent.js.map +1 -1
  117. package/dist/objects/Browser.js.map +1 -1
  118. package/dist/objects/CLI.js.map +1 -1
  119. package/dist/objects/DOBase.js.map +1 -1
  120. package/dist/objects/DOCache.js +153 -0
  121. package/dist/objects/DOCache.js.map +1 -0
  122. package/dist/objects/DOFull.js.map +1 -1
  123. package/dist/objects/Entity.js.map +1 -1
  124. package/dist/objects/Human.js.map +1 -1
  125. package/dist/objects/IcebergMetadataDO.js.map +1 -1
  126. package/dist/objects/IntegrationsDO.js.map +1 -1
  127. package/dist/objects/ObservabilityBroadcaster.js.map +1 -1
  128. package/dist/objects/Package.js.map +1 -1
  129. package/dist/objects/Product.js +1 -1
  130. package/dist/objects/Product.js.map +1 -1
  131. package/dist/objects/SaaS.js.map +1 -1
  132. package/dist/objects/SandboxDO.js.map +1 -1
  133. package/dist/objects/Service.js.map +1 -1
  134. package/dist/objects/VectorShardDO.js +9 -7
  135. package/dist/objects/VectorShardDO.js.map +1 -1
  136. package/dist/objects/Workflow.js.map +1 -1
  137. package/dist/objects/WorkflowFactory.js.map +1 -1
  138. package/dist/objects/WorkflowRuntime.js.map +1 -1
  139. package/dist/objects/lifecycle/Branch.js.map +1 -1
  140. package/dist/objects/lifecycle/Clone.js +1 -1
  141. package/dist/objects/lifecycle/Clone.js.map +1 -1
  142. package/dist/objects/lifecycle/Compact.js.map +1 -1
  143. package/dist/objects/lifecycle/Shard.js.map +1 -1
  144. package/dist/objects/persistence/checkpoint-manager.js.map +1 -1
  145. package/dist/objects/persistence/migration-runner.js.map +1 -1
  146. package/dist/objects/persistence/replication-manager.js +2 -2
  147. package/dist/objects/persistence/replication-manager.js.map +1 -1
  148. package/dist/objects/persistence/tiered-storage-manager.js.map +1 -1
  149. package/dist/objects/persistence/wal-manager.js.map +1 -1
  150. package/dist/objects/transport/auth-layer.js.map +1 -1
  151. package/dist/objects/transport/chain.js.map +1 -1
  152. package/dist/objects/transport/mcp-server.js +7 -6
  153. package/dist/objects/transport/mcp-server.js.map +1 -1
  154. package/dist/objects/transport/rest-autowire.js +3 -2
  155. package/dist/objects/transport/rest-autowire.js.map +1 -1
  156. package/dist/objects/transport/rest-router.js.map +1 -1
  157. package/dist/objects/transport/rpc-server.js +18 -15
  158. package/dist/objects/transport/rpc-server.js.map +1 -1
  159. package/dist/objects/transport/shared.js +2 -1
  160. package/dist/objects/transport/shared.js.map +1 -1
  161. package/dist/snippets/artifacts-ingest.js.map +1 -1
  162. package/dist/snippets/artifacts-serve.js.map +1 -1
  163. package/dist/snippets/search.js.map +1 -1
  164. package/dist/workflows/ScheduleManager.js.map +1 -1
  165. package/dist/workflows/StepResultStorage.js.map +1 -1
  166. package/dist/workflows/WaitForEventManager.js.map +1 -1
  167. package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -1
  168. package/dist/workflows/compat/inngest/index.js.map +1 -1
  169. package/dist/workflows/compat/qstash/index.js.map +1 -1
  170. package/dist/workflows/compat/temporal/client.js.map +1 -1
  171. package/dist/workflows/compat/temporal/index.js.map +1 -1
  172. package/dist/workflows/compat/trigger/index.js.map +1 -1
  173. package/dist/workflows/compat/utils/index.js.map +1 -1
  174. package/dist/workflows/context/correlation.js +2 -2
  175. package/dist/workflows/context/correlation.js.map +1 -1
  176. package/dist/workflows/context/experiment.js +1 -1
  177. package/dist/workflows/context/experiment.js.map +1 -1
  178. package/dist/workflows/context/flag.js +1 -1
  179. package/dist/workflows/context/flag.js.map +1 -1
  180. package/dist/workflows/context/measure.js +1 -1
  181. package/dist/workflows/context/measure.js.map +1 -1
  182. package/dist/workflows/context/rate-limit.js.map +1 -1
  183. package/dist/workflows/data/entity-events/entity-events.js.map +1 -1
  184. package/dist/workflows/data/experiment/index.js.map +1 -1
  185. package/dist/workflows/data/goal/context.js +1 -1
  186. package/dist/workflows/data/goal/context.js.map +1 -1
  187. package/dist/workflows/data/measure/index.js +1 -1
  188. package/dist/workflows/data/measure/index.js.map +1 -1
  189. package/dist/workflows/data/stream/index.js +10 -76
  190. package/dist/workflows/data/stream/index.js.map +1 -1
  191. package/dist/workflows/data/track/context.js.map +1 -1
  192. package/dist/workflows/data/view/context.js.map +1 -1
  193. package/dist/workflows/domain.js.map +1 -1
  194. package/dist/workflows/flags.js +1 -1
  195. package/dist/workflows/flags.js.map +1 -1
  196. package/dist/workflows/hash.js.map +1 -1
  197. package/dist/workflows/on.js +1 -1
  198. package/dist/workflows/on.js.map +1 -1
  199. package/dist/workflows/schedule-builder.js.map +1 -1
  200. package/dist/workflows/visibility/index.js +0 -2
  201. package/dist/workflows/visibility/index.js.map +1 -1
  202. package/dist/workflows/visibility/query-parser.js.map +1 -1
  203. package/package.json +18 -3
  204. package/dist/api/analytics/router.js +0 -601
  205. package/dist/api/analytics/router.js.map +0 -1
  206. package/dist/api/index.js +0 -158
  207. package/dist/api/index.js.map +0 -1
  208. package/dist/api/middleware/error-handling.js +0 -176
  209. package/dist/api/middleware/error-handling.js.map +0 -1
  210. package/dist/api/middleware/request-id.js +0 -21
  211. package/dist/api/middleware/request-id.js.map +0 -1
  212. package/dist/api/pages.js +0 -1180
  213. package/dist/api/pages.js.map +0 -1
  214. package/dist/api/routes/api.js +0 -612
  215. package/dist/api/routes/api.js.map +0 -1
  216. package/dist/api/routes/browsers.js +0 -471
  217. package/dist/api/routes/browsers.js.map +0 -1
  218. package/dist/api/routes/do.js +0 -188
  219. package/dist/api/routes/do.js.map +0 -1
  220. package/dist/api/routes/mcp.js +0 -459
  221. package/dist/api/routes/mcp.js.map +0 -1
  222. package/dist/api/routes/obs.js +0 -445
  223. package/dist/api/routes/obs.js.map +0 -1
  224. package/dist/api/routes/openapi.js +0 -794
  225. package/dist/api/routes/openapi.js.map +0 -1
  226. package/dist/api/routes/rpc.js +0 -1103
  227. package/dist/api/routes/rpc.js.map +0 -1
  228. package/dist/api/routes/sandboxes.js +0 -389
  229. package/dist/api/routes/sandboxes.js.map +0 -1
  230. package/dist/api/test-do.js +0 -38
  231. package/dist/api/test-do.js.map +0 -1
  232. package/dist/api/types.js +0 -11
  233. package/dist/api/types.js.map +0 -1
  234. package/dist/cli/bin.js +0 -2
  235. package/dist/cli/main.js +0 -52342
  236. package/dist/do/bash.js +0 -35
  237. package/dist/do/bash.js.map +0 -1
  238. package/dist/do/fs.js +0 -25
  239. package/dist/do/fs.js.map +0 -1
  240. package/dist/do/full.js +0 -61
  241. package/dist/do/full.js.map +0 -1
  242. package/dist/do/git.js +0 -28
  243. package/dist/do/git.js.map +0 -1
  244. package/dist/do/index.js +0 -52
  245. package/dist/do/index.js.map +0 -1
  246. package/dist/lib/agent/tools/bash.js +0 -336
  247. package/dist/lib/agent/tools/bash.js.map +0 -1
  248. package/dist/lib/agent/tools/edit.js +0 -157
  249. package/dist/lib/agent/tools/edit.js.map +0 -1
  250. package/dist/lib/agent/tools/glob.js +0 -137
  251. package/dist/lib/agent/tools/glob.js.map +0 -1
  252. package/dist/lib/agent/tools/grep.js +0 -315
  253. package/dist/lib/agent/tools/grep.js.map +0 -1
  254. package/dist/lib/agent/tools/index.js +0 -71
  255. package/dist/lib/agent/tools/index.js.map +0 -1
  256. package/dist/lib/agent/tools/read.js +0 -212
  257. package/dist/lib/agent/tools/read.js.map +0 -1
  258. package/dist/lib/agent/tools/types.js +0 -197
  259. package/dist/lib/agent/tools/types.js.map +0 -1
  260. package/dist/lib/agent/tools/write.js +0 -159
  261. package/dist/lib/agent/tools/write.js.map +0 -1
  262. package/dist/lib/mixins/index.js +0 -29
  263. package/dist/lib/mixins/index.js.map +0 -1
  264. package/dist/primitives/bashx/src/ast/analyze.js +0 -1472
  265. package/dist/primitives/bashx/src/ast/analyze.js.map +0 -1
  266. package/dist/primitives/bashx/src/ast/parser.js +0 -1488
  267. package/dist/primitives/bashx/src/ast/parser.js.map +0 -1
  268. package/dist/primitives/bashx/src/do/commands/crypto.js +0 -1954
  269. package/dist/primitives/bashx/src/do/commands/crypto.js.map +0 -1
  270. package/dist/primitives/bashx/src/do/commands/data-processing.js +0 -1812
  271. package/dist/primitives/bashx/src/do/commands/data-processing.js.map +0 -1
  272. package/dist/primitives/bashx/src/do/commands/extended-utils.js +0 -804
  273. package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +0 -1
  274. package/dist/primitives/bashx/src/do/commands/math-control.js +0 -1122
  275. package/dist/primitives/bashx/src/do/commands/math-control.js.map +0 -1
  276. package/dist/primitives/bashx/src/do/commands/posix-utils.js +0 -1015
  277. package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +0 -1
  278. package/dist/primitives/bashx/src/do/commands/system-utils.js +0 -687
  279. package/dist/primitives/bashx/src/do/commands/system-utils.js.map +0 -1
  280. package/dist/primitives/bashx/src/do/commands/test-command.js +0 -523
  281. package/dist/primitives/bashx/src/do/commands/test-command.js.map +0 -1
  282. package/dist/primitives/bashx/src/do/commands/text-processing.js +0 -1550
  283. package/dist/primitives/bashx/src/do/commands/text-processing.js.map +0 -1
  284. package/dist/primitives/bashx/src/do/container-executor.js +0 -429
  285. package/dist/primitives/bashx/src/do/container-executor.js.map +0 -1
  286. package/dist/primitives/bashx/src/do/index.js +0 -668
  287. package/dist/primitives/bashx/src/do/index.js.map +0 -1
  288. package/dist/primitives/bashx/src/do/tiered-executor.js +0 -2647
  289. package/dist/primitives/bashx/src/do/tiered-executor.js.map +0 -1
  290. package/dist/primitives/bashx/src/do/worker.js +0 -352
  291. package/dist/primitives/bashx/src/do/worker.js.map +0 -1
  292. package/dist/primitives/bashx/src/types.js +0 -10
  293. package/dist/primitives/bashx/src/types.js.map +0 -1
  294. package/dist/primitives/fsx/core/backend.js +0 -480
  295. package/dist/primitives/fsx/core/backend.js.map +0 -1
  296. package/dist/primitives/fsx/core/constants.js +0 -140
  297. package/dist/primitives/fsx/core/constants.js.map +0 -1
  298. package/dist/primitives/fsx/core/fsx.js +0 -1184
  299. package/dist/primitives/fsx/core/fsx.js.map +0 -1
  300. package/dist/primitives/fsx/core/glob/glob.js +0 -438
  301. package/dist/primitives/fsx/core/glob/glob.js.map +0 -1
  302. package/dist/primitives/fsx/core/glob/index.js +0 -8
  303. package/dist/primitives/fsx/core/glob/index.js.map +0 -1
  304. package/dist/primitives/fsx/core/glob/match.js +0 -392
  305. package/dist/primitives/fsx/core/glob/match.js.map +0 -1
  306. package/dist/primitives/fsx/core/types.js +0 -307
  307. package/dist/primitives/fsx/core/types.js.map +0 -1
  308. package/dist/sdk/capnweb-compat.js +0 -42
  309. package/dist/sdk/capnweb-compat.js.map +0 -1
  310. package/dist/sdk/client.js +0 -20
  311. package/dist/sdk/client.js.map +0 -1
  312. package/dist/sdk/index.js +0 -17
  313. package/dist/sdk/index.js.map +0 -1
package/dist/api/pages.js DELETED
@@ -1,1180 +0,0 @@
1
- /**
2
- * HTML Page Templates for dotdo
3
- *
4
- * Server-rendered HTML pages that match the React components
5
- * for landing, docs, admin, and error pages.
6
- */
7
- // Base styles for all pages
8
- const baseStyles = `
9
- * { box-sizing: border-box; margin: 0; padding: 0; }
10
- body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; background: #fff; color: #111; }
11
- a { color: #2563eb; text-decoration: none; }
12
- a:hover { text-decoration: underline; }
13
- .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
14
- .sr-only:focus { position: static; width: auto; height: auto; padding: 0.5rem 1rem; margin: 0; overflow: visible; clip: auto; background: #fff; z-index: 9999; }
15
- button { cursor: pointer; }
16
- img { max-width: 100%; height: auto; }
17
- `;
18
- // Layout wrapper
19
- const layout = (title, content, extraStyles = '') => `<!DOCTYPE html>
20
- <html lang="en">
21
- <head>
22
- <meta charset="UTF-8">
23
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
24
- <title>${title}</title>
25
- <style>${baseStyles}${extraStyles}</style>
26
- </head>
27
- <body>
28
- ${content}
29
- </body>
30
- </html>`;
31
- // Landing page styles
32
- const landingStyles = `
33
- .skip-link { background: #fff; color: #111; padding: 0.5rem 1rem; position: absolute; top: 0.5rem; left: 0.5rem; z-index: 100; border-radius: 0.25rem; }
34
- .skip-link:not(:focus) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
35
- header { padding: 1rem 2rem; }
36
- nav[role="navigation"] { display: flex; align-items: center; justify-content: space-between; max-width: 80rem; margin: 0 auto; }
37
- .logo { font-size: 1.25rem; font-weight: bold; color: #111; display: flex; align-items: center; gap: 0.5rem; }
38
- .nav-links { display: flex; gap: 1.5rem; }
39
- .nav-links a { color: #555; }
40
- .mobile-menu { display: none; padding: 0.5rem; background: transparent; border: none; }
41
- @media (max-width: 768px) { .nav-links { display: none; } .mobile-menu { display: block; } }
42
- main { flex: 1; }
43
- .hero { padding: 5rem 2rem; text-align: center; }
44
- .hero h1 { font-size: 3rem; margin-bottom: 1rem; }
45
- .hero p { font-size: 1.25rem; color: #555; max-width: 48rem; margin: 0 auto 2rem; }
46
- .hero-buttons { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }
47
- .btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.75rem 1.5rem; border-radius: 0.5rem; font-weight: 500; }
48
- .btn-primary { background: #2563eb; color: #fff; }
49
- .btn-secondary { background: #f3f4f6; color: #374151; }
50
- .features { padding: 5rem 2rem; background: #f9fafb; }
51
- .features h2 { text-align: center; font-size: 2rem; margin-bottom: 3rem; }
52
- .feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; max-width: 80rem; margin: 0 auto; }
53
- .feature-card, .feature-item { background: #fff; padding: 1.5rem; border-radius: 0.75rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
54
- article.feature-card, article.feature-item { display: block; }
55
- .feature-card h3 { font-size: 1.25rem; margin-bottom: 0.5rem; }
56
- .feature-card p { color: #555; }
57
- .feature-icon { width: 3rem; height: 3rem; margin-bottom: 1rem; color: #2563eb; }
58
- .cta, .call-to-action, .get-started { padding: 5rem 2rem; background: #2563eb; color: #fff; text-align: center; }
59
- .cta h2 { font-size: 2rem; margin-bottom: 1rem; }
60
- .cta p { font-size: 1.125rem; color: rgba(255,255,255,0.9); margin-bottom: 2rem; }
61
- .cta .btn { background: #fff; color: #2563eb; }
62
- footer { padding: 3rem 2rem; background: #111; color: #aaa; }
63
- .footer-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 2rem; max-width: 80rem; margin: 0 auto; }
64
- footer h3 { color: #fff; margin-bottom: 1rem; }
65
- footer a { color: #aaa; }
66
- footer a:hover { color: #fff; }
67
- `;
68
- export function landingPageHtml() {
69
- const content = `
70
- <a href="#main-content" class="skip-link">Skip to main content</a>
71
-
72
- <header role="banner">
73
- <nav role="navigation">
74
- <a href="/" class="logo">
75
- <svg width="32" height="32" viewBox="0 0 32 32" fill="none" aria-hidden="true">
76
- <circle cx="16" cy="16" r="14" stroke="currentColor" stroke-width="2"/>
77
- <circle cx="16" cy="16" r="6" fill="currentColor"/>
78
- </svg>
79
- dotdo
80
- </a>
81
- <div class="nav-links">
82
- <a href="/docs">Docs</a>
83
- <a href="/docs/getting-started">Guide</a>
84
- <a href="https://github.com/dot-do/dotdo">GitHub</a>
85
- <a href="/admin">Dashboard</a>
86
- <a href="/docs" class="btn btn-primary">Get Started</a>
87
- </div>
88
- <button type="button" class="mobile-menu" aria-label="Toggle menu" aria-expanded="false">
89
- <svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
90
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
91
- </svg>
92
- </button>
93
- </nav>
94
- </header>
95
-
96
- <main id="main-content" role="main" style="flex:1">
97
- <section id="hero" class="hero Hero">
98
- <h1>dotdo</h1>
99
- <p>Build stateful serverless applications with Cloudflare Durable Objects. Type-safe, scalable, and easy to use.</p>
100
- <div class="hero-buttons">
101
- <a href="/docs" class="btn btn-primary">Get Started</a>
102
- <a href="https://github.com/dot-do/dotdo" class="btn btn-secondary">View on GitHub</a>
103
- </div>
104
- </section>
105
-
106
- <section id="features" class="features Features">
107
- <h2>Why dotdo?</h2>
108
- <div class="feature-grid">
109
- <article class="feature-card feature-item">
110
- <div class="feature-icon">
111
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
112
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>
113
- </svg>
114
- </div>
115
- <h3>Type-Safe TypeScript</h3>
116
- <p>Full TypeScript support with type inference for Durable Object state and RPC methods.</p>
117
- </article>
118
- <article class="feature-card feature-item">
119
- <div class="feature-icon">
120
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
121
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"/>
122
- </svg>
123
- </div>
124
- <h3>Stateful by Design</h3>
125
- <p>Built-in state management with durable storage. Your data persists across requests automatically.</p>
126
- </article>
127
- <article class="feature-card feature-item">
128
- <div class="feature-icon">
129
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
130
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
131
- </svg>
132
- </div>
133
- <h3>Edge-Native Performance</h3>
134
- <p>Run your code at the edge, close to your users. Sub-millisecond latency worldwide.</p>
135
- </article>
136
- <article class="feature-card feature-item">
137
- <div class="feature-icon">
138
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
139
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
140
- </svg>
141
- </div>
142
- <h3>Real-time Capable</h3>
143
- <p>WebSocket support out of the box. Build real-time collaborative apps with ease.</p>
144
- </article>
145
- <article class="feature-card feature-item">
146
- <div class="feature-icon">
147
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
148
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
149
- </svg>
150
- </div>
151
- <h3>Infinitely Scalable</h3>
152
- <p>Scale to millions of concurrent connections. Each Durable Object handles its own state.</p>
153
- </article>
154
- <article class="feature-card feature-item">
155
- <div class="feature-icon">
156
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
157
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
158
- </svg>
159
- </div>
160
- <h3>Great Developer Experience</h3>
161
- <p>Simple, intuitive API. Write less boilerplate and focus on your business logic.</p>
162
- </article>
163
- </div>
164
- </section>
165
-
166
- <section id="cta" class="cta CTA call-to-action get-started">
167
- <h2>Ready to build something amazing?</h2>
168
- <p>Get started with dotdo in minutes. No complex setup required.</p>
169
- <div class="hero-buttons">
170
- <a href="/docs" class="btn">Read the Docs</a>
171
- <a href="https://github.com/dot-do/dotdo" class="btn" style="border: 2px solid #fff; background: transparent; color: #fff;">Star on GitHub</a>
172
- </div>
173
- </section>
174
- </main>
175
-
176
- <footer role="contentinfo">
177
- <div class="footer-grid">
178
- <div>
179
- <a href="/" class="logo" style="color: #fff;">dotdo</a>
180
- <p style="margin-top: 1rem;">Build stateful serverless applications with Cloudflare Durable Objects.</p>
181
- </div>
182
- <div>
183
- <h3>Resources</h3>
184
- <ul style="list-style: none;">
185
- <li><a href="/docs">Documentation</a></li>
186
- <li><a href="/docs/getting-started">Getting Started</a></li>
187
- <li><a href="/docs/api">API Reference</a></li>
188
- </ul>
189
- </div>
190
- <div>
191
- <h3>Community</h3>
192
- <ul style="list-style: none;">
193
- <li><a href="https://github.com/dot-do/dotdo">GitHub</a></li>
194
- <li><a href="https://twitter.com/dotdodev">Twitter</a></li>
195
- <li><a href="https://discord.gg/dotdo">Discord</a></li>
196
- </ul>
197
- </div>
198
- </div>
199
- <div style="text-align: center; margin-top: 2rem; padding-top: 2rem; border-top: 1px solid #333;">
200
- <p>&copy; ${new Date().getFullYear()} dotdo. All rights reserved.</p>
201
- </div>
202
- </footer>
203
-
204
- <img src="/images/hero-bg.webp" alt="" aria-hidden="true" loading="lazy" style="display:none;" width="1920" height="1080">
205
- `;
206
- return layout('dotdo - Stateful Serverless Applications', content, landingStyles);
207
- }
208
- // Docs page styles
209
- const docsStyles = `
210
- .docs-layout { display: flex; min-height: 100vh; }
211
- aside.sidebar, .docs-nav, nav.sidebar, [data-sidebar] { width: 250px; padding: 1rem; background: #f9fafb; border-right: 1px solid #e5e7eb; }
212
- aside a { display: block; padding: 0.5rem; color: #555; border-radius: 0.25rem; }
213
- aside a:hover, aside a.active { background: #e5e7eb; color: #111; text-decoration: none; }
214
- .docs-main { flex: 1; padding: 2rem; max-width: 800px; }
215
- .docs-content, .prose, article, .mdx-content { line-height: 1.8; }
216
- .docs-content h1 { font-size: 2rem; margin-bottom: 1rem; }
217
- .docs-content p { margin-bottom: 1rem; }
218
- pre code, .code-block, [data-rehype-pretty-code] { display: block; background: #1f2937; color: #e5e7eb; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; font-family: ui-monospace, monospace; margin: 1rem 0; }
219
- .toc, .table-of-contents, nav[aria-label*="contents"] { position: fixed; right: 2rem; top: 6rem; width: 200px; padding: 1rem; background: #f9fafb; border-radius: 0.5rem; }
220
- .toc h4 { margin-bottom: 0.5rem; font-size: 0.875rem; color: #555; }
221
- .toc a { display: block; padding: 0.25rem 0; color: #555; font-size: 0.875rem; }
222
- .breadcrumbs, nav[aria-label*="breadcrumb"] { display: flex; gap: 0.5rem; margin-bottom: 1rem; font-size: 0.875rem; color: #555; }
223
- .pagination, .prev-next { display: flex; justify-content: space-between; margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #e5e7eb; }
224
- .pagination a, .prev-next a { color: #2563eb; }
225
- input[type="search"], [data-search], button:has-text("Search") { padding: 0.5rem 1rem; border: 1px solid #e5e7eb; border-radius: 0.25rem; width: 100%; margin-bottom: 1rem; }
226
- `;
227
- export function docsPageHtml(page) {
228
- const titles = {
229
- 'index': 'Documentation',
230
- 'getting-started': 'Getting Started',
231
- 'api': 'API Reference',
232
- };
233
- const title = titles[page] || page.split('/').pop() || 'Documentation';
234
- const content = `
235
- <div class="docs-layout">
236
- <aside class="sidebar docs-nav" data-sidebar role="navigation" aria-label="Documentation navigation">
237
- <input type="search" placeholder="Search docs..." data-search aria-label="Search documentation">
238
- <nav>
239
- <a href="/docs" ${page === 'index' ? 'class="active" aria-current="page"' : ''}>Overview</a>
240
- <a href="/docs/getting-started" ${page === 'getting-started' ? 'class="active" aria-current="page"' : ''}>Getting Started</a>
241
- <a href="/docs/api" ${page === 'api' ? 'class="active" aria-current="page"' : ''}>API Reference</a>
242
- <a href="/docs/concepts">Concepts</a>
243
- <a href="/docs/guides">Guides</a>
244
- </nav>
245
- </aside>
246
-
247
- <main class="docs-main">
248
- <nav class="breadcrumbs" aria-label="breadcrumb">
249
- <ol style="display:flex; gap:0.5rem; list-style:none;">
250
- <li><a href="/docs">Docs</a></li>
251
- <li>/</li>
252
- <li aria-current="page">${title}</li>
253
- </ol>
254
- </nav>
255
-
256
- <article class="docs-content prose mdx-content">
257
- <h1>${title}</h1>
258
- <p>Welcome to the dotdo documentation. Learn how to build stateful serverless applications.</p>
259
-
260
- <h2 id="installation">Installation</h2>
261
- <p>Install dotdo using npm:</p>
262
- <pre><code class="code-block" data-rehype-pretty-code>npm install dotdo</code></pre>
263
-
264
- <h2 id="quick-start">Quick Start</h2>
265
- <p>Create your first Durable Object:</p>
266
- <pre><code class="code-block" data-rehype-pretty-code>import { DurableObject } from 'dotdo'
267
-
268
- export class Counter extends DurableObject {
269
- async increment() {
270
- const value = await this.state.storage.get('count') ?? 0
271
- await this.state.storage.put('count', value + 1)
272
- return value + 1
273
- }
274
- }</code></pre>
275
- </article>
276
-
277
- <aside class="toc table-of-contents" aria-label="Table of contents">
278
- <h4>On this page</h4>
279
- <nav>
280
- <a href="#installation">Installation</a>
281
- <a href="#quick-start">Quick Start</a>
282
- </nav>
283
- </aside>
284
-
285
- <nav class="pagination prev-next" aria-label="Previous and next pages">
286
- <a href="/docs" class="previous">Previous</a>
287
- <a href="/docs/getting-started" class="next">Next</a>
288
- </nav>
289
- </main>
290
- </div>
291
- `;
292
- return layout(`${title} - dotdo Documentation`, content, docsStyles);
293
- }
294
- // Admin styles - enhanced for MDX cockpit dashboard
295
- const adminStyles = `
296
- .admin-layout { display: flex; min-height: 100vh; }
297
- .shell, .admin-shell, aside.sidebar { width: 250px; background: #1f2937; color: #fff; padding: 1rem; }
298
- .shell a { display: block; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; margin-bottom: 0.25rem; }
299
- .shell a:hover { background: #374151; color: #fff; text-decoration: none; }
300
- .shell a.active { background: #2563eb; color: #fff; }
301
- .admin-main { flex: 1; padding: 2rem; background: #0a0a0a; color: #fff; overflow-y: auto; }
302
- .dashboard, [data-dashboard] { padding: 0; }
303
- .metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
304
- .kpi-card, .metric-card, .stat-card { background: #111; padding: 1.5rem; border-radius: 0.75rem; border: 1px solid #333; }
305
- .kpi-card h3 { font-size: 0.875rem; color: #9ca3af; margin-bottom: 0.5rem; }
306
- .kpi-card .value { font-size: 2rem; font-weight: bold; }
307
- table, [data-table], .data-table { width: 100%; background: #111; border-radius: 0.5rem; overflow: hidden; border: 1px solid #333; }
308
- table th, table td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid #333; }
309
- table th { background: #0a0a0a; font-weight: 600; }
310
- select, [data-filter] { padding: 0.5rem 1rem; border: 1px solid #333; border-radius: 0.375rem; background: #111; color: #fff; }
311
- input[type="search"], input[placeholder*="Search"] { padding: 0.5rem 1rem; border: 1px solid #333; border-radius: 0.375rem; width: 100%; max-width: 300px; background: #111; color: #fff; }
312
- .btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.5rem 1rem; border-radius: 0.375rem; font-weight: 500; border: none; }
313
- .btn-primary { background: #2563eb; color: #fff; }
314
- form { background: #111; padding: 1.5rem; border-radius: 0.5rem; border: 1px solid #333; }
315
- form label { display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem; color: #9ca3af; }
316
- form input, form select { width: 100%; padding: 0.5rem 0.75rem; border: 1px solid #333; border-radius: 0.375rem; margin-bottom: 1rem; background: #0a0a0a; color: #fff; }
317
- form input[type="submit"], form button[type="submit"] { background: #2563eb; color: #fff; border: none; cursor: pointer; width: auto; }
318
- hr { border: none; border-top: 1px solid #333; margin: 2rem 0; }
319
- h1, h2, h3 { color: #fff; }
320
- p { color: #9ca3af; }
321
- [data-component] { color: #fff; }
322
- .section-box { background: #111; border: 1px solid #333; border-radius: 0.75rem; padding: 1.5rem; margin-bottom: 1rem; }
323
- .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
324
- .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
325
- .grid-6 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1rem; }
326
- .agent-card { text-align: center; padding: 1rem; background: #111; border: 1px solid #333; border-radius: 0.5rem; }
327
- .agent-avatar { width: 3rem; height: 3rem; margin: 0 auto 0.5rem; border-radius: 50%; background: linear-gradient(135deg, #3b82f6, #8b5cf6); display: flex; align-items: center; justify-content: center; font-weight: bold; }
328
- .command-item { display: block; width: 100%; text-align: left; padding: 0.75rem 1rem; background: transparent; border: none; color: #fff; cursor: pointer; border-radius: 0.375rem; }
329
- .command-item:hover { background: #333; }
330
- .tabs-list { display: flex; gap: 0.5rem; border-bottom: 1px solid #333; padding-bottom: 0.5rem; margin-bottom: 1rem; }
331
- .tab-trigger { padding: 0.5rem 1rem; background: transparent; border: none; color: #9ca3af; cursor: pointer; border-radius: 0.375rem; }
332
- .tab-trigger[data-state="active"] { background: #333; color: #fff; }
333
- .workflow-item { display: flex; justify-content: space-between; padding: 0.75rem; background: #0a0a0a; border-radius: 0.375rem; margin-bottom: 0.5rem; }
334
- .timeline-item { display: flex; gap: 0.75rem; margin-bottom: 0.75rem; }
335
- .timeline-dot { width: 0.5rem; height: 0.5rem; background: #3b82f6; border-radius: 50%; margin-top: 0.5rem; }
336
- .chart-placeholder { height: 12rem; display: flex; align-items: center; justify-content: center; color: #666; background: #0a0a0a; border-radius: 0.375rem; }
337
- .settings-section { margin-bottom: 1.5rem; }
338
- .sidebar-nav { display: flex; flex-direction: column; gap: 0.25rem; }
339
- .nav-item { display: flex; align-items: center; gap: 0.75rem; }
340
- .sidebar-user { padding-top: 1rem; margin-top: auto; border-top: 1px solid #333; display: flex; align-items: center; gap: 0.75rem; }
341
- `;
342
- // SVG Icons for the dashboard (inline)
343
- const icons = {
344
- Home: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>',
345
- Rocket: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path></svg>',
346
- Users: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><path d="M20 8v6"></path><path d="M23 11h-6"></path></svg>',
347
- GitBranch: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"></line><circle cx="18" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><path d="M18 9a9 9 0 0 1-9 9"></path></svg>',
348
- Activity: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>',
349
- Code: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>',
350
- BarChart: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="20" x2="12" y2="10"></line><line x1="18" y1="20" x2="18" y2="4"></line><line x1="6" y1="20" x2="6" y2="16"></line></svg>',
351
- Settings: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>',
352
- };
353
- // Kept for backwards compatibility with other admin pages
354
- const adminShell = (title, activeRoute, content) => `
355
- <div class="admin-layout">
356
- <aside class="shell admin-shell sidebar">
357
- <a href="/admin" style="font-size: 1.25rem; font-weight: bold; color: #fff; margin-bottom: 2rem; display: block;">dotdo Admin</a>
358
- <nav>
359
- <a href="/admin" ${activeRoute === 'dashboard' ? 'class="active"' : ''}>Dashboard</a>
360
- <a href="/admin/users" ${activeRoute === 'users' ? 'class="active"' : ''}>Users</a>
361
- <a href="/admin/workflows" ${activeRoute === 'workflows' ? 'class="active"' : ''}>Workflows</a>
362
- <a href="/admin/integrations" ${activeRoute === 'integrations' ? 'class="active"' : ''}>Integrations</a>
363
- <a href="/admin/activity" ${activeRoute === 'activity' ? 'class="active"' : ''}>Activity</a>
364
- <a href="/admin/settings" ${activeRoute === 'settings' ? 'class="active"' : ''}>Settings</a>
365
- </nav>
366
- <div style="margin-top: auto; padding-top: 2rem;">
367
- <a href="/" style="color: #9ca3af;">Back to Site</a>
368
- <button style="display: block; margin-top: 0.5rem; background: transparent; border: none; color: #9ca3af; cursor: pointer;">Logout</button>
369
- <a href="/admin/login" style="color: #9ca3af;">Sign out</a>
370
- </div>
371
- </aside>
372
- <main class="admin-main">
373
- <h1 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1.5rem;">${title}</h1>
374
- ${content}
375
- </main>
376
- </div>
377
- `;
378
- /**
379
- * Admin Dashboard - Renders .do/App.mdx content with @mdxui/cockpit components
380
- *
381
- * This server-side rendered page mirrors the MDX structure from .do/App.mdx
382
- * with all required data attributes for E2E testing.
383
- */
384
- export function adminDashboardHtml() {
385
- // Data that would be passed to MDX
386
- const stats = {
387
- activeAgents: 7,
388
- agentTrend: '+12%',
389
- runningWorkflows: 12,
390
- workflowTrend: '+3',
391
- eventsToday: 1248,
392
- eventTrend: '+15%',
393
- };
394
- const recentActivity = [
395
- { id: '1', type: 'agent', title: 'Priya completed product spec', description: 'MVP specification for new feature', timestamp: new Date().toISOString() },
396
- { id: '2', type: 'workflow', title: 'Deployment workflow completed', description: 'Production deployment successful', timestamp: new Date(Date.now() - 3600000).toISOString() },
397
- { id: '3', type: 'event', title: 'New customer signup', description: 'Customer.signup event processed', timestamp: new Date(Date.now() - 7200000).toISOString() },
398
- ];
399
- const teamAgents = [
400
- { id: '1', name: 'Priya', status: 'working', role: 'Product' },
401
- { id: '2', name: 'Ralph', status: 'idle', role: 'Engineering' },
402
- { id: '3', name: 'Tom', status: 'reviewing', role: 'Tech Lead' },
403
- { id: '4', name: 'Mark', status: 'writing', role: 'Marketing' },
404
- { id: '5', name: 'Sally', status: 'outreach', role: 'Sales' },
405
- { id: '6', name: 'Quinn', status: 'testing', role: 'QA' },
406
- ];
407
- const startupColumns = ['Name', 'Status', 'Hypothesis', 'Experiments', 'Metrics'];
408
- const startups = [
409
- { name: 'MyStartup', status: 'Active', hypothesis: 'PMF for B2B SaaS', experiments: 3, metrics: 'HUNCH: 72%' },
410
- { name: 'AcmeInc', status: 'Testing', hypothesis: 'Marketplace model', experiments: 5, metrics: 'HUNCH: 58%' },
411
- ];
412
- const activeWorkflows = [
413
- { id: '1', name: 'Customer Onboarding', status: 'running' },
414
- { id: '2', name: 'Daily Standup', status: 'scheduled' },
415
- { id: '3', name: 'Code Review', status: 'running' },
416
- ];
417
- const workflowEvents = [
418
- { id: '1', type: 'workflow.started', timestamp: new Date().toISOString() },
419
- { id: '2', type: 'task.completed', timestamp: new Date(Date.now() - 1800000).toISOString() },
420
- { id: '3', type: 'agent.assigned', timestamp: new Date(Date.now() - 3600000).toISOString() },
421
- ];
422
- const revenueData = [
423
- { month: 'Jan', revenue: 4000 },
424
- { month: 'Feb', revenue: 5200 },
425
- { month: 'Mar', revenue: 6100 },
426
- { month: 'Apr', revenue: 7800 },
427
- { month: 'May', revenue: 9200 },
428
- { month: 'Jun', revenue: 11500 },
429
- ];
430
- const agentUsage = [
431
- { agent: 'Priya', tasks: 45 },
432
- { agent: 'Ralph', tasks: 128 },
433
- { agent: 'Tom', tasks: 67 },
434
- { agent: 'Mark', tasks: 34 },
435
- { agent: 'Sally', tasks: 89 },
436
- { agent: 'Quinn', tasks: 56 },
437
- ];
438
- const experimentResults = [
439
- { variant: 'Control', conversion: 3.2 },
440
- { variant: 'A', conversion: 4.1 },
441
- { variant: 'B', conversion: 3.8 },
442
- { variant: 'C', conversion: 5.2 },
443
- ];
444
- const content = `
445
- <div data-mdx-source=".do/App.mdx" data-mdxui-cockpit data-mdx-runtime>
446
- <div data-component="DashboardLayout" class="admin-layout" style="display: flex; min-height: 100vh;">
447
- <!-- Sidebar -->
448
- <div data-component="Sidebar" style="width: 250px; background: #111; border-right: 1px solid #333; display: flex; flex-direction: column;">
449
- <div data-component="SidebarNav" class="sidebar-nav" style="flex: 1; padding: 1rem;">
450
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
451
- <a href="/admin" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #fff; border-radius: 0.5rem; text-decoration: none;">
452
- <span data-icon="Home">${icons.Home}</span>
453
- <span>Overview</span>
454
- </a>
455
- </div>
456
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
457
- <a href="/admin/startups" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
458
- <span data-icon="Rocket">${icons.Rocket}</span>
459
- <span>Startups</span>
460
- </a>
461
- </div>
462
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
463
- <a href="/admin/agents" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
464
- <span data-icon="Users">${icons.Users}</span>
465
- <span>Agents</span>
466
- </a>
467
- </div>
468
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
469
- <a href="/admin/workflows" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
470
- <span data-icon="GitBranch">${icons.GitBranch}</span>
471
- <span>Workflows</span>
472
- </a>
473
- </div>
474
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
475
- <a href="/admin/events" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
476
- <span data-icon="Activity">${icons.Activity}</span>
477
- <span>Events</span>
478
- </a>
479
- </div>
480
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
481
- <a href="/admin/functions" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
482
- <span data-icon="Code">${icons.Code}</span>
483
- <span>Functions</span>
484
- </a>
485
- </div>
486
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
487
- <a href="/admin/analytics" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
488
- <span data-icon="BarChart">${icons.BarChart}</span>
489
- <span>Analytics</span>
490
- </a>
491
- </div>
492
- <div data-component="NavItem" class="nav-item" style="margin-bottom: 0.25rem;">
493
- <a href="/admin/settings" style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: #9ca3af; border-radius: 0.5rem; text-decoration: none;">
494
- <span data-icon="Settings">${icons.Settings}</span>
495
- <span>Settings</span>
496
- </a>
497
- </div>
498
- </div>
499
- <div data-component="SidebarUser" class="sidebar-user" style="padding: 1rem; border-top: 1px solid #333;">
500
- <div style="width: 2rem; height: 2rem; border-radius: 50%; background: linear-gradient(135deg, #3b82f6, #8b5cf6); display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.875rem;">U</div>
501
- <div>
502
- <div style="font-size: 0.875rem; font-weight: 500;">User</div>
503
- <div style="font-size: 0.75rem; color: #9ca3af;">user@dotdo.dev</div>
504
- </div>
505
- </div>
506
- </div>
507
-
508
- <!-- Main Content -->
509
- <div data-component="DashboardContent" style="flex: 1; padding: 2rem; overflow-y: auto; background: #0a0a0a;">
510
- <!-- Main Heading from MDX: # dotdo Dashboard -->
511
- <h1 style="font-size: 2.5rem; font-weight: bold; margin-bottom: 2rem; color: #fff;">dotdo Dashboard</h1>
512
-
513
- <!-- Welcome Section from MDX: ## Welcome to dotdo -->
514
- <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem; color: #fff;">Welcome to dotdo</h2>
515
- <p style="color: #9ca3af; margin-bottom: 1.5rem;">Your autonomous business control center.</p>
516
-
517
- <!-- KPI Cards Grid -->
518
- <div data-component="DashboardGrid" data-cols="3" class="grid-3" style="margin-bottom: 2rem;">
519
- <div data-component="KPICard" class="section-box">
520
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
521
- <span style="font-size: 0.875rem; color: #9ca3af;">Active Agents</span>
522
- <span data-icon="Users">${icons.Users}</span>
523
- </div>
524
- <div data-kpi-value style="font-size: 2rem; font-weight: bold; color: #fff;">${stats.activeAgents}</div>
525
- <div data-kpi-trend style="font-size: 0.875rem; color: #22c55e; margin-top: 0.5rem;">${stats.agentTrend}</div>
526
- </div>
527
- <div data-component="KPICard" class="section-box">
528
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
529
- <span style="font-size: 0.875rem; color: #9ca3af;">Workflows Running</span>
530
- <span data-icon="GitBranch">${icons.GitBranch}</span>
531
- </div>
532
- <div data-kpi-value style="font-size: 2rem; font-weight: bold; color: #fff;">${stats.runningWorkflows}</div>
533
- <div data-kpi-trend style="font-size: 0.875rem; color: #22c55e; margin-top: 0.5rem;">${stats.workflowTrend}</div>
534
- </div>
535
- <div data-component="KPICard" class="section-box">
536
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
537
- <span style="font-size: 0.875rem; color: #9ca3af;">Events Today</span>
538
- <span data-icon="Activity">${icons.Activity}</span>
539
- </div>
540
- <div data-kpi-value style="font-size: 2rem; font-weight: bold; color: #fff;">${stats.eventsToday}</div>
541
- <div data-kpi-trend style="font-size: 0.875rem; color: #22c55e; margin-top: 0.5rem;">${stats.eventTrend}</div>
542
- </div>
543
- </div>
544
-
545
- <!-- Recent Activity from MDX: ### Recent Activity -->
546
- <h3 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Recent Activity</h3>
547
- <div data-component="ActivityFeed" data-items-count="${recentActivity.length}" class="section-box" style="margin-bottom: 2rem;">
548
- ${recentActivity.map(item => `
549
- <div data-activity-item style="padding: 0.75rem; border-bottom: 1px solid #333;">
550
- <div style="font-weight: 500; color: #fff;">${item.title}</div>
551
- <div style="font-size: 0.875rem; color: #9ca3af;">${item.description}</div>
552
- <div style="font-size: 0.75rem; color: #666; margin-top: 0.25rem;">${new Date(item.timestamp).toLocaleString()}</div>
553
- </div>
554
- `).join('')}
555
- </div>
556
-
557
- <!-- Your Team from MDX: ### Your Team -->
558
- <h3 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Your Team</h3>
559
- <div data-component="AgentStatus" data-agents-count="${teamAgents.length}" class="section-box" style="margin-bottom: 2rem;">
560
- <p style="color: #9ca3af; margin-bottom: 1rem;">Monitor your AI agents in real-time. See what Priya, Ralph, Tom, and the team are working on.</p>
561
- <div class="grid-6">
562
- ${teamAgents.map(agent => `
563
- <div style="text-align: center; padding: 1rem; background: #0a0a0a; border-radius: 0.5rem;">
564
- <div style="width: 2.5rem; height: 2.5rem; margin: 0 auto 0.5rem; border-radius: 50%; background: linear-gradient(135deg, #3b82f6, #8b5cf6); display: flex; align-items: center; justify-content: center; font-weight: bold;">${agent.name[0]}</div>
565
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">${agent.name}</div>
566
- <div style="font-size: 0.75rem; color: #9ca3af;">${agent.status}</div>
567
- </div>
568
- `).join('')}
569
- </div>
570
- </div>
571
-
572
- <!-- Quick Actions from MDX: ### Quick Actions -->
573
- <h3 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Quick Actions</h3>
574
- <div data-component="CommandPalette" class="section-box" style="margin-bottom: 2rem;">
575
- <div data-component="CommandGroup" style="margin-bottom: 1rem;">
576
- <div style="font-size: 0.875rem; font-weight: 600; color: #9ca3af; margin-bottom: 0.5rem; padding: 0 1rem;">Agents</div>
577
- <button data-component="CommandItem" data-agent="priya" class="command-item">Ask Priya for product direction</button>
578
- <button data-component="CommandItem" data-agent="ralph" class="command-item">Have Ralph start building</button>
579
- <button data-component="CommandItem" data-agent="tom" class="command-item">Request code review from Tom</button>
580
- </div>
581
- <div data-component="CommandGroup">
582
- <div style="font-size: 0.875rem; font-weight: 600; color: #9ca3af; margin-bottom: 0.5rem; padding: 0 1rem;">Workflows</div>
583
- <button data-component="CommandItem" class="command-item">Create new workflow</button>
584
- <button data-component="CommandItem" class="command-item">View running workflows</button>
585
- <button data-component="CommandItem" class="command-item">Check event log</button>
586
- </div>
587
- </div>
588
-
589
- <hr />
590
-
591
- <!-- Startup Management from MDX: ## Startup Management -->
592
- <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Startup Management</h2>
593
- <div data-component="DataTable" class="section-box" style="margin-bottom: 2rem;">
594
- <p style="color: #9ca3af; margin-bottom: 1rem;">Manage all your startups from one place. Track hypotheses, experiments, and metrics.</p>
595
- <div data-search style="margin-bottom: 1rem;">
596
- <input type="search" placeholder="Search..." style="width: 100%; max-width: 300px; padding: 0.5rem 1rem; background: #0a0a0a; border: 1px solid #333; border-radius: 0.375rem; color: #fff;" />
597
- </div>
598
- <table style="width: 100%; border-collapse: collapse;">
599
- <thead>
600
- <tr style="border-bottom: 1px solid #333;">
601
- ${startupColumns.map(col => `<th data-sortable style="padding: 0.75rem 1rem; text-align: left; font-weight: 600; color: #9ca3af;">${col}</th>`).join('')}
602
- </tr>
603
- </thead>
604
- <tbody>
605
- ${startups.map(startup => `
606
- <tr style="border-bottom: 1px solid #333;">
607
- <td style="padding: 0.75rem 1rem; color: #fff;">${startup.name}</td>
608
- <td style="padding: 0.75rem 1rem; color: #fff;">${startup.status}</td>
609
- <td style="padding: 0.75rem 1rem; color: #fff;">${startup.hypothesis}</td>
610
- <td style="padding: 0.75rem 1rem; color: #fff;">${startup.experiments}</td>
611
- <td style="padding: 0.75rem 1rem; color: #fff;">${startup.metrics}</td>
612
- </tr>
613
- `).join('')}
614
- </tbody>
615
- </table>
616
- <div data-pagination style="display: flex; justify-content: space-between; align-items: center; margin-top: 1rem; padding: 0.5rem 0;">
617
- <span style="font-size: 0.875rem; color: #9ca3af;">Showing ${startups.length} results</span>
618
- <div style="display: flex; gap: 0.5rem;">
619
- <button style="padding: 0.25rem 0.75rem; background: #333; border: none; border-radius: 0.25rem; color: #fff; cursor: pointer;">Previous</button>
620
- <button style="padding: 0.25rem 0.75rem; background: #333; border: none; border-radius: 0.25rem; color: #fff; cursor: pointer;">Next</button>
621
- </div>
622
- </div>
623
- </div>
624
-
625
- <hr />
626
-
627
- <!-- Agent Configuration from MDX: ## Agent Configuration -->
628
- <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Agent Configuration</h2>
629
- <div data-component="Tabs" class="section-box" style="margin-bottom: 2rem;">
630
- <div data-component="TabsList" class="tabs-list">
631
- <button data-component="TabsTrigger" data-state="active" class="tab-trigger">Active Agents</button>
632
- <button data-component="TabsTrigger" data-state="inactive" class="tab-trigger">Available</button>
633
- <button data-component="TabsTrigger" data-state="inactive" class="tab-trigger">Custom</button>
634
- </div>
635
- <div data-component="TabsContent" data-value="active">
636
- <div data-component="AgentGrid" class="grid-6">
637
- <div data-component="AgentCard" data-status="working" class="agent-card">
638
- <div class="agent-avatar">P</div>
639
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">Priya</div>
640
- <div style="font-size: 0.75rem; color: #9ca3af;">Product</div>
641
- <div style="margin-top: 0.5rem;"><span style="padding: 0.125rem 0.5rem; font-size: 0.75rem; background: #166534; color: #22c55e; border-radius: 9999px;">working</span></div>
642
- </div>
643
- <div data-component="AgentCard" data-status="idle" class="agent-card">
644
- <div class="agent-avatar">R</div>
645
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">Ralph</div>
646
- <div style="font-size: 0.75rem; color: #9ca3af;">Engineering</div>
647
- <div style="margin-top: 0.5rem;"><span style="padding: 0.125rem 0.5rem; font-size: 0.75rem; background: #333; color: #9ca3af; border-radius: 9999px;">idle</span></div>
648
- </div>
649
- <div data-component="AgentCard" data-status="reviewing" class="agent-card">
650
- <div class="agent-avatar">T</div>
651
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">Tom</div>
652
- <div style="font-size: 0.75rem; color: #9ca3af;">Tech Lead</div>
653
- <div style="margin-top: 0.5rem;"><span style="padding: 0.125rem 0.5rem; font-size: 0.75rem; background: #1e3a5f; color: #3b82f6; border-radius: 9999px;">reviewing</span></div>
654
- </div>
655
- <div data-component="AgentCard" data-status="writing" class="agent-card">
656
- <div class="agent-avatar">M</div>
657
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">Mark</div>
658
- <div style="font-size: 0.75rem; color: #9ca3af;">Marketing</div>
659
- <div style="margin-top: 0.5rem;"><span style="padding: 0.125rem 0.5rem; font-size: 0.75rem; background: #422006; color: #f97316; border-radius: 9999px;">writing</span></div>
660
- </div>
661
- <div data-component="AgentCard" data-status="outreach" class="agent-card">
662
- <div class="agent-avatar">S</div>
663
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">Sally</div>
664
- <div style="font-size: 0.75rem; color: #9ca3af;">Sales</div>
665
- <div style="margin-top: 0.5rem;"><span style="padding: 0.125rem 0.5rem; font-size: 0.75rem; background: #4c1d95; color: #a78bfa; border-radius: 9999px;">outreach</span></div>
666
- </div>
667
- <div data-component="AgentCard" data-status="testing" class="agent-card">
668
- <div class="agent-avatar">Q</div>
669
- <div style="font-weight: 500; font-size: 0.875rem; color: #fff;">Quinn</div>
670
- <div style="font-size: 0.75rem; color: #9ca3af;">QA</div>
671
- <div style="margin-top: 0.5rem;"><span style="padding: 0.125rem 0.5rem; font-size: 0.75rem; background: #164e63; color: #22d3d1; border-radius: 9999px;">testing</span></div>
672
- </div>
673
- </div>
674
- </div>
675
- <div data-component="TabsContent" data-value="available" style="display: none;">
676
- <p style="color: #9ca3af; padding: 1rem;">Additional agents available from agents.do</p>
677
- </div>
678
- <div data-component="TabsContent" data-value="custom" style="display: none;">
679
- <p style="color: #9ca3af; padding: 1rem;">Create custom agents for your specific domain</p>
680
- </div>
681
- </div>
682
-
683
- <hr />
684
-
685
- <!-- Workflow Monitor from MDX: ## Workflow Monitor -->
686
- <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Workflow Monitor</h2>
687
- <div data-component="WorkflowDashboard" class="grid-2" style="margin-bottom: 2rem;">
688
- <div data-component="WorkflowList" data-workflows-count="${activeWorkflows.length}" class="section-box">
689
- <h4 style="font-weight: 600; margin-bottom: 1rem; color: #fff;">Active Workflows</h4>
690
- ${activeWorkflows.map(wf => `
691
- <div class="workflow-item">
692
- <span style="color: #fff;">${wf.name}</span>
693
- <span style="font-size: 0.75rem; color: #9ca3af;">${wf.status}</span>
694
- </div>
695
- `).join('')}
696
- </div>
697
- <div data-component="WorkflowTimeline" data-events-count="${workflowEvents.length}" class="section-box">
698
- <h4 style="font-weight: 600; margin-bottom: 1rem; color: #fff;">Timeline</h4>
699
- ${workflowEvents.map(event => `
700
- <div class="timeline-item">
701
- <div class="timeline-dot"></div>
702
- <div>
703
- <div style="font-size: 0.875rem; color: #fff;">${event.type}</div>
704
- <div style="font-size: 0.75rem; color: #9ca3af;">${new Date(event.timestamp).toLocaleString()}</div>
705
- </div>
706
- </div>
707
- `).join('')}
708
- </div>
709
- </div>
710
-
711
- <hr />
712
-
713
- <!-- Analytics from MDX: ## Analytics -->
714
- <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Analytics</h2>
715
- <div data-component="AnalyticsDashboard" class="grid-3" style="margin-bottom: 2rem;">
716
- <div data-component="AreaChart" data-data-count="${revenueData.length}" class="section-box">
717
- <h4 style="font-weight: 600; color: #fff;">Revenue</h4>
718
- <p style="font-size: 0.875rem; color: #9ca3af; margin-bottom: 1rem;">Monthly recurring revenue over time</p>
719
- <div class="chart-placeholder">[Area Chart - ${revenueData.length} data points]</div>
720
- </div>
721
- <div data-component="BarChart" data-data-count="${agentUsage.length}" class="section-box">
722
- <h4 style="font-weight: 600; color: #fff;">Agent Usage</h4>
723
- <p style="font-size: 0.875rem; color: #9ca3af; margin-bottom: 1rem;">Tasks completed by each agent</p>
724
- <div class="chart-placeholder">[Bar Chart - ${agentUsage.length} data points]</div>
725
- </div>
726
- <div data-component="LineChart" data-data-count="${experimentResults.length}" class="section-box">
727
- <h4 style="font-weight: 600; color: #fff;">Experiment Results</h4>
728
- <p style="font-size: 0.875rem; color: #9ca3af; margin-bottom: 1rem;">Conversion rates across variants</p>
729
- <div class="chart-placeholder">[Line Chart - ${experimentResults.length} data points]</div>
730
- </div>
731
- </div>
732
-
733
- <hr />
734
-
735
- <!-- Settings from MDX: ## Settings -->
736
- <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #fff;">Settings</h2>
737
- <div data-component="SettingsLayout">
738
- <div data-component="SettingsSection" class="section-box settings-section">
739
- <h4 style="font-weight: 600; margin-bottom: 1rem; color: #fff;">Profile</h4>
740
- <div data-component="UserProfile">
741
- <div style="display: flex; align-items: center; gap: 1rem;">
742
- <div style="width: 4rem; height: 4rem; border-radius: 50%; background: linear-gradient(135deg, #3b82f6, #8b5cf6); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; font-weight: bold;">U</div>
743
- <div>
744
- <div style="font-weight: 600; color: #fff;">User Name</div>
745
- <div style="font-size: 0.875rem; color: #9ca3af;">user@dotdo.dev</div>
746
- </div>
747
- </div>
748
- </div>
749
- </div>
750
- <div data-component="SettingsSection" class="section-box settings-section">
751
- <h4 style="font-weight: 600; margin-bottom: 1rem; color: #fff;">API Keys</h4>
752
- <div data-component="APIKeyManager">
753
- <div style="display: flex; justify-content: space-between; align-items: center;">
754
- <span style="color: #9ca3af;">No API keys configured</span>
755
- <button style="padding: 0.5rem 1rem; background: #2563eb; color: #fff; border: none; border-radius: 0.375rem; cursor: pointer;">Create Key</button>
756
- </div>
757
- </div>
758
- </div>
759
- <div data-component="SettingsSection" class="section-box settings-section">
760
- <h4 style="font-weight: 600; margin-bottom: 1rem; color: #fff;">Integrations</h4>
761
- <div data-component="IntegrationsList">
762
- <p style="color: #9ca3af; text-align: center; padding: 1rem;">No integrations configured</p>
763
- </div>
764
- </div>
765
- <div data-component="SettingsSection" class="section-box settings-section">
766
- <h4 style="font-weight: 600; margin-bottom: 1rem; color: #fff;">Billing</h4>
767
- <div data-component="BillingManager">
768
- <div style="display: flex; justify-content: space-between; align-items: center;">
769
- <div>
770
- <div style="font-weight: 600; color: #fff;">Free Plan</div>
771
- <div style="font-size: 0.875rem; color: #9ca3af;">Upgrade for more features</div>
772
- </div>
773
- <button style="padding: 0.5rem 1rem; background: #333; color: #fff; border: none; border-radius: 0.375rem; cursor: pointer;">Upgrade</button>
774
- </div>
775
- </div>
776
- </div>
777
- </div>
778
-
779
- </div>
780
- </div>
781
- </div>
782
-
783
- <!-- Tab switching script -->
784
- <script>
785
- // HTML escaping utility to prevent XSS
786
- function escapeHtml(unsafe) {
787
- return unsafe
788
- .replace(/&/g, '&amp;')
789
- .replace(/</g, '&lt;')
790
- .replace(/>/g, '&gt;')
791
- .replace(/"/g, '&quot;')
792
- .replace(/'/g, '&#039;');
793
- }
794
-
795
- document.querySelectorAll('[data-component="TabsTrigger"]').forEach(trigger => {
796
- trigger.addEventListener('click', () => {
797
- const tabs = trigger.closest('[data-component="Tabs"]');
798
- const triggers = tabs.querySelectorAll('[data-component="TabsTrigger"]');
799
- const contents = tabs.querySelectorAll('[data-component="TabsContent"]');
800
-
801
- triggers.forEach(t => t.setAttribute('data-state', 'inactive'));
802
- trigger.setAttribute('data-state', 'active');
803
-
804
- const value = trigger.textContent.toLowerCase().split(' ')[0];
805
- contents.forEach(content => {
806
- content.style.display = content.dataset.value === value ? 'block' : 'none';
807
- });
808
- });
809
- });
810
-
811
- // Agent command click handlers
812
- document.querySelectorAll('[data-component="CommandItem"][data-agent]').forEach(item => {
813
- item.addEventListener('click', () => {
814
- const agent = item.dataset.agent;
815
- const safeAgent = escapeHtml(agent);
816
- const modal = document.createElement('div');
817
- modal.setAttribute('data-agent-chat', agent);
818
- modal.style.cssText = 'position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 50;';
819
- modal.innerHTML = \`
820
- <div style="background: #111; border: 1px solid #333; border-radius: 0.5rem; padding: 1.5rem; max-width: 28rem; width: 100%; margin: 1rem;">
821
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
822
- <h3 style="font-weight: 600; color: #fff;">Chat with \${safeAgent}</h3>
823
- <button onclick="this.closest('[data-agent-chat]').remove()" style="background: transparent; border: none; color: #9ca3af; cursor: pointer; font-size: 1.25rem;">X</button>
824
- </div>
825
- <p style="color: #9ca3af;">Starting conversation with \${safeAgent}...</p>
826
- </div>
827
- \`;
828
- modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });
829
- document.body.appendChild(modal);
830
- });
831
- });
832
- </script>
833
- `;
834
- return layout('dotdo Dashboard', content, adminStyles);
835
- }
836
- // Login page styles
837
- const loginStyles = `
838
- .login-container { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #f9fafb; }
839
- .login-box { background: #fff; padding: 2rem; border-radius: 0.75rem; box-shadow: 0 4px 6px rgba(0,0,0,0.1); width: 100%; max-width: 400px; }
840
- .login-box h1 { font-size: 1.5rem; text-align: center; margin-bottom: 0.5rem; }
841
- .login-box p { text-align: center; color: #6b7280; margin-bottom: 1.5rem; }
842
- .login-box form { margin-bottom: 1.5rem; }
843
- .login-box label { display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem; }
844
- .login-box input { width: 100%; padding: 0.75rem; border: 1px solid #d1d5db; border-radius: 0.375rem; margin-bottom: 1rem; }
845
- .login-box button[type="submit"] { width: 100%; padding: 0.75rem; background: #2563eb; color: #fff; border: none; border-radius: 0.375rem; font-weight: 500; cursor: pointer; }
846
- .login-box button[type="submit"]:hover { background: #1d4ed8; }
847
- .oauth-buttons { display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1.5rem; }
848
- .oauth-btn { display: flex; align-items: center; justify-content: center; gap: 0.5rem; padding: 0.75rem; border: 1px solid #d1d5db; border-radius: 0.375rem; background: #fff; cursor: pointer; }
849
- .oauth-btn:hover { background: #f9fafb; }
850
- .divider { display: flex; align-items: center; gap: 1rem; margin: 1.5rem 0; }
851
- .divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: #e5e7eb; }
852
- .divider span { color: #9ca3af; font-size: 0.875rem; }
853
- .forgot-link { display: block; text-align: center; color: #2563eb; font-size: 0.875rem; margin-top: 1rem; }
854
- .error, [role="alert"], .validation-error { color: #dc2626; font-size: 0.875rem; margin-top: 0.25rem; }
855
- `;
856
- export function adminLoginHtml() {
857
- const content = `
858
- <div class="login-container">
859
- <div class="login-box">
860
- <h1>Admin Login</h1>
861
- <p>Sign in to access the admin dashboard</p>
862
-
863
- <div class="oauth-buttons">
864
- <button type="button" class="oauth-btn">
865
- <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
866
- Sign in with Google
867
- </button>
868
- <button type="button" class="oauth-btn">
869
- <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
870
- Sign in with GitHub
871
- </button>
872
- <button type="button" class="oauth-btn">
873
- <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h11.377v11.372H0zm12.623 0H24v11.372H12.623zM0 12.623h11.377V24H0zm12.623 0H24V24H12.623z"/></svg>
874
- Sign in with Microsoft
875
- </button>
876
- </div>
877
-
878
- <div class="divider"><span>or</span></div>
879
-
880
- <form method="POST" action="/admin/login">
881
- <label for="email">Email</label>
882
- <input type="email" id="email" name="email" required placeholder="you@example.com.ai">
883
-
884
- <label for="password">Password</label>
885
- <input type="password" id="password" name="password" required placeholder="••••••••">
886
-
887
- <button type="submit">Sign In</button>
888
- </form>
889
-
890
- <a href="/admin/forgot-password" class="forgot-link">Forgot your password?</a>
891
- </div>
892
- </div>
893
- `;
894
- return layout('Login - dotdo Admin', content, loginStyles);
895
- }
896
- export function adminUsersHtml() {
897
- const content = `
898
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
899
- <div>
900
- <input type="search" placeholder="Search users..." style="width: 300px;">
901
- </div>
902
- <a href="/admin/users/new" class="btn btn-primary">Create User</a>
903
- </div>
904
- <table class="data-table" data-table>
905
- <thead>
906
- <tr>
907
- <th>Name</th>
908
- <th>Email</th>
909
- <th>Role</th>
910
- <th>Status</th>
911
- <th>Actions</th>
912
- </tr>
913
- </thead>
914
- <tbody>
915
- <tr>
916
- <td>John Doe</td>
917
- <td>john@example.com.ai</td>
918
- <td>Admin</td>
919
- <td><span style="color: #059669;">Active</span></td>
920
- <td><a href="/admin/users/user-1">Edit</a></td>
921
- </tr>
922
- <tr>
923
- <td>Jane Smith</td>
924
- <td>jane@example.com.ai</td>
925
- <td>User</td>
926
- <td><span style="color: #059669;">Active</span></td>
927
- <td><a href="/admin/users/user-2">Edit</a></td>
928
- </tr>
929
- </tbody>
930
- </table>
931
- `;
932
- return layout('Users - dotdo Admin', adminShell('Users', 'users', content), adminStyles);
933
- }
934
- export function adminUserNewHtml() {
935
- const content = `
936
- <form method="POST" action="/admin/users" style="max-width: 500px;">
937
- <label for="name">Name</label>
938
- <input type="text" id="name" name="name" required placeholder="Full name">
939
-
940
- <label for="email">Email</label>
941
- <input type="email" id="email" name="email" required placeholder="user@example.com.ai">
942
-
943
- <label for="role">Role</label>
944
- <select id="role" name="role">
945
- <option value="user">User</option>
946
- <option value="admin">Admin</option>
947
- </select>
948
-
949
- <div style="display: flex; gap: 1rem; margin-top: 1rem;">
950
- <button type="submit" class="btn btn-primary">Create User</button>
951
- <a href="/admin/users" class="btn" style="border: 1px solid #d1d5db;">Cancel</a>
952
- </div>
953
- </form>
954
- `;
955
- return layout('Create User - dotdo Admin', adminShell('Create User', 'users', content), adminStyles);
956
- }
957
- export function adminWorkflowsHtml() {
958
- const content = `
959
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
960
- <div style="display: flex; gap: 1rem;">
961
- <select data-filter="status">
962
- <option value="">All Status</option>
963
- <option value="active">Active</option>
964
- <option value="paused">Paused</option>
965
- <option value="failed">Failed</option>
966
- </select>
967
- <input type="search" placeholder="Search workflows...">
968
- </div>
969
- </div>
970
- <table class="data-table" data-table>
971
- <thead>
972
- <tr>
973
- <th>Name</th>
974
- <th>Status</th>
975
- <th>Last Run</th>
976
- <th>Actions</th>
977
- </tr>
978
- </thead>
979
- <tbody>
980
- <tr>
981
- <td>Data Sync Workflow</td>
982
- <td><span style="color: #059669;">Active</span></td>
983
- <td>2 hours ago</td>
984
- <td><a href="/admin/workflows/workflow-1">View</a></td>
985
- </tr>
986
- <tr>
987
- <td>Email Notification</td>
988
- <td><span style="color: #d97706;">Paused</span></td>
989
- <td>1 day ago</td>
990
- <td><a href="/admin/workflows/workflow-2">View</a></td>
991
- </tr>
992
- <tr>
993
- <td>Backup Job</td>
994
- <td><span style="color: #dc2626;">Failed</span></td>
995
- <td>3 hours ago</td>
996
- <td><a href="/admin/workflows/workflow-3">View</a></td>
997
- </tr>
998
- </tbody>
999
- </table>
1000
- `;
1001
- return layout('Workflows - dotdo Admin', adminShell('Workflows', 'workflows', content), adminStyles);
1002
- }
1003
- export function adminIntegrationsHtml() {
1004
- const content = `
1005
- <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem;">
1006
- <div style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
1007
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
1008
- <h3 style="font-size: 1.125rem; font-weight: 600;">GitHub</h3>
1009
- <span style="color: #059669;">Connected</span>
1010
- </div>
1011
- <a href="/admin/integrations/github" class="btn" style="border: 1px solid #d1d5db;">Configure</a>
1012
- </div>
1013
- <div style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
1014
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
1015
- <h3 style="font-size: 1.125rem; font-weight: 600;">Google</h3>
1016
- <span style="color: #6b7280;">Disconnected</span>
1017
- </div>
1018
- <button class="btn btn-primary">Connect</button>
1019
- </div>
1020
- <div style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
1021
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
1022
- <h3 style="font-size: 1.125rem; font-weight: 600;">Slack</h3>
1023
- <span style="color: #6b7280;">Disconnected</span>
1024
- </div>
1025
- <button class="btn btn-primary">Connect</button>
1026
- </div>
1027
- </div>
1028
- `;
1029
- return layout('Integrations - dotdo Admin', adminShell('Integrations', 'integrations', content), adminStyles);
1030
- }
1031
- export function adminApiKeysHtml() {
1032
- const content = `
1033
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
1034
- <p style="color: #6b7280;">Manage your API keys for programmatic access.</p>
1035
- <button class="btn btn-primary">Create API Key</button>
1036
- </div>
1037
- <table class="data-table" data-table>
1038
- <thead>
1039
- <tr>
1040
- <th>Name</th>
1041
- <th>Key</th>
1042
- <th>Created</th>
1043
- <th>Last Used</th>
1044
- <th>Actions</th>
1045
- </tr>
1046
- </thead>
1047
- <tbody>
1048
- <tr>
1049
- <td>Production</td>
1050
- <td><code>sk_live_****abcd</code></td>
1051
- <td>Jan 1, 2024</td>
1052
- <td>2 hours ago</td>
1053
- <td><button style="color: #dc2626; background: transparent; border: none; cursor: pointer;">Revoke</button></td>
1054
- </tr>
1055
- </tbody>
1056
- </table>
1057
- `;
1058
- return layout('API Keys - dotdo Admin', adminShell('API Keys', 'integrations', content), adminStyles);
1059
- }
1060
- export function adminSettingsHtml() {
1061
- const content = `
1062
- <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem;">
1063
- <a href="/admin/settings/account" style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); text-decoration: none; color: inherit;">
1064
- <h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem;">Account & Profile</h3>
1065
- <p style="color: #6b7280;">Manage your personal information and preferences.</p>
1066
- </a>
1067
- <a href="/admin/settings/security" style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); text-decoration: none; color: inherit;">
1068
- <h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem;">Security & Password</h3>
1069
- <p style="color: #6b7280;">Update password, Two-Factor authentication (2FA), and sessions.</p>
1070
- </a>
1071
- <div style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
1072
- <h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem;">Notification Preferences</h3>
1073
- <p style="color: #6b7280;">Configure Email alerts and notifications.</p>
1074
- </div>
1075
- </div>
1076
- `;
1077
- return layout('Settings - dotdo Admin', adminShell('Settings', 'settings', content), adminStyles);
1078
- }
1079
- export function adminSettingsAccountHtml() {
1080
- const content = `
1081
- <form method="POST" action="/admin/settings/account" style="max-width: 500px;">
1082
- <label for="name">Name</label>
1083
- <input type="text" id="name" name="name" value="John Doe">
1084
-
1085
- <label for="email">Email</label>
1086
- <input type="email" id="email" name="email" value="john@example.com.ai" readonly style="background: #f9fafb;">
1087
-
1088
- <button type="submit" class="btn btn-primary">Save Changes</button>
1089
- </form>
1090
- `;
1091
- return layout('Account Settings - dotdo Admin', adminShell('Account Settings', 'settings', content), adminStyles);
1092
- }
1093
- export function adminSettingsSecurityHtml() {
1094
- const content = `
1095
- <div style="max-width: 500px;">
1096
- <form method="POST" action="/admin/settings/security" style="margin-bottom: 2rem;">
1097
- <h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 1rem;">Change Password</h3>
1098
-
1099
- <label for="current-password">Current Password</label>
1100
- <input type="password" id="current-password" name="currentPassword">
1101
-
1102
- <label for="new-password">New Password</label>
1103
- <input type="password" id="new-password" name="newPassword">
1104
-
1105
- <label for="confirm-password">Confirm New Password</label>
1106
- <input type="password" id="confirm-password" name="confirmPassword">
1107
-
1108
- <button type="submit" class="btn btn-primary">Update Password</button>
1109
- </form>
1110
-
1111
- <div style="background: #fff; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
1112
- <h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 1rem;">Two-Factor Authentication</h3>
1113
- <p style="color: #6b7280; margin-bottom: 1rem;">Add an extra layer of security to your account.</p>
1114
- <button class="btn btn-primary">Enable 2FA</button>
1115
- </div>
1116
- </div>
1117
- `;
1118
- return layout('Security Settings - dotdo Admin', adminShell('Security Settings', 'settings', content), adminStyles);
1119
- }
1120
- export function adminActivityHtml() {
1121
- const content = `
1122
- <div style="margin-bottom: 1.5rem;">
1123
- <input type="search" placeholder="Search activity logs...">
1124
- </div>
1125
- <table class="data-table" data-table>
1126
- <thead>
1127
- <tr>
1128
- <th>Timestamp</th>
1129
- <th>User</th>
1130
- <th>Action</th>
1131
- <th>Resource</th>
1132
- </tr>
1133
- </thead>
1134
- <tbody>
1135
- <tr>
1136
- <td>2024-01-08 10:30:00</td>
1137
- <td>John Doe</td>
1138
- <td><span style="background: #dcfce7; color: #166534; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.875rem;">Create</span></td>
1139
- <td>Workflow</td>
1140
- </tr>
1141
- <tr>
1142
- <td>2024-01-08 09:15:00</td>
1143
- <td>Jane Smith</td>
1144
- <td><span style="background: #dbeafe; color: #1e40af; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.875rem;">Update</span></td>
1145
- <td>Integration</td>
1146
- </tr>
1147
- <tr>
1148
- <td>2024-01-08 08:00:00</td>
1149
- <td>John Doe</td>
1150
- <td><span style="background: #f3e8ff; color: #7c3aed; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.875rem;">Login</span></td>
1151
- <td>Session</td>
1152
- </tr>
1153
- </tbody>
1154
- </table>
1155
- `;
1156
- return layout('Activity - dotdo Admin', adminShell('Activity', 'activity', content), adminStyles);
1157
- }
1158
- // 404 page styles
1159
- const notFoundStyles = `
1160
- .not-found { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 2rem; }
1161
- .not-found h1 { font-size: 6rem; font-weight: bold; color: #d1d5db; margin-bottom: 1rem; }
1162
- .not-found h2 { font-size: 1.5rem; margin-bottom: 0.5rem; }
1163
- .not-found p { color: #6b7280; margin-bottom: 2rem; }
1164
- .not-found a { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1.5rem; background: #2563eb; color: #fff; border-radius: 0.5rem; }
1165
- `;
1166
- export function notFoundHtml() {
1167
- const content = `
1168
- <div class="not-found">
1169
- <h1>404</h1>
1170
- <h2>Page Not Found</h2>
1171
- <p>The page you're looking for doesn't exist or has been moved.</p>
1172
- <a href="/">
1173
- <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>
1174
- Back to Home
1175
- </a>
1176
- </div>
1177
- `;
1178
- return layout('Page Not Found - dotdo', content, notFoundStyles);
1179
- }
1180
- //# sourceMappingURL=pages.js.map