@docubook/create 1.14.1 → 1.14.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docubook/create",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "description": "CLI to create DocuBook projects",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -12,15 +12,17 @@ export function initializeProgram(version) {
12
12
  program
13
13
  .version(version)
14
14
  .description("CLI to create a new DocuBook project")
15
- // 1. Define optional argument here (handled below with .argument)
16
15
  .argument("[directory]", "The name of the project directory")
17
- .action(async (directory) => { // 2. Capture argument in action function
16
+ .action(async (directory) => {
18
17
  try {
19
18
  displayIntro();
20
- // 3. Pass the argument to the prompt function
21
- const options = await collectUserInput(directory);
19
+ const userInput = await collectUserInput(directory);
22
20
 
23
- await createProject(options);
21
+ // Pass all user input AND the DocuBook version to createProject
22
+ await createProject({
23
+ ...userInput,
24
+ docubookVersion: version, // Add DocuBook version here
25
+ });
24
26
  } catch (err) {
25
27
  log.error(err.message || "An unexpected error occurred.");
26
28
  process.exit(1);
@@ -28,4 +30,4 @@ export function initializeProgram(version) {
28
30
  });
29
31
 
30
32
  return program;
31
- }
33
+ }
@@ -16,7 +16,6 @@ export async function collectUserInput(cliProvidedDir) {
16
16
 
17
17
  const questions = [
18
18
  {
19
- // Skip this question if directory name is provided
20
19
  type: cliProvidedDir ? null : "text",
21
20
  name: "directoryName",
22
21
  message: "What is your project named?",
@@ -50,18 +49,18 @@ export async function collectUserInput(cliProvidedDir) {
50
49
  },
51
50
  });
52
51
 
53
- // Combine answers from CLI and from prompt
54
52
  answers = { ...answers, ...promptAnswers };
55
53
 
56
54
  // Validate the selected package manager
57
- const version = getPackageManagerVersion(answers.packageManager);
58
- if (!version) {
55
+ const pmVersion = getPackageManagerVersion(answers.packageManager);
56
+ if (!pmVersion) {
59
57
  throw new Error(`${chalk.bold(answers.packageManager)} is not installed on your system. Please install it to continue.`);
60
58
  }
61
59
 
60
+ // Return all answers, including the package manager version with a clear property name
62
61
  return {
63
62
  ...answers,
64
63
  directoryName: answers.directoryName.trim(),
65
- version,
64
+ pmVersion,
66
65
  };
67
- }
66
+ }
@@ -25,7 +25,7 @@ export default function Home() {
25
25
  )}
26
26
  >
27
27
  <AnimatedShinyText className="inline-flex items-center justify-center px-4 py-1 transition ease-out hover:text-neutral-100 hover:duration-300 hover:dark:text-neutral-200">
28
- <span>🚀 New Version - Release v1.14.1</span>
28
+ <span>🚀 New Version - Release v1.14.2</span>
29
29
  <ArrowRightIcon className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
30
30
  </AnimatedShinyText>
31
31
  </div>
@@ -0,0 +1,198 @@
1
+ "use client";
2
+
3
+ import { useRouter } from "next/navigation";
4
+ import { useEffect, useMemo, useState, useRef } from "react";
5
+ import { ArrowUpIcon, ArrowDownIcon, CornerDownLeftIcon, FileTextIcon } from "lucide-react";
6
+ import Anchor from "./anchor";
7
+ import { advanceSearch, cn } from "@/lib/utils";
8
+ import { ScrollArea } from "@/components/ui/scroll-area";
9
+ import { page_routes } from "@/lib/routes-config";
10
+ import {
11
+ DialogContent,
12
+ DialogHeader,
13
+ DialogFooter,
14
+ DialogClose,
15
+ DialogTitle,
16
+ DialogDescription,
17
+ } from "@/components/ui/dialog";
18
+
19
+ type ContextInfo = {
20
+ icon: string;
21
+ description: string;
22
+ title?: string;
23
+ };
24
+
25
+ type SearchResult = {
26
+ title: string;
27
+ href: string;
28
+ noLink?: boolean;
29
+ items?: undefined;
30
+ score?: number;
31
+ context?: ContextInfo;
32
+ };
33
+
34
+ const paddingMap = {
35
+ 1: "pl-2",
36
+ 2: "pl-4",
37
+ 3: "pl-10",
38
+ } as const;
39
+
40
+ interface SearchModalProps {
41
+ isOpen: boolean;
42
+ setIsOpen: (open: boolean) => void;
43
+ }
44
+
45
+ export function SearchModal({ isOpen, setIsOpen }: SearchModalProps) {
46
+ const router = useRouter();
47
+ const [searchedInput, setSearchedInput] = useState("");
48
+ const [selectedIndex, setSelectedIndex] = useState(0);
49
+ const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
50
+
51
+ useEffect(() => {
52
+ if (!isOpen) {
53
+ setSearchedInput("");
54
+ }
55
+ }, [isOpen]);
56
+
57
+ const filteredResults = useMemo<SearchResult[]>(() => {
58
+ const trimmedInput = searchedInput.trim();
59
+
60
+ if (trimmedInput.length < 3) {
61
+ return page_routes
62
+ .filter((route) => !route.href.endsWith('/'))
63
+ .slice(0, 6)
64
+ .map((route: { title: string; href: string; noLink?: boolean; context?: ContextInfo }) => ({
65
+ title: route.title,
66
+ href: route.href,
67
+ noLink: route.noLink,
68
+ context: route.context,
69
+ }));
70
+ }
71
+ return advanceSearch(trimmedInput) as unknown as SearchResult[];
72
+ }, [searchedInput]);
73
+
74
+ useEffect(() => {
75
+ setSelectedIndex(0);
76
+ }, [filteredResults]);
77
+
78
+ useEffect(() => {
79
+ const handleNavigation = (event: KeyboardEvent) => {
80
+ if (!isOpen || filteredResults.length === 0) return;
81
+
82
+ if (event.key === "ArrowDown") {
83
+ event.preventDefault();
84
+ setSelectedIndex((prev) => (prev + 1) % filteredResults.length);
85
+ } else if (event.key === "ArrowUp") {
86
+ event.preventDefault();
87
+ setSelectedIndex((prev) => (prev - 1 + filteredResults.length) % filteredResults.length);
88
+ } else if (event.key === "Enter") {
89
+ event.preventDefault();
90
+ const selectedItem = filteredResults[selectedIndex];
91
+ if (selectedItem) {
92
+ router.push(`/docs${selectedItem.href}`);
93
+ setIsOpen(false);
94
+ }
95
+ }
96
+ };
97
+
98
+ window.addEventListener("keydown", handleNavigation);
99
+ return () => window.removeEventListener("keydown", handleNavigation);
100
+ }, [isOpen, filteredResults, selectedIndex, router, setIsOpen]);
101
+
102
+ useEffect(() => {
103
+ if (itemRefs.current[selectedIndex]) {
104
+ itemRefs.current[selectedIndex]?.scrollIntoView({
105
+ behavior: "smooth",
106
+ block: "nearest",
107
+ });
108
+ }
109
+ }, [selectedIndex]);
110
+
111
+ return (
112
+ <DialogContent className="p-0 max-w-[650px] sm:top-[38%] top-[45%] !rounded-md">
113
+ <DialogHeader>
114
+ <DialogTitle className="sr-only">Search Documentation</DialogTitle>
115
+ <DialogDescription className="sr-only">Search through the documentation</DialogDescription>
116
+ </DialogHeader>
117
+
118
+ <input
119
+ value={searchedInput}
120
+ onChange={(e) => setSearchedInput(e.target.value)}
121
+ placeholder="Type something to search..."
122
+ autoFocus
123
+ className="h-14 px-6 bg-transparent border-b text-[14px] outline-none w-full"
124
+ aria-label="Search documentation"
125
+ />
126
+
127
+ {filteredResults.length == 0 && searchedInput && (
128
+ <p className="text-muted-foreground mx-auto mt-2 text-sm">
129
+ No results found for{" "}
130
+ <span className="text-primary">{`"${searchedInput}"`}</span>
131
+ </p>
132
+ )}
133
+ <ScrollArea className="max-h-[400px] overflow-y-auto">
134
+ <div className="flex flex-col items-start overflow-y-auto sm:px-2 px-1 pb-4">
135
+ {filteredResults.map((item, index) => {
136
+ const level = (item.href.split("/").slice(1).length - 1) as keyof typeof paddingMap;
137
+ const paddingClass = paddingMap[level] || 'pl-2';
138
+ const isActive = index === selectedIndex;
139
+
140
+ return (
141
+ <DialogClose key={item.href} asChild>
142
+ <Anchor
143
+ ref={(el) => {
144
+ itemRefs.current[index] = el as HTMLDivElement | null;
145
+ }}
146
+ className={cn(
147
+ "dark:hover:bg-accent/15 hover:bg-accent/10 w-full px-3 rounded-sm text-sm flex items-center gap-2.5",
148
+ isActive && "bg-primary/20 dark:bg-primary/30",
149
+ paddingClass
150
+ )}
151
+ href={`/docs${item.href}`}
152
+ tabIndex={-1}
153
+ >
154
+ <div
155
+ className={cn(
156
+ "flex items-center w-full h-full py-3 gap-1.5 px-2 justify-between",
157
+ level > 1 && "border-l pl-4"
158
+ )}
159
+ >
160
+ <div className="flex items-center">
161
+ <FileTextIcon className="h-[1.1rem] w-[1.1rem] mr-1" />
162
+ <span>{item.title}</span>
163
+ </div>
164
+ {isActive && (
165
+ <div className="hidden md:flex items-center text-xs text-muted-foreground">
166
+ <span>Return</span>
167
+ <CornerDownLeftIcon className="h-3 w-3 ml-1" />
168
+ </div>
169
+ )}
170
+ </div>
171
+ </Anchor>
172
+ </DialogClose>
173
+ );
174
+ })}
175
+ </div>
176
+ </ScrollArea>
177
+ <DialogFooter className="md:flex md:justify-start hidden h-14 px-6 bg-transparent border-t text-[14px] outline-none">
178
+ <div className="flex items-center gap-2">
179
+ <span className="dark:bg-accent/15 bg-slate-200 border rounded p-2">
180
+ <ArrowUpIcon className="w-3 h-3"/>
181
+ </span>
182
+ <span className="dark:bg-accent/15 bg-slate-200 border rounded p-2">
183
+ <ArrowDownIcon className="w-3 h-3"/>
184
+ </span>
185
+ <p className="text-muted-foreground">to navigate</p>
186
+ <span className="dark:bg-accent/15 bg-slate-200 border rounded p-2">
187
+ <CornerDownLeftIcon className="w-3 h-3"/>
188
+ </span>
189
+ <p className="text-muted-foreground">to select</p>
190
+ <span className="dark:bg-accent/15 bg-slate-200 border rounded px-2 py-1">
191
+ esc
192
+ </span>
193
+ <p className="text-muted-foreground">to close</p>
194
+ </div>
195
+ </DialogFooter>
196
+ </DialogContent>
197
+ );
198
+ }
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ import { CommandIcon, SearchIcon } from "lucide-react";
4
+ import { DialogTrigger } from "@/components/ui/dialog";
5
+ import { Input } from "@/components/ui/input";
6
+
7
+ export function SearchTrigger() {
8
+ return (
9
+ <DialogTrigger asChild>
10
+ <div className="relative flex-1 cursor-pointer max-w-[140px]">
11
+ <div className="flex items-center">
12
+ <div className="md:hidden p-2 -ml-2">
13
+ <SearchIcon className="h-5 w-5 text-muted-foreground" />
14
+ </div>
15
+ <div className="hidden md:block w-full">
16
+ <SearchIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
17
+ <Input
18
+ className="w-full rounded-full dark:bg-background/95 bg-background border h-9 pl-10 pr-0 sm:pr-4 text-sm shadow-sm overflow-ellipsis"
19
+ placeholder="Search"
20
+ readOnly // This input is for display only
21
+ />
22
+ <div className="flex absolute top-1/2 -translate-y-1/2 right-2 text-xs font-medium font-mono items-center gap-0.5 dark:bg-accent bg-accent text-white px-2 py-0.5 rounded-full">
23
+ <CommandIcon className="w-3 h-3" />
24
+ <span>K</span>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </DialogTrigger>
30
+ );
31
+ }
@@ -1,52 +1,28 @@
1
1
  "use client";
2
2
 
3
- import { useRouter } from "next/navigation";
4
- import { useEffect, useMemo, useState, useRef } from "react";
5
- import { ArrowUpIcon, ArrowDownIcon, CommandIcon, FileTextIcon, SearchIcon, CornerDownLeftIcon } from "lucide-react";
6
- import { Input } from "@/components/ui/input";
7
- import {
8
- Dialog,
9
- DialogContent,
10
- DialogHeader,
11
- DialogFooter,
12
- DialogTrigger,
13
- DialogClose,
14
- DialogTitle,
15
- DialogDescription,
16
- } from "@/components/ui/dialog";
17
- import Anchor from "./anchor";
18
- import { advanceSearch, cn } from "@/lib/utils";
19
- import { ScrollArea } from "@/components/ui/scroll-area";
20
- import { page_routes } from "@/lib/routes-config";
21
-
22
- // Define the ContextInfo type to match the one in routes-config
23
- type ContextInfo = {
24
- icon: string;
25
- description: string;
26
- title?: string;
27
- };
28
-
29
- type SearchResult = {
30
- title: string;
31
- href: string;
32
- noLink?: boolean;
33
- items?: undefined;
34
- score?: number;
35
- context?: ContextInfo;
36
- };
3
+ import { useState, useEffect } from "react";
4
+ import { Dialog } from "@/components/ui/dialog";
5
+ import { SearchTrigger } from "@/components/SearchTrigger";
6
+ import { SearchModal } from "@/components/SearchModal";
7
+
8
+ // Define props for the Search component
9
+ interface SearchProps {
10
+ /**
11
+ * Specify the type of search engine to use.
12
+ * @default 'default'
13
+ */
14
+ type?: "default" | "algolia";
15
+ }
37
16
 
38
- export default function Search() {
39
- const router = useRouter();
40
- const [searchedInput, setSearchedInput] = useState("");
17
+ export default function Search({ type = "default" }: SearchProps) {
41
18
  const [isOpen, setIsOpen] = useState(false);
42
- const [selectedIndex, setSelectedIndex] = useState(0);
43
- const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
44
19
 
20
+ // Effect to handle keyboard shortcut (Cmd/Ctrl + K)
45
21
  useEffect(() => {
46
22
  const handleKeyDown = (event: KeyboardEvent) => {
47
23
  if ((event.ctrlKey || event.metaKey) && event.key === "k") {
48
24
  event.preventDefault();
49
- setIsOpen(true);
25
+ setIsOpen((open) => !open);
50
26
  }
51
27
  };
52
28
 
@@ -56,191 +32,20 @@ export default function Search() {
56
32
  };
57
33
  }, []);
58
34
 
59
- const filteredResults = useMemo<SearchResult[]>(() => {
60
- const trimmedInput = searchedInput.trim();
61
-
62
- // If search input is empty or less than 3 characters, show initial suggestions
63
- if (trimmedInput.length < 3) {
64
- return page_routes
65
- .filter((route: { href: string }) => !route.href.endsWith('/')) // Filter out directory routes
66
- .slice(0, 6) // Limit to 6 posts
67
- .map((route: { title: string; href: string; noLink?: boolean; context?: ContextInfo }) => ({
68
- title: route.title,
69
- href: route.href,
70
- noLink: route.noLink,
71
- context: route.context
72
- }));
73
- }
74
-
75
- // For search with 3 or more characters, use the advance search
76
- return advanceSearch(trimmedInput) as unknown as SearchResult[];
77
- }, [searchedInput]);
78
-
79
- useEffect(() => {
80
- setSelectedIndex(0);
81
- }, [filteredResults]);
82
-
83
- useEffect(() => {
84
- const handleNavigation = (event: KeyboardEvent) => {
85
- if (!isOpen || filteredResults.length === 0) return;
86
-
87
- if (event.key === "ArrowDown") {
88
- event.preventDefault();
89
- setSelectedIndex((prev) => (prev + 1) % filteredResults.length);
90
- }
91
-
92
- if (event.key === "ArrowUp") {
93
- event.preventDefault();
94
- setSelectedIndex((prev) => (prev - 1 + filteredResults.length) % filteredResults.length);
95
- }
96
-
97
- if (event.key === "Enter") {
98
- event.preventDefault();
99
- const selectedItem = filteredResults[selectedIndex];
100
- if (selectedItem) {
101
- router.push(`/docs${selectedItem.href}`);
102
- setIsOpen(false);
103
- }
104
- }
105
- };
106
-
107
- window.addEventListener("keydown", handleNavigation);
108
- return () => {
109
- window.removeEventListener("keydown", handleNavigation);
110
- };
111
- }, [isOpen, filteredResults, selectedIndex, router]);
112
-
113
- useEffect(() => {
114
- if (itemRefs.current[selectedIndex]) {
115
- itemRefs.current[selectedIndex]?.scrollIntoView({
116
- behavior: "smooth",
117
- block: "nearest",
118
- });
119
- }
120
- }, [selectedIndex]);
35
+ // Here you can add logic for different search types if needed in the future
36
+ if (type === "algolia") {
37
+ // return <AlgoliaSearchComponent />; // Example for future implementation
38
+ console.warn("Tipe pencarian 'algolia' belum diimplementasikan.");
39
+ // For now, we will fall back to the default search implementation
40
+ }
121
41
 
42
+ // Render the default search components
122
43
  return (
123
44
  <div>
124
- <Dialog
125
- open={isOpen}
126
- onOpenChange={(open) => {
127
- if (!open) setSearchedInput("");
128
- setIsOpen(open);
129
- }}
130
- >
131
- <DialogTrigger asChild>
132
- <div className="relative flex-1 cursor-pointer max-w-[140px]">
133
- <div className="flex items-center">
134
- <div className="md:hidden p-2 -ml-2">
135
- <SearchIcon className="h-5 w-5 text-muted-foreground" />
136
- </div>
137
- <div className="hidden md:block w-full">
138
- <SearchIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
139
- <Input
140
- className="w-full rounded-full dark:bg-background/95 bg-background border h-9 pl-10 pr-0 sm:pr-4 text-sm shadow-sm overflow-ellipsis"
141
- placeholder="Search"
142
- type="search"
143
- />
144
- <div className="flex absolute top-1/2 -translate-y-1/2 right-2 text-xs font-medium font-mono items-center gap-0.5 dark:bg-accent bg-accent text-white px-2 py-0.5 rounded-full">
145
- <CommandIcon className="w-3 h-3" />
146
- <span>K</span>
147
- </div>
148
- </div>
149
- </div>
150
- </div>
151
- </DialogTrigger>
152
- <DialogContent className="p-0 max-w-[650px] sm:top-[38%] top-[45%] !rounded-md">
153
- <DialogHeader>
154
- <DialogTitle className="sr-only">Search Documentation</DialogTitle>
155
- </DialogHeader>
156
- <DialogDescription className="sr-only">
157
- Search through the documentation
158
- </DialogDescription>
159
- <input
160
- value={searchedInput}
161
- onChange={(e) => setSearchedInput(e.target.value)}
162
- placeholder="Type something to search..."
163
- autoFocus
164
- className="h-14 px-6 bg-transparent border-b text-[14px] outline-none w-full"
165
- aria-label="Search documentation"
166
- />
167
- {filteredResults.length == 0 && searchedInput && (
168
- <p className="text-muted-foreground mx-auto mt-2 text-sm">
169
- No results found for{" "}
170
- <span className="text-primary">{`"${searchedInput}"`}</span>
171
- </p>
172
- )}
173
- <ScrollArea className="max-h-[400px] overflow-y-auto">
174
- <div className="flex flex-col items-start overflow-y-auto sm:px-2 px-1 pb-4">
175
- {filteredResults.map((item, index) => {
176
- const level = (item.href.split("/").slice(1).length - 1) as keyof typeof paddingMap;
177
- const paddingClass = paddingMap[level];
178
- const isActive = index === selectedIndex;
179
-
180
- return (
181
- <DialogClose key={item.href} asChild>
182
- <Anchor
183
- ref={(el) => {
184
- itemRefs.current[index] = el as HTMLDivElement | null;
185
- }}
186
- className={cn(
187
- "dark:hover:bg-accent/15 hover:bg-accent/10 w-full px-3 rounded-sm text-sm flex items-center gap-2.5",
188
- isActive && "bg-primary/20 dark:bg-primary/30",
189
- paddingClass
190
- )}
191
- href={`/docs${item.href}`}
192
- tabIndex={0}
193
- >
194
- <div
195
- className={cn(
196
- "flex items-center w-full h-full py-3 gap-1.5 px-2 justify-between",
197
- level > 1 && "border-l pl-4"
198
- )}
199
- >
200
- <div className="flex items-center">
201
- <FileTextIcon className="h-[1.1rem] w-[1.1rem] mr-1" />
202
- <span>{item.title}</span>
203
- </div>
204
- {isActive && (
205
- <div className="hidden md:flex items-center text-xs text-muted-foreground">
206
- <span>Return</span>
207
- <CornerDownLeftIcon className="h-3 w-3 ml-1" />
208
- </div>
209
- )}
210
- </div>
211
- </Anchor>
212
- </DialogClose>
213
- );
214
- })}
215
- </div>
216
- </ScrollArea>
217
- <DialogFooter className="md:flex md:justify-start hidden h-14 px-6 bg-transparent border-t text-[14px] outline-none">
218
- <div className="flex items-center gap-2">
219
- <span className="dark:bg-accent/15 bg-slate-200 border rounded p-2">
220
- <ArrowUpIcon className="w-3 h-3"/>
221
- </span>
222
- <span className="dark:bg-accent/15 bg-slate-200 border rounded p-2">
223
- <ArrowDownIcon className="w-3 h-3"/>
224
- </span>
225
- <p className="text-muted-foreground">to navigate</p>
226
- <span className="dark:bg-accent/15 bg-slate-200 border rounded p-2">
227
- <CornerDownLeftIcon className="w-3 h-3"/>
228
- </span>
229
- <p className="text-muted-foreground">to select</p>
230
- <span className="dark:bg-accent/15 bg-slate-200 border rounded px-2 py-1">
231
- esc
232
- </span>
233
- <p className="text-muted-foreground">to close</p>
234
- </div>
235
- </DialogFooter>
236
- </DialogContent>
45
+ <Dialog open={isOpen} onOpenChange={setIsOpen}>
46
+ <SearchTrigger />
47
+ <SearchModal isOpen={isOpen} setIsOpen={setIsOpen} />
237
48
  </Dialog>
238
49
  </div>
239
50
  );
240
- }
241
-
242
- const paddingMap = {
243
- 1: "pl-2",
244
- 2: "pl-4",
245
- 3: "pl-10",
246
- } as const;
51
+ }
@@ -8,6 +8,22 @@ date: 02-08-2025
8
8
  This changelog contains a list of all the changes made to the DocuBook template. It will be updated with each new release and will include information about new features, bug fixes, and other improvements.
9
9
  </Note>
10
10
 
11
+ <div className="sr-only">
12
+ ### v 1.14.2
13
+ </div>
14
+
15
+ <Release version="1.14.2" date="2025-08-05" title="Refactor & Fix: Decouple Search Component and Resolve Type Errors">
16
+ <Changes type="added">
17
+ - Refactor Search component into three distinct components: Search, SearchTrigger, and SearchModal for better maintainability and scalability.
18
+ - New SearchTrigger components
19
+ - New SearchModal components
20
+ </Changes>
21
+ <Changes type="fixed">
22
+ - Resolve TypeScript error for missing 'noLink' property on the 'Page' type after refactoring.
23
+ - Fix TypeScript error for missing 'context' property by providing a complete inline type annotation in the SearchModal component.
24
+ </Changes>
25
+ </Release>
26
+
11
27
  <div className="sr-only">
12
28
  ### v 1.14.0
13
29
  </div>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docubook",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "dev": "next dev",
@@ -12,16 +12,16 @@ import { displayManualSteps, simulateInstallation, displayNextSteps } from "../u
12
12
  * Creates a new DocuBook project.
13
13
  * @param {Object} options - Installation options.
14
14
  */
15
- export async function createProject({ directoryName, packageManager, version, installNow }) {
15
+ export async function createProject({ directoryName, packageManager, pmVersion, docubookVersion, installNow }) {
16
16
  const projectPath = path.resolve(process.cwd(), directoryName);
17
17
 
18
18
  if (fs.existsSync(projectPath)) {
19
19
  throw new Error(`Directory "${directoryName}" already exists.`);
20
20
  }
21
21
 
22
- log.info(`Creating a new DocuBook project in ${chalk.cyan(projectPath)}...`);
22
+ log.info(`Creating a new DocuBook project in ${chalk.green(projectPath)}...`);
23
23
 
24
- const spinner = ora("Setting up project files...").start();
24
+ const spinner = ora("Creating project files...").start();
25
25
 
26
26
  try {
27
27
  // 1. Create project directory and copy template files
@@ -29,25 +29,27 @@ export async function createProject({ directoryName, packageManager, version, in
29
29
  const __dirname = path.dirname(__filename);
30
30
  const templatePath = path.join(__dirname, "../dist");
31
31
  copyDirectoryRecursive(templatePath, projectPath);
32
- spinner.succeed("Project files created.");
33
32
 
34
33
  // 2. Configure package manager specific settings
35
- spinner.start("Configuring package manager...");
34
+ spinner.text = "Configuring package manager...";
36
35
  configurePackageManager(packageManager, projectPath);
37
- spinner.succeed("Package manager configured.");
38
36
 
39
37
  // 3. Update package.json
40
- spinner.start("Updating package.json...");
38
+ spinner.text = "Updating package.json...";
41
39
  const pkgPath = path.join(projectPath, "package.json");
42
40
  if (fs.existsSync(pkgPath)) {
43
41
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
44
42
  pkg.name = directoryName; // Set project name
45
- pkg.packageManager = `${packageManager}@${version}`;
43
+ // Use the package manager version here
44
+ pkg.packageManager = `${packageManager}@${pmVersion}`;
46
45
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
47
46
  }
48
- spinner.succeed("package.json updated.");
49
47
 
50
- log.success(`DocuBook project ready to go!`);
48
+ // Combine all success messages into one dynamic line
49
+ // Use the docubookVersion for the success message
50
+ spinner.succeed(
51
+ chalk.green(`Successfully installed DocuBook - v${docubookVersion} with ${packageManager}`)
52
+ );
51
53
 
52
54
  if (installNow) {
53
55
  await installDependencies(directoryName, packageManager, projectPath);
@@ -97,7 +99,7 @@ function copyDirectoryRecursive(source, destination) {
97
99
  */
98
100
  async function installDependencies(directoryName, packageManager, projectPath) {
99
101
  log.info("Installing dependencies...");
100
- const installSpinner = ora(`Running ${chalk.cyan(`${packageManager} install`)}...`).start();
102
+ const installSpinner = ora(`Running ${chalk.green(`${packageManager} install`)}...`).start();
101
103
 
102
104
  try {
103
105
  execSync(`${packageManager} install`, { cwd: projectPath, stdio: "ignore" });
@@ -107,4 +109,4 @@ async function installDependencies(directoryName, packageManager, projectPath) {
107
109
  displayManualSteps(directoryName, packageManager);
108
110
  throw new Error("Dependency installation failed.");
109
111
  }
110
- }
112
+ }
@@ -16,7 +16,7 @@ export function displayIntro() {
16
16
  export async function simulateInstallation() {
17
17
  const bar = new cliProgress.SingleBar(
18
18
  {
19
- format: `Finishing setup... ${chalk.cyan("{bar}")} | {percentage}%`,
19
+ format: `Finishing setup... ${chalk.greenBright("{bar}")} | {percentage}%`,
20
20
  barCompleteChar: "\u2588",
21
21
  barIncompleteChar: "\u2591",
22
22
  hideCursor: true,
@@ -42,9 +42,9 @@ export function displayManualSteps(projectDirectory, packageManager) {
42
42
  const steps = `
43
43
  ${chalk.yellow("Automatic installation failed.")} Please finish setup manually:
44
44
 
45
- 1. ${chalk.cyan(`cd ${projectDirectory}`)}
46
- 2. ${chalk.cyan(`${packageManager} install`)}
47
- 3. ${chalk.cyan(`${packageManager} run dev`)}
45
+ 1. ${chalk.blueBright(`cd ${projectDirectory}`)}
46
+ 2. ${chalk.blueBright(`${packageManager} install`)}
47
+ 3. ${chalk.blueBright(`${packageManager} run dev`)}
48
48
  `;
49
49
  console.log(
50
50
  boxen(steps, {
@@ -67,8 +67,8 @@ export function displayNextSteps(directoryName, packageManager) {
67
67
  const steps = `
68
68
  ${chalk.bold("Next steps:")}
69
69
 
70
- 1. ${chalk.cyan(`cd ${directoryName}`)}
71
- 2. ${chalk.cyan(`${packageManager} run dev`)}
70
+ 1. ${chalk.blueBright(`cd ${directoryName}`)}
71
+ 2. ${chalk.blueBright(`${packageManager} run dev`)}
72
72
  `;
73
73
 
74
74
  console.log(