@onexapis/cli 1.1.1 → 1.1.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.
@@ -147,9 +147,25 @@ function discoverPageConfigs(
147
147
 
148
148
  // ===== 6. PREVIEW APP COMPONENT =====
149
149
 
150
+ /**
151
+ * Match URL pathname to a page index.
152
+ * "/" or "/home" → home page, "/showcase" → showcase page, etc.
153
+ */
154
+ function getInitialPageFromURL(pages: Array<{ key: string; label: string; config: any }>): number {
155
+ const pathname = window.location.pathname.replace(/^\/+|\/+$/g, "").toLowerCase();
156
+ if (!pathname || pathname === "home") return 0; // home is always first after sorting
157
+
158
+ const idx = pages.findIndex((p) => {
159
+ const pageSlug = p.label.toLowerCase().replace(/\s+/g, "-");
160
+ const pageKey = p.key.replace("PageConfig", "").toLowerCase();
161
+ return pageSlug === pathname || pageKey === pathname;
162
+ });
163
+ return idx >= 0 ? idx : -2; // -2 = page not found
164
+ }
165
+
150
166
  function PreviewApp() {
151
167
  const [themeExports, setThemeExports] = useState<ThemeExports | null>(null);
152
- const [selectedPage, setSelectedPage] = useState(0);
168
+ const [selectedPage, setSelectedPage] = useState(-1); // -1 = not yet resolved from URL
153
169
  const [wsStatus, setWsStatus] = useState<
154
170
  "connected" | "disconnected" | "rebuilding"
155
171
  >("disconnected");
@@ -173,6 +189,16 @@ function PreviewApp() {
173
189
  loadTheme();
174
190
  }, [loadTheme]);
175
191
 
192
+ // Handle browser back/forward navigation
193
+ useEffect(() => {
194
+ const handlePopState = () => {
195
+ // Reset to -1 to re-resolve from URL
196
+ setSelectedPage(-1);
197
+ };
198
+ window.addEventListener("popstate", handlePopState);
199
+ return () => window.removeEventListener("popstate", handlePopState);
200
+ }, []);
201
+
176
202
  // WebSocket connection for hot reload
177
203
  useEffect(() => {
178
204
  function connect() {
@@ -242,7 +268,43 @@ function PreviewApp() {
242
268
  const pages = discoverPageConfigs(themeExports);
243
269
  const themeId = themeExports.themeConfig?.id || "";
244
270
  const themePrefix = themeId ? `${themeId}-` : "";
245
- const currentPage = pages[selectedPage] || pages[0];
271
+
272
+ // Resolve initial page from URL path (once pages are discovered)
273
+ const resolvedPage = selectedPage === -1 ? getInitialPageFromURL(pages) : selectedPage;
274
+
275
+ // Page not found (-2)
276
+ if (resolvedPage === -2) {
277
+ const pathname = window.location.pathname;
278
+ return (
279
+ <div style={{ padding: "80px 40px", textAlign: "center", fontFamily: "system-ui" }}>
280
+ <h1 style={{ fontSize: "4rem", margin: 0, color: "#E11D48" }}>404</h1>
281
+ <p style={{ fontSize: "1.25rem", color: "#333", marginTop: 12 }}>
282
+ Page not found: <code style={{ background: "#f1f5f9", padding: "2px 8px", borderRadius: 4 }}>{pathname}</code>
283
+ </p>
284
+ <p style={{ color: "#888", marginTop: 8 }}>Available pages:</p>
285
+ <div style={{ display: "flex", gap: 8, justifyContent: "center", marginTop: 12 }}>
286
+ {pages.map((page) => {
287
+ const pageSlug = page.key.replace("PageConfig", "").toLowerCase();
288
+ const pagePath = pageSlug === "home" ? "/" : `/${pageSlug}`;
289
+ return (
290
+ <a
291
+ key={page.key}
292
+ href={pagePath}
293
+ style={{
294
+ padding: "6px 16px", borderRadius: 6, background: "#E11D48",
295
+ color: "white", textDecoration: "none", fontSize: 14, fontWeight: 500,
296
+ }}
297
+ >
298
+ {page.label}
299
+ </a>
300
+ );
301
+ })}
302
+ </div>
303
+ </div>
304
+ );
305
+ }
306
+
307
+ const currentPage = pages[resolvedPage] || pages[0];
246
308
 
247
309
  if (!currentPage) {
248
310
  return (
@@ -290,28 +352,33 @@ function PreviewApp() {
290
352
  gap: 4,
291
353
  }}
292
354
  >
293
- {pages.map((page, i) => (
294
- <button
295
- key={page.key}
296
- onClick={() => {
297
- setSelectedPage(i);
298
- const el = document.getElementById("page-indicator");
299
- if (el) el.textContent = page.label;
300
- }}
301
- style={{
302
- padding: "4px 12px",
303
- border: "none",
304
- borderRadius: 4,
305
- cursor: "pointer",
306
- background: i === selectedPage ? "#E11D48" : "transparent",
307
- color: i === selectedPage ? "white" : "#333",
308
- fontSize: 12,
309
- fontWeight: 500,
310
- }}
311
- >
312
- {page.label}
313
- </button>
314
- ))}
355
+ {pages.map((page, i) => {
356
+ const pageSlug = page.key.replace("PageConfig", "").toLowerCase();
357
+ const pagePath = pageSlug === "home" ? "/" : `/${pageSlug}`;
358
+ return (
359
+ <button
360
+ key={page.key}
361
+ onClick={() => {
362
+ setSelectedPage(i);
363
+ window.history.pushState(null, "", pagePath);
364
+ const el = document.getElementById("page-indicator");
365
+ if (el) el.textContent = page.label;
366
+ }}
367
+ style={{
368
+ padding: "4px 12px",
369
+ border: "none",
370
+ borderRadius: 4,
371
+ cursor: "pointer",
372
+ background: i === resolvedPage ? "#E11D48" : "transparent",
373
+ color: i === resolvedPage ? "white" : "#333",
374
+ fontSize: 12,
375
+ fontWeight: 500,
376
+ }}
377
+ >
378
+ {page.label}
379
+ </button>
380
+ );
381
+ })}
315
382
  </div>
316
383
  )}
317
384
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onexapis/cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "CLI tool for OneX theme development - scaffolds themes using @onexapis/core",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -52,16 +52,17 @@
52
52
  "adm-zip": "^0.5.16",
53
53
  "archiver": "^7.0.1",
54
54
  "chalk": "^5.3.0",
55
+ "chokidar": "^4.0.0",
55
56
  "commander": "^12.1.0",
56
57
  "dotenv": "^17.3.1",
57
58
  "ejs": "^3.1.10",
59
+ "esbuild": "^0.25.0",
58
60
  "form-data": "^4.0.5",
59
61
  "fs-extra": "^11.2.0",
60
62
  "glob": "^10.3.10",
61
63
  "inquirer": "^9.2.12",
64
+ "jiti": "^2.6.1",
62
65
  "node-fetch": "^3.3.2",
63
- "chokidar": "^4.0.0",
64
- "esbuild": "^0.25.0",
65
66
  "open": "^10.1.0",
66
67
  "ora": "^8.0.1",
67
68
  "ws": "^8.18.0"