@tailor-platform/app-shell 1.14.0 → 1.16.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 (101) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/app-shell-core.css +1 -1
  3. package/dist/app-shell.d.ts +1203 -316
  4. package/dist/app-shell.js +4562 -3307
  5. package/dist/{appshell-CtS8Y3LW.js → appshell-B0wh5Lq0.js} +850 -549
  6. package/dist/style-entry.d.ts +118 -98
  7. package/dist/testing.d.ts +118 -98
  8. package/dist/testing.js +1 -1
  9. package/dist/themes/_template.css +1 -1
  10. package/dist/vite-plugin.d.ts +118 -98
  11. package/package.json +13 -13
  12. package/skills/app-shell-patterns/SKILL.md +108 -24
  13. package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
  14. package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
  15. package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
  16. package/skills/app-shell-patterns/references/api/define-module.md +247 -0
  17. package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
  18. package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
  19. package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
  20. package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
  21. package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
  22. package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
  23. package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
  24. package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
  25. package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
  26. package/skills/app-shell-patterns/references/api/use-app-shell-config.md +126 -0
  27. package/skills/app-shell-patterns/references/api/use-app-shell-data.md +146 -0
  28. package/skills/app-shell-patterns/references/api/use-app-shell.md +143 -0
  29. package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
  30. package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
  31. package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
  32. package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
  33. package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
  34. package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
  35. package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
  36. package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
  37. package/skills/app-shell-patterns/references/api/use-toast.md +107 -0
  38. package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
  39. package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
  40. package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
  41. package/skills/app-shell-patterns/references/components/alert.md +202 -0
  42. package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
  43. package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
  44. package/skills/app-shell-patterns/references/components/attachment.md +138 -0
  45. package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
  46. package/skills/app-shell-patterns/references/components/avatar.md +80 -0
  47. package/skills/app-shell-patterns/references/components/badge.md +309 -0
  48. package/skills/app-shell-patterns/references/components/button.md +164 -0
  49. package/skills/app-shell-patterns/references/components/card.md +109 -0
  50. package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
  51. package/skills/app-shell-patterns/references/components/combobox.md +318 -0
  52. package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
  53. package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
  54. package/skills/app-shell-patterns/references/components/data-table.md +953 -0
  55. package/skills/app-shell-patterns/references/components/date-picker.md +248 -0
  56. package/skills/app-shell-patterns/references/components/default-header.md +75 -0
  57. package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
  58. package/skills/app-shell-patterns/references/components/description-card.md +569 -0
  59. package/skills/app-shell-patterns/references/components/dialog.md +164 -0
  60. package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
  61. package/skills/app-shell-patterns/references/components/form.md +378 -0
  62. package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
  63. package/skills/app-shell-patterns/references/components/grid.md +184 -0
  64. package/skills/app-shell-patterns/references/components/input.md +83 -0
  65. package/skills/app-shell-patterns/references/components/layout.md +539 -0
  66. package/skills/app-shell-patterns/references/components/menu.md +218 -0
  67. package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
  68. package/skills/app-shell-patterns/references/components/select.md +283 -0
  69. package/skills/app-shell-patterns/references/components/sheet.md +237 -0
  70. package/skills/app-shell-patterns/references/components/sidebar-group.md +466 -0
  71. package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
  72. package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
  73. package/skills/app-shell-patterns/references/components/spinner.md +81 -0
  74. package/skills/app-shell-patterns/references/components/table.md +188 -0
  75. package/skills/app-shell-patterns/references/components/tabs.md +158 -0
  76. package/skills/app-shell-patterns/references/components/textarea.md +122 -0
  77. package/skills/app-shell-patterns/references/components/timeline.md +317 -0
  78. package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
  79. package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
  80. package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
  81. package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
  82. package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
  83. package/skills/app-shell-patterns/references/concepts/file-based-routing.md +350 -0
  84. package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +11 -4
  85. package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
  86. package/skills/app-shell-patterns/references/concepts/routing-navigation.md +387 -0
  87. package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +388 -0
  88. package/skills/app-shell-patterns/references/{fundamental/design-system.md → concepts/styling-theming.md} +260 -105
  89. package/skills/app-shell-patterns/references/migrations.md +34 -0
  90. package/skills/app-shell-patterns/references/pages/document-detail.md +1125 -0
  91. package/skills/app-shell-patterns/references/patterns/form-composer.md +44 -46
  92. package/skills/app-shell-patterns/references/patterns/form-modal.md +39 -100
  93. package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
  94. package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
  95. package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
  96. package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
  97. package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +14 -28
  98. package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
  99. package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +11 -86
  100. package/skills/app-shell-patterns/references/fundamental/components.md +0 -981
  101. package/skills/app-shell-patterns/references/patterns/detail-hero-with-actions.md +0 -201
@@ -0,0 +1,387 @@
1
+ ---
2
+ title: Routing and Navigation
3
+ description: Learn how to navigate between pages using React Router hooks and components re-exported from AppShell
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/concepts/routing-navigation.docs.outline.md — do not edit by hand. -->
7
+
8
+ # Routing and Navigation
9
+
10
+ AppShell owns the router. It builds the route tree from your modules and resources, constructs the router, and renders the `RouterProvider` itself. Your pages render _inside_ that router.
11
+
12
+ ## Your app should not depend on `react-router`
13
+
14
+ **Do not add `react-router` to your application's dependencies.** Import everything you need for routing from `@tailor-platform/app-shell` instead.
15
+
16
+ This is not a style preference — it is what keeps your app working:
17
+
18
+ ```tsx
19
+ // ✅ Do this
20
+ import { useNavigate, Link } from "@tailor-platform/app-shell";
21
+
22
+ // ❌ Not this
23
+ import { useNavigate, Link } from "react-router";
24
+ ```
25
+
26
+ React Router keeps its state in React context, and context identity belongs to a _module instance_. If your app resolves its own copy of `react-router` — a different version to the one AppShell resolved — the bundle ends up with two copies and therefore two unrelated routers. AppShell's own navigation keeps working, so the app looks fine at a glance, while every hook and component you imported yourself throws:
27
+
28
+ ```
29
+ useNavigate() may be used only in the context of a <Router> component.
30
+ ```
31
+
32
+ TypeScript cannot see this, so the build passes and the failure only appears at runtime. Under pnpm, an app that has not declared `react-router` cannot import it at all, which is the intended outcome: the rule enforces itself.
33
+
34
+ If you already have a direct dependency, the fix is to delete it and change the import paths. The APIs are identical — they are the same functions, re-exported.
35
+
36
+ ## What AppShell gives you
37
+
38
+ Everything you need to _consume_ the router AppShell created:
39
+
40
+ **Location and route matching**
41
+
42
+ - `useLocation` — the current location object
43
+ - `useParams` — dynamic route parameters
44
+ - `useSearchParams` — read and update the query string
45
+ - `useMatch` — test whether a path matches the current location
46
+ - `useResolvedPath` — resolve a relative path against the current route
47
+
48
+ **Navigating**
49
+
50
+ - `useNavigate` — navigate programmatically
51
+ - `useNavigation` — the in-flight navigation, for pending UI
52
+ - `Link` — a client-side link
53
+ - `NavLink` — a link that knows when it is active, for nav menus
54
+ - `Navigate` — redirect declaratively during render
55
+
56
+ **Guarding unsaved work**
57
+
58
+ - `useBlocker` — intercept an in-app navigation away from unsaved changes
59
+ - `useBeforeUnload` — the same for closing or reloading the tab
60
+
61
+ **Errors**
62
+
63
+ - `useRouteError` — read the error inside an error boundary
64
+
65
+ Types come with them: `Location`, `NavigateFunction`, `NavigateOptions`, `To`, `Params`, `PathMatch`, `LinkProps`, `NavLinkProps`, `Navigation`, `Blocker`, and `BlockerFunction`.
66
+
67
+ ### What it deliberately does not give you
68
+
69
+ **Router construction** — `createBrowserRouter`, `RouterProvider`, `BrowserRouter`, `MemoryRouter`, `Routes`, `Route`. AppShell builds the router from your modules and resources; a second one nested inside it is the bug this page is about. To control routing in tests, use the [`/testing` entry point](#testing) rather than building your own router.
70
+
71
+ **Data-router APIs** — `useLoaderData`, `Form`, `useSubmit`, `useFetcher`, `useActionData`, `useRevalidator`. AppShell does not wire up React Router's data layer, so these have nothing to talk to. Load data in the page component or your own data layer. (AppShell exports its own `Form`, which is part of the [form system](../components/form.md) and unrelated to React Router's.)
72
+
73
+ Missing something you need? Ask for it to be added here rather than adding a direct `react-router` dependency — that is the trade this rule makes, and requests are the mechanism that keeps it honest.
74
+
75
+ ### One caveat
76
+
77
+ A third-party library that itself depends on `react-router` can still pull in a second copy. That is rare, and outside what AppShell can control. If routing starts throwing the error above, look for a duplicate — under pnpm, `pnpm why react-router`.
78
+
79
+ ## Testing
80
+
81
+ Test helpers live in a separate entry point, `@tailor-platform/app-shell/testing`. Memory routing is kept out of the main entry because an app that shipped it would render correctly while the URL bar silently stopped tracking navigation — the `AppShell` you import from `@tailor-platform/app-shell` always uses browser routing, and pins it off regardless of what you pass.
82
+
83
+ ### Pages and integration tests
84
+
85
+ Because AppShell owns the router, a test mounts AppShell rather than building a router. The `AppShell` exported from `/testing` is the same shell, and additionally accepts `memory` with `initialEntries` so it starts at a known URL without touching `window.location`:
86
+
87
+ ```tsx
88
+ import { render, screen } from "@testing-library/react";
89
+ import { AppShell } from "@tailor-platform/app-shell/testing";
90
+ import { SidebarLayout } from "@tailor-platform/app-shell";
91
+
92
+ render(
93
+ <AppShell memory initialEntries={["/orders/A42"]} modules={modules}>
94
+ <SidebarLayout />
95
+ </AppShell>,
96
+ );
97
+
98
+ expect(await screen.findByText("Order A42")).toBeDefined();
99
+ ```
100
+
101
+ `initialEntries` is the history stack to start with — the last entry is the current location. Each render owns its history, so tests do not leak navigation state into one another.
102
+
103
+ ### Unit tests
104
+
105
+ A component that calls `useNavigate` or renders a `<Link>` needs a router above it, but mounting a whole AppShell for that is heavy. `TestRouter` provides just the router context:
106
+
107
+ ```tsx
108
+ import { TestRouter } from "@tailor-platform/app-shell/testing";
109
+
110
+ render(
111
+ <TestRouter initialEntries={["/orders/A42"]}>
112
+ <SaveButton />
113
+ </TestRouter>,
114
+ );
115
+ ```
116
+
117
+ **If the component reads the route**, pass `path`. A router on its own matches nothing, so `useParams()` comes back empty even when `initialEntries` holds a URL that looks like it should match:
118
+
119
+ ```tsx
120
+ render(
121
+ <TestRouter path="/orders/:id" initialEntries={["/orders/A42"]}>
122
+ <OrderBadge /> {/* useParams() -> { id: "A42" } */}
123
+ </TestRouter>,
124
+ );
125
+ ```
126
+
127
+ The same applies to `useMatch` and to relative `<Link>` targets. Compose your own providers — a GraphQL client, say — around `TestRouter` as needed.
128
+
129
+ Reach for `TestRouter` when the component under test is the subject; mount `AppShell memory` when the routing itself is.
130
+
131
+ ## Example
132
+
133
+ ```tsx
134
+ import { useNavigate, useParams, useLocation, Link } from "@tailor-platform/app-shell";
135
+
136
+ const MyComponent = () => {
137
+ const navigate = useNavigate();
138
+ const { id } = useParams();
139
+ const location = useLocation();
140
+
141
+ const handleClick = () => {
142
+ // Navigate programmatically
143
+ navigate("/dashboard/overview");
144
+ };
145
+
146
+ return (
147
+ <div>
148
+ <p>Current path: {location.pathname}</p>
149
+ <p>Route param ID: {id}</p>
150
+
151
+ {/* Client-side link */}
152
+ <Link to="/products">View Products</Link>
153
+
154
+ <button onClick={handleClick}>Go to Dashboard</button>
155
+ </div>
156
+ );
157
+ };
158
+ ```
159
+
160
+ ## Declarative Redirects
161
+
162
+ `Navigate` redirects as a render result, which avoids the `useEffect` + `navigate()` pattern (that pattern renders the old page for a frame before it redirects):
163
+
164
+ ```tsx
165
+ import { Navigate, useAppShellData } from "@tailor-platform/app-shell";
166
+
167
+ const AdminPage = () => {
168
+ const { currentUser } = useAppShellData();
169
+
170
+ if (currentUser?.role !== "admin") {
171
+ return <Navigate to="/dashboard" replace />;
172
+ }
173
+
174
+ return <AdminDashboard />;
175
+ };
176
+ ```
177
+
178
+ Pass `replace` when the redirect should not leave the abandoned route in history — otherwise the browser Back button lands the user right back on it, and bounces them forward again.
179
+
180
+ ### Choosing between `Navigate` and `redirectTo()`
181
+
182
+ Both redirect, but they run at different points:
183
+
184
+ | | Runs | Use for |
185
+ | ---------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------- |
186
+ | [`redirectTo()`](../api/guards/redirect-to.md) | Route guard, before the component mounts | Route-level access control — the preferred option when the decision can be made from guard context |
187
+ | `Navigate` | During render, from inside a component | Decisions that depend on component state, hooks, or fetched data |
188
+
189
+ Reach for `redirectTo()` first: it never mounts the component. `Navigate` is the fallback for cases a guard cannot express — notably [`WithGuard`](../components/with-guard.md), which does not support `redirectTo()`.
190
+
191
+ ## Breadcrumbs
192
+
193
+ AppShell automatically generates breadcrumbs from your module and resource hierarchy. Each path segment corresponds to a breadcrumb item, using the `title` (or `meta.breadcrumbTitle`) defined in `defineModule` / `defineResource`.
194
+
195
+ ### Static Breadcrumb Titles
196
+
197
+ Set a fixed breadcrumb title via `meta.breadcrumbTitle`:
198
+
199
+ ```tsx
200
+ defineResource({
201
+ path: ":id",
202
+ meta: {
203
+ breadcrumbTitle: (segment) => `Order #${segment}`,
204
+ },
205
+ component: OrderDetailPage,
206
+ });
207
+ // Breadcrumb shows: "Orders > Order #12345"
208
+ ```
209
+
210
+ ### Dynamic Breadcrumb Titles
211
+
212
+ Use the `useOverrideBreadcrumb` hook to replace a breadcrumb segment with a data-driven value from within the rendered page component:
213
+
214
+ ```tsx
215
+ import { useOverrideBreadcrumb } from "@tailor-platform/app-shell";
216
+
217
+ defineResource({
218
+ path: ":id",
219
+ component: () => {
220
+ const { data } = useQuery(GET_ORDER, { variables: { id } });
221
+
222
+ // Breadcrumb updates reactively once data loads
223
+ useOverrideBreadcrumb(data?.order?.name);
224
+
225
+ return <OrderDetail />;
226
+ },
227
+ });
228
+ ```
229
+
230
+ While `title` is `undefined` (e.g., loading), the override is cleared and the static title is shown. The override is automatically cleaned up on unmount.
231
+
232
+ See [useOverrideBreadcrumb](../api/use-override-breadcrumb.md) for the full API reference.
233
+
234
+ ## Command Palette for Quick Navigation
235
+
236
+ AppShell includes a `CommandPalette` component that provides keyboard-driven quick navigation to any page in your application.
237
+
238
+ ### Features
239
+
240
+ - **Keyboard Shortcut**: `Cmd+K` (Mac) or `Ctrl+K` (Windows/Linux)
241
+ - **Fuzzy Search**: Search by page title or path
242
+ - **Hierarchical Display**: Shows module > resource breadcrumbs
243
+ - **Keyboard Navigation**: Use arrow keys and Enter to navigate
244
+ - **Multilingual**: Supports English and Japanese locales
245
+
246
+ ### Setup
247
+
248
+ The CommandPalette is built into `AppShell` and rendered automatically:
249
+
250
+ ```tsx
251
+ import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
252
+
253
+ const App = () => (
254
+ <AppShell modules={modules} locale="en">
255
+ <SidebarLayout />
256
+ </AppShell>
257
+ );
258
+ ```
259
+
260
+ The CommandPalette automatically:
261
+
262
+ - Collects all navigable routes from your module definitions
263
+ - Respects guards (modules/resources returning `hidden()` won't appear)
264
+ - Updates when navigation items change
265
+ - Adapts to the current locale
266
+
267
+ ### User Experience
268
+
269
+ 1. User presses `Cmd+K` / `Ctrl+K` anywhere in the app
270
+ 2. Command palette dialog opens with fuzzy search
271
+ 3. User types to filter pages (e.g., "order detail")
272
+ 4. Navigate results with arrow keys
273
+ 5. Press Enter to navigate to selected page
274
+
275
+ No configuration needed - it just works!
276
+
277
+ ## Type-Safe Navigation with Generated Routes
278
+
279
+ When using file-based routing with the vite-plugin, you can enable automatic generation of type-safe route helpers. This provides compile-time checking for route paths and their parameters.
280
+
281
+ ### Setup
282
+
283
+ Enable `generateTypedRoutes` in your vite config:
284
+
285
+ ```ts
286
+ // vite.config.ts
287
+ import { defineConfig } from "vite";
288
+ import react from "@vitejs/plugin-react";
289
+ import { appShellRoutes } from "@tailor-platform/app-shell/vite-plugin";
290
+
291
+ export default defineConfig({
292
+ plugins: [
293
+ react(),
294
+ appShellRoutes({
295
+ pagesDir: "src/pages",
296
+ // Enable with default output path ("src/routes.generated.ts")
297
+ generateTypedRoutes: true,
298
+ // Or customize output path:
299
+ // generateTypedRoutes: { output: "src/my-routes.ts" },
300
+ }),
301
+ ],
302
+ });
303
+ ```
304
+
305
+ This generates a `src/routes.generated.ts` file containing type definitions for all your routes.
306
+
307
+ ### Generated File
308
+
309
+ The generated file exports a `paths` helper with a type-safe `for()` method:
310
+
311
+ ```ts
312
+ // src/routes.generated.ts (auto-generated)
313
+ import { createTypedPaths } from "@tailor-platform/app-shell";
314
+
315
+ type RouteParams = {
316
+ "/": {};
317
+ "/dashboard": {};
318
+ "/orders": {};
319
+ "/orders/:id": { id: string };
320
+ "/orders/:orderId/items/:itemId": { orderId: string; itemId: string };
321
+ };
322
+
323
+ export const paths = createTypedPaths<RouteParams>();
324
+
325
+ export type { RouteParams };
326
+ ```
327
+
328
+ ### Usage
329
+
330
+ ```tsx
331
+ import { useNavigate } from "@tailor-platform/app-shell";
332
+ import { paths } from "./routes.generated";
333
+
334
+ const MyComponent = () => {
335
+ const navigate = useNavigate();
336
+
337
+ // ✅ Static route - no params needed
338
+ const goToDashboard = () => {
339
+ navigate(paths.for("/dashboard"));
340
+ };
341
+
342
+ // ✅ Dynamic route - params required and type-checked
343
+ const goToOrder = (orderId: string) => {
344
+ navigate(paths.for("/orders/:id", { id: orderId }));
345
+ };
346
+
347
+ // ✅ Multiple params
348
+ const goToOrderItem = (orderId: string, itemId: string) => {
349
+ navigate(paths.for("/orders/:orderId/items/:itemId", { orderId, itemId }));
350
+ };
351
+
352
+ // ✅ Query string passthrough
353
+ const goToOrderWithTab = (orderId: string) => {
354
+ navigate(paths.for("/orders/:id?tab=details", { id: orderId }));
355
+ };
356
+
357
+ // ✅ Dynamic query values via template literal
358
+ const goToOrderWithDynamicQuery = (orderId: string, tab: string) => {
359
+ navigate(paths.for(`/orders/:id?tab=${tab}`, { id: orderId }));
360
+ };
361
+
362
+ // ❌ TypeScript error: missing required params
363
+ // navigate(paths.for("/orders/:id"));
364
+
365
+ // ❌ TypeScript error: invalid path
366
+ // navigate(paths.for("/invalid/path"));
367
+
368
+ return <button onClick={goToDashboard}>Go to Dashboard</button>;
369
+ };
370
+ ```
371
+
372
+ ### Opt-In Design
373
+
374
+ This feature is opt-in. If you don't enable `generateTypedRoutes`, you can continue building paths dynamically:
375
+
376
+ ```tsx
377
+ // Still works without typed routes
378
+ navigate(`/orders/${orderId}`);
379
+ ```
380
+
381
+ ### HMR Support
382
+
383
+ The generated file is automatically regenerated when:
384
+
385
+ - A new `page.tsx` is added
386
+ - A `page.tsx` is deleted
387
+ - The dev server starts