@mars-stack/cli 0.2.2 → 2.0.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.
package/dist/index.js CHANGED
@@ -686,6 +686,21 @@ var init_blog = __esm({
686
686
  }
687
687
  });
688
688
 
689
+ // src/utils/client-file-patch.ts
690
+ function insertImportAfterDirectives(content, lineToInsert) {
691
+ if (CLIENT_DIRECTIVE.test(content)) {
692
+ return content.replace(/^(['"]use client['"];?\s*\n)/, `$1${lineToInsert}`);
693
+ }
694
+ return lineToInsert + content;
695
+ }
696
+ var CLIENT_DIRECTIVE;
697
+ var init_client_file_patch = __esm({
698
+ "src/utils/client-file-patch.ts"() {
699
+ "use strict";
700
+ CLIENT_DIRECTIVE = /^['"]use client['"];?\s*\n/;
701
+ }
702
+ });
703
+
689
704
  // src/generators/features/dark-mode.ts
690
705
  var dark_mode_exports = {};
691
706
  __export(dark_mode_exports, {
@@ -716,9 +731,13 @@ async function generateDarkMode(projectRoot) {
716
731
  await fs6.writeFile(fullPath, content);
717
732
  count++;
718
733
  }
734
+ await wireLayout(projectRoot, ctx);
735
+ await wireProviders(projectRoot, ctx);
736
+ await wireProtectedNav(projectRoot, ctx);
737
+ await wireTailwindDarkMode(projectRoot, ctx);
719
738
  await setConfigFlag2(projectRoot, ctx);
720
739
  await ctx.commit();
721
- log.success(`Generated dark mode feature with ${count} files`);
740
+ log.success(`Generated and wired dark mode feature (${count} files created)`);
722
741
  log.blank();
723
742
  log.step("src/lib/shared/components/providers/ThemeProvider.tsx \u2014 context, hook, persistence");
724
743
  log.step("src/lib/shared/components/patterns/ThemeToggle.tsx \u2014 three-option toggle");
@@ -726,14 +745,12 @@ async function generateDarkMode(projectRoot) {
726
745
  log.step("src/features/dark-mode/theme-script.ts \u2014 FOUC prevention script");
727
746
  log.step("src/features/dark-mode/index.ts \u2014 barrel exports");
728
747
  log.blank();
729
- log.warn("Complete setup in your root layout (src/app/layout.tsx):");
730
- log.step("1. Add suppressHydrationWarning to <html>");
731
- log.step("2. Add the theme script to <head>:");
732
- log.step(' import { getThemeScript } from "@/features/dark-mode/theme-script"');
733
- log.step(" <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />");
734
- log.step("3. Wrap the app with <ThemeProvider>");
735
- log.step("4. Add <ThemeToggle /> or <ThemeToggleSimple /> to your navbar");
736
- log.step('5. Ensure darkMode: "class" is set in your Tailwind config');
748
+ log.step("Wired automatically:");
749
+ log.step(" \u2713 FOUC-prevention script added to <head> in layout.tsx");
750
+ log.step(" \u2713 suppressHydrationWarning on <html>");
751
+ log.step(" \u2713 ThemeProvider wrapping app in providers.tsx");
752
+ log.step(" \u2713 ThemeToggleSimple added to navigation bar");
753
+ log.step(" \u2713 Tailwind dark mode class variant configured in globals.css");
737
754
  log.blank();
738
755
  } catch (error) {
739
756
  await ctx.rollback();
@@ -748,6 +765,84 @@ async function setConfigFlag2(projectRoot, ctx) {
748
765
  const updated = content.replace(/darkMode:\s*false/, "darkMode: true");
749
766
  await fs6.writeFile(configPath, updated);
750
767
  }
768
+ async function wireLayout(projectRoot, ctx) {
769
+ const layoutPath = path6.join(projectRoot, "src", "app", "layout.tsx");
770
+ if (!await fs6.pathExists(layoutPath)) return;
771
+ await ctx.trackModifiedFile(layoutPath);
772
+ let content = await fs6.readFile(layoutPath, "utf-8");
773
+ if (!content.includes("getThemeScript")) {
774
+ content = `import { getThemeScript } from '@/features/dark-mode';
775
+ ${content}`;
776
+ }
777
+ if (!content.includes("suppressHydrationWarning")) {
778
+ content = content.replace(/<html\s+lang="en"/, '<html lang="en" suppressHydrationWarning');
779
+ }
780
+ if (!content.includes("getThemeScript()")) {
781
+ content = content.replace(
782
+ /(<html[^>]*>)\s*\n(\s*<body)/,
783
+ "$1\n <head>\n <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />\n </head>\n$2"
784
+ );
785
+ }
786
+ await fs6.writeFile(layoutPath, content);
787
+ }
788
+ async function wireProviders(projectRoot, ctx) {
789
+ const providersPath = path6.join(projectRoot, "src", "app", "providers.tsx");
790
+ if (!await fs6.pathExists(providersPath)) return;
791
+ await ctx.trackModifiedFile(providersPath);
792
+ let content = await fs6.readFile(providersPath, "utf-8");
793
+ if (content.includes("ThemeProvider")) return;
794
+ content = insertImportAfterDirectives(
795
+ content,
796
+ `import { ThemeProvider } from '@/features/dark-mode';
797
+ `
798
+ );
799
+ content = content.replace(
800
+ /return\s+(?:\(\s*)?((?:<\w+[^>]*>)[\s\S]*?\{children\}[\s\S]*?(?:<\/\w+>))\s*(?:\)\s*)?;/,
801
+ (_match, jsx) => {
802
+ const wrapped = jsx.replace(
803
+ /(\{children\})/,
804
+ "<ThemeProvider>$1</ThemeProvider>"
805
+ );
806
+ return `return (
807
+ ${wrapped}
808
+ );`;
809
+ }
810
+ );
811
+ await fs6.writeFile(providersPath, content);
812
+ }
813
+ async function wireProtectedNav(projectRoot, ctx) {
814
+ const navPath = path6.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
815
+ if (!await fs6.pathExists(navPath)) return;
816
+ await ctx.trackModifiedFile(navPath);
817
+ let content = await fs6.readFile(navPath, "utf-8");
818
+ if (content.includes("ThemeToggleSimple")) return;
819
+ content = insertImportAfterDirectives(
820
+ content,
821
+ `import { ThemeToggleSimple } from '@/features/dark-mode';
822
+ `
823
+ );
824
+ content = content.replace(
825
+ /(<div className="hidden md:block">)\s*\n(\s*)(<UserMenu \/>)/,
826
+ "$1\n$2<ThemeToggleSimple />\n$2$3"
827
+ );
828
+ content = content.replace(
829
+ /(<div className="border-t border-border-default px-4 py-3">\s*\n\s*<UserMenu \/>)/,
830
+ '$1\n <div className="mt-2">\n <ThemeToggleSimple />\n </div>'
831
+ );
832
+ await fs6.writeFile(navPath, content);
833
+ }
834
+ async function wireTailwindDarkMode(projectRoot, ctx) {
835
+ const globalsPath = path6.join(projectRoot, "src", "styles", "globals.css");
836
+ if (!await fs6.pathExists(globalsPath)) return;
837
+ await ctx.trackModifiedFile(globalsPath);
838
+ let content = await fs6.readFile(globalsPath, "utf-8");
839
+ if (content.includes("@custom-variant dark")) return;
840
+ content = content.replace(
841
+ /@import 'tailwindcss';/,
842
+ "@import 'tailwindcss';\n@custom-variant dark (&:where(.dark, .dark *));"
843
+ );
844
+ await fs6.writeFile(globalsPath, content);
845
+ }
751
846
  function themeProvider() {
752
847
  return `${STAMP2}
753
848
  'use client';
@@ -1017,9 +1112,10 @@ var GENERATOR_VERSION2, STAMP2;
1017
1112
  var init_dark_mode = __esm({
1018
1113
  "src/generators/features/dark-mode.ts"() {
1019
1114
  "use strict";
1115
+ init_client_file_patch();
1020
1116
  init_logger();
1021
1117
  init_rollback();
1022
- GENERATOR_VERSION2 = "0.1.0";
1118
+ GENERATOR_VERSION2 = "0.2.0";
1023
1119
  STAMP2 = `// @mars-generated dark-mode@${GENERATOR_VERSION2}`;
1024
1120
  }
1025
1121
  });
@@ -1069,6 +1165,7 @@ async function generateNotifications(projectRoot) {
1069
1165
  "src/features/notifications/components/NotificationBell.tsx": notificationBell(),
1070
1166
  "src/features/notifications/components/NotificationPanel.tsx": notificationPanel(),
1071
1167
  "src/features/notifications/components/index.ts": componentIndex2(),
1168
+ "src/features/notifications/index.ts": featureIndex(),
1072
1169
  "src/app/api/protected/notifications/route.ts": listRoute(),
1073
1170
  "src/app/api/protected/notifications/[id]/read/route.ts": markReadRoute(),
1074
1171
  "src/app/api/protected/notifications/read-all/route.ts": markAllReadRoute(),
@@ -1083,18 +1180,21 @@ async function generateNotifications(projectRoot) {
1083
1180
  count++;
1084
1181
  }
1085
1182
  await addUserRelation(projectRoot, "notifications Notification[]", ctx);
1183
+ await wireProtectedNav2(projectRoot, ctx);
1086
1184
  await setConfigFlag3(projectRoot, ctx);
1087
1185
  await ctx.commit();
1088
- log.success(`Generated notifications feature with ${count} files`);
1186
+ log.success(`Generated and wired notifications feature (${count} files created)`);
1089
1187
  log.blank();
1090
1188
  log.step("prisma/schema/notification.prisma \u2014 Notification model");
1091
1189
  log.step("src/features/notifications/ \u2014 types, validation, server logic, hooks, components");
1092
1190
  log.step("src/app/api/protected/notifications/ \u2014 CRUD API routes");
1093
1191
  log.blank();
1192
+ log.step("Wired automatically:");
1193
+ log.step(" \u2713 NotificationBell added to navigation bar");
1194
+ log.blank();
1094
1195
  log.warn("Next steps:");
1095
1196
  log.step("1. Run yarn db:push to sync the Prisma schema");
1096
- log.step("2. Add <NotificationBell /> to your navbar/header");
1097
- log.step("3. Import createNotification from server to trigger notifications from other features:");
1197
+ log.step("2. Import createNotification from server to trigger notifications from other features:");
1098
1198
  log.step(' import { createNotification } from "@/features/notifications/server"');
1099
1199
  log.blank();
1100
1200
  } catch (error) {
@@ -1102,6 +1202,23 @@ async function generateNotifications(projectRoot) {
1102
1202
  throw error;
1103
1203
  }
1104
1204
  }
1205
+ async function wireProtectedNav2(projectRoot, ctx) {
1206
+ const navPath = path8.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
1207
+ if (!await fs8.pathExists(navPath)) return;
1208
+ await ctx.trackModifiedFile(navPath);
1209
+ let content = await fs8.readFile(navPath, "utf-8");
1210
+ if (content.includes("NotificationBell")) return;
1211
+ content = insertImportAfterDirectives(
1212
+ content,
1213
+ `import { NotificationBell } from '@/features/notifications';
1214
+ `
1215
+ );
1216
+ content = content.replace(
1217
+ /(<div className="flex items-center gap-3">)\s*\n(\s*)(<div className="hidden md:block">)/,
1218
+ "$1\n$2<NotificationBell />\n$2$3"
1219
+ );
1220
+ await fs8.writeFile(navPath, content);
1221
+ }
1105
1222
  async function setConfigFlag3(projectRoot, ctx) {
1106
1223
  const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
1107
1224
  if (!await fs8.pathExists(configPath)) return;
@@ -1585,6 +1702,11 @@ export function NotificationPanel({
1585
1702
  }
1586
1703
  `;
1587
1704
  }
1705
+ function featureIndex() {
1706
+ return `${STAMP3}
1707
+ export { NotificationBell, NotificationPanel } from './components';
1708
+ `;
1709
+ }
1588
1710
  function componentIndex2() {
1589
1711
  return `${STAMP3}
1590
1712
  export { NotificationBell } from './NotificationBell';
@@ -1685,6 +1807,7 @@ var GENERATOR_VERSION3, STAMP3;
1685
1807
  var init_notifications = __esm({
1686
1808
  "src/generators/features/notifications.ts"() {
1687
1809
  "use strict";
1810
+ init_client_file_patch();
1688
1811
  init_logger();
1689
1812
  init_rollback();
1690
1813
  init_prisma();
@@ -1723,15 +1846,19 @@ async function generateAnalytics(projectRoot) {
1723
1846
  await fs9.writeFile(fullPath, content);
1724
1847
  count++;
1725
1848
  }
1849
+ await wireProviders2(projectRoot, ctx);
1726
1850
  await setConfigFlag4(projectRoot, ctx);
1727
1851
  await ctx.commit();
1728
- log.success(`Generated analytics feature with ${count} files`);
1852
+ log.success(`Generated and wired analytics feature (${count} files created)`);
1729
1853
  log.blank();
1730
1854
  log.step("src/features/analytics/ \u2014 types and barrel exports");
1731
1855
  log.step("src/lib/shared/components/providers/AnalyticsProvider.tsx \u2014 provider wrapper");
1732
1856
  log.step("src/lib/shared/utils/analytics.ts \u2014 unified tracking API");
1733
1857
  log.step("src/lib/shared/components/patterns/ConsentBanner.tsx \u2014 cookie consent banner");
1734
1858
  log.blank();
1859
+ log.step("Wired automatically:");
1860
+ log.step(" \u2713 AnalyticsProvider wrapping app in providers.tsx");
1861
+ log.blank();
1735
1862
  log.warn("Install dependencies for your chosen provider:");
1736
1863
  log.step("Vercel: yarn add @vercel/analytics @vercel/speed-insights");
1737
1864
  log.step("PostHog: yarn add posthog-js");
@@ -1740,13 +1867,39 @@ async function generateAnalytics(projectRoot) {
1740
1867
  log.warn("Next steps:");
1741
1868
  log.step("Set the provider in appConfig.services.analytics.provider");
1742
1869
  log.step("Set required environment variables for the chosen provider");
1743
- log.step("Add <AnalyticsProvider> and <ConsentBanner /> to your root layout");
1744
1870
  log.blank();
1745
1871
  } catch (error) {
1746
1872
  await ctx.rollback();
1747
1873
  throw error;
1748
1874
  }
1749
1875
  }
1876
+ async function wireProviders2(projectRoot, ctx) {
1877
+ const providersPath = path9.join(projectRoot, "src", "app", "providers.tsx");
1878
+ if (!await fs9.pathExists(providersPath)) return;
1879
+ await ctx.trackModifiedFile(providersPath);
1880
+ let content = await fs9.readFile(providersPath, "utf-8");
1881
+ if (content.includes("AnalyticsProvider")) return;
1882
+ content = insertImportAfterDirectives(
1883
+ content,
1884
+ `import { AnalyticsProvider } from '@/lib/shared/components/providers/AnalyticsProvider';
1885
+ `
1886
+ );
1887
+ content = content.replace(
1888
+ /return\s+(?:\(\s*)?((?:<\w+[^>]*>)[\s\S]*?\{children\}[\s\S]*?(?:<\/\w+>))\s*(?:\)\s*)?;/,
1889
+ (_match, jsx) => {
1890
+ return `return (
1891
+ <AnalyticsProvider>${jsx}</AnalyticsProvider>
1892
+ );`;
1893
+ }
1894
+ );
1895
+ await fs9.writeFile(providersPath, content);
1896
+ const written = await fs9.readFile(providersPath, "utf-8");
1897
+ if (!written.includes("AnalyticsProvider")) {
1898
+ throw new Error(
1899
+ "wireProviders: AnalyticsProvider was not inserted into providers.tsx \u2014 the return statement pattern did not match. Review the template file structure."
1900
+ );
1901
+ }
1902
+ }
1750
1903
  async function setConfigFlag4(projectRoot, ctx) {
1751
1904
  const configPath = path9.join(projectRoot, "src", "config", "app.config.ts");
1752
1905
  if (!await fs9.pathExists(configPath)) return;
@@ -1776,7 +1929,7 @@ function analyticsProvider() {
1776
1929
  return `${STAMP4}
1777
1930
  'use client';
1778
1931
 
1779
- import { useEffect, useState } from 'react';
1932
+ import { Suspense, useEffect, useState } from 'react';
1780
1933
  import { usePathname, useSearchParams } from 'next/navigation';
1781
1934
  import Script from 'next/script';
1782
1935
  import { appConfig } from '@/config/app.config';
@@ -1799,7 +1952,9 @@ export function AnalyticsProvider({ children }: AnalyticsProviderProps) {
1799
1952
  {provider === 'vercel' && <VercelAnalyticsProvider />}
1800
1953
  {provider === 'posthog' && <PostHogAnalyticsProvider />}
1801
1954
  {provider === 'google' && <GoogleAnalyticsProvider />}
1802
- <PageViewTracker />
1955
+ <Suspense>
1956
+ <PageViewTracker />
1957
+ </Suspense>
1803
1958
  {children}
1804
1959
  </>
1805
1960
  );
@@ -1963,22 +2118,18 @@ export function trackEvent(
1963
2118
 
1964
2119
  switch (provider) {
1965
2120
  case 'vercel': {
1966
- try {
1967
- const { track } = require('@vercel/analytics');
1968
- track(eventName, properties);
1969
- } catch {
1970
- // @vercel/analytics not available
1971
- }
2121
+ const vercelSpec = '@vercel/analytics';
2122
+ import(/* webpackIgnore: true */ vercelSpec)
2123
+ .then((mod) => mod.track(eventName, properties))
2124
+ .catch(() => {});
1972
2125
  break;
1973
2126
  }
1974
2127
 
1975
2128
  case 'posthog': {
1976
- try {
1977
- const posthog = require('posthog-js').default;
1978
- posthog.capture(eventName, properties);
1979
- } catch {
1980
- // posthog-js not available
1981
- }
2129
+ const phSpec = 'posthog-js';
2130
+ import(/* webpackIgnore: true */ phSpec)
2131
+ .then((mod) => mod.default.capture(eventName, properties))
2132
+ .catch(() => {});
1982
2133
  break;
1983
2134
  }
1984
2135
 
@@ -2009,12 +2160,10 @@ export function identifyUser(
2009
2160
  break;
2010
2161
 
2011
2162
  case 'posthog': {
2012
- try {
2013
- const posthog = require('posthog-js').default;
2014
- posthog.identify(userId, traits);
2015
- } catch {
2016
- // posthog-js not available
2017
- }
2163
+ const phSpec = 'posthog-js';
2164
+ import(/* webpackIgnore: true */ phSpec)
2165
+ .then((mod) => mod.default.identify(userId, traits))
2166
+ .catch(() => {});
2018
2167
  break;
2019
2168
  }
2020
2169
 
@@ -2103,6 +2252,7 @@ var GENERATOR_VERSION4, STAMP4;
2103
2252
  var init_analytics = __esm({
2104
2253
  "src/generators/features/analytics.ts"() {
2105
2254
  "use strict";
2255
+ init_client_file_patch();
2106
2256
  init_logger();
2107
2257
  init_rollback();
2108
2258
  GENERATOR_VERSION4 = "0.1.0";
@@ -2133,7 +2283,7 @@ async function generateCommandPalette(projectRoot) {
2133
2283
  "src/features/command-palette/components/CommandPalette.tsx": commandPalette(),
2134
2284
  "src/features/command-palette/components/CommandTrigger.tsx": commandTrigger(),
2135
2285
  "src/features/command-palette/components/index.ts": componentIndex3(),
2136
- "src/features/command-palette/index.ts": featureIndex()
2286
+ "src/features/command-palette/index.ts": featureIndex2()
2137
2287
  };
2138
2288
  let count = 0;
2139
2289
  for (const [filePath, content] of Object.entries(files)) {
@@ -2144,15 +2294,17 @@ async function generateCommandPalette(projectRoot) {
2144
2294
  count++;
2145
2295
  }
2146
2296
  await addDependencies(projectRoot, { cmdk: "^1.0.0" });
2297
+ await wireLayout2(projectRoot, ctx);
2147
2298
  await setConfigFlag5(projectRoot, ctx);
2148
2299
  await ctx.commit();
2149
- log.success(`Generated command palette feature with ${count} files`);
2300
+ log.success(`Generated and wired command palette feature (${count} files created)`);
2150
2301
  log.blank();
2151
2302
  log.step("src/features/command-palette/ \u2014 types, actions, registry, recent, components");
2152
2303
  log.blank();
2153
- log.warn("Integration steps:");
2154
- log.step("Add <CommandPalette /> to your protected layout");
2155
- log.step("Optionally add <CommandTrigger /> to your navbar for discoverability");
2304
+ log.step("Wired automatically:");
2305
+ log.step(" \u2713 CommandPalette overlay mounted in root layout");
2306
+ log.step(" \u2713 Press \u2318K / Ctrl+K to open");
2307
+ log.blank();
2156
2308
  log.step("Register custom actions with registerAction() from any feature");
2157
2309
  log.blank();
2158
2310
  } catch (error) {
@@ -2160,6 +2312,20 @@ async function generateCommandPalette(projectRoot) {
2160
2312
  throw error;
2161
2313
  }
2162
2314
  }
2315
+ async function wireLayout2(projectRoot, ctx) {
2316
+ const layoutPath = path10.join(projectRoot, "src", "app", "layout.tsx");
2317
+ if (!await fs10.pathExists(layoutPath)) return;
2318
+ await ctx.trackModifiedFile(layoutPath);
2319
+ let content = await fs10.readFile(layoutPath, "utf-8");
2320
+ if (content.includes("CommandPalette")) return;
2321
+ content = `import { CommandPalette } from '@/features/command-palette';
2322
+ ${content}`;
2323
+ content = content.replace(
2324
+ /(\s*)(<Providers>\{children\}<\/Providers>)/,
2325
+ "$1$2\n$1<CommandPalette />"
2326
+ );
2327
+ await fs10.writeFile(layoutPath, content);
2328
+ }
2163
2329
  async function setConfigFlag5(projectRoot, ctx) {
2164
2330
  const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
2165
2331
  if (!await fs10.pathExists(configPath)) return;
@@ -2556,7 +2722,7 @@ export { CommandPalette } from './CommandPalette';
2556
2722
  export { CommandTrigger } from './CommandTrigger';
2557
2723
  `;
2558
2724
  }
2559
- function featureIndex() {
2725
+ function featureIndex2() {
2560
2726
  return `${STAMP5}
2561
2727
  export { CommandPalette, CommandTrigger } from './components';
2562
2728
  export { registerAction, getCustomActions } from './registry';
@@ -2604,7 +2770,7 @@ async function generateOnboarding(projectRoot) {
2604
2770
  "src/app/(protected)/onboarding/page.tsx": onboardingPage(),
2605
2771
  "src/app/api/protected/onboarding/route.ts": getProgressRoute(),
2606
2772
  "src/app/api/protected/onboarding/step/route.ts": stepActionRoute(),
2607
- "src/features/onboarding/index.ts": featureIndex2()
2773
+ "src/features/onboarding/index.ts": featureIndex3()
2608
2774
  };
2609
2775
  let count = 0;
2610
2776
  for (const [filePath, content] of Object.entries(files)) {
@@ -3135,7 +3301,7 @@ export const POST = withAuthNoParams(async (request: AuthenticatedRequest) => {
3135
3301
  });
3136
3302
  `;
3137
3303
  }
3138
- function featureIndex2() {
3304
+ function featureIndex3() {
3139
3305
  return `${STAMP6}
3140
3306
  export { ONBOARDING_STEPS } from './config';
3141
3307
  export type { OnboardingStep } from './config';
@@ -3184,7 +3350,7 @@ async function generateSearch(projectRoot) {
3184
3350
  "src/features/search/hooks/use-search.ts": useSearchHook(),
3185
3351
  "src/features/search/components/SearchInput.tsx": searchInput(),
3186
3352
  "src/features/search/components/index.ts": componentIndex5(),
3187
- "src/features/search/index.ts": featureIndex3(),
3353
+ "src/features/search/index.ts": featureIndex4(),
3188
3354
  "src/app/api/protected/search/route.ts": searchRoute()
3189
3355
  };
3190
3356
  let count = 0;
@@ -3496,7 +3662,7 @@ function componentIndex5() {
3496
3662
  export { SearchInput } from './SearchInput';
3497
3663
  `;
3498
3664
  }
3499
- function featureIndex3() {
3665
+ function featureIndex4() {
3500
3666
  return `${STAMP7}
3501
3667
  export type { SearchResult, SearchOptions, SearchProvider } from './types';
3502
3668
  export { searchParamsSchema } from './validation/schemas';
@@ -3568,7 +3734,7 @@ async function generateRealtime(projectRoot) {
3568
3734
  "src/features/realtime/server/sse.ts": sseProvider(),
3569
3735
  "src/features/realtime/hooks/use-event-source.ts": useEventSourceHook(),
3570
3736
  "src/features/realtime/hooks/index.ts": hooksIndex(),
3571
- "src/features/realtime/index.ts": featureIndex4(),
3737
+ "src/features/realtime/index.ts": featureIndex5(),
3572
3738
  "src/app/api/protected/realtime/stream/route.ts": sseStreamRoute()
3573
3739
  };
3574
3740
  let count = 0;
@@ -3816,7 +3982,7 @@ function hooksIndex() {
3816
3982
  export { useEventSource } from './use-event-source';
3817
3983
  `;
3818
3984
  }
3819
- function featureIndex4() {
3985
+ function featureIndex5() {
3820
3986
  return `${STAMP8}
3821
3987
  export type {
3822
3988
  RealtimeProvider,
@@ -4426,7 +4592,7 @@ async function generateCookieConsent(projectRoot) {
4426
4592
  "src/features/cookie-consent/components/CookieConsentBanner.tsx": cookieConsentBanner(),
4427
4593
  "src/features/cookie-consent/components/CookiePreferencesDialog.tsx": cookiePreferencesDialog(),
4428
4594
  "src/features/cookie-consent/components/index.ts": componentsIndex(),
4429
- "src/features/cookie-consent/index.ts": featureIndex5()
4595
+ "src/features/cookie-consent/index.ts": featureIndex6()
4430
4596
  };
4431
4597
  let count = 0;
4432
4598
  for (const [filePath, content] of Object.entries(files)) {
@@ -4436,17 +4602,19 @@ async function generateCookieConsent(projectRoot) {
4436
4602
  await fs15.writeFile(fullPath, content);
4437
4603
  count++;
4438
4604
  }
4605
+ await wireLayout3(projectRoot, ctx);
4439
4606
  await setConfigFlag10(projectRoot, ctx);
4440
4607
  await ctx.commit();
4441
- log.success(`Generated cookie consent feature with ${count} files`);
4608
+ log.success(`Generated and wired cookie consent feature (${count} files created)`);
4442
4609
  log.blank();
4443
4610
  log.step("src/features/cookie-consent/types.ts \u2014 ConsentStatus, ConsentPreferences");
4444
4611
  log.step("src/features/cookie-consent/hooks/use-consent.ts \u2014 useConsent hook");
4445
4612
  log.step("src/features/cookie-consent/components/CookieConsentBanner.tsx \u2014 banner");
4446
4613
  log.step("src/features/cookie-consent/components/CookiePreferencesDialog.tsx \u2014 preferences modal");
4447
4614
  log.blank();
4448
- log.warn("Complete setup:");
4449
- log.step("Add <CookieConsentBanner /> to your root layout");
4615
+ log.step("Wired automatically:");
4616
+ log.step(" \u2713 CookieConsentBanner mounted in root layout");
4617
+ log.blank();
4450
4618
  log.step("Optionally add <CookiePreferencesDialog /> for granular consent management");
4451
4619
  log.step("Check consent status with useConsent() before loading tracking scripts");
4452
4620
  log.blank();
@@ -4455,6 +4623,20 @@ async function generateCookieConsent(projectRoot) {
4455
4623
  throw error;
4456
4624
  }
4457
4625
  }
4626
+ async function wireLayout3(projectRoot, ctx) {
4627
+ const layoutPath = path15.join(projectRoot, "src", "app", "layout.tsx");
4628
+ if (!await fs15.pathExists(layoutPath)) return;
4629
+ await ctx.trackModifiedFile(layoutPath);
4630
+ let content = await fs15.readFile(layoutPath, "utf-8");
4631
+ if (content.includes("CookieConsentBanner")) return;
4632
+ content = `import { CookieConsentBanner } from '@/features/cookie-consent';
4633
+ ${content}`;
4634
+ content = content.replace(
4635
+ /(\s*)(<Providers>\{children\}<\/Providers>)/,
4636
+ "$1$2\n$1<CookieConsentBanner />"
4637
+ );
4638
+ await fs15.writeFile(layoutPath, content);
4639
+ }
4458
4640
  async function setConfigFlag10(projectRoot, ctx) {
4459
4641
  const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
4460
4642
  if (!await fs15.pathExists(configPath)) return;
@@ -4723,7 +4905,7 @@ export { CookieConsentBanner } from './CookieConsentBanner';
4723
4905
  export { CookiePreferencesDialog } from './CookiePreferencesDialog';
4724
4906
  `;
4725
4907
  }
4726
- function featureIndex5() {
4908
+ function featureIndex6() {
4727
4909
  return `${STAMP10}
4728
4910
  export type { ConsentStatus, ConsentPreferences } from './types';
4729
4911
  export { useConsent } from './hooks/use-consent';
@@ -5196,7 +5378,7 @@ async function generateFeatureFlags(projectRoot) {
5196
5378
  "src/features/feature-flags/hooks/use-feature-flag.ts": useFeatureFlag(),
5197
5379
  "src/features/feature-flags/components/FeatureGate.tsx": featureGate(),
5198
5380
  "src/features/feature-flags/components/index.ts": componentsIndex3(),
5199
- "src/features/feature-flags/index.ts": featureIndex6(),
5381
+ "src/features/feature-flags/index.ts": featureIndex7(),
5200
5382
  "src/config/feature-flags.json": featureFlagsJson(),
5201
5383
  "src/app/api/protected/feature-flags/route.ts": apiRoute()
5202
5384
  };
@@ -5407,7 +5589,7 @@ function componentsIndex3() {
5407
5589
  export { FeatureGate } from './FeatureGate';
5408
5590
  `;
5409
5591
  }
5410
- function featureIndex6() {
5592
+ function featureIndex7() {
5411
5593
  return `${STAMP13}
5412
5594
  export { getFlag } from './server';
5413
5595
  export { useFeatureFlag } from './hooks/use-feature-flag';
@@ -5901,6 +6083,7 @@ async function promptTheme() {
5901
6083
  init_logger();
5902
6084
  import fs2 from "fs-extra";
5903
6085
  import path2 from "path";
6086
+ import { generateBrandCss } from "@mars-stack/ui/utils";
5904
6087
 
5905
6088
  // src/generators/app-config.ts
5906
6089
  function escapeQuotes(value) {
@@ -6374,10 +6557,24 @@ async function scaffoldProject(targetDir, config) {
6374
6557
  await fs2.writeFile(path2.join(targetDir, ".env"), generateEnvFile(config));
6375
6558
  await fs2.writeFile(path2.join(targetDir, ".env.example"), generateEnvExample(config));
6376
6559
  await fs2.writeFile(path2.join(targetDir, ".gitignore"), generateGitignore());
6377
- await patchGlobalsCssForScaffoldedProject(targetDir);
6560
+ await patchGlobalsCssForScaffoldedProject(targetDir, config);
6561
+ await generateBrandCssForProject(targetDir, config);
6378
6562
  return { fileCount };
6379
6563
  }
6380
- async function patchGlobalsCssForScaffoldedProject(targetDir) {
6564
+ async function generateBrandCssForProject(targetDir, config) {
6565
+ const brandPath = path2.join(targetDir, "src", "styles", "brand.css");
6566
+ if (!await fs2.pathExists(brandPath)) return;
6567
+ const css = generateBrandCss(config.theme.primaryColor);
6568
+ await fs2.writeFile(brandPath, css);
6569
+ }
6570
+ var VALID_DESIGN_DIRECTIONS = [
6571
+ "modern-saas",
6572
+ "minimal",
6573
+ "enterprise",
6574
+ "creative",
6575
+ "dashboard"
6576
+ ];
6577
+ async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
6381
6578
  const globalsPath = path2.join(targetDir, "src", "styles", "globals.css");
6382
6579
  if (!await fs2.pathExists(globalsPath)) return;
6383
6580
  let content = await fs2.readFile(globalsPath, "utf-8");
@@ -6385,6 +6582,20 @@ async function patchGlobalsCssForScaffoldedProject(targetDir) {
6385
6582
  '@source "../../../node_modules/@mars-stack/ui/dist/**/*.js";',
6386
6583
  '@source "../../node_modules/@mars-stack/ui/dist/**/*.js";'
6387
6584
  );
6585
+ const direction = VALID_DESIGN_DIRECTIONS.includes(
6586
+ config.theme.designDirection
6587
+ ) ? config.theme.designDirection : "modern-saas";
6588
+ const currentMetaImport = /@import\s+['"]@mars-stack\/ui\/styles\/meta-[^'"]+['"]\s*;/;
6589
+ const newImport = `@import '@mars-stack/ui/styles/meta-${direction}.css';`;
6590
+ if (currentMetaImport.test(content)) {
6591
+ content = content.replace(currentMetaImport, newImport);
6592
+ } else {
6593
+ content = content.replace(
6594
+ "@import '@mars-stack/ui/styles/index.css';",
6595
+ `@import '@mars-stack/ui/styles/index.css';
6596
+ ${newImport}`
6597
+ );
6598
+ }
6388
6599
  await fs2.writeFile(globalsPath, content);
6389
6600
  }
6390
6601