@tanstack/cta-ui-base 0.15.4

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 (172) hide show
  1. package/LICENSE +21 -0
  2. package/components.json +21 -0
  3. package/dist/app.d.ts +1 -0
  4. package/dist/app.js +10 -0
  5. package/dist/components/add-on-info-dialog.d.ts +5 -0
  6. package/dist/components/add-on-info-dialog.js +5 -0
  7. package/dist/components/background-animation.d.ts +1 -0
  8. package/dist/components/background-animation.js +144 -0
  9. package/dist/components/cta-provider.d.ts +3 -0
  10. package/dist/components/cta-provider.js +11 -0
  11. package/dist/components/cta-sidebar.d.ts +1 -0
  12. package/dist/components/cta-sidebar.js +15 -0
  13. package/dist/components/custom-add-on-dialog.d.ts +1 -0
  14. package/dist/components/custom-add-on-dialog.js +38 -0
  15. package/dist/components/file-navigator.d.ts +2 -0
  16. package/dist/components/file-navigator.js +86 -0
  17. package/dist/components/file-tree.d.ts +5 -0
  18. package/dist/components/file-tree.js +14 -0
  19. package/dist/components/file-viewer.d.ts +5 -0
  20. package/dist/components/file-viewer.js +40 -0
  21. package/dist/components/header.d.ts +1 -0
  22. package/dist/components/header.js +5 -0
  23. package/dist/components/icons/tailwind.d.ts +3 -0
  24. package/dist/components/icons/tailwind.js +5 -0
  25. package/dist/components/icons/tanstack.d.ts +3 -0
  26. package/dist/components/icons/tanstack.js +5 -0
  27. package/dist/components/icons/typescript.d.ts +3 -0
  28. package/dist/components/icons/typescript.js +5 -0
  29. package/dist/components/query-provider.d.ts +3 -0
  30. package/dist/components/query-provider.js +7 -0
  31. package/dist/components/sidebar-items/add-ons.d.ts +1 -0
  32. package/dist/components/sidebar-items/add-ons.js +27 -0
  33. package/dist/components/sidebar-items/mode-selector.d.ts +1 -0
  34. package/dist/components/sidebar-items/mode-selector.js +19 -0
  35. package/dist/components/sidebar-items/project-name.d.ts +1 -0
  36. package/dist/components/sidebar-items/project-name.js +12 -0
  37. package/dist/components/sidebar-items/run-add-ons.d.ts +1 -0
  38. package/dist/components/sidebar-items/run-add-ons.js +25 -0
  39. package/dist/components/sidebar-items/run-create-app.d.ts +1 -0
  40. package/dist/components/sidebar-items/run-create-app.js +28 -0
  41. package/dist/components/sidebar-items/sidebar-container.d.ts +3 -0
  42. package/dist/components/sidebar-items/sidebar-container.js +4 -0
  43. package/dist/components/sidebar-items/sidebar-group.d.ts +3 -0
  44. package/dist/components/sidebar-items/sidebar-group.js +4 -0
  45. package/dist/components/sidebar-items/starter.d.ts +1 -0
  46. package/dist/components/sidebar-items/starter.js +42 -0
  47. package/dist/components/sidebar-items/typescript-switch.d.ts +1 -0
  48. package/dist/components/sidebar-items/typescript-switch.js +18 -0
  49. package/dist/components/starters-carousel.d.ts +3 -0
  50. package/dist/components/starters-carousel.js +12 -0
  51. package/dist/components/startup-dialog.d.ts +1 -0
  52. package/dist/components/startup-dialog.js +30 -0
  53. package/dist/components/status-list.d.ts +5 -0
  54. package/dist/components/status-list.js +4 -0
  55. package/dist/components/toaster.d.ts +4 -0
  56. package/dist/components/toaster.js +15 -0
  57. package/dist/components/ui/button.d.ts +10 -0
  58. package/dist/components/ui/button.js +32 -0
  59. package/dist/components/ui/carousel.d.ts +20 -0
  60. package/dist/components/ui/carousel.js +90 -0
  61. package/dist/components/ui/checkbox.d.ts +4 -0
  62. package/dist/components/ui/checkbox.js +9 -0
  63. package/dist/components/ui/dialog.d.ts +15 -0
  64. package/dist/components/ui/dialog.js +36 -0
  65. package/dist/components/ui/dropdown-menu.d.ts +25 -0
  66. package/dist/components/ui/dropdown-menu.js +51 -0
  67. package/dist/components/ui/input.d.ts +3 -0
  68. package/dist/components/ui/input.js +7 -0
  69. package/dist/components/ui/label.d.ts +4 -0
  70. package/dist/components/ui/label.js +8 -0
  71. package/dist/components/ui/popover.d.ts +7 -0
  72. package/dist/components/ui/popover.js +17 -0
  73. package/dist/components/ui/separator.d.ts +4 -0
  74. package/dist/components/ui/separator.js +9 -0
  75. package/dist/components/ui/sheet.d.ts +13 -0
  76. package/dist/components/ui/sheet.js +40 -0
  77. package/dist/components/ui/skeleton.d.ts +2 -0
  78. package/dist/components/ui/skeleton.js +6 -0
  79. package/dist/components/ui/sonner.d.ts +3 -0
  80. package/dist/components/ui/sonner.js +12 -0
  81. package/dist/components/ui/switch.d.ts +4 -0
  82. package/dist/components/ui/switch.js +8 -0
  83. package/dist/components/ui/table.d.ts +10 -0
  84. package/dist/components/ui/table.js +28 -0
  85. package/dist/components/ui/tabs.d.ts +7 -0
  86. package/dist/components/ui/tabs.js +17 -0
  87. package/dist/components/ui/toggle-group.d.ts +7 -0
  88. package/dist/components/ui/toggle-group.js +20 -0
  89. package/dist/components/ui/toggle.d.ts +9 -0
  90. package/dist/components/ui/toggle.js +27 -0
  91. package/dist/components/ui/tooltip.d.ts +7 -0
  92. package/dist/components/ui/tooltip.js +18 -0
  93. package/dist/components/ui/tree-view.d.ts +25 -0
  94. package/dist/components/ui/tree-view.js +151 -0
  95. package/dist/file-classes.d.ts +8 -0
  96. package/dist/file-classes.js +41 -0
  97. package/dist/hooks/use-mounted.d.ts +1 -0
  98. package/dist/hooks/use-mounted.js +8 -0
  99. package/dist/hooks/use-preferred-reduced-motion.d.ts +5 -0
  100. package/dist/hooks/use-preferred-reduced-motion.js +20 -0
  101. package/dist/hooks/use-streaming-status.d.ts +6 -0
  102. package/dist/hooks/use-streaming-status.js +55 -0
  103. package/dist/index.d.ts +20 -0
  104. package/dist/index.js +20 -0
  105. package/dist/lib/api.d.ts +14 -0
  106. package/dist/lib/api.js +74 -0
  107. package/dist/lib/utils.d.ts +2 -0
  108. package/dist/lib/utils.js +5 -0
  109. package/dist/store/add-ons.d.ts +7 -0
  110. package/dist/store/add-ons.js +59 -0
  111. package/dist/store/project.d.ts +76 -0
  112. package/dist/store/project.js +269 -0
  113. package/package.json +50 -0
  114. package/src/app.tsx +28 -0
  115. package/src/components/add-on-info-dialog.tsx +39 -0
  116. package/src/components/background-animation.tsx +224 -0
  117. package/src/components/cta-provider.tsx +22 -0
  118. package/src/components/cta-sidebar.tsx +43 -0
  119. package/src/components/custom-add-on-dialog.tsx +79 -0
  120. package/src/components/file-navigator.tsx +207 -0
  121. package/src/components/file-tree.tsx +35 -0
  122. package/src/components/file-viewer.tsx +67 -0
  123. package/src/components/header.tsx +29 -0
  124. package/src/components/icons/tailwind.tsx +26 -0
  125. package/src/components/icons/tanstack.tsx +338 -0
  126. package/src/components/icons/typescript.tsx +23 -0
  127. package/src/components/query-provider.tsx +10 -0
  128. package/src/components/sidebar-items/add-ons.tsx +94 -0
  129. package/src/components/sidebar-items/mode-selector.tsx +56 -0
  130. package/src/components/sidebar-items/project-name.tsx +32 -0
  131. package/src/components/sidebar-items/run-add-ons.tsx +71 -0
  132. package/src/components/sidebar-items/run-create-app.tsx +82 -0
  133. package/src/components/sidebar-items/sidebar-container.tsx +11 -0
  134. package/src/components/sidebar-items/sidebar-group.tsx +11 -0
  135. package/src/components/sidebar-items/starter.tsx +123 -0
  136. package/src/components/sidebar-items/typescript-switch.tsx +58 -0
  137. package/src/components/starters-carousel.tsx +41 -0
  138. package/src/components/startup-dialog.tsx +72 -0
  139. package/src/components/status-list.tsx +22 -0
  140. package/src/components/toaster.tsx +29 -0
  141. package/src/components/ui/button.tsx +61 -0
  142. package/src/components/ui/carousel.tsx +239 -0
  143. package/src/components/ui/checkbox.tsx +30 -0
  144. package/src/components/ui/dialog.tsx +138 -0
  145. package/src/components/ui/dropdown-menu.tsx +255 -0
  146. package/src/components/ui/input.tsx +21 -0
  147. package/src/components/ui/label.tsx +22 -0
  148. package/src/components/ui/popover.tsx +46 -0
  149. package/src/components/ui/separator.tsx +28 -0
  150. package/src/components/ui/sheet.tsx +137 -0
  151. package/src/components/ui/skeleton.tsx +13 -0
  152. package/src/components/ui/sonner.tsx +24 -0
  153. package/src/components/ui/switch.tsx +29 -0
  154. package/src/components/ui/table.tsx +114 -0
  155. package/src/components/ui/tabs.tsx +64 -0
  156. package/src/components/ui/toggle-group.tsx +72 -0
  157. package/src/components/ui/toggle.tsx +49 -0
  158. package/src/components/ui/tooltip.tsx +61 -0
  159. package/src/components/ui/tree-view.tsx +497 -0
  160. package/src/file-classes.ts +54 -0
  161. package/src/hooks/use-mounted.ts +9 -0
  162. package/src/hooks/use-preferred-reduced-motion.ts +27 -0
  163. package/src/hooks/use-streaming-status.ts +70 -0
  164. package/src/index.ts +44 -0
  165. package/src/lib/api.ts +100 -0
  166. package/src/lib/utils.ts +8 -0
  167. package/src/store/add-ons.ts +81 -0
  168. package/src/store/project.ts +345 -0
  169. package/src/types.d.ts +109 -0
  170. package/tests/store/add-ons.test.ts +222 -0
  171. package/tsconfig.json +25 -0
  172. package/vitest.config.ts +6 -0
@@ -0,0 +1,55 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { InfoIcon, MessageCircleCodeIcon, PackageIcon, SquarePenIcon, TerminalIcon, } from 'lucide-react';
3
+ const iconMap = {
4
+ file: SquarePenIcon,
5
+ command: TerminalIcon,
6
+ 'package-manager': PackageIcon,
7
+ info: InfoIcon,
8
+ other: MessageCircleCodeIcon,
9
+ };
10
+ export default function useStreamingStatus() {
11
+ const [streamItems, setStreamItems] = useState([]);
12
+ const [finished, setFinished] = useState(false);
13
+ const monitorStream = useCallback(async (res) => {
14
+ setFinished(false);
15
+ const reader = res.body?.getReader();
16
+ const decoder = new TextDecoder();
17
+ let rawStream = '';
18
+ while (true) {
19
+ const result = await reader?.read();
20
+ if (result?.done)
21
+ break;
22
+ rawStream += decoder.decode(result?.value);
23
+ let currentId;
24
+ const newStreamItems = [];
25
+ for (const line of rawStream.split('\n')) {
26
+ if (line.startsWith('{') && line.endsWith('}')) {
27
+ const item = JSON.parse(line);
28
+ if (item.msgType === 'start') {
29
+ if (currentId === item.id) {
30
+ newStreamItems[newStreamItems.length - 1].message = item.message;
31
+ }
32
+ else {
33
+ currentId = item.id;
34
+ newStreamItems.push({
35
+ id: currentId,
36
+ icon: iconMap[item.type],
37
+ message: item.message,
38
+ });
39
+ }
40
+ }
41
+ else {
42
+ if (newStreamItems.length > 0) {
43
+ newStreamItems[newStreamItems.length - 1].message = item.message;
44
+ }
45
+ currentId = undefined;
46
+ }
47
+ }
48
+ }
49
+ setStreamItems(newStreamItems);
50
+ }
51
+ setFinished(true);
52
+ return rawStream;
53
+ }, []);
54
+ return { finished, streamItems, monitorStream };
55
+ }
@@ -0,0 +1,20 @@
1
+ import RootComponent from './app';
2
+ import { AppSidebar } from './components/cta-sidebar';
3
+ import { AppHeader } from './components/header';
4
+ import { BackgroundAnimation } from './components/background-animation';
5
+ import { Toaster } from './components/toaster';
6
+ import FileNavigator from './components/file-navigator';
7
+ import StartupDialog from './components/startup-dialog';
8
+ import { QueryProvider } from './components/query-provider';
9
+ import { CTAProvider } from './components/cta-provider';
10
+ import SelectedAddOns from './components/sidebar-items/add-ons';
11
+ import RunAddOns from './components/sidebar-items/run-add-ons';
12
+ import RunCreateApp from './components/sidebar-items/run-create-app';
13
+ import ProjectName from './components/sidebar-items/project-name';
14
+ import ModeSelector from './components/sidebar-items/mode-selector';
15
+ import TypescriptSwitch from './components/sidebar-items/typescript-switch';
16
+ import StarterDialog from './components/sidebar-items/starter';
17
+ import SidebarGroup from './components/sidebar-items/sidebar-group';
18
+ import { useApplicationMode, useManager, useReady } from './store/project';
19
+ export { FileNavigator, AppSidebar, AppHeader, BackgroundAnimation, Toaster, StartupDialog, QueryProvider, CTAProvider, SelectedAddOns, RunAddOns, RunCreateApp, ProjectName, ModeSelector, TypescriptSwitch, StarterDialog, SidebarGroup, useApplicationMode, useManager, useReady, };
20
+ export default RootComponent;
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ import RootComponent from './app';
2
+ import { AppSidebar } from './components/cta-sidebar';
3
+ import { AppHeader } from './components/header';
4
+ import { BackgroundAnimation } from './components/background-animation';
5
+ import { Toaster } from './components/toaster';
6
+ import FileNavigator from './components/file-navigator';
7
+ import StartupDialog from './components/startup-dialog';
8
+ import { QueryProvider } from './components/query-provider';
9
+ import { CTAProvider } from './components/cta-provider';
10
+ import SelectedAddOns from './components/sidebar-items/add-ons';
11
+ import RunAddOns from './components/sidebar-items/run-add-ons';
12
+ import RunCreateApp from './components/sidebar-items/run-create-app';
13
+ import ProjectName from './components/sidebar-items/project-name';
14
+ import ModeSelector from './components/sidebar-items/mode-selector';
15
+ import TypescriptSwitch from './components/sidebar-items/typescript-switch';
16
+ import StarterDialog from './components/sidebar-items/starter';
17
+ import SidebarGroup from './components/sidebar-items/sidebar-group';
18
+ import { useApplicationMode, useManager, useReady } from './store/project';
19
+ export { FileNavigator, AppSidebar, AppHeader, BackgroundAnimation, Toaster, StartupDialog, QueryProvider, CTAProvider, SelectedAddOns, RunAddOns, RunCreateApp, ProjectName, ModeSelector, TypescriptSwitch, StarterDialog, SidebarGroup, useApplicationMode, useManager, useReady, };
20
+ export default RootComponent;
@@ -0,0 +1,14 @@
1
+ import type { SerializedOptions } from '@tanstack/cta-engine';
2
+ import type { AddOnInfo, DryRunOutput, InitialData, StarterInfo } from '../types';
3
+ export declare function createAppStreaming(options: SerializedOptions, chosenAddOns: Array<string>, projectStarter?: StarterInfo): Promise<Response>;
4
+ export declare function addToAppStreaming(chosenAddOns: Array<string>): Promise<Response>;
5
+ export declare function shutdown(): Promise<Response>;
6
+ export declare function loadRemoteAddOn(url: string): Promise<AddOnInfo | {
7
+ error: string;
8
+ }>;
9
+ export declare function loadRemoteStarter(url: string): Promise<StarterInfo | {
10
+ error: string;
11
+ }>;
12
+ export declare function loadInitialData(): Promise<InitialData>;
13
+ export declare function dryRunCreateApp(options: SerializedOptions, chosenAddOns: Array<string>, projectStarter?: StarterInfo): Promise<DryRunOutput>;
14
+ export declare function dryRunAddToApp(addOns: Array<string>): Promise<DryRunOutput>;
@@ -0,0 +1,74 @@
1
+ // @ts-ignore - import.meta.env is not available in the browser
2
+ const baseUrl = import.meta.env.VITE_API_BASE_URL || '';
3
+ export async function createAppStreaming(options, chosenAddOns, projectStarter) {
4
+ return await fetch(`${baseUrl}/api/create-app`, {
5
+ method: 'POST',
6
+ body: JSON.stringify({
7
+ options: {
8
+ ...options,
9
+ chosenAddOns,
10
+ starter: projectStarter?.url || undefined,
11
+ },
12
+ }),
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ },
16
+ });
17
+ }
18
+ export async function addToAppStreaming(chosenAddOns) {
19
+ return await fetch(`${baseUrl}/api/add-to-app`, {
20
+ method: 'POST',
21
+ body: JSON.stringify({
22
+ addOns: chosenAddOns,
23
+ }),
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ });
28
+ }
29
+ export function shutdown() {
30
+ return fetch(`${baseUrl}/api/shutdown`, {
31
+ method: 'POST',
32
+ });
33
+ }
34
+ export async function loadRemoteAddOn(url) {
35
+ const response = await fetch(`${baseUrl}/api/load-remote-add-on?url=${url}`);
36
+ return (await response.json());
37
+ }
38
+ export async function loadRemoteStarter(url) {
39
+ const response = await fetch(`${baseUrl}/api/load-starter?url=${url}`);
40
+ return (await response.json());
41
+ }
42
+ const initialDataRequest = fetch(`${baseUrl}/api/initial-payload`);
43
+ export async function loadInitialData() {
44
+ const payloadReq = await initialDataRequest;
45
+ return (await payloadReq.json());
46
+ }
47
+ export async function dryRunCreateApp(options, chosenAddOns, projectStarter) {
48
+ const outputReq = await fetch(`${baseUrl}/api/dry-run-create-app`, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: JSON.stringify({
54
+ options: {
55
+ ...options,
56
+ chosenAddOns: chosenAddOns,
57
+ starter: projectStarter?.url,
58
+ },
59
+ }),
60
+ });
61
+ return outputReq.json();
62
+ }
63
+ export async function dryRunAddToApp(addOns) {
64
+ const outputReq = await fetch(`${baseUrl}/api/dry-run-add-to-app`, {
65
+ method: 'POST',
66
+ headers: {
67
+ 'Content-Type': 'application/json',
68
+ },
69
+ body: JSON.stringify({
70
+ addOns,
71
+ }),
72
+ });
73
+ return outputReq.json();
74
+ }
@@ -0,0 +1,2 @@
1
+ import type { ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: Array<ClassValue>): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { AddOnInfo } from '../types';
2
+ export declare function getAddOnStatus(availableAddOns: Array<AddOnInfo>, chosenAddOns: Array<string>, originalAddOns: Array<string>): {
3
+ [k: string]: {
4
+ enabled: boolean;
5
+ selected: boolean;
6
+ };
7
+ };
@@ -0,0 +1,59 @@
1
+ export function getAddOnStatus(availableAddOns, chosenAddOns, originalAddOns) {
2
+ const addOnMap = new Map();
3
+ for (const addOn of availableAddOns) {
4
+ addOnMap.set(addOn.id, {
5
+ selected: false,
6
+ enabled: true,
7
+ dependedUpon: false,
8
+ });
9
+ }
10
+ // Guard against cycles in the dependency graph. The results won't be great. But it won't crash.
11
+ function cycleGuardedSelectAndDisableDependsOn(startingAddOnId) {
12
+ const visited = new Set();
13
+ function selectAndDisableDependsOn(addOnId) {
14
+ if (visited.has(addOnId)) {
15
+ return;
16
+ }
17
+ visited.add(addOnId);
18
+ const selectedAddOn = availableAddOns.find((addOn) => addOn.id === addOnId);
19
+ if (selectedAddOn) {
20
+ for (const dependsOnId of selectedAddOn.dependsOn || []) {
21
+ const dependsOnAddOn = addOnMap.get(dependsOnId);
22
+ if (dependsOnAddOn) {
23
+ dependsOnAddOn.selected = true;
24
+ dependsOnAddOn.enabled = false;
25
+ dependsOnAddOn.dependedUpon = true;
26
+ selectAndDisableDependsOn(dependsOnId);
27
+ }
28
+ }
29
+ const addOn = addOnMap.get(addOnId);
30
+ if (addOn) {
31
+ addOn.selected = true;
32
+ if (!addOn.dependedUpon) {
33
+ addOn.enabled = true;
34
+ }
35
+ }
36
+ }
37
+ }
38
+ selectAndDisableDependsOn(startingAddOnId);
39
+ }
40
+ for (const addOn of originalAddOns) {
41
+ const addOnInfo = addOnMap.get(addOn);
42
+ if (addOnInfo) {
43
+ addOnInfo.selected = true;
44
+ addOnInfo.enabled = false;
45
+ addOnInfo.dependedUpon = true;
46
+ }
47
+ cycleGuardedSelectAndDisableDependsOn(addOn);
48
+ }
49
+ for (const addOnId of chosenAddOns) {
50
+ cycleGuardedSelectAndDisableDependsOn(addOnId);
51
+ }
52
+ return Object.fromEntries(Array.from(addOnMap.entries()).map(([v, addOn]) => [
53
+ v,
54
+ {
55
+ enabled: addOn.enabled,
56
+ selected: addOn.selected,
57
+ },
58
+ ]));
59
+ }
@@ -0,0 +1,76 @@
1
+ import type { AddOnInfo, DryRunOutput, StarterInfo } from '../types';
2
+ export declare const useProjectOptions: import("zustand").UseBoundStore<import("zustand").StoreApi<Omit<import("@tanstack/cta-engine").Options, "chosenAddOns" | "starter" | "framework"> & {
3
+ chosenAddOns: Array<string>;
4
+ starter?: string | undefined;
5
+ framework: string;
6
+ } & {
7
+ initialized: boolean;
8
+ }>>;
9
+ export declare const useReady: () => boolean;
10
+ export declare const useRegistry: () => import("../types").Registry | undefined;
11
+ export declare const useProjectLocalFiles: () => Record<string, string> | undefined;
12
+ export declare const useOriginalOutput: () => any;
13
+ export declare const useOriginalOptions: () => any;
14
+ export declare const useOriginalSelectedAddOns: () => any;
15
+ export declare const useApplicationMode: () => import("../types").ApplicationMode | undefined;
16
+ export declare const useAddOnsByMode: () => Record<string, AddOnInfo[]> | undefined;
17
+ export declare const useSupportedModes: () => Record<string, {
18
+ displayName: string;
19
+ description: string;
20
+ forceTypescript: boolean;
21
+ }> | undefined;
22
+ export declare const useProjectStarter: import("zustand").UseBoundStore<import("zustand").StoreApi<{
23
+ projectStarter: StarterInfo | undefined;
24
+ }>>;
25
+ export declare function addCustomAddOn(addOn: AddOnInfo): void;
26
+ export declare function useAddOns(): {
27
+ toggleAddOn: (addOnId: string) => void;
28
+ chosenAddOns: string[];
29
+ availableAddOns: AddOnInfo[];
30
+ userSelectedAddOns: string[];
31
+ originalSelectedAddOns: any;
32
+ addOnState: {
33
+ [k: string]: {
34
+ enabled: boolean;
35
+ selected: boolean;
36
+ };
37
+ };
38
+ };
39
+ export declare const useModeEditable: () => boolean;
40
+ export declare const useTypeScriptEditable: () => boolean;
41
+ export declare const useTailwindEditable: () => boolean;
42
+ export declare const useProjectName: () => string;
43
+ export declare const useRouterMode: () => string;
44
+ export declare function useFilters(): {
45
+ includedFiles: string[];
46
+ toggleFilter: (filter: string) => void;
47
+ };
48
+ export declare function useDryRun(): DryRunOutput;
49
+ type StartupDialogState = {
50
+ open: boolean;
51
+ dontShowAgain: boolean;
52
+ setOpen: (open: boolean) => void;
53
+ setDontShowAgain: (dontShowAgain: boolean) => void;
54
+ };
55
+ export declare const useStartupDialog: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<StartupDialogState>, "persist"> & {
56
+ persist: {
57
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<StartupDialogState, {
58
+ dontShowAgain: boolean;
59
+ }>>) => void;
60
+ clearStorage: () => void;
61
+ rehydrate: () => Promise<void> | void;
62
+ hasHydrated: () => boolean;
63
+ onHydrate: (fn: (state: StartupDialogState) => void) => () => void;
64
+ onFinishHydration: (fn: (state: StartupDialogState) => void) => () => void;
65
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<StartupDialogState, {
66
+ dontShowAgain: boolean;
67
+ }>>;
68
+ };
69
+ }>;
70
+ export declare const setProjectName: (projectName: string) => void;
71
+ export declare const setRouterMode: (mode: string) => void;
72
+ export declare function setTypeScript(typescript: boolean): void;
73
+ export declare function setTailwind(tailwind: boolean): void;
74
+ export declare function setProjectStarter(starter: StarterInfo | undefined): void;
75
+ export declare function useManager(): void;
76
+ export {};
@@ -0,0 +1,269 @@
1
+ import { useCallback, useEffect, useMemo } from 'react';
2
+ import { create } from 'zustand';
3
+ import { persist } from 'zustand/middleware';
4
+ import { useQuery } from '@tanstack/react-query';
5
+ import { dryRunAddToApp, dryRunCreateApp, loadInitialData } from '../lib/api';
6
+ import { getAddOnStatus } from './add-ons';
7
+ export const useProjectOptions = create(() => ({
8
+ initialized: false,
9
+ framework: '',
10
+ mode: '',
11
+ projectName: '',
12
+ targetDir: '',
13
+ typescript: true,
14
+ tailwind: true,
15
+ git: true,
16
+ chosenAddOns: [],
17
+ packageManager: 'pnpm',
18
+ }));
19
+ const useInitialData = () => useQuery({
20
+ queryKey: ['initial-data'],
21
+ queryFn: loadInitialData,
22
+ });
23
+ export const useReady = () => {
24
+ const { data } = useInitialData();
25
+ return data !== undefined;
26
+ };
27
+ const useForcedRouterMode = () => useInitialData().data?.forcedRouterMode;
28
+ const useForcedAddOns = () => useInitialData().data?.forcedAddOns;
29
+ export const useRegistry = () => useInitialData().data?.registry;
30
+ export const useProjectLocalFiles = () => useInitialData().data?.localFiles;
31
+ export const useOriginalOutput = () => useInitialData().data?.output;
32
+ export const useOriginalOptions = () => useInitialData().data?.options;
33
+ export const useOriginalSelectedAddOns = () => useOriginalOptions()?.chosenAddOns;
34
+ export const useApplicationMode = () => useInitialData().data?.applicationMode;
35
+ export const useAddOnsByMode = () => useInitialData().data?.addOns;
36
+ export const useSupportedModes = () => useInitialData().data?.supportedModes;
37
+ const useApplicationSettings = create(() => ({
38
+ includeFiles: ['unchanged', 'added', 'modified', 'deleted', 'overwritten'],
39
+ }));
40
+ const useMutableAddOns = create(() => ({
41
+ userSelectedAddOns: [],
42
+ customAddOns: [],
43
+ }));
44
+ export const useProjectStarter = create(() => ({
45
+ projectStarter: undefined,
46
+ }));
47
+ export function addCustomAddOn(addOn) {
48
+ useMutableAddOns.setState((state) => ({
49
+ customAddOns: [...state.customAddOns, addOn],
50
+ }));
51
+ if (addOn.modes.includes(useProjectOptions.getState().mode)) {
52
+ useMutableAddOns.setState((state) => ({
53
+ userSelectedAddOns: [...state.userSelectedAddOns, addOn.id],
54
+ }));
55
+ }
56
+ }
57
+ export function useAddOns() {
58
+ const ready = useReady();
59
+ const routerMode = useRouterMode();
60
+ const originalSelectedAddOns = useOriginalSelectedAddOns();
61
+ const addOnsByMode = useAddOnsByMode();
62
+ const forcedAddOns = useForcedAddOns();
63
+ const { userSelectedAddOns, customAddOns } = useMutableAddOns();
64
+ const projectStarter = useProjectStarter().projectStarter;
65
+ const availableAddOns = useMemo(() => {
66
+ if (!ready)
67
+ return [];
68
+ const baseAddOns = addOnsByMode?.[routerMode] || [];
69
+ return [
70
+ ...baseAddOns,
71
+ ...customAddOns.filter((addOn) => addOn.modes.includes(routerMode)),
72
+ ];
73
+ }, [ready, routerMode, addOnsByMode, customAddOns]);
74
+ const addOnState = useMemo(() => {
75
+ if (!ready)
76
+ return {};
77
+ const originalAddOns = new Set();
78
+ for (const addOn of projectStarter?.dependsOn || []) {
79
+ originalAddOns.add(addOn);
80
+ }
81
+ for (const addOn of originalSelectedAddOns) {
82
+ originalAddOns.add(addOn);
83
+ }
84
+ for (const addOn of forcedAddOns || []) {
85
+ originalAddOns.add(addOn);
86
+ }
87
+ return getAddOnStatus(availableAddOns, userSelectedAddOns, Array.from(originalAddOns));
88
+ }, [
89
+ ready,
90
+ availableAddOns,
91
+ userSelectedAddOns,
92
+ originalSelectedAddOns,
93
+ projectStarter?.dependsOn,
94
+ forcedAddOns,
95
+ ]);
96
+ const chosenAddOns = useMemo(() => {
97
+ if (!ready)
98
+ return [];
99
+ const addOns = new Set(Object.keys(addOnState).filter((addOn) => addOnState[addOn].selected));
100
+ for (const addOn of forcedAddOns || []) {
101
+ addOns.add(addOn);
102
+ }
103
+ return Array.from(addOns);
104
+ }, [ready, addOnState, forcedAddOns]);
105
+ const toggleAddOn = useCallback((addOnId) => {
106
+ if (!ready)
107
+ return;
108
+ if (addOnState[addOnId].enabled) {
109
+ if (addOnState[addOnId].selected) {
110
+ useMutableAddOns.setState((state) => ({
111
+ userSelectedAddOns: state.userSelectedAddOns.filter((addOn) => addOn !== addOnId),
112
+ }));
113
+ }
114
+ else {
115
+ useMutableAddOns.setState((state) => ({
116
+ userSelectedAddOns: [...state.userSelectedAddOns, addOnId],
117
+ }));
118
+ }
119
+ }
120
+ }, [ready, addOnState]);
121
+ return {
122
+ toggleAddOn,
123
+ chosenAddOns,
124
+ availableAddOns,
125
+ userSelectedAddOns,
126
+ originalSelectedAddOns,
127
+ addOnState,
128
+ };
129
+ }
130
+ const useHasProjectStarter = () => useProjectStarter((state) => state.projectStarter === undefined);
131
+ export const useModeEditable = () => {
132
+ const ready = useReady();
133
+ const forcedRouterMode = useForcedRouterMode();
134
+ const hasProjectStarter = useHasProjectStarter();
135
+ return ready ? !forcedRouterMode && hasProjectStarter : false;
136
+ };
137
+ export const useTypeScriptEditable = () => {
138
+ const ready = useReady();
139
+ const hasProjectStarter = useHasProjectStarter();
140
+ const routerMode = useRouterMode();
141
+ return ready ? hasProjectStarter && routerMode === 'code-router' : false;
142
+ };
143
+ export const useTailwindEditable = () => {
144
+ const ready = useReady();
145
+ const hasProjectStarter = useHasProjectStarter();
146
+ const routerMode = useRouterMode();
147
+ return ready ? hasProjectStarter && routerMode === 'code-router' : false;
148
+ };
149
+ export const useProjectName = () => useProjectOptions((state) => state.projectName);
150
+ export const useRouterMode = () => {
151
+ const ready = useReady();
152
+ const forcedRouterMode = useForcedRouterMode();
153
+ const userMode = useProjectOptions((state) => state.mode);
154
+ return ready ? forcedRouterMode || userMode : 'file-router';
155
+ };
156
+ export function useFilters() {
157
+ const ready = useReady();
158
+ const includedFiles = useApplicationSettings((state) => state.includeFiles);
159
+ const toggleFilter = useCallback((filter) => {
160
+ if (!ready)
161
+ return;
162
+ useApplicationSettings.setState((state) => ({
163
+ includeFiles: state.includeFiles.includes(filter)
164
+ ? state.includeFiles.filter((f) => f !== filter)
165
+ : [...state.includeFiles, filter],
166
+ }));
167
+ }, [ready]);
168
+ return {
169
+ includedFiles,
170
+ toggleFilter,
171
+ };
172
+ }
173
+ export function useDryRun() {
174
+ const ready = useReady();
175
+ const applicationMode = useApplicationMode();
176
+ const { initialized, ...projectOptions } = useProjectOptions();
177
+ const { userSelectedAddOns, chosenAddOns } = useAddOns();
178
+ const projectStarter = useProjectStarter().projectStarter;
179
+ const { data: dryRunOutput } = useQuery({
180
+ queryKey: [
181
+ 'dry-run',
182
+ applicationMode,
183
+ JSON.stringify(projectOptions),
184
+ JSON.stringify(userSelectedAddOns),
185
+ projectStarter?.url,
186
+ ],
187
+ queryFn: async () => {
188
+ if (applicationMode === 'none' || !ready || !initialized) {
189
+ return {
190
+ files: {},
191
+ commands: [],
192
+ deletedFiles: [],
193
+ };
194
+ }
195
+ else if (applicationMode === 'setup') {
196
+ return dryRunCreateApp(projectOptions, chosenAddOns, projectStarter);
197
+ }
198
+ else {
199
+ return dryRunAddToApp(userSelectedAddOns);
200
+ }
201
+ },
202
+ enabled: ready,
203
+ initialData: {
204
+ files: {},
205
+ commands: [],
206
+ deletedFiles: [],
207
+ },
208
+ });
209
+ return dryRunOutput;
210
+ }
211
+ export const useStartupDialog = create()(persist((set) => ({
212
+ open: false,
213
+ dontShowAgain: false,
214
+ setOpen: (open) => set({ open }),
215
+ setDontShowAgain: (dontShowAgain) => set({ dontShowAgain }),
216
+ }), {
217
+ name: 'startup-dialog',
218
+ partialize: (state) => ({
219
+ dontShowAgain: state.dontShowAgain,
220
+ }),
221
+ merge: (persistedState, currentState) => {
222
+ if (persistedState &&
223
+ persistedState.dontShowAgain) {
224
+ currentState.open = false;
225
+ }
226
+ else {
227
+ currentState.open = true;
228
+ }
229
+ return currentState;
230
+ },
231
+ }));
232
+ export const setProjectName = (projectName) => useProjectOptions.setState({
233
+ projectName,
234
+ });
235
+ export const setRouterMode = (mode) => useProjectOptions.setState({
236
+ mode,
237
+ });
238
+ export function setTypeScript(typescript) {
239
+ useProjectOptions.setState({
240
+ typescript,
241
+ });
242
+ }
243
+ export function setTailwind(tailwind) {
244
+ useProjectOptions.setState({
245
+ tailwind,
246
+ });
247
+ }
248
+ export function setProjectStarter(starter) {
249
+ useProjectStarter.setState(() => ({
250
+ projectStarter: starter,
251
+ }));
252
+ if (starter) {
253
+ useProjectOptions.setState({
254
+ mode: starter.mode,
255
+ });
256
+ }
257
+ }
258
+ export function useManager() {
259
+ const ready = useReady();
260
+ const originalOptions = useOriginalOptions();
261
+ useEffect(() => {
262
+ if (ready) {
263
+ useProjectOptions.setState({
264
+ ...originalOptions,
265
+ initialized: true,
266
+ });
267
+ }
268
+ }, [ready]);
269
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@tanstack/cta-ui-base",
3
+ "type": "module",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "dependencies": {
7
+ "@codemirror/lang-css": "^6.3.1",
8
+ "@codemirror/lang-html": "^6.4.9",
9
+ "@codemirror/lang-javascript": "^6.2.3",
10
+ "@codemirror/lang-json": "^6.0.1",
11
+ "@radix-ui/react-accordion": "^1.2.3",
12
+ "@radix-ui/react-checkbox": "^1.1.4",
13
+ "@radix-ui/react-dialog": "^1.1.10",
14
+ "@radix-ui/react-dropdown-menu": "^2.1.11",
15
+ "@radix-ui/react-label": "^2.1.4",
16
+ "@radix-ui/react-popover": "^1.1.10",
17
+ "@radix-ui/react-separator": "^1.1.4",
18
+ "@radix-ui/react-slot": "^1.2.0",
19
+ "@radix-ui/react-switch": "^1.2.2",
20
+ "@radix-ui/react-tabs": "^1.1.3",
21
+ "@radix-ui/react-toggle": "^1.1.2",
22
+ "@radix-ui/react-toggle-group": "^1.1.2",
23
+ "@radix-ui/react-tooltip": "^1.2.3",
24
+ "@tanstack/react-query": "^5.66.5",
25
+ "@uiw/codemirror-theme-github": "^4.23.10",
26
+ "@uiw/react-codemirror": "^4.23.10",
27
+ "chalk": "^5.4.1",
28
+ "class-variance-authority": "^0.7.1",
29
+ "clsx": "^2.1.1",
30
+ "embla-carousel-react": "^8.6.0",
31
+ "lucide-react": "^0.476.0",
32
+ "next-themes": "^0.4.6",
33
+ "react": "^19.0.0",
34
+ "react-codemirror-merge": "^4.23.10",
35
+ "react-dom": "^19.0.0",
36
+ "sonner": "^2.0.3",
37
+ "tailwind-merge": "^3.0.2",
38
+ "zustand": "^5.0.3",
39
+ "@tanstack/cta-engine": "0.15.4"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^19.0.8",
43
+ "@types/react-dom": "^19.0.3",
44
+ "typescript": "^5.7.2",
45
+ "vite-tsconfig-paths": "^5.1.4",
46
+ "vitest": "^3.1.4"
47
+ },
48
+ "version": "0.15.4",
49
+ "scripts": {}
50
+ }