@databricks/appkit 0.3.0 → 0.4.1

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 (239) hide show
  1. package/CLAUDE.md +121 -1231
  2. package/NOTICE.md +1 -1
  3. package/bin/appkit.js +3 -0
  4. package/dist/appkit/package.js +1 -1
  5. package/dist/cache/index.js +3 -3
  6. package/dist/cache/index.js.map +1 -1
  7. package/dist/cli/commands/docs.js +47 -0
  8. package/dist/cli/commands/docs.js.map +1 -0
  9. package/dist/cli/commands/generate-types.js +38 -0
  10. package/dist/cli/commands/generate-types.js.map +1 -0
  11. package/dist/cli/commands/lint.js +104 -0
  12. package/dist/cli/commands/lint.js.map +1 -0
  13. package/dist/cli/commands/setup.js +121 -0
  14. package/dist/cli/commands/setup.js.map +1 -0
  15. package/dist/cli/index.d.ts +1 -0
  16. package/dist/cli/index.js +24 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/plugin/plugin.d.ts +1 -0
  20. package/dist/plugin/plugin.d.ts.map +1 -1
  21. package/dist/plugin/plugin.js +1 -0
  22. package/dist/plugin/plugin.js.map +1 -1
  23. package/dist/server/remote-tunnel/remote-tunnel-manager.js +9 -9
  24. package/dist/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  25. package/dist/server/utils.js +6 -6
  26. package/dist/server/utils.js.map +1 -1
  27. package/dist/shared/src/execute.d.ts +1 -0
  28. package/dist/shared/src/execute.d.ts.map +1 -1
  29. package/dist/shared/src/plugin.d.ts +3 -0
  30. package/dist/shared/src/plugin.d.ts.map +1 -1
  31. package/dist/telemetry/types.d.ts +1 -0
  32. package/dist/telemetry/types.d.ts.map +1 -1
  33. package/dist/type-generator/index.js +1 -1
  34. package/dist/type-generator/index.js.map +1 -1
  35. package/docs/docs/api/appkit/Class.AppKitError/index.html +77 -0
  36. package/docs/docs/api/appkit/Class.AppKitError.md +154 -0
  37. package/docs/docs/api/appkit/Class.AuthenticationError/index.html +110 -0
  38. package/docs/docs/api/appkit/Class.AuthenticationError.md +236 -0
  39. package/docs/docs/api/appkit/Class.ConfigurationError/index.html +112 -0
  40. package/docs/docs/api/appkit/Class.ConfigurationError.md +243 -0
  41. package/docs/docs/api/appkit/Class.ConnectionError/index.html +120 -0
  42. package/docs/docs/api/appkit/Class.ConnectionError.md +265 -0
  43. package/docs/docs/api/appkit/Class.ExecutionError/index.html +116 -0
  44. package/docs/docs/api/appkit/Class.ExecutionError.md +250 -0
  45. package/docs/docs/api/appkit/Class.InitializationError/index.html +104 -0
  46. package/docs/docs/api/appkit/Class.InitializationError.md +222 -0
  47. package/docs/docs/api/appkit/Class.Plugin/index.html +149 -0
  48. package/docs/docs/api/appkit/Class.Plugin.md +392 -0
  49. package/docs/docs/api/appkit/Class.ServerError/index.html +108 -0
  50. package/docs/docs/api/appkit/Class.ServerError.md +229 -0
  51. package/docs/docs/api/appkit/Class.TunnelError/index.html +108 -0
  52. package/docs/docs/api/appkit/Class.TunnelError.md +231 -0
  53. package/docs/docs/api/appkit/Class.ValidationError/index.html +106 -0
  54. package/docs/docs/api/appkit/Class.ValidationError.md +225 -0
  55. package/docs/docs/api/appkit/Function.appKitTypesPlugin/index.html +24 -0
  56. package/docs/docs/api/appkit/Function.appKitTypesPlugin.md +20 -0
  57. package/docs/docs/api/appkit/Function.createApp/index.html +24 -0
  58. package/docs/docs/api/appkit/Function.createApp.md +31 -0
  59. package/docs/docs/api/appkit/Function.isSQLTypeMarker/index.html +25 -0
  60. package/docs/docs/api/appkit/Function.isSQLTypeMarker.md +32 -0
  61. package/docs/docs/api/appkit/Interface.BasePluginConfig/index.html +28 -0
  62. package/docs/docs/api/appkit/Interface.BasePluginConfig.md +37 -0
  63. package/docs/docs/api/appkit/Interface.CacheConfig/index.html +63 -0
  64. package/docs/docs/api/appkit/Interface.CacheConfig.md +131 -0
  65. package/docs/docs/api/appkit/Interface.ITelemetry/index.html +73 -0
  66. package/docs/docs/api/appkit/Interface.ITelemetry.md +144 -0
  67. package/docs/docs/api/appkit/Interface.StreamExecutionSettings/index.html +26 -0
  68. package/docs/docs/api/appkit/Interface.StreamExecutionSettings.md +30 -0
  69. package/docs/docs/api/appkit/Interface.TelemetryConfig/index.html +32 -0
  70. package/docs/docs/api/appkit/Interface.TelemetryConfig.md +48 -0
  71. package/docs/docs/api/appkit/TypeAlias.IAppRouter/index.html +18 -0
  72. package/docs/docs/api/appkit/TypeAlias.IAppRouter.md +8 -0
  73. package/docs/docs/api/appkit/Variable.sql/index.html +98 -0
  74. package/docs/docs/api/appkit/Variable.sql.md +260 -0
  75. package/docs/docs/api/appkit/index.html +28 -0
  76. package/docs/docs/api/appkit-ui/data/AreaChart/index.html +29 -0
  77. package/docs/docs/api/appkit-ui/data/AreaChart.md +79 -0
  78. package/docs/docs/api/appkit-ui/data/BarChart/index.html +29 -0
  79. package/docs/docs/api/appkit-ui/data/BarChart.md +74 -0
  80. package/docs/docs/api/appkit-ui/data/DataTable/index.html +36 -0
  81. package/docs/docs/api/appkit-ui/data/DataTable.md +69 -0
  82. package/docs/docs/api/appkit-ui/data/DonutChart/index.html +29 -0
  83. package/docs/docs/api/appkit-ui/data/DonutChart.md +72 -0
  84. package/docs/docs/api/appkit-ui/data/HeatmapChart/index.html +35 -0
  85. package/docs/docs/api/appkit-ui/data/HeatmapChart.md +91 -0
  86. package/docs/docs/api/appkit-ui/data/LineChart/index.html +29 -0
  87. package/docs/docs/api/appkit-ui/data/LineChart.md +77 -0
  88. package/docs/docs/api/appkit-ui/data/PieChart/index.html +29 -0
  89. package/docs/docs/api/appkit-ui/data/PieChart.md +72 -0
  90. package/docs/docs/api/appkit-ui/data/RadarChart/index.html +29 -0
  91. package/docs/docs/api/appkit-ui/data/RadarChart.md +74 -0
  92. package/docs/docs/api/appkit-ui/data/ScatterChart/index.html +29 -0
  93. package/docs/docs/api/appkit-ui/data/ScatterChart.md +76 -0
  94. package/docs/docs/api/appkit-ui/index.html +23 -0
  95. package/docs/docs/api/appkit-ui/styling/index.html +74 -0
  96. package/docs/docs/api/appkit-ui/styling.md +81 -0
  97. package/docs/docs/api/appkit-ui/ui/Accordion/index.html +48 -0
  98. package/docs/docs/api/appkit-ui/ui/Accordion.md +139 -0
  99. package/docs/docs/api/appkit-ui/ui/Alert/index.html +41 -0
  100. package/docs/docs/api/appkit-ui/ui/Alert.md +89 -0
  101. package/docs/docs/api/appkit-ui/ui/AlertDialog/index.html +97 -0
  102. package/docs/docs/api/appkit-ui/ui/AlertDialog.md +282 -0
  103. package/docs/docs/api/appkit-ui/ui/AspectRatio/index.html +27 -0
  104. package/docs/docs/api/appkit-ui/ui/AspectRatio.md +46 -0
  105. package/docs/docs/api/appkit-ui/ui/Avatar/index.html +41 -0
  106. package/docs/docs/api/appkit-ui/ui/Avatar.md +90 -0
  107. package/docs/docs/api/appkit-ui/ui/Badge/index.html +27 -0
  108. package/docs/docs/api/appkit-ui/ui/Badge.md +38 -0
  109. package/docs/docs/api/appkit-ui/ui/Breadcrumb/index.html +69 -0
  110. package/docs/docs/api/appkit-ui/ui/Breadcrumb.md +193 -0
  111. package/docs/docs/api/appkit-ui/ui/Button/index.html +27 -0
  112. package/docs/docs/api/appkit-ui/ui/Button.md +39 -0
  113. package/docs/docs/api/appkit-ui/ui/ButtonGroup/index.html +38 -0
  114. package/docs/docs/api/appkit-ui/ui/ButtonGroup.md +68 -0
  115. package/docs/docs/api/appkit-ui/ui/Calendar/index.html +34 -0
  116. package/docs/docs/api/appkit-ui/ui/Calendar.md +154 -0
  117. package/docs/docs/api/appkit-ui/ui/Card/index.html +69 -0
  118. package/docs/docs/api/appkit-ui/ui/Card.md +222 -0
  119. package/docs/docs/api/appkit-ui/ui/Carousel/index.html +55 -0
  120. package/docs/docs/api/appkit-ui/ui/Carousel.md +152 -0
  121. package/docs/docs/api/appkit-ui/ui/ChartContainer/index.html +58 -0
  122. package/docs/docs/api/appkit-ui/ui/ChartContainer.md +343 -0
  123. package/docs/docs/api/appkit-ui/ui/Checkbox/index.html +27 -0
  124. package/docs/docs/api/appkit-ui/ui/Checkbox.md +53 -0
  125. package/docs/docs/api/appkit-ui/ui/Collapsible/index.html +41 -0
  126. package/docs/docs/api/appkit-ui/ui/Collapsible.md +125 -0
  127. package/docs/docs/api/appkit-ui/ui/Command/index.html +83 -0
  128. package/docs/docs/api/appkit-ui/ui/Command.md +287 -0
  129. package/docs/docs/api/appkit-ui/ui/ContextMenu/index.html +111 -0
  130. package/docs/docs/api/appkit-ui/ui/ContextMenu.md +419 -0
  131. package/docs/docs/api/appkit-ui/ui/Dialog/index.html +90 -0
  132. package/docs/docs/api/appkit-ui/ui/Dialog.md +285 -0
  133. package/docs/docs/api/appkit-ui/ui/Drawer/index.html +90 -0
  134. package/docs/docs/api/appkit-ui/ui/Drawer.md +387 -0
  135. package/docs/docs/api/appkit-ui/ui/DropdownMenu/index.html +111 -0
  136. package/docs/docs/api/appkit-ui/ui/DropdownMenu.md +478 -0
  137. package/docs/docs/api/appkit-ui/ui/Empty/index.html +54 -0
  138. package/docs/docs/api/appkit-ui/ui/Empty.md +109 -0
  139. package/docs/docs/api/appkit-ui/ui/Field/index.html +87 -0
  140. package/docs/docs/api/appkit-ui/ui/Field.md +201 -0
  141. package/docs/docs/api/appkit-ui/ui/FormControl/index.html +59 -0
  142. package/docs/docs/api/appkit-ui/ui/FormControl.md +128 -0
  143. package/docs/docs/api/appkit-ui/ui/HoverCard/index.html +39 -0
  144. package/docs/docs/api/appkit-ui/ui/HoverCard.md +131 -0
  145. package/docs/docs/api/appkit-ui/ui/Input/index.html +27 -0
  146. package/docs/docs/api/appkit-ui/ui/Input.md +35 -0
  147. package/docs/docs/api/appkit-ui/ui/InputGroup/index.html +59 -0
  148. package/docs/docs/api/appkit-ui/ui/InputGroup.md +123 -0
  149. package/docs/docs/api/appkit-ui/ui/InputOTP/index.html +48 -0
  150. package/docs/docs/api/appkit-ui/ui/InputOTP.md +124 -0
  151. package/docs/docs/api/appkit-ui/ui/Item/index.html +78 -0
  152. package/docs/docs/api/appkit-ui/ui/Item.md +185 -0
  153. package/docs/docs/api/appkit-ui/ui/Kbd/index.html +30 -0
  154. package/docs/docs/api/appkit-ui/ui/Kbd.md +39 -0
  155. package/docs/docs/api/appkit-ui/ui/Label/index.html +27 -0
  156. package/docs/docs/api/appkit-ui/ui/Label.md +44 -0
  157. package/docs/docs/api/appkit-ui/ui/Menubar/index.html +117 -0
  158. package/docs/docs/api/appkit-ui/ui/Menubar.md +484 -0
  159. package/docs/docs/api/appkit-ui/ui/NavigationMenu/index.html +76 -0
  160. package/docs/docs/api/appkit-ui/ui/NavigationMenu.md +338 -0
  161. package/docs/docs/api/appkit-ui/ui/Pagination/index.html +69 -0
  162. package/docs/docs/api/appkit-ui/ui/Pagination.md +191 -0
  163. package/docs/docs/api/appkit-ui/ui/Popover/index.html +45 -0
  164. package/docs/docs/api/appkit-ui/ui/Popover.md +173 -0
  165. package/docs/docs/api/appkit-ui/ui/Progress/index.html +27 -0
  166. package/docs/docs/api/appkit-ui/ui/Progress.md +51 -0
  167. package/docs/docs/api/appkit-ui/ui/RadioGroup/index.html +33 -0
  168. package/docs/docs/api/appkit-ui/ui/RadioGroup.md +83 -0
  169. package/docs/docs/api/appkit-ui/ui/ResizableHandle/index.html +41 -0
  170. package/docs/docs/api/appkit-ui/ui/ResizableHandle.md +136 -0
  171. package/docs/docs/api/appkit-ui/ui/ScrollArea/index.html +34 -0
  172. package/docs/docs/api/appkit-ui/ui/ScrollArea.md +83 -0
  173. package/docs/docs/api/appkit-ui/ui/Select/index.html +82 -0
  174. package/docs/docs/api/appkit-ui/ui/Select.md +267 -0
  175. package/docs/docs/api/appkit-ui/ui/Separator/index.html +27 -0
  176. package/docs/docs/api/appkit-ui/ui/Separator.md +56 -0
  177. package/docs/docs/api/appkit-ui/ui/Sheet/index.html +76 -0
  178. package/docs/docs/api/appkit-ui/ui/Sheet.md +236 -0
  179. package/docs/docs/api/appkit-ui/ui/Sidebar/index.html +183 -0
  180. package/docs/docs/api/appkit-ui/ui/Sidebar.md +490 -0
  181. package/docs/docs/api/appkit-ui/ui/Skeleton/index.html +27 -0
  182. package/docs/docs/api/appkit-ui/ui/Skeleton.md +43 -0
  183. package/docs/docs/api/appkit-ui/ui/Slider/index.html +27 -0
  184. package/docs/docs/api/appkit-ui/ui/Slider.md +61 -0
  185. package/docs/docs/api/appkit-ui/ui/Spinner/index.html +24 -0
  186. package/docs/docs/api/appkit-ui/ui/Spinner.md +22 -0
  187. package/docs/docs/api/appkit-ui/ui/Switch/index.html +27 -0
  188. package/docs/docs/api/appkit-ui/ui/Switch.md +46 -0
  189. package/docs/docs/api/appkit-ui/ui/Table/index.html +69 -0
  190. package/docs/docs/api/appkit-ui/ui/Table.md +236 -0
  191. package/docs/docs/api/appkit-ui/ui/Tabs/index.html +48 -0
  192. package/docs/docs/api/appkit-ui/ui/Tabs.md +177 -0
  193. package/docs/docs/api/appkit-ui/ui/Textarea/index.html +27 -0
  194. package/docs/docs/api/appkit-ui/ui/Textarea.md +35 -0
  195. package/docs/docs/api/appkit-ui/ui/Toaster/index.html +27 -0
  196. package/docs/docs/api/appkit-ui/ui/Toaster.md +75 -0
  197. package/docs/docs/api/appkit-ui/ui/Toggle/index.html +27 -0
  198. package/docs/docs/api/appkit-ui/ui/Toggle.md +48 -0
  199. package/docs/docs/api/appkit-ui/ui/ToggleGroup/index.html +33 -0
  200. package/docs/docs/api/appkit-ui/ui/ToggleGroup.md +88 -0
  201. package/docs/docs/api/appkit-ui/ui/Tooltip/index.html +46 -0
  202. package/docs/docs/api/appkit-ui/ui/Tooltip.md +134 -0
  203. package/docs/docs/api/appkit-ui.md +15 -0
  204. package/docs/docs/api/appkit.md +48 -0
  205. package/docs/docs/api/index.html +28 -0
  206. package/docs/docs/api.md +24 -0
  207. package/docs/docs/app-management/index.html +106 -0
  208. package/docs/docs/app-management.md +171 -0
  209. package/docs/docs/architecture/index.html +71 -0
  210. package/docs/docs/architecture.md +69 -0
  211. package/docs/docs/category/development/index.html +16 -0
  212. package/docs/docs/category/development.md +3 -0
  213. package/docs/docs/configuration/index.html +66 -0
  214. package/docs/docs/configuration.md +150 -0
  215. package/docs/docs/core-principles/index.html +38 -0
  216. package/docs/docs/core-principles.md +31 -0
  217. package/docs/docs/development/index.html +34 -0
  218. package/docs/docs/development/llm-guide/index.html +74 -0
  219. package/docs/docs/development/llm-guide.md +74 -0
  220. package/docs/docs/development/local-development/index.html +27 -0
  221. package/docs/docs/development/local-development.md +20 -0
  222. package/docs/docs/development/project-setup/index.html +69 -0
  223. package/docs/docs/development/project-setup.md +246 -0
  224. package/docs/docs/development/remote-bridge/index.html +76 -0
  225. package/docs/docs/development/remote-bridge.md +80 -0
  226. package/docs/docs/development/type-generation/index.html +65 -0
  227. package/docs/docs/development/type-generation.md +110 -0
  228. package/docs/docs/development.md +21 -0
  229. package/docs/docs/index.html +58 -0
  230. package/docs/docs/plugins/index.html +151 -0
  231. package/docs/docs/plugins.md +313 -0
  232. package/docs/docs.md +64 -0
  233. package/llms.txt +121 -1231
  234. package/package.json +11 -11
  235. package/scripts/postinstall.js +1 -1
  236. package/AGENTS.md +0 -1234
  237. package/bin/appkit-lint.js +0 -129
  238. package/bin/generate-types.js +0 -27
  239. package/bin/setup-claude.js +0 -190
package/AGENTS.md DELETED
@@ -1,1234 +0,0 @@
1
- # llms.txt — LLM Guide for Building Great Databricks Apps with AppKit
2
- Project: Databricks AppKit
3
-
4
- This document is written *for LLMs* generating code in a brand-new project folder that installs AppKit from npm. It is intentionally prescriptive.
5
-
6
- ## High-level mission
7
-
8
- Build **full-stack TypeScript apps** on Databricks using:
9
-
10
- - **Backend**: `@databricks/appkit`
11
- - **Frontend**: `@databricks/appkit-ui`
12
- - **Analytics**: SQL files in `config/queries/*.sql` executed via the AppKit analytics plugin
13
-
14
- This file is designed to work even when you *do not* have access to the AppKit source repo. Prefer only public package APIs and portable project structures.
15
-
16
- ## Hard rules (LLM guardrails)
17
-
18
- - **Do not invent APIs**. If unsure, stick to the patterns shown in this file and only documented exports from `@databricks/appkit` and `@databricks/appkit-ui`.
19
- - **`createApp()` is async**. Prefer **top-level `await createApp(...)`**. If you can’t, use `void createApp(...)` and do not ignore promise rejection.
20
- - **Always memoize query parameters** passed to `useAnalyticsQuery` / charts to avoid refetch loops.
21
- - **Always handle loading/error/empty states** in UI (use `Skeleton`, error text, empty state).
22
- - **Always use `sql.*` helpers** for query parameters (do not pass raw strings/numbers unless the query expects none).
23
- - **Never construct SQL strings dynamically**. Use parameterized queries with `:paramName`.
24
- - **Never use `require()`**. Use ESM `import/export`.
25
-
26
- ## TypeScript import rules (when using `verbatimModuleSyntax`)
27
-
28
- If your `tsconfig.json` uses `"verbatimModuleSyntax": true`, **always use `import type` for type-only imports** (otherwise builds can fail in strict setups):
29
-
30
- ```ts
31
- import type { ReactNode } from "react";
32
- import { useMemo } from "react";
33
- ```
34
-
35
- ## Canonical project layout
36
-
37
- Recommended structure (client/server split):
38
-
39
- ```
40
- my-app/
41
- ├── server/
42
- │ ├── index.ts # backend entry point (AppKit)
43
- │ └── .env # optional local dev env vars (do not commit)
44
- ├── client/
45
- │ ├── index.html
46
- │ ├── vite.config.ts
47
- │ └── src/
48
- │ ├── main.tsx
49
- │ └── App.tsx
50
- ├── config/
51
- │ └── queries/
52
- │ └── my_query.sql
53
- ├── app.yaml
54
- ├── package.json
55
- └── tsconfig.json
56
- ```
57
-
58
- Why this layout:
59
-
60
- - The AppKit `server()` plugin automatically serves:
61
- - **Dev**: Vite dev server (HMR) from `client/`
62
- - **Prod**: static files from `client/dist` (built by Vite)
63
-
64
- ## Project scaffolding (start here)
65
-
66
- ### `package.json`
67
-
68
- ```json
69
- {
70
- "name": "my-app",
71
- "private": true,
72
- "version": "0.0.0",
73
- "type": "module",
74
- "scripts": {
75
- "dev": "NODE_ENV=development tsx watch server/index.ts",
76
- "build": "npm run build:server && npm run build:client",
77
- "build:server": "tsdown --out-dir build server/index.ts",
78
- "build:client": "tsc -b && vite build --config client/vite.config.ts",
79
- "start": "node build/index.mjs"
80
- },
81
- "dependencies": {
82
- "@databricks/appkit": "^0.1.2"
83
- "@databricks/appkit-ui": "^0.1.2",
84
- "react": "^19.2.3",
85
- "react-dom": "^19.2.3"
86
- },
87
- "devDependencies": {
88
- "@types/node": "^20.0.0",
89
- "@types/react": "^19.0.0",
90
- "@types/react-dom": "^19.0.0",
91
- "@vitejs/plugin-react": "^5.1.1",
92
- "tsdown": "^0.15.7",
93
- "tsx": "^4.19.0",
94
- "typescript": "~5.6.0",
95
- "vite": "^7.2.4"
96
- }
97
- }
98
- ```
99
-
100
- ### `client/index.html`
101
-
102
- ```html
103
- <!doctype html>
104
- <html lang="en">
105
- <head>
106
- <meta charset="UTF-8" />
107
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
108
- <title>My App</title>
109
- </head>
110
- <body>
111
- <div id="root"></div>
112
- <script type="module" src="/src/main.tsx"></script>
113
- </body>
114
- </html>
115
- ```
116
-
117
- ### `client/src/main.tsx`
118
-
119
- ```tsx
120
- import { StrictMode } from "react";
121
- import { createRoot } from "react-dom/client";
122
- import App from "./App";
123
-
124
- createRoot(document.getElementById("root")!).render(
125
- <StrictMode>
126
- <App />
127
- </StrictMode>,
128
- );
129
- ```
130
-
131
- ### `client/src/App.tsx` (minimal)
132
-
133
- ```tsx
134
- export default function App() {
135
- return (
136
- <div className="p-8">
137
- <h1 className="text-2xl font-bold">My App</h1>
138
- </div>
139
- );
140
- }
141
- ```
142
-
143
- ### `client/vite.config.ts`
144
-
145
- ```ts
146
- import { defineConfig } from "vite";
147
- import react from "@vitejs/plugin-react";
148
-
149
- export default defineConfig({
150
- plugins: [react()],
151
- });
152
- ```
153
-
154
- ### `tsconfig.json`
155
-
156
- ```json
157
- {
158
- "compilerOptions": {
159
- "target": "ES2022",
160
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
161
- "module": "ESNext",
162
- "moduleResolution": "bundler",
163
- "jsx": "react-jsx",
164
- "strict": true,
165
- "skipLibCheck": true,
166
- "noEmit": true,
167
- "allowImportingTsExtensions": true,
168
- "verbatimModuleSyntax": true
169
- },
170
- "include": ["server", "client/src"]
171
- }
172
- ```
173
-
174
- ### `server/index.ts`
175
-
176
- ```ts
177
- import { createApp, server } from "@databricks/appkit";
178
-
179
- await createApp({
180
- plugins: [server()],
181
- });
182
- ```
183
-
184
- ### Running the app
185
-
186
- ```bash
187
- # Install dependencies
188
- npm install
189
-
190
- # Development (starts backend + Vite dev server)
191
- npm run dev
192
-
193
- # Production build
194
- npm run build
195
- npm start
196
- ```
197
-
198
- ## Integrating into an existing app
199
-
200
- If you already have a React/Vite app and want to add AppKit:
201
-
202
- ### 1. Install dependencies
203
-
204
- ```bash
205
- npm install @databricks/appkit @databricks/appkit-ui react react-dom
206
- npm install -D tsx tsdown vite @vitejs/plugin-react typescript
207
-
208
- # If you don't already have a client/ folder, create one and move your Vite app into it:
209
- # - move index.html -> client/index.html
210
- # - move vite.config.ts -> client/vite.config.ts
211
- # - move src/ -> client/src/
212
- #
213
- ```
214
-
215
- ### 2. Create `server/index.ts` (new file)
216
-
217
- ```ts
218
- import { createApp, server } from "@databricks/appkit";
219
-
220
- await createApp({
221
- plugins: [server()],
222
- });
223
- ```
224
-
225
- ### 3. Update `package.json` scripts
226
-
227
- ```json
228
- {
229
- "scripts": {
230
- "dev": "NODE_ENV=development tsx watch server/index.ts",
231
- "build": "npm run build:server && npm run build:client",
232
- "build:server": "tsdown --out-dir build server/index.ts",
233
- "build:client": "tsc -b && vite build --config client/vite.config.ts",
234
- "start": "node build/index.mjs"
235
- }
236
- }
237
- ```
238
-
239
- ### 4. That's it
240
-
241
- - AppKit's server plugin will automatically serve your Vite app in dev mode and `client/dist` in production.
242
- - If your Vite app must stay at the repo root (no `client/` folder), AppKit can still work, but the recommended layout is `client/` + `server/`.
243
-
244
- ### Adding analytics to an existing app
245
-
246
- ```ts
247
- // server/index.ts
248
- import { createApp, server, analytics } from "@databricks/appkit";
249
-
250
- await createApp({
251
- plugins: [server(), analytics()],
252
- });
253
- ```
254
-
255
- Then create `config/queries/` and add your `.sql` files.
256
-
257
- ## Environment variables
258
-
259
- ### Required for Databricks Apps deployment
260
-
261
- These are typically **provided by Databricks Apps runtime** (exact set can vary by platform/version):
262
-
263
- | Variable | Description |
264
- |----------|-------------|
265
- | `DATABRICKS_HOST` | Workspace URL (e.g. `https://xxx.cloud.databricks.com`) |
266
- | `DATABRICKS_APP_PORT` | Port to bind (default: `8000`) |
267
- | `DATABRICKS_APP_NAME` | App name in Databricks |
268
-
269
- ### Required for SQL queries (analytics plugin)
270
-
271
- | Variable | Description | How to set |
272
- |----------|-------------|------------|
273
- | `DATABRICKS_WAREHOUSE_ID` | SQL warehouse ID | In `app.yaml`: `valueFrom: sql-warehouse` |
274
-
275
- ### Optional
276
-
277
- | Variable | Description | Default |
278
- |----------|-------------|---------|
279
- | `DATABRICKS_WORKSPACE_ID` | Workspace ID | Auto-fetched from API |
280
- | `NODE_ENV` | `"development"` or `"production"` | — |
281
- | `FLASK_RUN_HOST` | Host to bind | `0.0.0.0` |
282
-
283
- ### Local development
284
-
285
- For local development, you need to authenticate with Databricks. Options:
286
-
287
- **Option 1: Databricks CLI Auth (recommended)**
288
-
289
- ```bash
290
- # Configure once
291
- databricks auth login --host [host] --profile [profile-name]
292
-
293
- # If you used `DEFAULT` as the profile name then you can just run
294
-
295
- `npm run dev`
296
-
297
- # To run with a specific profile
298
- DATABRICKS_CONFIG_PROFILE=my-profile npm run dev
299
- # If your Databricks SDK expects a different variable name, try:
300
- # DATABRICKS_PROFILE=my-profile npm run dev
301
- ```
302
-
303
- **Option 2: Environment variables**
304
-
305
- ```bash
306
- export DATABRICKS_HOST="https://xxx.cloud.databricks.com"
307
- export DATABRICKS_TOKEN="dapi..."
308
- export DATABRICKS_WAREHOUSE_ID="abc123..."
309
- npm run dev
310
- ```
311
-
312
- **Option 3: `.env` file (auto-loaded by AppKit)**
313
-
314
- ```bash
315
- # .env (add to .gitignore!)
316
- DATABRICKS_HOST=https://xxx.cloud.databricks.com
317
- DATABRICKS_TOKEN=dapi...
318
- DATABRICKS_WAREHOUSE_ID=abc123...
319
- ```
320
-
321
- ### Telemetry (optional)
322
-
323
- | Variable | Description |
324
- |----------|-------------|
325
- | `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry collector endpoint |
326
- | `OTEL_SERVICE_NAME` | Service name for traces |
327
-
328
- ## Backend: `@databricks/appkit`
329
-
330
- ### Minimal server (golden template)
331
-
332
- The smallest valid AppKit server:
333
-
334
- ```ts
335
- // server/index.ts
336
- import { createApp, server } from "@databricks/appkit";
337
-
338
- await createApp({
339
- plugins: [server()],
340
- });
341
- ```
342
-
343
- ### Server plugin (`server()`)
344
-
345
- What it does:
346
-
347
- - Starts an Express server (default `host=0.0.0.0`, `port=8000`)
348
- - Mounts plugin routes under `/api/<pluginName>/...`
349
- - Adds `/health` (returns `{ status: "ok" }`)
350
- - Serves frontend:
351
- - **Development** (`NODE_ENV=development`): runs a Vite dev server in middleware mode
352
- - **Production**: auto-detects static frontend directory (checks `dist`, `client/dist`, `build`, `public`, `out`)
353
-
354
- Config (real options):
355
-
356
- ```ts
357
- import { createApp, server } from "@databricks/appkit";
358
-
359
- await createApp({
360
- plugins: [
361
- server({
362
- port: 8000, // default: Number(process.env.DATABRICKS_APP_PORT) || 8000
363
- host: "0.0.0.0", // default: process.env.FLASK_RUN_HOST || "0.0.0.0"
364
- autoStart: true, // default: true
365
- staticPath: "dist", // optional: force a specific static directory
366
- }),
367
- ],
368
- });
369
- ```
370
-
371
- Manual server start (when you need to `.extend()` Express):
372
-
373
- ```ts
374
- import { createApp, server } from "@databricks/appkit";
375
-
376
- const appkit = await createApp({
377
- plugins: [server({ autoStart: false })],
378
- });
379
-
380
- appkit.server.extend((app) => {
381
- app.get("/custom", (_req, res) => res.json({ ok: true }));
382
- });
383
-
384
- await appkit.server.start();
385
- ```
386
-
387
- ### Analytics plugin (`analytics()`)
388
-
389
- Add SQL query execution backed by Databricks SQL Warehouses.
390
-
391
- ```ts
392
- import { analytics, createApp, server } from "@databricks/appkit";
393
-
394
- await createApp({
395
- plugins: [server(), analytics({})],
396
- });
397
- ```
398
-
399
- Where queries live:
400
-
401
- - Put `.sql` files in `config/queries/`.
402
- - Query key is the filename without `.sql` (e.g. `spend_summary.sql` → `"spend_summary"`).
403
-
404
- SQL parameters:
405
-
406
- - Use `:paramName` placeholders.
407
- - Optionally annotate parameter types using SQL comments:
408
-
409
- ```sql
410
- -- @param startDate DATE
411
- -- @param endDate DATE
412
- -- @param limit NUMERIC
413
- SELECT ...
414
- WHERE usage_date BETWEEN :startDate AND :endDate
415
- LIMIT :limit
416
- ```
417
-
418
- Supported `-- @param` types (case-insensitive):
419
-
420
- - `STRING`, `NUMERIC`, `BOOLEAN`, `DATE`, `TIMESTAMP`, `BINARY`
421
-
422
- Server-injected params (important):
423
-
424
- - `:workspaceId` is **injected by the server** and **must not** be annotated.
425
- - Example:
426
-
427
- ```sql
428
- WHERE workspace_id = :workspaceId
429
- ```
430
-
431
- HTTP endpoints exposed (mounted under `/api/analytics`):
432
-
433
- - `POST /api/analytics/query/:query_key`
434
- - `GET /api/analytics/arrow-result/:jobId`
435
-
436
- **Query file naming convention determines execution context:**
437
-
438
- - `config/queries/<query_key>.sql` - Executes as service principal (shared cache)
439
- - `config/queries/<query_key>.obo.sql` - Executes as user (OBO = On-Behalf-Of, per-user cache)
440
-
441
- Formats:
442
-
443
- - `format: "JSON"` (default) returns JSON rows
444
- - `format: "ARROW"` returns an Arrow “statement_id” payload over SSE, then the client fetches binary Arrow from `/api/analytics/arrow-result/:jobId`
445
-
446
- ### Execution context and `asUser(req)`
447
-
448
- AppKit manages Databricks authentication via two contexts:
449
-
450
- - **ServiceContext** (singleton): Initialized at app startup with service principal credentials
451
- - **ExecutionContext**: Determined at runtime - either service principal or user context
452
-
453
- **Headers used for user context:**
454
-
455
- - `x-forwarded-user`: required in production; identifies the user
456
- - `x-forwarded-access-token`: required for user token passthrough
457
-
458
- **Using `asUser(req)` for user-scoped operations:**
459
-
460
- The `asUser(req)` pattern allows plugins to execute operations using the requesting user's credentials:
461
-
462
- ```ts
463
- // In a custom plugin route handler
464
- router.post("/users/me/data", async (req, res) => {
465
- // Execute as the user (uses their Databricks permissions)
466
- const result = await this.asUser(req).query("SELECT ...");
467
- res.json(result);
468
- });
469
-
470
- // Service principal execution (default)
471
- router.post("/system/data", async (req, res) => {
472
- const result = await this.query("SELECT ...");
473
- res.json(result);
474
- });
475
- ```
476
-
477
- **Context helper functions (exported from `@databricks/appkit`):**
478
-
479
- - `getExecutionContext()`: Returns current context (user or service)
480
- - `getCurrentUserId()`: Returns user ID in user context, service user ID otherwise
481
- - `getWorkspaceClient()`: Returns the appropriate WorkspaceClient for current context
482
- - `getWarehouseId()`: `Promise<string>` (from `DATABRICKS_WAREHOUSE_ID` or auto-selected in dev)
483
- - `getWorkspaceId()`: `Promise<string>` (from `DATABRICKS_WORKSPACE_ID` or fetched)
484
- - `isInUserContext()`: Returns `true` if currently executing in user context
485
-
486
- **Development mode behavior:**
487
-
488
- In local development (`NODE_ENV=development`), if `asUser(req)` is called without a user token, it logs a warning and falls back to the service principal.
489
-
490
- ### Custom plugins (backend)
491
-
492
- If you need custom API routes or background logic, implement an AppKit plugin.
493
-
494
- ```ts
495
- import { Plugin, toPlugin } from "@databricks/appkit";
496
- import type express from "express";
497
-
498
- class MyPlugin extends Plugin {
499
- name = "my-plugin";
500
- envVars = []; // list required env vars here
501
-
502
- injectRoutes(router: express.Router) {
503
- this.route(router, {
504
- name: "hello",
505
- method: "get",
506
- path: "/hello",
507
- handler: async (_req, res) => {
508
- res.json({ ok: true });
509
- },
510
- });
511
- }
512
- }
513
-
514
- export const myPlugin = toPlugin<typeof MyPlugin, Record<string, never>, "my-plugin">(
515
- MyPlugin,
516
- "my-plugin",
517
- );
518
- ```
519
-
520
- ### Caching (global + plugin-level)
521
-
522
- Global:
523
-
524
- ```ts
525
- await createApp({
526
- plugins: [server(), analytics({})],
527
- cache: {
528
- enabled: true,
529
- ttl: 3600, // seconds
530
- strictPersistence: false,
531
- },
532
- });
533
- ```
534
-
535
- - Storage auto-selects **Lakebase persistent cache when healthy**, otherwise falls back to in-memory.
536
-
537
- Plugin-level:
538
-
539
- ```ts
540
- // inside a Plugin subclass:
541
- const value = await this.cache.getOrExecute(
542
- ["my-plugin", "data", userId],
543
- async () => expensiveWork(),
544
- userKey,
545
- { ttl: 300 },
546
- );
547
- ```
548
-
549
- ## Frontend: `@databricks/appkit-ui`
550
-
551
- ### Imports
552
-
553
- - React-facing APIs: `@databricks/appkit-ui/react`
554
- - Non-React utilities (sql markers, arrow, SSE): `@databricks/appkit-ui/js`
555
-
556
- ```tsx
557
- import { useAnalyticsQuery, Card, Skeleton } from "@databricks/appkit-ui/react";
558
- import { sql } from "@databricks/appkit-ui/js";
559
- ```
560
-
561
- ### `useAnalyticsQuery(queryKey, parameters, options?)`
562
-
563
- Facts:
564
-
565
- - Uses **SSE** under the hood (not `fetch()` polling).
566
- - By default it hits `POST /api/analytics/query/:queryKey`.
567
- - Returns `{ data, loading, error }` where `data` is `null` until loaded.
568
- - `format` is `"JSON"` or `"ARROW"` (uppercase).
569
-
570
- When to use it:
571
-
572
- - Use `useAnalyticsQuery` **only** when you need a custom UI (cards/KPIs/forms/conditional rendering).
573
- - If you just need a standard chart or table, prefer the built-in components (`BarChart`, `LineChart`, `DataTable`, etc.) so you don’t re-implement loading/error/empty states.
574
-
575
- Limitations (common LLM pitfall):
576
-
577
- - There is **no `enabled` option**. Use conditional rendering to mount/unmount the component.
578
- - There is **no `refetch()`**. Change `parameters` (memoized) or re-mount to re-run the query.
579
-
580
- Recommended usage pattern (memoized params + explicit states):
581
-
582
- ```tsx
583
- import { useMemo } from "react";
584
- import { useAnalyticsQuery, Skeleton } from "@databricks/appkit-ui/react";
585
- import { sql } from "@databricks/appkit-ui/js";
586
-
587
- export function Users() {
588
- const params = useMemo(
589
- () => ({
590
- status: sql.string("active"),
591
- limit: sql.number(50),
592
- }),
593
- [],
594
- );
595
-
596
- const { data, loading, error } = useAnalyticsQuery("users_list", params);
597
-
598
- if (loading) return <Skeleton className="h-24 w-full" />;
599
- if (error) return <div className="text-destructive">Error: {error}</div>;
600
- if (!data || data.length === 0) return <div>No results</div>;
601
-
602
- return <pre>{JSON.stringify(data[0], null, 2)}</pre>;
603
- }
604
- ```
605
-
606
- Options:
607
-
608
- - `format?: "JSON" | "ARROW"` (default `"JSON"`)
609
- - `autoStart?: boolean` (default `true`)
610
- - `maxParametersSize?: number` (default `100 * 1024` bytes)
611
-
612
- ### `useChartData({ queryKey, parameters, format, transformer })`
613
-
614
- - `format` here is **lowercase**: `"json" | "arrow" | "auto"` (default `"auto"`)
615
- - Auto-selection heuristics:
616
- - If `parameters._preferArrow === true` → Arrow
617
- - If `parameters._preferJson === true` → JSON
618
- - If `parameters.limit` is a number > 500 → Arrow
619
- - If `parameters.startDate` and `parameters.endDate` exist → Arrow
620
-
621
- ### Charts (unified query/data API)
622
-
623
- All charts support:
624
-
625
- - **Query mode**: `queryKey` + `parameters`
626
- - **Data mode**: `data` (inline JSON, no server)
627
-
628
- Available chart components:
629
-
630
- - `BarChart`, `LineChart`, `AreaChart`, `PieChart`, `DonutChart`, `HeatmapChart`, `ScatterChart`, `RadarChart`
631
-
632
- Avoid double-fetching:
633
-
634
- ```tsx
635
- // ❌ Wrong: fetches the same query twice
636
- // const { data } = useAnalyticsQuery("spend_data", params);
637
- // return <LineChart queryKey="spend_data" parameters={params} />;
638
-
639
- // ✅ Correct: let the chart fetch
640
- return <LineChart queryKey="spend_data" parameters={params} />;
641
- ```
642
-
643
- Query mode (recommended for Databricks-backed analytics):
644
-
645
- ```tsx
646
- import { LineChart } from "@databricks/appkit-ui/react";
647
- import { sql } from "@databricks/appkit-ui/js";
648
- import { useMemo } from "react";
649
-
650
- export function SpendChart() {
651
- const params = useMemo(
652
- () => ({
653
- startDate: sql.date("2024-01-01"),
654
- endDate: sql.date("2024-12-31"),
655
- aggregationLevel: sql.string("day"),
656
- }),
657
- [],
658
- );
659
-
660
- return (
661
- <LineChart
662
- queryKey="spend_data"
663
- parameters={params}
664
- format="auto" // "auto" | "json" | "arrow"
665
- xKey="period"
666
- yKey="cost_usd"
667
- smooth
668
- showSymbol={false}
669
- />
670
- );
671
- }
672
- ```
673
-
674
- **Chart props reference (important):**
675
-
676
- Charts are **self-contained ECharts components**. Configure via props, NOT children:
677
-
678
- ```tsx
679
- // ✅ Correct: use props for customization
680
- <BarChart
681
- queryKey="sales_by_region"
682
- parameters={{}}
683
- xKey="region" // X-axis field
684
- yKey={["revenue", "expenses"]} // Y-axis field(s) - string or string[]
685
- colors={['#40d1f5', '#4462c9']} // Custom colors
686
- stacked // Stack bars (BarChart, AreaChart)
687
- orientation="horizontal" // "vertical" (default) | "horizontal"
688
- showLegend // Show legend
689
- height={400} // Height in pixels (default: 300)
690
- />
691
-
692
- <LineChart
693
- queryKey="trend_data"
694
- parameters={{}}
695
- xKey="date"
696
- yKey="value"
697
- smooth // Smooth curves (default: true)
698
- showSymbol={false} // Hide data point markers
699
- />
700
- ```
701
-
702
- **❌ CRITICAL: Charts do NOT accept Recharts children**
703
-
704
- ```tsx
705
- // ❌ WRONG - AppKit charts are NOT Recharts wrappers
706
- import { BarChart } from "@databricks/appkit-ui/react";
707
- import { Bar, XAxis, YAxis, CartesianGrid } from "recharts";
708
-
709
- <BarChart queryKey="data" parameters={{}}>
710
- <CartesianGrid /> // ❌ This will cause TypeScript errors
711
- <XAxis dataKey="x" /> // ❌ Not supported
712
- <Bar dataKey="y" /> // ❌ Not supported
713
- </BarChart>
714
-
715
- // ✅ CORRECT - use props instead
716
- <BarChart
717
- queryKey="data"
718
- parameters={{}}
719
- xKey="x"
720
- yKey="y"
721
- />
722
- ```
723
-
724
- ### SQL helpers (`sql.*`)
725
-
726
- Use these to build typed parameters (they return marker objects: `{ __sql_type, value }`):
727
-
728
- - `sql.string(value)` → STRING (accepts string|number|boolean)
729
- - `sql.number(value)` → NUMERIC (accepts number|string)
730
- - `sql.boolean(value)` → BOOLEAN (accepts boolean|string("true"/"false")|number(1/0))
731
- - `sql.date(value)` → DATE (accepts Date or `"YYYY-MM-DD"`)
732
- - `sql.timestamp(value)` → TIMESTAMP (accepts Date, ISO string, or unix time)
733
-
734
- Binary parameters (important):
735
-
736
- - Databricks SQL Warehouse doesn't support `BINARY` as a parameter type.
737
- - `sql.binary(value)` returns a **STRING marker containing hex**, so use `UNHEX(:param)` in SQL.
738
- - `sql.binary` accepts `Uint8Array`, `ArrayBuffer`, or a hex string.
739
-
740
- ### SQL result types (important)
741
-
742
- Databricks SQL JSON results can return some numeric-like fields (especially `DECIMAL`) as strings. If a field behaves like a string at runtime, convert explicitly:
743
-
744
- ```ts
745
- const value = Number(row.amount);
746
- ```
747
-
748
- If you need more reliable numeric fidelity for large datasets, prefer `format: "ARROW"` and process Arrow on the client.
749
-
750
- ### `connectSSE` (custom SSE connections)
751
-
752
- For custom streaming endpoints (not analytics), use the `connectSSE` utility:
753
-
754
- ```tsx
755
- import { connectSSE } from "@databricks/appkit-ui/js";
756
- import { useEffect, useState } from "react";
757
-
758
- function useCustomStream(endpoint: string) {
759
- const [messages, setMessages] = useState<string[]>([]);
760
- const [connected, setConnected] = useState(false);
761
-
762
- useEffect(() => {
763
- const controller = new AbortController();
764
-
765
- connectSSE({
766
- url: endpoint,
767
- payload: { key: "value" }, // optional: makes it a POST
768
- onMessage: async ({ data }) => {
769
- setConnected(true);
770
- setMessages((prev) => [...prev, data]);
771
- },
772
- onError: (error) => {
773
- console.error("SSE error:", error);
774
- setConnected(false);
775
- },
776
- signal: controller.signal,
777
- maxRetries: 3, // default: 3
778
- retryDelay: 2000, // default: 2000ms (exponential backoff)
779
- timeout: 300000, // default: 5 minutes
780
- maxBufferSize: 1048576, // default: 1MB
781
- });
782
-
783
- return () => controller.abort();
784
- }, [endpoint]);
785
-
786
- return { messages, connected };
787
- }
788
- ```
789
-
790
- Options:
791
-
792
- - `url`: SSE endpoint URL (required)
793
- - `payload`: Optional request body (if provided, uses POST; otherwise GET)
794
- - `onMessage({ id, data })`: Called for each SSE message
795
- - `onError(error)`: Called on connection errors
796
- - `signal`: AbortSignal to cancel the connection
797
- - `lastEventId`: Resume from a specific event ID
798
- - `maxRetries`: Max retry attempts (default: 3)
799
- - `retryDelay`: Base delay between retries in ms (default: 2000)
800
- - `timeout`: Connection timeout in ms (default: 300000)
801
- - `maxBufferSize`: Max buffer size in bytes (default: 1MB)
802
-
803
- ### `ArrowClient` (advanced Arrow processing)
804
-
805
- For low-level Arrow data handling:
806
-
807
- ```tsx
808
- import { ArrowClient } from "@databricks/appkit-ui/js";
809
-
810
- // Process Arrow buffer
811
- const table = await ArrowClient.processArrowBuffer(buffer);
812
-
813
- // Fetch and process Arrow data in one call
814
- const table = await ArrowClient.fetchAndProcessArrow(url, headers);
815
-
816
- // Extract fields from table
817
- const fields = ArrowClient.extractArrowFields(table);
818
- // → [{ name: "date", type: ... }, { name: "value", type: ... }]
819
-
820
- // Extract columns as arrays
821
- const columns = ArrowClient.extractArrowColumns(table);
822
- // → { date: [...], value: [...] }
823
-
824
- // Extract chart data
825
- const { xData, yDataMap } = ArrowClient.extractChartData(table, "date", ["value", "count"]);
826
- // → { xData: [...], yDataMap: { value: [...], count: [...] } }
827
-
828
- // Auto-detect chart fields from Arrow table
829
- const detected = ArrowClient.detectFieldsFromArrow(table);
830
- // → { xField: "date", yFields: ["value"], chartType: "timeseries" }
831
- ```
832
-
833
- ### DataTable
834
-
835
- `DataTable` is a production-ready table integrated with `useAnalyticsQuery`.
836
-
837
- Key behaviors:
838
-
839
- - `parameters` is required (use `{}` if none)
840
- - Supports opinionated mode (auto columns) and full-control mode (`children(table)`)
841
-
842
- ```tsx
843
- import { DataTable } from "@databricks/appkit-ui/react";
844
-
845
- export function UsersTable() {
846
- return (
847
- <DataTable
848
- queryKey="users_list"
849
- parameters={{}}
850
- filterColumn="email"
851
- filterPlaceholder="Filter by email..."
852
- pageSize={25}
853
- pageSizeOptions={[10, 25, 50, 100]}
854
- />
855
- );
856
- }
857
- ```
858
-
859
- ### UI components (primitives)
860
-
861
- AppKit-UI ships shadcn-style primitives. Import from `@databricks/appkit-ui/react`.
862
-
863
- Note: Exact exports can vary by AppKit-UI version. Prefer using IDE auto-import/autocomplete to confirm what your installed version exports.
864
-
865
- Radix constraint (common bug):
866
-
867
- - `SelectItem` cannot have `value=""`. Use a sentinel value like `"all"` or `"none"`.
868
-
869
- **Available components:**
870
-
871
- `Accordion`, `Alert`, `AlertDialog`, `AspectRatio`, `Avatar`, `Badge`, `Breadcrumb`, `Button`, `ButtonGroup`, `Calendar`, `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, `Carousel`, `Checkbox`, `Collapsible`, `Command`, `ContextMenu`, `Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogTitle`, `DialogDescription`, `DialogFooter`, `Drawer`, `DropdownMenu`, `Empty`, `Field`, `Form`, `HoverCard`, `Input`, `InputGroup`, `InputOtp`, `Item`, `Kbd`, `Label`, `Menubar`, `NavigationMenu`, `Pagination`, `Popover`, `Progress`, `RadioGroup`, `Resizable`, `ScrollArea`, `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`, `Slider`, `Sonner`, `Spinner`, `Switch`, `Table`, `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `Textarea`, `Toggle`, `ToggleGroup`, `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`
872
-
873
- ### Card pattern
874
-
875
- ```tsx
876
- import {
877
- Card,
878
- CardHeader,
879
- CardTitle,
880
- CardDescription,
881
- CardContent,
882
- CardFooter,
883
- } from "@databricks/appkit-ui/react";
884
-
885
- function MetricCard({ title, value, description }: Props) {
886
- return (
887
- <Card>
888
- <CardHeader>
889
- <CardDescription>{description}</CardDescription>
890
- <CardTitle className="text-3xl">{value}</CardTitle>
891
- </CardHeader>
892
- <CardContent>
893
- {/* Optional content */}
894
- </CardContent>
895
- <CardFooter>
896
- {/* Optional footer */}
897
- </CardFooter>
898
- </Card>
899
- );
900
- }
901
- ```
902
-
903
- ### Select pattern
904
-
905
- ```tsx
906
- import {
907
- Select,
908
- SelectTrigger,
909
- SelectValue,
910
- SelectContent,
911
- SelectItem,
912
- } from "@databricks/appkit-ui/react";
913
-
914
- function DateRangeSelect({ value, onChange }: Props) {
915
- return (
916
- <Select value={value} onValueChange={onChange}>
917
- <SelectTrigger className="w-40">
918
- <SelectValue placeholder="Select range" />
919
- </SelectTrigger>
920
- <SelectContent>
921
- <SelectItem value="7d">Last 7 days</SelectItem>
922
- <SelectItem value="30d">Last 30 days</SelectItem>
923
- <SelectItem value="90d">Last 90 days</SelectItem>
924
- </SelectContent>
925
- </Select>
926
- );
927
- }
928
- ```
929
-
930
- ### Tabs pattern
931
-
932
- ```tsx
933
- import { Tabs, TabsList, TabsTrigger, TabsContent } from "@databricks/appkit-ui/react";
934
-
935
- function Dashboard() {
936
- return (
937
- <Tabs defaultValue="overview">
938
- <TabsList>
939
- <TabsTrigger value="overview">Overview</TabsTrigger>
940
- <TabsTrigger value="analytics">Analytics</TabsTrigger>
941
- </TabsList>
942
- <TabsContent value="overview">
943
- <p>Overview content</p>
944
- </TabsContent>
945
- <TabsContent value="analytics">
946
- <p>Analytics content</p>
947
- </TabsContent>
948
- </Tabs>
949
- );
950
- }
951
- ```
952
-
953
- ### Dialog pattern
954
-
955
- ```tsx
956
- import {
957
- Dialog,
958
- DialogTrigger,
959
- DialogContent,
960
- DialogHeader,
961
- DialogTitle,
962
- DialogDescription,
963
- DialogFooter,
964
- Button,
965
- } from "@databricks/appkit-ui/react";
966
-
967
- function ConfirmDialog() {
968
- return (
969
- <Dialog>
970
- <DialogTrigger asChild>
971
- <Button variant="destructive">Delete</Button>
972
- </DialogTrigger>
973
- <DialogContent>
974
- <DialogHeader>
975
- <DialogTitle>Confirm deletion</DialogTitle>
976
- <DialogDescription>
977
- This action cannot be undone.
978
- </DialogDescription>
979
- </DialogHeader>
980
- <DialogFooter>
981
- <Button variant="outline">Cancel</Button>
982
- <Button variant="destructive">Delete</Button>
983
- </DialogFooter>
984
- </DialogContent>
985
- </Dialog>
986
- );
987
- }
988
- ```
989
-
990
- ### TooltipProvider requirement
991
-
992
- If using tooltips anywhere in your app, wrap your root component with `TooltipProvider`:
993
-
994
- ```tsx
995
- import { TooltipProvider } from "@databricks/appkit-ui/react";
996
-
997
- function App() {
998
- return (
999
- <TooltipProvider>
1000
- {/* Your app content */}
1001
- </TooltipProvider>
1002
- );
1003
- }
1004
- ```
1005
-
1006
- ### Button variants
1007
-
1008
- ```tsx
1009
- import { Button } from "@databricks/appkit-ui/react";
1010
-
1011
- <Button variant="default">Primary</Button>
1012
- <Button variant="secondary">Secondary</Button>
1013
- <Button variant="outline">Outline</Button>
1014
- <Button variant="ghost">Ghost</Button>
1015
- <Button variant="destructive">Destructive</Button>
1016
- <Button variant="link">Link</Button>
1017
- ```
1018
-
1019
- ### Loading skeleton pattern
1020
-
1021
- ```tsx
1022
- import { Card, CardHeader, Skeleton } from "@databricks/appkit-ui/react";
1023
-
1024
- function LoadingCard() {
1025
- return (
1026
- <Card>
1027
- <CardHeader>
1028
- <Skeleton className="h-4 w-24 mb-2" />
1029
- <Skeleton className="h-8 w-20 mb-2" />
1030
- <Skeleton className="h-4 w-28" />
1031
- </CardHeader>
1032
- </Card>
1033
- );
1034
- }
1035
- ```
1036
-
1037
- ## Stylesheet
1038
-
1039
- In the main css file import the following
1040
-
1041
- ```css
1042
- @import "@databricks/appkit-ui/styles.css";
1043
- ```
1044
-
1045
- That will provide a default theme for the app using css variables.
1046
-
1047
- ### Customizing theme (light/dark mode)
1048
-
1049
- - Full list of variables to customize the theme.
1050
-
1051
- ```css
1052
- @import "@databricks/appkit-ui/styles.css";
1053
-
1054
- :root {
1055
- --radius: 0.625rem;
1056
- --background: oklch(1 0 0);
1057
- --foreground: oklch(0.141 0.005 285.823);
1058
- --card: oklch(1 0 0);
1059
- --card-foreground: oklch(0.141 0.005 285.823);
1060
- --popover: oklch(1 0 0);
1061
- --popover-foreground: oklch(0.141 0.005 285.823);
1062
- --primary: oklch(0.21 0.006 285.885);
1063
- --primary-foreground: oklch(0.985 0 0);
1064
- --secondary: oklch(0.967 0.001 286.375);
1065
- --secondary-foreground: oklch(0.21 0.006 285.885);
1066
- --muted: oklch(0.967 0.001 286.375);
1067
- --muted-foreground: oklch(0.552 0.016 285.938);
1068
- --accent: oklch(0.967 0.001 286.375);
1069
- --accent-foreground: oklch(0.21 0.006 285.885);
1070
- --destructive: oklch(0.577 0.245 27.325);
1071
- --destructive-foreground: oklch(0.985 0 0);
1072
- --success: oklch(0.603 0.135 166.892);
1073
- --success-foreground: oklch(1 0 0);
1074
- --warning: oklch(0.795 0.157 78.748);
1075
- --warning-foreground: oklch(0.199 0.027 238.732);
1076
- --border: oklch(0.92 0.004 286.32);
1077
- --input: oklch(0.92 0.004 286.32);
1078
- --ring: oklch(0.705 0.015 286.067);
1079
- --chart-1: oklch(0.646 0.222 41.116);
1080
- --chart-2: oklch(0.6 0.118 184.704);
1081
- --chart-3: oklch(0.398 0.07 227.392);
1082
- --chart-4: oklch(0.828 0.189 84.429);
1083
- --chart-5: oklch(0.769 0.188 70.08);
1084
- --sidebar: oklch(0.985 0 0);
1085
- --sidebar-foreground: oklch(0.141 0.005 285.823);
1086
- --sidebar-primary: oklch(0.21 0.006 285.885);
1087
- --sidebar-primary-foreground: oklch(0.985 0 0);
1088
- --sidebar-accent: oklch(0.967 0.001 286.375);
1089
- --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
1090
- --sidebar-border: oklch(0.92 0.004 286.32);
1091
- --sidebar-ring: oklch(0.705 0.015 286.067);
1092
- }
1093
-
1094
- @media (prefers-color-scheme: dark) {
1095
- :root {
1096
- --background: oklch(0.141 0.005 285.823);
1097
- --foreground: oklch(0.985 0 0);
1098
- --card: oklch(0.21 0.006 285.885);
1099
- --card-foreground: oklch(0.985 0 0);
1100
- --popover: oklch(0.21 0.006 285.885);
1101
- --popover-foreground: oklch(0.985 0 0);
1102
- --primary: oklch(0.92 0.004 286.32);
1103
- --primary-foreground: oklch(0.21 0.006 285.885);
1104
- --secondary: oklch(0.274 0.006 286.033);
1105
- --secondary-foreground: oklch(0.985 0 0);
1106
- --muted: oklch(0.274 0.006 286.033);
1107
- --muted-foreground: oklch(0.705 0.015 286.067);
1108
- --accent: oklch(0.274 0.006 286.033);
1109
- --accent-foreground: oklch(0.985 0 0);
1110
- --destructive: oklch(0.704 0.191 22.216);
1111
- --destructive-foreground: oklch(0.985 0 0);
1112
- --success: oklch(0.67 0.12 167);
1113
- --success-foreground: oklch(1 0 0);
1114
- --warning: oklch(0.83 0.165 85);
1115
- --warning-foreground: oklch(0.199 0.027 238.732);
1116
- --border: oklch(1 0 0 / 10%);
1117
- --input: oklch(1 0 0 / 15%);
1118
- --ring: oklch(0.552 0.016 285.938);
1119
- --chart-1: oklch(0.488 0.243 264.376);
1120
- --chart-2: oklch(0.696 0.17 162.48);
1121
- --chart-3: oklch(0.769 0.188 70.08);
1122
- --chart-4: oklch(0.627 0.265 303.9);
1123
- --chart-5: oklch(0.645 0.246 16.439);
1124
- --sidebar: oklch(0.21 0.006 285.885);
1125
- --sidebar-foreground: oklch(0.985 0 0);
1126
- --sidebar-primary: oklch(0.488 0.243 264.376);
1127
- --sidebar-primary-foreground: oklch(0.985 0 0);
1128
- --sidebar-accent: oklch(0.274 0.006 286.033);
1129
- --sidebar-accent-foreground: oklch(0.985 0 0);
1130
- --sidebar-border: oklch(1 0 0 / 10%);
1131
- --sidebar-ring: oklch(0.552 0.016 285.938);
1132
- }
1133
- }
1134
-
1135
- ```
1136
-
1137
- - If any variable is changed, it must be changed for both light and dark mode.
1138
-
1139
- ## Type generation (QueryRegistry + IntelliSense)
1140
-
1141
- Goal: generate `client/src/appKitTypes.d.ts` so query keys, params, and result rows are type-safe.
1142
-
1143
- ### Vite plugin: `appKitTypesPlugin`
1144
-
1145
- Correct option names:
1146
-
1147
- - `outFile?: string` (default `src/appKitTypes.d.ts`)
1148
- - `watchFolders?: string[]` (default `["../config/queries"]`)
1149
-
1150
- ```ts
1151
- // client/vite.config.ts
1152
- import { defineConfig } from "vite";
1153
- import react from "@vitejs/plugin-react";
1154
- import { appKitTypesPlugin } from "@databricks/appkit";
1155
-
1156
- export default defineConfig({
1157
- plugins: [
1158
- react(),
1159
- appKitTypesPlugin({
1160
- outFile: "src/appKitTypes.d.ts",
1161
- watchFolders: ["../config/queries"],
1162
- }),
1163
- ],
1164
- });
1165
- ```
1166
-
1167
- Important nuance:
1168
-
1169
- - When the frontend is served through AppKit in dev mode, AppKit’s dev server already includes `appKitTypesPlugin()` internally.
1170
- - You still want it in your client build pipeline if you run `vite build` separately.
1171
-
1172
- ### CLI: `appkit-generate-types`
1173
-
1174
- ```bash
1175
- # Requires DATABRICKS_WAREHOUSE_ID (or pass as 3rd arg)
1176
- npx appkit-generate-types [rootDir] [outFile] [warehouseId]
1177
-
1178
- # Example:
1179
- npx appkit-generate-types . client/src/appKitTypes.d.ts
1180
-
1181
- # Force regeneration (skip cache):
1182
- npx appkit-generate-types --no-cache
1183
- ```
1184
-
1185
- ## Databricks Apps config: `app.yaml`
1186
-
1187
- Bind a SQL warehouse for Apps runtime:
1188
-
1189
- ```yaml
1190
- env:
1191
- - name: DATABRICKS_WAREHOUSE_ID
1192
- valueFrom: sql-warehouse
1193
- ```
1194
-
1195
- Full example with command:
1196
-
1197
- ```yaml
1198
- command:
1199
- - node
1200
- - build/index.mjs
1201
- env:
1202
- - name: DATABRICKS_WAREHOUSE_ID
1203
- valueFrom: sql-warehouse
1204
- ```
1205
-
1206
- ## LLM checklist (before you "finalize" code)
1207
-
1208
- - **Project setup**
1209
- - `package.json` has `"type": "module"`
1210
- - `tsx` is in devDependencies for dev server
1211
- - `dev` script uses `NODE_ENV=development tsx watch server/index.ts`
1212
- - `client/index.html` exists with `<div id="root"></div>` and script pointing to `client/src/main.tsx`
1213
-
1214
- - **Backend**
1215
- - `await createApp({ plugins: [...] })` is used (or `void createApp` with intent)
1216
- - `server()` is included (always)
1217
- - If using SQL: `analytics({})` included + `config/queries/*.sql` present
1218
- - Queries use `:param` placeholders, and params are passed from UI using `sql.*`
1219
- - If query needs workspace scoping: uses `:workspaceId`
1220
-
1221
- - **Frontend**
1222
- - `useMemo` wraps parameters objects
1223
- - Loading/error/empty states are explicit
1224
- - Charts use `format="auto"` unless you have a reason to force `"json"`/`"arrow"`
1225
- - Charts use props (`xKey`, `yKey`, `colors`) NOT children (they're ECharts-based, not Recharts)
1226
- - If using tooltips: root is wrapped with `<TooltipProvider>`
1227
-
1228
- - **Never**
1229
- - Don't build SQL strings manually
1230
- - Don't pass untyped raw params for annotated queries
1231
- - Don't ignore `createApp()`'s promise
1232
- - Don't invent UI components not listed in this file
1233
- - Don't pass Recharts children (`<Bar>`, `<XAxis>`, etc.) to AppKit chart components
1234
-