@tailor-platform/app-shell 1.15.0 → 1.17.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 (109) hide show
  1. package/dist/app-shell-core.css +1 -1
  2. package/dist/app-shell.d.ts +811 -131
  3. package/dist/app-shell.js +7155 -5797
  4. package/dist/{appshell-DLVBFo5J.js → appshell-CahhZy7v.js} +656 -524
  5. package/dist/semantic-contrast.test.ts +154 -0
  6. package/dist/style-entry.d.ts +147 -92
  7. package/dist/testing.d.ts +152 -95
  8. package/dist/testing.js +1 -1
  9. package/dist/theme-bridge.test.ts +101 -1
  10. package/dist/theme.bridge.css +91 -0
  11. package/dist/theme.css +6 -1
  12. package/dist/themes/_template.css +7 -1
  13. package/dist/themes/bloom.css +0 -6
  14. package/dist/themes/cream.css +0 -6
  15. package/dist/themes/default.css +389 -69
  16. package/dist/vite-plugin.d.ts +147 -92
  17. package/package.json +8 -8
  18. package/skills/app-shell-patterns/SKILL.md +108 -19
  19. package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
  20. package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
  21. package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
  22. package/skills/app-shell-patterns/references/api/define-module.md +247 -0
  23. package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
  24. package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
  25. package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
  26. package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
  27. package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
  28. package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
  29. package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
  30. package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
  31. package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
  32. package/skills/app-shell-patterns/references/api/use-app-shell-config.md +132 -0
  33. package/skills/app-shell-patterns/references/api/use-app-shell-data.md +150 -0
  34. package/skills/app-shell-patterns/references/api/use-app-shell.md +150 -0
  35. package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
  36. package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
  37. package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
  38. package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
  39. package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
  40. package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
  41. package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
  42. package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
  43. package/skills/app-shell-patterns/references/api/use-toast.md +104 -0
  44. package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
  45. package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
  46. package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
  47. package/skills/app-shell-patterns/references/components/alert.md +202 -0
  48. package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
  49. package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
  50. package/skills/app-shell-patterns/references/components/attachment.md +138 -0
  51. package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
  52. package/skills/app-shell-patterns/references/components/avatar.md +80 -0
  53. package/skills/app-shell-patterns/references/components/badge.md +309 -0
  54. package/skills/app-shell-patterns/references/components/button.md +164 -0
  55. package/skills/app-shell-patterns/references/components/card.md +181 -0
  56. package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
  57. package/skills/app-shell-patterns/references/components/combobox.md +318 -0
  58. package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
  59. package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
  60. package/skills/app-shell-patterns/references/components/data-table.md +1168 -0
  61. package/skills/app-shell-patterns/references/components/date-picker.md +259 -0
  62. package/skills/app-shell-patterns/references/components/default-header.md +75 -0
  63. package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
  64. package/skills/app-shell-patterns/references/components/description-card.md +569 -0
  65. package/skills/app-shell-patterns/references/components/dialog.md +164 -0
  66. package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
  67. package/skills/app-shell-patterns/references/components/filter-rail.md +165 -0
  68. package/skills/app-shell-patterns/references/components/form.md +378 -0
  69. package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
  70. package/skills/app-shell-patterns/references/components/grid.md +184 -0
  71. package/skills/app-shell-patterns/references/components/input.md +83 -0
  72. package/skills/app-shell-patterns/references/components/layout.md +539 -0
  73. package/skills/app-shell-patterns/references/components/menu.md +218 -0
  74. package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
  75. package/skills/app-shell-patterns/references/components/select.md +283 -0
  76. package/skills/app-shell-patterns/references/components/sheet.md +237 -0
  77. package/skills/app-shell-patterns/references/components/sidebar-group.md +472 -0
  78. package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
  79. package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
  80. package/skills/app-shell-patterns/references/components/sidebar-section.md +114 -0
  81. package/skills/app-shell-patterns/references/components/spinner.md +81 -0
  82. package/skills/app-shell-patterns/references/components/table.md +188 -0
  83. package/skills/app-shell-patterns/references/components/tabs.md +158 -0
  84. package/skills/app-shell-patterns/references/components/textarea.md +122 -0
  85. package/skills/app-shell-patterns/references/components/timeline.md +317 -0
  86. package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
  87. package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
  88. package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
  89. package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
  90. package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
  91. package/skills/app-shell-patterns/references/concepts/file-based-routing.md +356 -0
  92. package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +31 -18
  93. package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
  94. package/skills/app-shell-patterns/references/concepts/routing-navigation.md +425 -0
  95. package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +420 -0
  96. package/skills/app-shell-patterns/references/concepts/styling-theming.md +785 -0
  97. package/skills/app-shell-patterns/references/migrations.md +55 -0
  98. package/skills/app-shell-patterns/references/pages/{detail.md → document-detail.md} +10 -33
  99. package/skills/app-shell-patterns/references/patterns/form-composer.md +43 -45
  100. package/skills/app-shell-patterns/references/patterns/form-modal.md +37 -92
  101. package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
  102. package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
  103. package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
  104. package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
  105. package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +136 -139
  106. package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
  107. package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +13 -88
  108. package/skills/app-shell-patterns/references/fundamental/components.md +0 -1009
  109. package/skills/app-shell-patterns/references/fundamental/design-system.md +0 -440
@@ -0,0 +1,413 @@
1
+ ---
2
+ title: CommandPalette
3
+ description: Keyboard-driven quick navigation with fuzzy search for all routes in your application
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/command-palette.docs.outline.md — do not edit by hand. -->
7
+
8
+ # CommandPalette
9
+
10
+ `CommandPalette` provides a keyboard-driven quick navigation interface that allows users to search and navigate to any page in your application. Activated with `Cmd+K` (Mac) or `Ctrl+K` (Windows), it offers fuzzy search across all accessible routes.
11
+
12
+ ## Usage
13
+
14
+ The CommandPalette is built into `AppShell` and rendered automatically. No explicit import or placement is needed:
15
+
16
+ ```tsx
17
+ import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
18
+
19
+ function App() {
20
+ return (
21
+ <AppShell modules={modules}>
22
+ <SidebarLayout />
23
+ </AppShell>
24
+ );
25
+ }
26
+ ```
27
+
28
+ The CommandPalette will automatically:
29
+
30
+ - ✅ Index all navigatable routes from your modules
31
+ - ✅ Provide fuzzy search functionality
32
+ - ✅ Show breadcrumb paths for context
33
+ - ✅ Resolve dynamic params on the current route branch into real paths
34
+ - ✅ Display module icons
35
+ - ✅ Respect access control (hidden routes won't appear)
36
+
37
+ ## Keyboard Shortcuts
38
+
39
+ ### Opening/Closing
40
+
41
+ | Shortcut | Action |
42
+ | -------------------------- | --------------------- |
43
+ | `Cmd + K` (Mac) | Toggle CommandPalette |
44
+ | `Ctrl + K` (Windows/Linux) | Toggle CommandPalette |
45
+ | `Esc` | Close CommandPalette |
46
+
47
+ ### Navigation
48
+
49
+ | Shortcut | Action |
50
+ | -------------- | -------------------------------- |
51
+ | `↑` / `↓` | Navigate up/down through results |
52
+ | `Enter` | Navigate to selected route |
53
+ | Type to search | Filter routes by fuzzy search |
54
+
55
+ ## Features
56
+
57
+ ### Fuzzy Search
58
+
59
+ The CommandPalette uses intelligent fuzzy search that matches:
60
+
61
+ - **Route titles** - Page names from your resource meta
62
+ - **Paths** - URL segments
63
+ - **Breadcrumbs** - Full navigation path
64
+
65
+ Example searches:
66
+
67
+ - `dash` → Matches "Dashboard"
68
+ - `prod det` → Matches "Products > Product Details"
69
+ - `ord` → Matches "Orders", "Order Details", etc.
70
+
71
+ ### Breadcrumb Display
72
+
73
+ Each result shows its full navigation path for context:
74
+
75
+ ```
76
+ 🏠 Dashboard > Analytics > Revenue
77
+ 📦 Products > Categories > Electronics
78
+ 📋 Orders > History > 2024
79
+ ```
80
+
81
+ When the current URL already fixes a dynamic segment, the palette keeps routes on that branch navigable and displays those resolved values in the path. It only substitutes params on the current route branch, not into sibling routes.
82
+
83
+ For example, on `/users/42` the palette can include `/users/42` and `/users/42/profile`, and does **not** incorrectly rewrite sibling routes like `/users/new`.
84
+
85
+ ### Access Control Integration
86
+
87
+ The CommandPalette automatically respects your route guards:
88
+
89
+ - Hidden routes (via `hidden()` guard) don't appear in search
90
+ - Protected routes are filtered based on current user permissions
91
+ - Dynamic updates when context changes
92
+
93
+ ### Icon Display
94
+
95
+ Module icons are displayed next to search results for visual recognition:
96
+
97
+ ```tsx
98
+ const productsModule = defineModule({
99
+ path: "products",
100
+ component: ProductsPage,
101
+ meta: {
102
+ title: "Products",
103
+ icon: <Package />, // Shown in CommandPalette
104
+ },
105
+ });
106
+ ```
107
+
108
+ ## Contextual Actions
109
+
110
+ In addition to navigation results, the CommandPalette can surface page-level actions. Use the `useRegisterCommandPaletteActions` hook to register actions that appear in the **Actions** section of the palette. Actions are unregistered automatically when the component unmounts.
111
+
112
+ ```tsx
113
+ import { useRegisterCommandPaletteActions } from "@tailor-platform/app-shell";
114
+
115
+ function OrderDetailPage() {
116
+ useRegisterCommandPaletteActions("Order Actions", [
117
+ { key: "confirm", label: "Confirm order", onSelect: handleConfirm },
118
+ { key: "cancel", label: "Cancel order", icon: <XIcon />, onSelect: handleCancel },
119
+ ]);
120
+ }
121
+ ```
122
+
123
+ ### Parameters
124
+
125
+ | Parameter | Type | Description |
126
+ | --------- | ------------------------ | -------------------------------------------------------- |
127
+ | `group` | `string` | Display group name for the actions shown in the palette |
128
+ | `actions` | `CommandPaletteAction[]` | List of actions to register (see `CommandPaletteAction`) |
129
+
130
+ ### `CommandPaletteAction`
131
+
132
+ | Property | Type | Description |
133
+ | ---------- | ----------------------------- | ----------------------------------------------------------------------------- |
134
+ | `key` | `string` | Unique key for React reconciliation |
135
+ | `label` | `string` | Visible label shown in the palette; also used for search matching |
136
+ | `icon` | `ReactNode` | Optional icon rendered next to the label |
137
+ | `group` | `string` | Optional group override; defaults to the `group` parameter passed to the hook |
138
+ | `onSelect` | `() => void \| Promise<void>` | Callback invoked when the user selects the action |
139
+
140
+ > **Note:** `icon` changes alone do not trigger re-registration. To reflect a dynamic icon update, also change the action's `key` or `label`.
141
+
142
+ ### ActionPanel Integration
143
+
144
+ When you use the [`ActionPanel`](./action-panel.md) component, its enabled actions (not `disabled`, not `loading`, and with an `onClick` handler) are automatically registered to the CommandPalette under the panel's `title`. No additional setup is required.
145
+
146
+ For more details see [`useRegisterCommandPaletteActions`](../api/use-register-command-palette-actions.md).
147
+
148
+ ## Async Search Sources
149
+
150
+ In addition to built-in page search and contextual actions, you can wire async, prefix-activated search sources into the CommandPalette via the `searchSources` prop on `AppShell`.
151
+
152
+ ### How It Works
153
+
154
+ 1. Pass an array of `SearchSource` objects to `AppShell`'s `searchSources` prop.
155
+ 2. When the user types a source's `prefix` followed by `:` (e.g. `ORD:`), the palette switches into **search mode**: the Actions and Pages sections are hidden and only results from that source are shown.
156
+ 3. On the **empty-input** state, the palette renders a **Search Modes** section listing every registered source so users can activate a mode with a single click.
157
+
158
+ ### Basic Example
159
+
160
+ ```tsx
161
+ import {
162
+ AppShell,
163
+ SidebarLayout,
164
+ DefaultSidebar,
165
+ type SearchSource,
166
+ } from "@tailor-platform/app-shell";
167
+
168
+ const searchSources: readonly SearchSource[] = [
169
+ {
170
+ prefix: "ORD",
171
+ title: "Orders",
172
+ search: async (query, { signal }) => {
173
+ const results = await api.searchOrders(query, { signal });
174
+ return results.map((o) => ({
175
+ key: o.id,
176
+ label: o.number,
177
+ description: `${o.customerName} — ${o.date}`,
178
+ path: `/orders/${o.id}`,
179
+ }));
180
+ },
181
+ },
182
+ {
183
+ prefix: "CUST",
184
+ title: "Customers",
185
+ search: async (query, { signal }) => {
186
+ const results = await api.searchCustomers(query, { signal });
187
+ return results.map((c) => ({
188
+ key: c.id,
189
+ label: c.name,
190
+ path: `/customers/${c.id}`,
191
+ }));
192
+ },
193
+ },
194
+ ];
195
+
196
+ function App() {
197
+ return (
198
+ <AppShell modules={modules} searchSources={searchSources}>
199
+ <SidebarLayout sidebar={<DefaultSidebar />} />
200
+ </AppShell>
201
+ );
202
+ }
203
+ ```
204
+
205
+ ### `SearchSource`
206
+
207
+ | Property | Type | Required | Description |
208
+ | -------- | -------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------- |
209
+ | `prefix` | `string` | ✅ | Mode-activating prefix (e.g. `"ORD"`). Case-sensitive, alphanumeric only. |
210
+ | `title` | `string` | ✅ | Heading shown above results and in the Search Modes list (e.g. `"Orders"`). |
211
+ | `icon` | `ReactNode` | No | Optional icon shown next to each result that lacks its own icon. |
212
+ | `search` | `(query: string, options: { signal: AbortSignal }) => Promise<CommandPaletteSearchResult[]>` | ✅ | Async function invoked with the query text after the prefix and an `AbortSignal` for cancellation. |
213
+
214
+ ### `CommandPaletteSearchResult`
215
+
216
+ | Property | Type | Required | Description |
217
+ | ------------- | ----------- | -------- | -------------------------------------------------------------- |
218
+ | `key` | `string` | ✅ | Unique key for React reconciliation. |
219
+ | `label` | `string` | ✅ | Visible label shown in the palette (e.g. `"Order #ORD-1234"`). |
220
+ | `description` | `string` | No | Optional secondary text (e.g. `"山田太郎 - 2024/01/15"`). |
221
+ | `icon` | `ReactNode` | No | Optional icon for this specific result. |
222
+ | `path` | `string` | ✅ | Resolved navigation path (e.g. `"/orders/abc-123"`). |
223
+
224
+ ### Prefix Syntax
225
+
226
+ The prefix match is **case-sensitive**. When a user types `ORD:`, the string after the colon (including any space) is forwarded to `search` as the query:
227
+
228
+ ```
229
+ User types: ORD:
230
+ → search("", { signal }) — empty query, show all or hint
231
+
232
+ User types: ORD:12345
233
+ → search("12345", { signal })
234
+
235
+ User types: ORD: alice
236
+ → search(" alice", { signal }) — leading space preserved
237
+ ```
238
+
239
+ ## Extending the built-in palette
240
+
241
+ The supported extension points are:
242
+
243
+ - `searchSources` on `AppShell` for async, prefix-activated search modes
244
+ - [`useRegisterCommandPaletteActions`](../api/use-register-command-palette-actions.md) for page-level actions
245
+ - [`ActionPanel`](./action-panel.md), whose enabled rows are registered automatically
246
+
247
+ `CommandPalette` itself is built into `AppShell`; there is no separate public hook for rebuilding the palette UI from scratch.
248
+
249
+ ## Examples
250
+
251
+ ### Basic Usage
252
+
253
+ The CommandPalette works automatically with your module definitions:
254
+
255
+ ```tsx
256
+ const modules = [
257
+ defineModule({
258
+ path: "dashboard",
259
+ component: DashboardPage,
260
+ meta: { title: "Dashboard", icon: <Home /> },
261
+ resources: [
262
+ defineResource({
263
+ path: "analytics",
264
+ component: AnalyticsPage,
265
+ meta: { title: "Analytics" },
266
+ }),
267
+ ],
268
+ }),
269
+ defineModule({
270
+ path: "products",
271
+ component: ProductsPage,
272
+ meta: { title: "Products", icon: <Package /> },
273
+ resources: [
274
+ defineResource({
275
+ path: ":id",
276
+ component: ProductDetailPage,
277
+ meta: { title: "Product Details" },
278
+ }),
279
+ ],
280
+ }),
281
+ ];
282
+
283
+ // CommandPalette will show:
284
+ // - Dashboard
285
+ // - Dashboard > Analytics
286
+ // - Products
287
+ // - Products > Product Details (when applicable)
288
+ ```
289
+
290
+ ### With Access Control
291
+
292
+ Routes hidden by guards won't appear in search:
293
+
294
+ ```tsx
295
+ const adminModule = defineModule({
296
+ path: "admin",
297
+ component: AdminPage,
298
+ meta: { title: "Admin Panel" },
299
+ guards: [
300
+ ({ context }) => {
301
+ return context.currentUser?.role === "admin" ? pass() : hidden(); // Won't show in CommandPalette for non-admins
302
+ },
303
+ ],
304
+ });
305
+ ```
306
+
307
+ ### Search Behavior
308
+
309
+ ```
310
+ User types: "prod"
311
+ Results:
312
+ 📦 Products
313
+ 📦 Products > Product Details
314
+ 📦 Products > Categories
315
+
316
+ User types: "dash ana"
317
+ Results:
318
+ 🏠 Dashboard > Analytics
319
+
320
+ User types: "ord 123"
321
+ Results:
322
+ 📋 Orders > Order #123 (if dynamic routes are indexed)
323
+ ```
324
+
325
+ ## Design
326
+
327
+ The CommandPalette follows these design principles:
328
+
329
+ ### Visual Design
330
+
331
+ - **Clean, focused UI** - Minimal distractions
332
+ - **Clear hierarchy** - Module icons, titles, and breadcrumbs
333
+ - **Active state** - Highlight selected result
334
+ - **Empty state** - "No results found" message
335
+
336
+ ### UX Patterns
337
+
338
+ - **Immediate feedback** - Results update as you type
339
+ - **Keyboard-first** - All actions accessible via keyboard
340
+ - **Contextual information** - Breadcrumbs show location
341
+ - **Quick access** - Single shortcut to open
342
+
343
+ ### Performance
344
+
345
+ - **Instant search** - Client-side fuzzy matching
346
+ - **Debounced input** - Smooth typing experience
347
+ - **Lazy loading** - Routes loaded only when needed
348
+
349
+ ## Best Practices
350
+
351
+ ### Do:
352
+
353
+ - ✅ Provide clear, descriptive titles in resource meta
354
+ - ✅ Use meaningful module icons for visual recognition
355
+ - ✅ Structure modules logically for better discoverability
356
+ - ✅ Keep route hierarchies shallow when possible
357
+
358
+ ### Don't:
359
+
360
+ - ❌ Use generic titles like "Page 1", "Section A"
361
+ - ❌ Create deeply nested resources (hard to navigate)
362
+ - ❌ Forget to set meta.title on resources
363
+ - ❌ Use similar titles for different routes
364
+
365
+ ## Accessibility
366
+
367
+ - **Keyboard navigation** - Full keyboard support
368
+ - **ARIA labels** - Proper labels for screen readers
369
+ - **Focus management** - Focus trapped in dialog when open
370
+ - **ESC to close** - Standard dialog behavior
371
+
372
+ ## Related Components
373
+
374
+ - [AppShell](./app-shell.md) - Root component that provides CommandPalette
375
+ - [SidebarLayout](./sidebar-layout.md) - Layout that includes CommandPalette
376
+
377
+ ## Related Concepts
378
+
379
+ - [Modules and Resources](../concepts/modules-and-resources.md) - Define routes that appear in CommandPalette
380
+ - [Routing and Navigation](../concepts/routing-navigation.md) - Navigation system
381
+ - [Guards and Permissions](../api/guards/overview.md) - Control route visibility
382
+
383
+ ## Troubleshooting
384
+
385
+ ### Routes not appearing in search
386
+
387
+ **Problem:** Some routes don't show up in CommandPalette
388
+
389
+ **Solutions:**
390
+
391
+ 1. Check that the resource has a `meta.title` defined
392
+ 2. Verify the route isn't hidden by a guard
393
+ 3. Ensure the route is navigatable (not a redirect-only route)
394
+
395
+ ### Search not matching expected routes
396
+
397
+ **Problem:** Typing a route name doesn't find it
398
+
399
+ **Solutions:**
400
+
401
+ 1. Try typing the breadcrumb path: "Products Categories"
402
+ 2. Use abbreviations: "prod cat"
403
+ 3. Check spelling of the title in resource meta
404
+
405
+ ### CommandPalette not opening
406
+
407
+ **Problem:** `Cmd+K` / `Ctrl+K` doesn't work
408
+
409
+ **Solutions:**
410
+
411
+ 1. Check if another app is capturing the shortcut
412
+ 2. Verify you're using `SidebarLayout` (CommandPalette is included)
413
+ 3. Check browser console for JavaScript errors
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: CsvImporter
3
+ description: Guided multi-step CSV import flow with drag-and-drop upload, column mapping, validation, and inline error correction
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/csv-importer.docs.outline.md — do not edit by hand. -->
7
+
8
+ # CsvImporter
9
+
10
+ The `CsvImporter` component provides a guided, multi-step CSV import flow rendered inside a drawer. It handles drag-and-drop file upload, interactive column mapping, Standard Schema validation, inline cell editing, and async server-side validation.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import {
16
+ CsvImporter,
17
+ useCsvImporter,
18
+ csv,
19
+ type CsvSchema,
20
+ type CsvColumn,
21
+ type CsvImportEvent,
22
+ type CsvCellIssue,
23
+ type CsvCorrection,
24
+ type CsvColumnMapping,
25
+ type ParsedRow,
26
+ type InferCsvRow,
27
+ } from "@tailor-platform/app-shell";
28
+ ```
29
+
30
+ ## Basic Usage
31
+
32
+ Use the `useCsvImporter` hook to manage state, then render `<CsvImporter>` with the returned props.
33
+
34
+ ```tsx
35
+ import { CsvImporter, useCsvImporter, csv } from "@tailor-platform/app-shell";
36
+ import { Button } from "@tailor-platform/app-shell";
37
+
38
+ function ProductImport() {
39
+ const { open, props } = useCsvImporter({
40
+ schema: {
41
+ columns: [
42
+ {
43
+ key: "name",
44
+ label: "Name",
45
+ required: true,
46
+ aliases: ["product_name"],
47
+ schema: csv.string({ min: 1 }),
48
+ },
49
+ {
50
+ key: "price",
51
+ label: "Price",
52
+ schema: csv.number({ min: 0 }),
53
+ },
54
+ {
55
+ key: "active",
56
+ label: "Active",
57
+ schema: csv.boolean(),
58
+ },
59
+ ],
60
+ },
61
+ onImport: (event) => {
62
+ console.log(event.summary);
63
+ },
64
+ });
65
+
66
+ return (
67
+ <>
68
+ <Button onClick={open}>Import CSV</Button>
69
+ <CsvImporter {...props} />
70
+ </>
71
+ );
72
+ }
73
+ ```
74
+
75
+ ## `useCsvImporter`
76
+
77
+ The `useCsvImporter` hook manages the open/close state and returns both an `open` function and `props` to spread onto `<CsvImporter>`.
78
+
79
+ ### Options
80
+
81
+ | Option | Type | Default | Description |
82
+ | ------------- | -------------------------------------------------- | ------------------ | --------------------------------------------------------------------------- |
83
+ | `schema` | `CsvSchema` | — | Column definitions for the import (see [CsvSchema](#csvschema)) |
84
+ | `onImport` | `(event: CsvImportEvent) => void \| Promise<void>` | — | Called when the user confirms the import after resolving all errors |
85
+ | `onValidate` | `(rows: ParsedRow[]) => Promise<CsvCellIssue[]>` | — | Optional async callback for server-side validation after schema checks pass |
86
+ | `maxFileSize` | `number` | `10485760` (10 MB) | Maximum allowed file size in bytes |
87
+
88
+ ### Return Value
89
+
90
+ | Property | Type | Description |
91
+ | -------- | ------------------ | ----------------------------------------------------- |
92
+ | `open` | `() => void` | Function to programmatically open the importer drawer |
93
+ | `props` | `CsvImporterProps` | Props object to spread directly onto `<CsvImporter>` |
94
+
95
+ ## `CsvImporter` Props
96
+
97
+ `CsvImporter` is intended to be used with props returned from `useCsvImporter`. Spread the `props` object directly onto the component.
98
+
99
+ ```tsx
100
+ <CsvImporter {...props} />
101
+ ```
102
+
103
+ ## Schemas
104
+
105
+ ### `CsvSchema`
106
+
107
+ ```ts
108
+ type CsvSchema = {
109
+ columns: CsvColumn[];
110
+ };
111
+ ```
112
+
113
+ ### `CsvColumn`
114
+
115
+ | Property | Type | Default | Description |
116
+ | ------------- | ------------------ | ------- | --------------------------------------------------------------------------------------- |
117
+ | `key` | `string` | — | Internal key; becomes the object key in parsed row data |
118
+ | `label` | `string` | — | Display label shown in the mapping UI |
119
+ | `description` | `string` | — | Optional hint shown in the mapping UI |
120
+ | `required` | `boolean` | `false` | Whether this column must be mapped before proceeding |
121
+ | `aliases` | `string[]` | — | Alternative CSV header names for automatic matching |
122
+ | `schema` | `StandardSchemaV1` | — | Standard Schema validator for coercion and validation (see [csv helpers](#csv-helpers)) |
123
+
124
+ ## `csv` Helpers
125
+
126
+ Built-in Standard Schema validators for common CSV column types. Each helper handles both coercion and validation in a single declaration.
127
+
128
+ | Helper | Output type | Description |
129
+ | ------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------- |
130
+ | `csv.string(options?: { min?: number; max?: number })` | `string` | Pass-through string with optional length constraints |
131
+ | `csv.number(options?: { min?: number; max?: number; integer?: boolean })` | `number` | Coerces raw CSV string to a number; rejects `NaN` |
132
+ | `csv.boolean(options?: { truthy?: string[]; falsy?: string[] })` | `boolean` | Recognises `"true"/"1"/"yes"` and `"false"/"0"/"no"` (case-insensitive) |
133
+ | `csv.date()` | `Date` | Coerces raw CSV string to a `Date`; rejects unparseable values |
134
+ | `csv.enum(values: string[])` | `T extends string` | Validates the value is one of the allowed strings (case-sensitive) |
135
+
136
+ ```tsx
137
+ // String with required check (min: 1)
138
+ csv.string({ min: 1 });
139
+
140
+ // Number with lower bound
141
+ csv.number({ min: 0 });
142
+
143
+ // Integer only
144
+ csv.number({ integer: true });
145
+
146
+ // Boolean with defaults: truthy = ["true","1","yes"], falsy = ["false","0","no"]
147
+ csv.boolean();
148
+
149
+ // Date
150
+ csv.date();
151
+
152
+ // Enum
153
+ csv.enum(["active", "inactive", "pending"]);
154
+ ```
155
+
156
+ ## Server-side Validation
157
+
158
+ Use `onValidate` to perform async checks (e.g. uniqueness constraints) after schema validation passes. Return an array of `CsvCellIssue` objects to mark cells with errors or warnings.
159
+
160
+ ```tsx
161
+ const { open, props } = useCsvImporter({
162
+ schema: { columns: [...] },
163
+ onValidate: async (rows) => {
164
+ const issues = await checkUniqueness(rows);
165
+ return issues; // CsvCellIssue[]
166
+ },
167
+ onImport: (event) => { /* ... */ },
168
+ });
169
+ ```
170
+
171
+ ### `CsvCellIssue`
172
+
173
+ | Property | Type | Description |
174
+ | ----------- | ---------------------- | ---------------------------------------------------- |
175
+ | `rowIndex` | `number` | 0-based row index |
176
+ | `columnKey` | `string` | The schema column key |
177
+ | `level` | `"error" \| "warning"` | `"error"` blocks import; `"warning"` allows import |
178
+ | `message` | `string` | Human-readable message displayed inline in the table |
179
+
180
+ ## Import Event
181
+
182
+ The `onImport` callback receives a `CsvImportEvent` with the following shape:
183
+
184
+ | Property | Type | Description |
185
+ | ------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------ |
186
+ | `file` | `File` | The original file selected by the user |
187
+ | `mappings` | `CsvColumnMapping[]` | The confirmed column mappings |
188
+ | `corrections` | `CsvCorrection[]` | Corrections made by the user in the review step |
189
+ | `issues` | `CsvCellIssue[]` | Any remaining issues (warnings only — errors are resolved before import) |
190
+ | `summary` | `{ totalRows, validRows, correctedRows, skippedRows, warningRows }` | Summary statistics |
191
+ | `buildRows()` | `() => Promise<InferCsvRow<T>[]>` | Reconstruct fully-processed rows with schema coercion and corrections |
192
+
193
+ ## `event.buildRows()`
194
+
195
+ A method on the import event that reconstructs the fully processed row data on the client side. The return type is inferred from the schema definition — column keys become object keys, and `StandardSchemaV1` output types are preserved.
196
+
197
+ ```tsx
198
+ onImport: async (event) => {
199
+ const rows = await event.buildRows();
200
+ // rows is typed based on schema — e.g. { name: string; price: number; ... }[]
201
+ await saveToBackend(rows);
202
+ },
203
+ ```
204
+
205
+ > If you are sending the file, mappings, and corrections to a backend for processing, you do not need `buildRows()`.
206
+
207
+ ## i18n
208
+
209
+ `CsvImporter` includes built-in English and Japanese labels. No additional setup is required.
210
+
211
+ ## Related Components
212
+
213
+ - [Sheet](./sheet.md) — Slide-in panel (the drawer used internally by CsvImporter)
214
+ - [Button](./button.md) — Use as the trigger to open the importer