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.
- package/CHANGELOG.md +11 -0
- package/dist/{blocks-D_-NWhwg.js → blocks-BdJaEmz1.js} +5 -8
- package/dist/cli.js +26 -52
- package/dist/extract.js +8 -14
- package/dist/{regions-B3r5LhXl.js → regions-BNl4QsPP.js} +12 -22
- package/dist/{sanitize-CKs8pAzZ.js → sanitize-DVFBkv8b.js} +14 -24
- package/dist/sanitize.js +1 -2
- package/dist/template/oxlint.config.ts +10 -14
- package/dist/template/package.json +20 -19
- package/dist/template/src/auth/AuthScreen.tsx +6 -26
- package/dist/template/src/auth/session.ts +7 -23
- package/dist/template/src/auth/states.tsx +5 -23
- package/dist/template/src/components/AppChrome.tsx +26 -74
- package/dist/template/src/components/SelectionFloater.tsx +10 -3
- package/dist/template/src/components/SidebarModeMenu.tsx +8 -13
- package/dist/template/src/nav.ts +5 -10
- package/dist/template/src/overview-fixtures.ts +6 -18
- package/dist/template/src/recipes/Chart.test.tsx +1 -3
- package/dist/template/src/recipes/Chart.tsx +4 -11
- package/dist/template/src/recipes/DataGrid.tsx +5 -19
- package/dist/template/src/routes/__root.tsx +6 -13
- package/dist/template/src/routes/signed-out.tsx +3 -7
- package/dist/template/src/sections/kpi.tsx +3 -7
- package/dist/template/src/slices/assistant.ts +3 -9
- package/dist/template/src/slices/shell.ts +4 -11
- package/dist/template/src/store.ts +9 -24
- package/dist/template/vite.config.ts +1 -4
- package/dist/template/vitest.config.ts +2 -5
- package/package.json +12 -11
|
@@ -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
|
-
//
|
|
57
|
-
//
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
20
|
-
//
|
|
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
|
|
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
|
|
40
|
-
//
|
|
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
|
-
//
|
|
48
|
-
// -
|
|
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
|
-
?
|
|
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
|
|
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
|
-
//
|
|
10
|
-
//
|
|
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.
|
|
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.
|
|
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.
|
|
50
|
+
"oxlint": "^1.89.0",
|
|
45
51
|
"tsdown": "^0.22.14",
|
|
46
52
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
47
|
-
"vite": "^8.
|
|
48
|
-
"vitest": "^
|
|
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 --
|
|
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
|
}
|