Skip to content

Commit bb75226

Browse files
cursoragenteluce2
andcommitted
Checkpoint before follow-up message
Co-authored-by: eric.luce <eric.luce@proofgeist.com>
1 parent 9c62bbd commit bb75226

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+981
-37
lines changed

.changeset/ui-default-shadcn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@proofkit/cli": minor
3+
---
4+
5+
CLI defaults to shadcn/ui for new projects. Legacy Mantine templates are still available via a hidden `--ui mantine` flag during `init`. The selected UI is persisted in `proofkit.json` as `ui`. Existing projects using Mantine are auto-detected and remain fully supported. For shadcn-based projects, adding new pages or auth via `proofkit add` is temporarily disabled while we work on a new component system.

packages/cli/src/cli/add/auth.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ export async function runAddAuthAction() {
1414
if (settings.appType !== "browser") {
1515
return p.cancel(`Auth is not supported for your app type.`);
1616
}
17+
if (settings.ui === "shadcn") {
18+
return p.cancel(
19+
"Adding auth is not yet supported for shadcn-based projects."
20+
);
21+
}
1722

1823
const authType =
1924
state.authType ??

packages/cli/src/cli/add/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ export const runAdd = async (name: string | undefined) => {
4747
})
4848
);
4949

50+
// For shadcn projects, block adding new pages or auth for now
51+
if (settings.ui === "shadcn") {
52+
if (addType === "page" || addType === "auth") {
53+
return p.cancel(
54+
"Adding new pages or auth is not yet supported for shadcn-based projects."
55+
);
56+
}
57+
}
58+
5059
if (addType === "auth") {
5160
await runAddAuthAction();
5261
} else if (addType === "data") {

packages/cli/src/cli/add/page/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ export const runAddPageAction = async (opts?: {
2929
const projectDir = state.projectDir;
3030

3131
const settings = getSettings();
32+
if (settings.ui === "shadcn") {
33+
return p.cancel(
34+
"Adding pages is not yet supported for shadcn-based projects."
35+
);
36+
}
3237

3338
const templates =
3439
state.appType === "browser"

packages/cli/src/cli/init.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,19 @@ import { initializeGit } from "~/helpers/git.js";
1414
import { installDependencies } from "~/helpers/installDependencies.js";
1515
import { logNextSteps } from "~/helpers/logNextSteps.js";
1616
import { setImportAlias } from "~/helpers/setImportAlias.js";
17+
import { getRegistryUrl, shadcnInstall } from "~/helpers/shadcn-cli.js";
1718
import { buildPkgInstallerMap } from "~/installers/index.js";
1819
import { ensureWebViewerAddonInstalled } from "~/installers/proofkit-webviewer.js";
1920
import { initProgramState, state } from "~/state.js";
2021
import { addPackageDependency } from "~/utils/addPackageDependency.js";
2122
import { getVersion } from "~/utils/getProofKitVersion.js";
2223
import { getUserPkgManager } from "~/utils/getUserPkgManager.js";
2324
import { parseNameAndPath } from "~/utils/parseNameAndPath.js";
25+
import {
26+
getSettings,
27+
setSettings,
28+
type Settings,
29+
} from "~/utils/parseSettings.js";
2430
import { validateAppName } from "~/utils/validateAppName.js";
2531
import { promptForFileMakerDataSource } from "./add/data-source/filemaker.js";
2632
import { abortIfCancel } from "./utils.js";
@@ -39,6 +45,8 @@ interface CliFlags {
3945
fmServerURL: string;
4046
auth: "none" | "next-auth" | "clerk";
4147
dataSource?: "filemaker" | "none" | "supabase";
48+
/** @internal UI library selection; hidden flag */
49+
ui?: "shadcn" | "mantine";
4250
/** @internal Used in CI. */
4351
CI: boolean;
4452
/** @internal Used in CI. */
@@ -72,6 +80,7 @@ const defaultOptions: CliFlags = {
7280
dataApiKey: "",
7381
fmServerURL: "",
7482
dataSource: undefined,
83+
ui: "shadcn",
7584
};
7685

7786
export const makeInitCommand = () => {
@@ -82,6 +91,8 @@ export const makeInitCommand = () => {
8291
"The name of the application, as well as the name of the directory to create"
8392
)
8493
.option("--appType [type]", "The type of app to create", undefined)
94+
// hidden UI selector; default is shadcn; pass --ui mantine to opt-in legacy Mantine templates
95+
.option("--ui [ui]", undefined, undefined)
8596
.option("--server [url]", "The URL of your FileMaker Server", undefined)
8697
.option(
8798
"--adminApiKey [key]",
@@ -166,6 +177,8 @@ type ProofKitPackageJSON = PackageJson & {
166177
export const runInit = async (name?: string, opts?: CliFlags) => {
167178
const pkgManager = getUserPkgManager();
168179
const cliOptions = opts ?? defaultOptions;
180+
// capture ui choice early into state
181+
state.ui = (cliOptions.ui ?? "shadcn") as "shadcn" | "mantine";
169182

170183
const projectName =
171184
name ||
@@ -211,13 +224,7 @@ export const runInit = async (name?: string, opts?: CliFlags) => {
211224
noInstall: cliOptions.noInstall,
212225
appRouter: cliOptions.appRouter,
213226
});
214-
state.projectDir = projectDir;
215227
setImportAlias(projectDir, "@/");
216-
addPackageDependency({
217-
dependencies: ["@proofkit/cli", "@types/node"],
218-
devMode: true,
219-
projectDir,
220-
});
221228

222229
// Write name to package.json
223230
const pkgJson = fs.readJSONSync(
@@ -238,6 +245,19 @@ export const runInit = async (name?: string, opts?: CliFlags) => {
238245
spaces: 2,
239246
});
240247

248+
// Ensure proofkit.json exists with initial settings including ui
249+
const initialSettings: Settings = {
250+
appType: state.appType ?? "browser",
251+
ui: (state.ui as "shadcn" | "mantine") ?? "shadcn",
252+
auth: { type: "none" },
253+
envFile: ".env",
254+
dataSources: [],
255+
tanstackQuery: false,
256+
replacedMainPage: false,
257+
appliedUpgrades: ["cursorRules"],
258+
};
259+
const { registryUrl } = setSettings(initialSettings);
260+
241261
// for webviewer apps FM is required, so don't ask
242262
let dataSource =
243263
state.appType === "webviewer" ? "filemaker" : cliOptions.dataSource;
@@ -285,11 +305,18 @@ export const runInit = async (name?: string, opts?: CliFlags) => {
285305

286306
await askForAuth({ projectDir });
287307

288-
if (!cliOptions.noInstall) {
289-
await installDependencies({ projectDir });
290-
await runCodegenCommand();
308+
await installDependencies({ projectDir });
309+
310+
if (state.ui === "shadcn") {
311+
await shadcnInstall([
312+
`${getRegistryUrl()}/r/mode-toggle`,
313+
"sonner",
314+
"button",
315+
]);
291316
}
292317

318+
await runCodegenCommand();
319+
293320
if (!cliOptions.noGit) {
294321
await initializeGit(projectDir);
295322
}

packages/cli/src/consts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ ${" ".repeat(61 - versionCharLength)}v${version}
2727
`;
2828
export const DEFAULT_APP_NAME = "my-proofkit-app";
2929
export const CREATE_FM_APP = cliName;
30+
31+
export const DEFAULT_REGISTRY_URL =
32+
process.env.NODE_ENV === "development"
33+
? "http://localhost:3005"
34+
: "https://proofkit.dev";

packages/cli/src/helpers/createProject.ts

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { state } from "~/state.js";
88
import { addPackageDependency } from "~/utils/addPackageDependency.js";
99
import { getUserPkgManager } from "~/utils/getUserPkgManager.js";
1010
import { replaceTextInFiles } from "./replaceText.js";
11+
import { shadcnInstall } from "./shadcn-cli.js";
1112

1213
interface CreateProjectOptions {
1314
projectName: string;
@@ -34,9 +35,14 @@ export const createBareProject = async ({
3435
noInstall,
3536
});
3637

37-
// Add new base dependencies for Tailwind v4 and shadcn/ui
38+
addPackageDependency({
39+
dependencies: ["@proofkit/cli", "@types/node"],
40+
devMode: true,
41+
});
42+
43+
// Add new base dependencies for Tailwind v4 and shadcn/ui or legacy Mantine
3844
// These should match the plan and dependencyVersionMap
39-
const BASE_DEPS = [
45+
const SHADCN_BASE_DEPS = [
4046
"@radix-ui/react-slot",
4147
"@tailwindcss/postcss",
4248
"class-variance-authority",
@@ -45,22 +51,48 @@ export const createBareProject = async ({
4551
"tailwind-merge",
4652
"tailwindcss",
4753
"tw-animate-css",
54+
"next-themes",
4855
] as AvailableDependencies[];
49-
const BASE_DEV_DEPS = [
56+
const SHADCN_BASE_DEV_DEPS = [
5057
"prettier",
5158
"prettier-plugin-tailwindcss",
5259
] as AvailableDependencies[];
5360

54-
addPackageDependency({
55-
dependencies: BASE_DEPS,
56-
devMode: false,
57-
projectDir: state.projectDir,
58-
});
59-
addPackageDependency({
60-
dependencies: BASE_DEV_DEPS,
61-
devMode: true,
62-
projectDir: state.projectDir,
63-
});
61+
const MANTINE_DEPS = [
62+
"@mantine/core",
63+
"@mantine/dates",
64+
"@mantine/hooks",
65+
"@mantine/modals",
66+
"@mantine/notifications",
67+
"mantine-react-table",
68+
] as AvailableDependencies[];
69+
const MANTINE_DEV_DEPS = [
70+
"postcss",
71+
"postcss-preset-mantine",
72+
"postcss-simple-vars",
73+
] as AvailableDependencies[];
74+
75+
if (state.ui === "mantine") {
76+
addPackageDependency({
77+
dependencies: MANTINE_DEPS,
78+
devMode: false,
79+
});
80+
addPackageDependency({
81+
dependencies: MANTINE_DEV_DEPS,
82+
devMode: true,
83+
});
84+
} else if (state.ui === "shadcn") {
85+
addPackageDependency({
86+
dependencies: SHADCN_BASE_DEPS,
87+
devMode: false,
88+
});
89+
addPackageDependency({
90+
dependencies: SHADCN_BASE_DEV_DEPS,
91+
devMode: true,
92+
});
93+
} else {
94+
throw new Error(`Unsupported UI library: ${state.ui}`);
95+
}
6496

6597
// Install the selected packages
6698
installPackages({

packages/cli/src/helpers/installDependencies.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const execWithSpinner = async (
1616
args?: string[];
1717
stdout?: StdoutStderrOption;
1818
onDataHandle?: (spinner: Ora) => (data: Buffer) => void;
19+
loadingMessage?: string;
1920
}
2021
) => {
2122
const { onDataHandle, args = ["install"], stdout = "pipe" } = options;
@@ -24,7 +25,9 @@ const execWithSpinner = async (
2425
args.push("--prefer-offline");
2526
}
2627

27-
const spinner = ora(`Running ${pkgManager} ${args.join(" ")} ...`).start();
28+
const spinner = ora(
29+
options.loadingMessage ?? `Running ${pkgManager} ${args.join(" ")} ...`
30+
).start();
2831
const subprocess = execa(pkgManager, args, { cwd: projectDir, stdout });
2932

3033
await new Promise<void>((res, rej) => {
@@ -93,14 +96,20 @@ export const installDependencies = async (args?: { projectDir?: string }) => {
9396

9497
export const runExecCommand = async ({
9598
command,
96-
projectDir,
99+
projectDir = state.projectDir,
97100
successMessage,
101+
loadingMessage,
98102
}: {
99103
command: string[];
100-
projectDir: string;
104+
projectDir?: string;
101105
successMessage?: string;
106+
loadingMessage?: string;
102107
}) => {
103-
const spinner = await _runExecCommand({ projectDir, command });
108+
const spinner = await _runExecCommand({
109+
projectDir,
110+
command,
111+
loadingMessage,
112+
});
104113

105114
// If the spinner was used to show the progress, use succeed method on it
106115
// If not, use the succeed on a new spinner
@@ -116,10 +125,12 @@ export const runExecCommand = async ({
116125
export const _runExecCommand = async ({
117126
projectDir,
118127
command,
128+
loadingMessage,
119129
}: {
120130
projectDir: string;
121131
exec?: boolean;
122132
command: string[];
133+
loadingMessage?: string;
123134
}): Promise<Ora | null> => {
124135
const pkgManager = getUserPkgManager();
125136
switch (pkgManager) {
@@ -133,8 +144,9 @@ export const _runExecCommand = async ({
133144
return null;
134145
// When using yarn or pnpm, use the stdout stream and ora spinner to show the progress
135146
case "pnpm":
136-
return execWithSpinner(projectDir, "pnpx", {
137-
args: [...command],
147+
return execWithSpinner(projectDir, "pnpm", {
148+
args: ["dlx", ...command],
149+
loadingMessage,
138150
onDataHandle: (spinner) => (data) => {
139151
const text = data.toString();
140152

@@ -148,6 +160,7 @@ export const _runExecCommand = async ({
148160
case "yarn":
149161
return execWithSpinner(projectDir, pkgManager, {
150162
args: [...command],
163+
loadingMessage,
151164
onDataHandle: (spinner) => (data) => {
152165
spinner.text = data.toString();
153166
},
@@ -157,6 +170,7 @@ export const _runExecCommand = async ({
157170
return execWithSpinner(projectDir, "bunx", {
158171
stdout: "ignore",
159172
args: [...command],
173+
loadingMessage,
160174
});
161175
}
162176
};

packages/cli/src/helpers/scaffoldProject.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ export const scaffoldProject = async ({
2020

2121
const srcDir = path.join(
2222
PKG_ROOT,
23-
state.appType === "browser" ? "template/nextjs" : "template/vite-wv"
23+
state.appType === "browser"
24+
? `template/${state.ui === "mantine" ? "nextjs-mantine" : "nextjs-shadcn"}`
25+
: "template/vite-wv"
2426
);
2527

2628
if (!noInstall) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { DEFAULT_REGISTRY_URL } from "~/consts.js";
2+
import { getSettings } from "~/utils/parseSettings.js";
3+
import { runExecCommand } from "./installDependencies.js";
4+
5+
export async function shadcnInstall(components: string | string[]) {
6+
const componentsArray = Array.isArray(components) ? components : [components];
7+
const command = ["shadcn@latest", "add", ...componentsArray];
8+
await runExecCommand({
9+
command,
10+
loadingMessage: "Installing components...",
11+
successMessage: "Components installed successfully!",
12+
});
13+
}
14+
15+
export function getRegistryUrl(): string {
16+
const url = getSettings().registryUrl ?? DEFAULT_REGISTRY_URL;
17+
return url.endsWith("/") ? url.slice(0, -1) : url;
18+
}

0 commit comments

Comments
 (0)