@postxl/generators 1.12.2 → 1.13.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 (126) hide show
  1. package/dist/backend-ai/ai.generator.d.ts +18 -0
  2. package/dist/backend-ai/ai.generator.js +174 -0
  3. package/dist/backend-ai/ai.generator.js.map +1 -0
  4. package/dist/backend-ai/generators/ai-agent-service.generator.d.ts +4 -0
  5. package/dist/backend-ai/generators/ai-agent-service.generator.js +264 -0
  6. package/dist/backend-ai/generators/ai-agent-service.generator.js.map +1 -0
  7. package/dist/backend-ai/generators/ai-cache-service.generator.d.ts +1 -0
  8. package/dist/backend-ai/generators/ai-cache-service.generator.js +110 -0
  9. package/dist/backend-ai/generators/ai-cache-service.generator.js.map +1 -0
  10. package/dist/backend-ai/generators/ai-config.generator.d.ts +1 -0
  11. package/dist/backend-ai/generators/ai-config.generator.js +27 -0
  12. package/dist/backend-ai/generators/ai-config.generator.js.map +1 -0
  13. package/dist/backend-ai/generators/ai-module.generator.d.ts +2 -0
  14. package/dist/backend-ai/generators/ai-module.generator.js +89 -0
  15. package/dist/backend-ai/generators/ai-module.generator.js.map +1 -0
  16. package/dist/backend-ai/generators/ai-route.generator.d.ts +1 -0
  17. package/dist/backend-ai/generators/ai-route.generator.js +29 -0
  18. package/dist/backend-ai/generators/ai-route.generator.js.map +1 -0
  19. package/dist/backend-ai/generators/ai-tools-service.generator.d.ts +4 -0
  20. package/dist/backend-ai/generators/ai-tools-service.generator.js +222 -0
  21. package/dist/backend-ai/generators/ai-tools-service.generator.js.map +1 -0
  22. package/dist/backend-ai/generators/model-provider-interface.generator.d.ts +1 -0
  23. package/dist/backend-ai/generators/model-provider-interface.generator.js +48 -0
  24. package/dist/backend-ai/generators/model-provider-interface.generator.js.map +1 -0
  25. package/dist/backend-ai/generators/openai-model-provider-service.generator.d.ts +1 -0
  26. package/dist/backend-ai/generators/openai-model-provider-service.generator.js +128 -0
  27. package/dist/backend-ai/generators/openai-model-provider-service.generator.js.map +1 -0
  28. package/dist/backend-ai/index.d.ts +4 -0
  29. package/dist/backend-ai/index.js +40 -0
  30. package/dist/backend-ai/index.js.map +1 -0
  31. package/dist/backend-core/generators/main.generator.js +4 -3
  32. package/dist/backend-core/generators/main.generator.js.map +1 -1
  33. package/dist/backend-e2e/backend-e2e.generator.js +4 -4
  34. package/dist/backend-e2e/backend-e2e.generator.js.map +1 -1
  35. package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js +1 -1
  36. package/dist/backend-router-trpc/generators/app-routes.generator.js +3 -1
  37. package/dist/backend-router-trpc/generators/app-routes.generator.js.map +1 -1
  38. package/dist/backend-router-trpc/generators/trpc-plugin.generator.js +3 -0
  39. package/dist/backend-router-trpc/generators/trpc-plugin.generator.js.map +1 -1
  40. package/dist/backend-router-trpc/generators/trpc-router-module.generator.js +2 -1
  41. package/dist/backend-router-trpc/generators/trpc-router-module.generator.js.map +1 -1
  42. package/dist/backend-router-trpc/generators/trpc-shared.generator.js +7 -1
  43. package/dist/backend-router-trpc/generators/trpc-shared.generator.js.map +1 -1
  44. package/dist/backend-router-trpc/router-trpc.generator.d.ts +2 -1
  45. package/dist/backend-router-trpc/router-trpc.generator.js +2 -0
  46. package/dist/backend-router-trpc/router-trpc.generator.js.map +1 -1
  47. package/dist/backend-seed/seed.generator.js +10 -1
  48. package/dist/backend-seed/seed.generator.js.map +1 -1
  49. package/dist/base/template/scripts/setup.sh +9 -4
  50. package/dist/base/template/sonar-project.properties +9 -1
  51. package/dist/devops/generators/bitbucket-pipelines-yml.generator.js +1 -0
  52. package/dist/devops/generators/bitbucket-pipelines-yml.generator.js.map +1 -1
  53. package/dist/devops/generators/e2e-yml.generator.js +35 -10
  54. package/dist/devops/generators/e2e-yml.generator.js.map +1 -1
  55. package/dist/devops/generators/jenkinsfile.generator.js +25 -1
  56. package/dist/devops/generators/jenkinsfile.generator.js.map +1 -1
  57. package/dist/e2e/template/e2e/specs/example.spec.ts-snapshots/Navigate-to-homepage-and-take-snapshot-1-chromium-linux.png +0 -0
  58. package/dist/frontend-actions/actions.generator.d.ts +9 -0
  59. package/dist/frontend-actions/actions.generator.js +111 -0
  60. package/dist/frontend-actions/actions.generator.js.map +1 -0
  61. package/dist/frontend-actions/generators/ai-action-text.utils.generator.d.ts +1 -0
  62. package/dist/frontend-actions/generators/ai-action-text.utils.generator.js +52 -0
  63. package/dist/frontend-actions/generators/ai-action-text.utils.generator.js.map +1 -0
  64. package/dist/frontend-actions/generators/ai-assistant-store.generator.d.ts +1 -0
  65. package/dist/frontend-actions/generators/ai-assistant-store.generator.js +230 -0
  66. package/dist/frontend-actions/generators/ai-assistant-store.generator.js.map +1 -0
  67. package/dist/frontend-actions/generators/ai-sidebar-content.generator.d.ts +1 -0
  68. package/dist/frontend-actions/generators/ai-sidebar-content.generator.js +139 -0
  69. package/dist/frontend-actions/generators/ai-sidebar-content.generator.js.map +1 -0
  70. package/dist/frontend-actions/generators/ai-sidepane.generator.d.ts +1 -0
  71. package/dist/frontend-actions/generators/ai-sidepane.generator.js +98 -0
  72. package/dist/frontend-actions/generators/ai-sidepane.generator.js.map +1 -0
  73. package/dist/frontend-actions/generators/base-global-actions.generator.d.ts +1 -0
  74. package/dist/frontend-actions/generators/base-global-actions.generator.js +405 -0
  75. package/dist/frontend-actions/generators/base-global-actions.generator.js.map +1 -0
  76. package/dist/frontend-actions/generators/command-palette-action.generator.d.ts +1 -0
  77. package/dist/frontend-actions/generators/command-palette-action.generator.js +87 -0
  78. package/dist/frontend-actions/generators/command-palette-action.generator.js.map +1 -0
  79. package/dist/frontend-actions/generators/command-palette-store.generator.d.ts +1 -0
  80. package/dist/frontend-actions/generators/command-palette-store.generator.js +288 -0
  81. package/dist/frontend-actions/generators/command-palette-store.generator.js.map +1 -0
  82. package/dist/frontend-actions/generators/command-palette.generator.d.ts +5 -0
  83. package/dist/frontend-actions/generators/command-palette.generator.js +332 -0
  84. package/dist/frontend-actions/generators/command-palette.generator.js.map +1 -0
  85. package/dist/frontend-actions/generators/filter-utils.generator.d.ts +1 -0
  86. package/dist/frontend-actions/generators/filter-utils.generator.js +50 -0
  87. package/dist/frontend-actions/generators/filter-utils.generator.js.map +1 -0
  88. package/dist/frontend-actions/generators/sidepanel-toggle.generator.d.ts +1 -0
  89. package/dist/frontend-actions/generators/sidepanel-toggle.generator.js +37 -0
  90. package/dist/frontend-actions/generators/sidepanel-toggle.generator.js.map +1 -0
  91. package/dist/frontend-actions/index.d.ts +4 -0
  92. package/dist/frontend-actions/index.js +40 -0
  93. package/dist/frontend-actions/index.js.map +1 -0
  94. package/dist/frontend-admin/admin.generator.d.ts +3 -1
  95. package/dist/frontend-admin/admin.generator.js +8 -1
  96. package/dist/frontend-admin/admin.generator.js.map +1 -1
  97. package/dist/frontend-admin/generators/admin-global-actions.generator.d.ts +4 -0
  98. package/dist/frontend-admin/generators/admin-global-actions.generator.js +152 -0
  99. package/dist/frontend-admin/generators/admin-global-actions.generator.js.map +1 -0
  100. package/dist/frontend-admin/generators/comment-sidebar.generator.js +5 -3
  101. package/dist/frontend-admin/generators/comment-sidebar.generator.js.map +1 -1
  102. package/dist/frontend-admin/generators/detail-sidebar.generator.js +40 -24
  103. package/dist/frontend-admin/generators/detail-sidebar.generator.js.map +1 -1
  104. package/dist/frontend-admin/generators/model-admin-page.generator.js +172 -11
  105. package/dist/frontend-admin/generators/model-admin-page.generator.js.map +1 -1
  106. package/dist/frontend-admin/utils.d.ts +1 -0
  107. package/dist/frontend-admin/utils.js +1 -0
  108. package/dist/frontend-admin/utils.js.map +1 -1
  109. package/dist/frontend-core/generators/tsconfig.generator.js +1 -0
  110. package/dist/frontend-core/generators/tsconfig.generator.js.map +1 -1
  111. package/dist/frontend-core/template/.env.example +3 -0
  112. package/dist/frontend-core/template/src/components/admin/table-view-panel.tsx +22 -4
  113. package/dist/frontend-core/template/src/components/ui/color-mode-toggle/color-mode-toggle.tsx +1 -1
  114. package/dist/frontend-core/template/src/lib/color.ts +6 -3
  115. package/dist/frontend-core/template/src/lib/config.ts +3 -1
  116. package/dist/frontend-tables/generators/model-table.generator.js +13 -0
  117. package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
  118. package/dist/generators.js +4 -0
  119. package/dist/generators.js.map +1 -1
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +8 -2
  122. package/dist/index.js.map +1 -1
  123. package/dist/types/template/ai.types.ts +34 -0
  124. package/dist/types/types.generator.js +1 -0
  125. package/dist/types/types.generator.js.map +1 -1
  126. package/package.json +4 -4
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCommandPaletteStore = generateCommandPaletteStore;
4
+ function generateCommandPaletteStore() {
5
+ return `import { useMemo, useSyncExternalStore } from 'react'
6
+
7
+ import { CommandPaletteAction, CommandPaletteActionPage, CommandPaletteActionResult } from './command-palette-action'
8
+
9
+ type GroupedActions = {
10
+ group: string
11
+ actions: CommandPaletteAction[]
12
+ }
13
+
14
+ type CommandPaletteState = {
15
+ page: CommandPaletteActionPage
16
+ componentActions: CommandPaletteAction[]
17
+ globalActions: CommandPaletteAction[]
18
+ running: boolean
19
+ search: string
20
+ globalRenderer: ((component: React.FC | null) => void) | null
21
+ }
22
+
23
+ type CommandPaletteActions = {
24
+ find: (key: string) => CommandPaletteAction | undefined
25
+ back: () => void
26
+ reset: () => void
27
+ cancel: () => void
28
+ select: (key: string, signal: AbortSignal, query?: string, input?: unknown) => Promise<CommandPaletteActionResult>
29
+ registerPageActions: (page: CommandPaletteActionPage) => void
30
+ deregisterPageActions: () => void
31
+ registerComponentActions: (actions: CommandPaletteAction[]) => void
32
+ deregisterComponentActions: (actions: CommandPaletteAction[]) => void
33
+ registerGlobalActions: (...actions: CommandPaletteAction[]) => void
34
+ registerGlobalRenderer: (render: (component: React.FC | null) => void) => void
35
+ calculateAvailableLLMActions: () => CommandPaletteAction[]
36
+ }
37
+
38
+ type CommandPaletteStore = CommandPaletteState & { actions: CommandPaletteActions }
39
+
40
+ const listeners = new Set<() => void>()
41
+ const rootPage: CommandPaletteActionPage = { actions: [] }
42
+
43
+ const into = (back: CommandPaletteActionPage, page: CommandPaletteActionPage): CommandPaletteActionPage => ({
44
+ ...page,
45
+ back,
46
+ })
47
+
48
+ const subscribe = (listener: () => void) => {
49
+ listeners.add(listener)
50
+ return () => listeners.delete(listener)
51
+ }
52
+
53
+ const notify = () => {
54
+ for (const listener of listeners) {
55
+ listener()
56
+ }
57
+ }
58
+
59
+ const setStore = (update: Partial<CommandPaletteState>) => {
60
+ store = {
61
+ ...store,
62
+ ...update,
63
+ }
64
+ notify()
65
+ }
66
+
67
+ const createActions = (): CommandPaletteActions => ({
68
+ calculateAvailableLLMActions() {
69
+ const { page, componentActions, globalActions } = store
70
+ const isRoot = !page.back
71
+ const isVisibleToLLM = (action: CommandPaletteAction) => action.visibility === 'visible' || action.visibility === 'llm'
72
+ const all = [
73
+ ...page.actions.filter(isVisibleToLLM),
74
+ ...(isRoot ? componentActions.filter(isVisibleToLLM) : []),
75
+ ...globalActions.filter(isVisibleToLLM),
76
+ ]
77
+
78
+ const seen = new Set<string>()
79
+ return all.filter((action) => {
80
+ if (seen.has(action.key)) {
81
+ return false
82
+ }
83
+
84
+ seen.add(action.key)
85
+ return true
86
+ })
87
+ },
88
+
89
+ find(key: string) {
90
+ const { page, componentActions, globalActions } = store
91
+ const isRoot = !page.back
92
+
93
+ return (
94
+ page.actions.find((action) => action.key === key) ??
95
+ (isRoot ? componentActions.find((action) => action.key === key) : undefined) ??
96
+ globalActions.find((action) => action.key === key)
97
+ )
98
+ },
99
+
100
+ back() {
101
+ const { page } = store
102
+ if (page.back) {
103
+ setStore({ page: page.back })
104
+ }
105
+ },
106
+
107
+ reset() {
108
+ let next = store.page
109
+
110
+ while (next.back) {
111
+ next = next.back
112
+ }
113
+
114
+ setStore({ page: next })
115
+ },
116
+
117
+ cancel() {
118
+ const { globalRenderer } = store
119
+ globalRenderer?.(null)
120
+ setStore({ running: false })
121
+ },
122
+
123
+ async select(key, signal, query = '', input = undefined) {
124
+ const { page, componentActions, globalActions, globalRenderer, running: wasRunning } = store
125
+ const isRoot = !page.back
126
+
127
+ const action =
128
+ page.actions.find((candidate) => candidate.key === key) ??
129
+ (isRoot ? componentActions.find((candidate) => candidate.key === key) : undefined) ??
130
+ globalActions.find((candidate) => candidate.key === key)
131
+
132
+ if (!action) {
133
+ return { kind: 'close' }
134
+ }
135
+
136
+ setStore({ running: true })
137
+
138
+ try {
139
+ signal.throwIfAborted()
140
+ const result = await action.run(query, input, signal, globalRenderer ?? (() => null))
141
+
142
+ if (result.kind === 'page') {
143
+ setStore({ page: into(page, result) })
144
+ }
145
+
146
+ return result
147
+ } finally {
148
+ globalRenderer?.(null)
149
+ setStore({ running: wasRunning })
150
+ }
151
+ },
152
+
153
+ registerPageActions(page) {
154
+ setStore({ page })
155
+ },
156
+
157
+ deregisterPageActions() {
158
+ setStore({ page: rootPage })
159
+ },
160
+
161
+ registerComponentActions(actions) {
162
+ const incoming = new Set(actions.map((action) => action.key))
163
+ const componentActions = [...store.componentActions.filter((action) => !incoming.has(action.key)), ...actions]
164
+ setStore({ componentActions })
165
+ },
166
+
167
+ deregisterComponentActions(actions) {
168
+ const toRemove = new Set(actions.map((action) => action.key))
169
+ setStore({ componentActions: store.componentActions.filter((action) => !toRemove.has(action.key)) })
170
+ },
171
+
172
+ registerGlobalActions(...actions) {
173
+ const incoming = new Set(actions.map((action) => action.key))
174
+ const globalActions = [...store.globalActions.filter((action) => !incoming.has(action.key)), ...actions]
175
+ setStore({ globalActions })
176
+ },
177
+
178
+ registerGlobalRenderer(render) {
179
+ setStore({ globalRenderer: render })
180
+ },
181
+ })
182
+
183
+ let store: CommandPaletteStore = {
184
+ page: rootPage,
185
+ componentActions: [],
186
+ globalActions: [],
187
+ running: false,
188
+ search: '',
189
+ globalRenderer: null,
190
+ actions: {} as CommandPaletteActions,
191
+ }
192
+ store.actions = createActions()
193
+
194
+ export const useCommandPaletteState = <T>(selector: (state: CommandPaletteState) => T): T =>
195
+ useSyncExternalStore(subscribe, () => selector(store), () => selector(store))
196
+
197
+ export const useCommandPaletteActions = (): CommandPaletteActions =>
198
+ useSyncExternalStore(subscribe, () => store.actions, () => store.actions)
199
+
200
+ export const useCommandPaletteAvailableActions = (query: string): GroupedActions[] => {
201
+ const page = useCommandPaletteState((state) => state.page)
202
+ const componentActions = useCommandPaletteState((state) => state.componentActions)
203
+ const globalActions = useCommandPaletteState((state) => state.globalActions)
204
+
205
+ return useMemo(
206
+ () => calculateAvailableActions({ page, componentActions, globalActions, query }),
207
+ [page, componentActions, globalActions, query],
208
+ )
209
+ }
210
+
211
+ const calculateAvailableActions = ({
212
+ page,
213
+ componentActions,
214
+ globalActions,
215
+ query,
216
+ }: {
217
+ page: CommandPaletteActionPage
218
+ componentActions: CommandPaletteAction[]
219
+ globalActions: CommandPaletteAction[]
220
+ query: string
221
+ }): GroupedActions[] => {
222
+ const hasQuery = query.trim().length > 0
223
+ const isRoot = !page.back
224
+
225
+ const canBeShownToUser = (action: CommandPaletteAction) => action.visibility === 'visible' || action.visibility === 'user'
226
+
227
+ const actionByGroup = new Map<string, CommandPaletteAction[]>()
228
+ const ungrouped: CommandPaletteAction[] = []
229
+ const seen = new Set<string>()
230
+
231
+ for (const action of [...page.actions, ...(isRoot ? componentActions : [])]) {
232
+ if (!canBeShownToUser(action)) {
233
+ continue
234
+ }
235
+
236
+ if (seen.has(action.key)) {
237
+ continue
238
+ }
239
+
240
+ if (action.group) {
241
+ const existing = actionByGroup.get(action.group) ?? []
242
+ actionByGroup.set(action.group, [...existing, action])
243
+ } else {
244
+ ungrouped.push(action)
245
+ }
246
+
247
+ seen.add(action.key)
248
+ }
249
+
250
+ const result: GroupedActions[] = []
251
+
252
+ if (isRoot) {
253
+ const filteredGlobal = globalActions.filter((action) => canBeShownToUser(action) && !seen.has(action.key))
254
+ if (filteredGlobal.length > 0) {
255
+ result.push({ group: 'Global actions', actions: filteredGlobal })
256
+ }
257
+ }
258
+
259
+ if (ungrouped.length > 0) {
260
+ result.push({ group: 'On this page', actions: ungrouped })
261
+ }
262
+
263
+ for (const [group, actions] of actionByGroup.entries()) {
264
+ result.push({ group, actions })
265
+ }
266
+
267
+ if (!hasQuery) {
268
+ return result
269
+ }
270
+
271
+ const terms = query
272
+ .toLowerCase()
273
+ .split(/\\s+/)
274
+ .map((term) => term.trim())
275
+ .filter(Boolean)
276
+
277
+ const matches = (action: CommandPaletteAction) => {
278
+ const text = [action.label, ...(action.keywords ?? [])].join(' ').toLowerCase()
279
+ return terms.every((term) => text.includes(term))
280
+ }
281
+
282
+ return result
283
+ .map((group) => ({ ...group, actions: group.actions.filter(matches) }))
284
+ .filter((group) => group.actions.length > 0)
285
+ }
286
+ `;
287
+ }
288
+ //# sourceMappingURL=command-palette-store.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-palette-store.generator.js","sourceRoot":"","sources":["../../../src/frontend-actions/generators/command-palette-store.generator.ts"],"names":[],"mappings":";;AAAA,kEA2RC;AA3RD,SAAgB,2BAA2B;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyRR,CAAA;AACD,CAAC"}
@@ -0,0 +1,5 @@
1
+ import * as Generator from '@postxl/generator';
2
+ import { WithFrontendActions } from '../actions.generator';
3
+ export declare function generateCommandPalette({ context }: {
4
+ context: WithFrontendActions<Generator.Context>;
5
+ }): string;
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateCommandPalette = generateCommandPalette;
37
+ const Generator = __importStar(require("@postxl/generator"));
38
+ function generateCommandPalette({ context }) {
39
+ const imports = Generator.ImportGenerator.from(Generator.toFilePath('./command-palette.tsx'))
40
+ .addImport({
41
+ from: Generator.toPackageName('react'),
42
+ items: [
43
+ Generator.toClassName('Component'),
44
+ Generator.toTypeName('ErrorInfo'),
45
+ Generator.toTypeName('PropsWithChildren'),
46
+ Generator.toFunctionName('useCallback'),
47
+ Generator.toFunctionName('useDeferredValue'),
48
+ Generator.toFunctionName('useEffect'),
49
+ Generator.toFunctionName('useMemo'),
50
+ Generator.toFunctionName('useRef'),
51
+ Generator.toFunctionName('useState'),
52
+ ],
53
+ })
54
+ .addImport({
55
+ from: Generator.toPackageName('@postxl/ui-components'),
56
+ items: [
57
+ Generator.toFunctionName('CommandDialog'),
58
+ Generator.toFunctionName('CommandGroup'),
59
+ Generator.toFunctionName('CommandInput'),
60
+ Generator.toFunctionName('CommandItem'),
61
+ Generator.toFunctionName('CommandList'),
62
+ ],
63
+ })
64
+ .addImport({ from: Generator.toBackendModuleLocation('@lib/router'), items: [Generator.toConstantName('router')] });
65
+ for (const hook of context.frontendActions.globalActionHooks) {
66
+ imports.add(hook);
67
+ }
68
+ const hookCalls = context.frontendActions.globalActionHooks.map((hook) => ` ${hook.name}()`).join('\n');
69
+ return `
70
+ ${imports.generate()}
71
+
72
+ import {
73
+ useCommandPaletteActions,
74
+ useCommandPaletteAvailableActions,
75
+ useCommandPaletteState,
76
+ } from './command-palette-store'
77
+
78
+ type CommandPaletteErrorBoundaryState = {
79
+ hasError: boolean
80
+ }
81
+
82
+ class CommandPaletteErrorBoundary extends Component<PropsWithChildren, CommandPaletteErrorBoundaryState> {
83
+ public constructor(props: PropsWithChildren) {
84
+ super(props)
85
+ this.state = { hasError: false }
86
+ }
87
+
88
+ public static getDerivedStateFromError(): CommandPaletteErrorBoundaryState {
89
+ return { hasError: true }
90
+ }
91
+
92
+ public componentDidCatch(error: unknown, info: ErrorInfo) {
93
+ console.error('CommandPalette rendering failed:', error, info)
94
+ }
95
+
96
+ public render() {
97
+ if (this.state.hasError) {
98
+ return null
99
+ }
100
+
101
+ return this.props.children
102
+ }
103
+ }
104
+
105
+ export function CommandPalette() {
106
+ return (
107
+ <CommandPaletteErrorBoundary>
108
+ <CommandPaletteInner />
109
+ </CommandPaletteErrorBoundary>
110
+ )
111
+ }
112
+
113
+ function CommandPaletteInner() {
114
+ const { find, cancel, registerGlobalRenderer, deregisterPageActions } = useCommandPaletteActions()
115
+ const isRunning = useCommandPaletteState((state) => state.running)
116
+ const {
117
+ mode,
118
+ open,
119
+ value,
120
+ inputRef,
121
+ handleKeydown,
122
+ handleOpenChange: onOpenChange,
123
+ handleSelect,
124
+ handleValueChange,
125
+ } = useCommandPaletteInput()
126
+
127
+ const deferredValue = useDeferredValue(value)
128
+ const availableActions = useCommandPaletteAvailableActions(deferredValue)
129
+
130
+ ${hookCalls}
131
+
132
+ const suggestions = useMemo(() => {
133
+ if (mode.value === 'search') {
134
+ return []
135
+ }
136
+
137
+ return find(mode.key)?.suggestions ?? []
138
+ }, [mode, find])
139
+
140
+ const [CustomContent, setCustomContent] = useState<React.FC | null>(null)
141
+
142
+ const handleOpenChange = useCallback(
143
+ (isOpen: boolean) => {
144
+ if (!isOpen) {
145
+ cancel()
146
+ setCustomContent(null)
147
+ }
148
+
149
+ onOpenChange(isOpen)
150
+ },
151
+ [cancel, onOpenChange],
152
+ )
153
+
154
+ useEffect(() => {
155
+ registerGlobalRenderer((component) => setCustomContent(() => component))
156
+ }, [registerGlobalRenderer])
157
+
158
+ useEffect(() => {
159
+ const down = (event: KeyboardEvent) => {
160
+ if (event.key.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) {
161
+ event.preventDefault()
162
+ handleOpenChange(true)
163
+ }
164
+
165
+ handleKeydown(event)
166
+ }
167
+
168
+ document.addEventListener('keydown', down)
169
+ return () => document.removeEventListener('keydown', down)
170
+ }, [handleKeydown, handleOpenChange])
171
+
172
+ useEffect(() => {
173
+ return router.subscribe('onBeforeRouteMount', () => {
174
+ deregisterPageActions()
175
+ })
176
+ }, [deregisterPageActions])
177
+
178
+ return (
179
+ <CommandDialog open={open} onOpenChange={handleOpenChange}>
180
+ <CommandInput
181
+ ref={inputRef}
182
+ value={value}
183
+ onValueChange={handleValueChange}
184
+ placeholder={mode.value === 'search' ? 'Type a command or search...' : 'Enter value...'}
185
+ />
186
+
187
+ {CustomContent ? (
188
+ <CustomContent />
189
+ ) : (
190
+ <CommandList>
191
+ {mode.value === 'input' &&
192
+ suggestions.map((suggestion) => (
193
+ <CommandItem key={suggestion} value={suggestion} onSelect={() => handleSelect(suggestion, 'input')}>
194
+ {suggestion}
195
+ </CommandItem>
196
+ ))}
197
+
198
+ {availableActions.map(({ group, actions }) => (
199
+ <CommandGroup key={group} heading={group}>
200
+ {actions.map((action) => (
201
+ <CommandItem
202
+ key={action.key}
203
+ value={action.key}
204
+ keywords={action.keywords}
205
+ onSelect={() => handleSelect(action.key, action.kind)}
206
+ >
207
+ {action.icon}
208
+ {action.label}
209
+ </CommandItem>
210
+ ))}
211
+ </CommandGroup>
212
+ ))}
213
+ </CommandList>
214
+ )}
215
+
216
+ {isRunning && <div className="px-3 py-2 text-sm text-muted-foreground">Running action...</div>}
217
+ </CommandDialog>
218
+ )
219
+ }
220
+
221
+ const useCommandPaletteInput = () => {
222
+ const { select, back, reset } = useCommandPaletteActions()
223
+
224
+ const didSelect = useRef(false)
225
+ const inputRef = useRef<HTMLInputElement | null>(null)
226
+ const abortRef = useRef<AbortController | null>(null)
227
+
228
+ const [open, setOpen] = useState(false)
229
+ const [value, setValue] = useState('')
230
+ const [mode, setMode] = useState<{ value: 'search' } | { value: 'input'; key: string }>({ value: 'search' })
231
+
232
+ const handleOpenChange = useCallback(
233
+ (isOpen: boolean) => {
234
+ setOpen(isOpen)
235
+
236
+ if (!isOpen) {
237
+ setValue('')
238
+ setMode({ value: 'search' })
239
+ reset()
240
+ abortRef.current?.abort()
241
+ }
242
+
243
+ if (isOpen) {
244
+ setTimeout(() => inputRef.current?.focus(), 75)
245
+ }
246
+ },
247
+ [reset],
248
+ )
249
+
250
+ const runAction = useCallback(
251
+ async (key: string, query = '', input: unknown = undefined) => {
252
+ abortRef.current = new AbortController()
253
+ const result = await select(key, abortRef.current.signal, query, input)
254
+ handleOpenChange(result.kind !== 'close')
255
+ setMode({ value: 'search' })
256
+ return result
257
+ },
258
+ [handleOpenChange, select],
259
+ )
260
+
261
+ const handleKeydown = useCallback(
262
+ (event: KeyboardEvent) => {
263
+ if (!open) {
264
+ return
265
+ }
266
+
267
+ if (event.key === 'Backspace' && value === '') {
268
+ if (mode.value === 'input') {
269
+ setMode({ value: 'search' })
270
+ return
271
+ }
272
+
273
+ back()
274
+ return
275
+ }
276
+
277
+ if (event.key === 'Escape') {
278
+ handleOpenChange(false)
279
+ return
280
+ }
281
+
282
+ if (event.key === 'Enter') {
283
+ event.preventDefault()
284
+
285
+ if (didSelect.current) {
286
+ didSelect.current = false
287
+ return
288
+ }
289
+
290
+ if (mode.value === 'input') {
291
+ void runAction(mode.key, value, { query: value })
292
+ return
293
+ }
294
+ }
295
+ },
296
+ [open, value, mode, back, handleOpenChange, runAction],
297
+ )
298
+
299
+ const handleSelect = useCallback(
300
+ (key: string, kind: 'command' | 'input') => {
301
+ didSelect.current = true
302
+
303
+ if (mode.value === 'search') {
304
+ if (kind === 'input') {
305
+ setValue('')
306
+ setMode({ value: 'input', key })
307
+ return
308
+ }
309
+
310
+ void runAction(key)
311
+ return
312
+ }
313
+
314
+ void runAction(mode.key, key, { query: key })
315
+ },
316
+ [mode, runAction],
317
+ )
318
+
319
+ return {
320
+ mode,
321
+ open,
322
+ value,
323
+ inputRef,
324
+ handleOpenChange,
325
+ handleKeydown,
326
+ handleSelect,
327
+ handleValueChange: setValue,
328
+ }
329
+ }
330
+ `;
331
+ }
332
+ //# sourceMappingURL=command-palette.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-palette.generator.js","sourceRoot":"","sources":["../../../src/frontend-actions/generators/command-palette.generator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,wDAwSC;AA5SD,6DAA8C;AAI9C,SAAgB,sBAAsB,CAAC,EAAE,OAAO,EAAuD;IACrG,MAAM,OAAO,GAAG,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC;SAC1F,SAAS,CAAC;QACT,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC;QACtC,KAAK,EAAE;YACL,SAAS,CAAC,WAAW,CAAC,WAAW,CAAC;YAClC,SAAS,CAAC,UAAU,CAAC,WAAW,CAAC;YACjC,SAAS,CAAC,UAAU,CAAC,mBAAmB,CAAC;YACzC,SAAS,CAAC,cAAc,CAAC,aAAa,CAAC;YACvC,SAAS,CAAC,cAAc,CAAC,kBAAkB,CAAC;YAC5C,SAAS,CAAC,cAAc,CAAC,WAAW,CAAC;YACrC,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC;YACnC,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC;YAClC,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC;SACrC;KACF,CAAC;SACD,SAAS,CAAC;QACT,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,uBAAuB,CAAC;QACtD,KAAK,EAAE;YACL,SAAS,CAAC,cAAc,CAAC,eAAe,CAAC;YACzC,SAAS,CAAC,cAAc,CAAC,cAAc,CAAC;YACxC,SAAS,CAAC,cAAc,CAAC,cAAc,CAAC;YACxC,SAAS,CAAC,cAAc,CAAC,aAAa,CAAC;YACvC,SAAS,CAAC,cAAc,CAAC,aAAa,CAAC;SACxC;KACF,CAAC;SACD,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,uBAAuB,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAA;IAErH,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,iBAAiB,EAAE,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAExG,OAAO;EACP,OAAO,CAAC,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4DlB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwMV,CAAA;AACD,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function generateFilterUtils(): string;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFilterUtils = generateFilterUtils;
4
+ function generateFilterUtils() {
5
+ return `import { z } from 'zod'
6
+
7
+ export const stringFilterInputSchema = z.object({
8
+ values: z.array(z.string()).optional(),
9
+ contains: z.string().optional(),
10
+ startsWith: z.string().optional(),
11
+ endsWith: z.string().optional(),
12
+ exclude: z.string().optional(),
13
+ })
14
+
15
+ export const numberFilterInputSchema = z.object({
16
+ values: z.array(z.number()).optional(),
17
+ min: z.number().optional(),
18
+ max: z.number().optional(),
19
+ })
20
+
21
+ export const dateFilterInputSchema = z.object({
22
+ values: z.array(z.string()).optional(),
23
+ start: z.string().optional(),
24
+ end: z.string().optional(),
25
+ })
26
+
27
+ export const booleanFilterInputSchema = z.object({
28
+ values: z.array(z.boolean()).optional(),
29
+ })
30
+
31
+ export const parseBooleanQuery = (query: string): boolean[] | undefined => {
32
+ const normalized = query.trim().toLowerCase()
33
+ if (normalized === 'true') {
34
+ return [true]
35
+ }
36
+ if (normalized === 'false') {
37
+ return [false]
38
+ }
39
+ return undefined
40
+ }
41
+
42
+ export const toFilterActionLabel = (value: string): string =>
43
+ value
44
+ .replaceAll(/([A-Z])([A-Z][a-z])/g, '$1 $2')
45
+ .replaceAll(/([a-z])([A-Z])/g, '$1 $2')
46
+ .replaceAll(/[-_]+/, ' ')
47
+ .replace(/^./, (char) => char.toUpperCase())
48
+ `;
49
+ }
50
+ //# sourceMappingURL=filter-utils.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-utils.generator.js","sourceRoot":"","sources":["../../../src/frontend-actions/generators/filter-utils.generator.ts"],"names":[],"mappings":";;AAAA,kDA6CC;AA7CD,SAAgB,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CR,CAAA;AACD,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function generateSidepanelToggle(): string;