create-keboola-app 0.2.0 → 0.2.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.
@@ -9,11 +9,7 @@ type ChatMessage = {
9
9
  };
10
10
 
11
11
  export type AssistantSlice = {
12
- /**
13
- * One transcript for the whole app. The home page's prompt input and the rail's
14
- * own composer both call `send`, so sending from the page is a handoff into the
15
- * rail rather than a second, parallel conversation.
16
- */
12
+ /** One app-wide transcript: the home prompt and the rail's composer both `send` into it. */
17
13
  messages: ChatMessage[];
18
14
  send: (text: string) => void;
19
15
  fullscreen: boolean;
@@ -53,10 +49,8 @@ export const createAssistantSlice: StateCreator<AppState, Mutators, [], Assistan
53
49
  messages: [
54
50
  ...s.messages,
55
51
  { id: crypto.randomUUID(), role: 'user', text },
56
- // A canned reply, not a backend call — swap the whole branch for a
57
- // real request. It answers in Markdown (including a ```mermaid fence)
58
- // because that is what an assistant actually returns, and rendering
59
- // that is the rail's job: see AssistantPanel.
52
+ // Canned stand-in for a backend call; Markdown with a ```mermaid fence,
53
+ // rendered by AssistantPanel.
60
54
  { id: crypto.randomUUID(), role: 'assistant', text: cannedReply(text) },
61
55
  ],
62
56
  }),
@@ -7,9 +7,7 @@ import type { AppState } from '../store';
7
7
  /**
8
8
  * The modes `sidebarMode` may hold, as a runtime value.
9
9
  *
10
- * - Keyed by `SidebarMode`, so adding a mode to the union fails the build here until
11
- * it is listed — a hand-written array would silently start rejecting the new mode.
12
- * - The values carry nothing; the keys are the whole point.
10
+ * - Keyed by `SidebarMode`, so a new mode fails the build until it is listed here.
13
11
  */
14
12
  const SIDEBAR_MODES: Record<SidebarMode, true> = {
15
13
  expanded: true,
@@ -22,12 +20,8 @@ export const isSidebarMode = (value: unknown): value is SidebarMode =>
22
20
 
23
21
  export type ShellSlice = {
24
22
  /**
25
- * The left rail's mode.
26
- *
27
- * - Lives here, not inside `SidebarProvider`, so it survives a reload; a layout
28
- * preference that resets reads as a broken control.
29
- * - `AppChrome` drives the provider from it (`leftMode` + `onLeftModeChange`).
30
- * - `SidebarModeMenu` is the UI that writes it.
23
+ * - Stored here, not in `SidebarProvider`, so it survives a reload.
24
+ * - `AppChrome` feeds it to the provider; `SidebarModeMenu` writes it.
31
25
  */
32
26
  sidebarMode: SidebarMode;
33
27
  setSidebarMode: (mode: SidebarMode) => void;
@@ -39,8 +33,7 @@ export type ShellSlice = {
39
33
  /**
40
34
  * The mutator tuple mirrors `store.ts`'s middleware stack, outside-in.
41
35
  *
42
- * - Without it `set` loses its third argument and every `set(partial, false, 'name')`
43
- * call fails to type-check.
36
+ * - Without it `set(partial, false, 'name')` fails to type-check.
44
37
  */
45
38
  type Mutators = [['zustand/devtools', never], ['zustand/persist', unknown]];
46
39
 
@@ -16,18 +16,14 @@ export type AppState = ShellSlice &
16
16
  // #region block:assistant
17
17
  AssistantSlice &
18
18
  // #endregion block:assistant
19
- // `unknown` is the identity for `&`, so the line above can go without leaving a dangling
20
- // operator. The comment keeps this alias on three lines, which is what `oxfmt` expects of
21
- // it in an app that dropped the middle term.
19
+ // - `unknown` is the identity for `&`, so the line above can go without a dangling operator.
20
+ // - This comment keeps the alias multi-line, as `oxfmt` expects once that line is gone.
22
21
  unknown;
23
22
 
24
23
  export const useAppStore = create<AppState>()(
25
24
  devtools(
26
- // `persist` inside `devtools` so rehydration lands in the devtools timeline as an
27
- // action. Only `sidebarMode` is written out — see `partialize`.
25
+ // `persist` inside `devtools` so rehydration shows in the devtools timeline.
28
26
  persist(
29
- // Zustand's slice pattern: `(...a)` forwards `set`, `get` and the store API to
30
- // each creator without naming the three of them per slice.
31
27
  (...a) => ({
32
28
  ...createShellSlice(...a),
33
29
  // #region block:assistant
@@ -36,26 +32,15 @@ export const useAppStore = create<AppState>()(
36
32
  }),
37
33
  {
38
34
  name: UI_SETTINGS_STORAGE_KEY,
39
- // No `version` here on purpose. Zustand discards a record whose version does not
40
- // match and no `migrate` covers, so a bare bump silently resets every returning
41
- // visitor. If the stored shape ever changes, add `version` and `migrate` in the
42
- // same change. A value that is merely unrecognised is `merge`'s job, below.
43
- // The layout preference and nothing else. The default is the whole store, which
44
- // would restore a stale transcript on reload and pin `fullscreen` open across
45
- // sessions.
35
+ // - No `version`: a bump without `migrate` resets every stored record; add both together.
36
+ // - Layout only; the whole store would restore a stale transcript and pin `fullscreen`.
46
37
  partialize: (state) => ({ sidebarMode: state.sidebarMode }),
47
- // Validate at the storage boundary instead of trusting what comes back.
48
- // - `localStorage` is user-writable, and a value written by an older build
49
- // outlives the code that wrote it.
50
- // - An unrecognised mode is not inert: `SidebarProvider` derives the rail's
51
- // `state` from it, no branch matches, so the rail sticks at icon width with
52
- // nothing checked in the mode menu — recoverable only by picking a mode.
53
- // - Falling back to `current` means the in-memory default, so a junk value
54
- // reads exactly like a first visit.
38
+ // - Untrusted: `localStorage` is user-writable and outlives the build that wrote it.
39
+ // - An unknown mode sticks the rail at icon width, so junk falls back to the default.
55
40
  merge: (persisted, current) => {
56
41
  const stored: unknown =
57
- typeof persisted === 'object' && persisted !== null
58
- ? Reflect.get(persisted, 'sidebarMode')
42
+ typeof persisted === 'object' && persisted !== null && 'sidebarMode' in persisted
43
+ ? persisted.sidebarMode
59
44
  : undefined;
60
45
 
61
46
  return { ...current, sidebarMode: isSidebarMode(stored) ? stored : current.sidebarMode };
@@ -10,11 +10,8 @@ export default defineConfig({
10
10
  routesDirectory: './src/routes',
11
11
  generatedRouteTree: './src/routeTree.gen.ts',
12
12
  /**
13
- * `routes/` holds route files and their tests, nothing else.
14
- *
15
13
  * - A test file exports no `Route`, so unmatched it costs a generator warning per build.
16
- * - Matched against the file name, not the path (`routeFileIgnorePrefix` still defaults
17
- * to `-`; nothing here relies on it).
14
+ * - Matched against the file name, not the path.
18
15
  */
19
16
  routeFileIgnorePattern: '\\.test\\.tsx?$',
20
17
  }),
@@ -6,11 +6,8 @@ export default defineConfig({
6
6
  globals: true,
7
7
  environment: 'jsdom',
8
8
  setupFiles: ['./src/test-setup.ts'],
9
- // This suite runs inside the repo-wide `Checks` job, so it shares a 2-core
10
- // runner with every other package's tests — ~25x slower per test than a dev
11
- // machine here (measured: AssistantPanel's two prompt tests are 156ms/199ms
12
- // locally, 3881ms/5189ms there). The 5s default clips whichever one loses the
13
- // race; the userEvent-driven tests get headroom instead.
9
+ // The shared 2-core `Checks` runner is ~25x slower than a dev machine; the 5s default
10
+ // clips userEvent-driven tests.
14
11
  testTimeout: 15000,
15
12
  },
16
13
  resolve: {
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "create-keboola-app",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Scaffold a Keboola-shaped frontend app — Vite + React 19 + TanStack Router/Query/Form + Zustand + @keboola/design — from a minimal shell plus the blocks you pick",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "bin": {
8
+ "create-keboola-app": "./dist/cli.js"
9
+ },
7
10
  "files": [
8
11
  "dist",
9
12
  "README.md",
@@ -35,19 +38,20 @@
35
38
  "cli"
36
39
  ],
37
40
  "dependencies": {
38
- "@tanstack/router-generator": "^1.120.4"
41
+ "@tanstack/router-generator": "^1.167.46"
39
42
  },
40
43
  "devDependencies": {
44
+ "@keboola/eslint-plugin-design": "^0.8.0",
45
+ "@keboola/oxlint-config": "^10.3.0",
46
+ "@keboola/tsconfig": "^0.2.1",
41
47
  "@types/node": "24.12.0",
42
48
  "@typescript/native": "npm:typescript@^7.0.2",
43
49
  "oxfmt": "^0.66.0",
44
- "oxlint": "^1.81.0",
50
+ "oxlint": "^1.89.0",
45
51
  "tsdown": "^0.22.14",
46
52
  "typescript": "npm:@typescript/typescript6@^6.0.2",
47
- "vite": "^8.1.5",
48
- "vitest": "^4.1.10",
49
- "@keboola/tsconfig": "^0.2.1",
50
- "@keboola/oxlint-config": "^10.0.2"
53
+ "vite": "^8.3.5",
54
+ "vitest": "^5.0.5"
51
55
  },
52
56
  "scripts": {
53
57
  "build": "tsdown && node dist/extract.js",
@@ -57,10 +61,7 @@
57
61
  "lint:oxlint": "oxlint . --quiet",
58
62
  "knip:check": "pnpm --dir ../.. exec knip --workspace packages/create-keboola-app",
59
63
  "knip:fix": "pnpm --dir ../.. exec knip --workspace packages/create-keboola-app --fix-type types,exports,files",
60
- "test": "vitest run --passWithNoTests --reporter=dot",
64
+ "test": "vitest run --reporter=dot",
61
65
  "type-check": "tsc"
62
- },
63
- "bin": {
64
- "create-keboola-app": "./dist/cli.js"
65
66
  }
66
67
  }