create-caspian-app 1.0.22 → 1.2.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.
@@ -0,0 +1,113 @@
1
+ from typing import Optional
2
+
3
+ from casp.component_decorator import html
4
+ from casp.layout import Metadata
5
+
6
+
7
+ metadata = Metadata(
8
+ title="Application Error",
9
+ description="An unexpected error occurred.",
10
+ extra={"robots": "noindex, nofollow"},
11
+ )
12
+
13
+
14
+ def page(error_message: str, error_trace: Optional[str] = None):
15
+ return html(r"""
16
+ <main class="container py-10">
17
+ <div class="mx-auto max-w-2xl rounded bg-white p-6 shadow-sm">
18
+ <div class="flex items-start gap-4">
19
+ <svg class="h-10 w-10 text-red-600"
20
+ viewBox="0 0 24 24"
21
+ fill="none"
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ aria-hidden="true">
24
+ <path d="M11.001 2a1 1 0 0 1 .998 0L21 7v9a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V7l8.001-5z"
25
+ stroke="currentColor"
26
+ stroke-width="1.5"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round" />
29
+ <path d="M12 8v4"
30
+ stroke="currentColor"
31
+ stroke-width="1.5"
32
+ stroke-linecap="round"
33
+ stroke-linejoin="round" />
34
+ <path d="M12 16h.01"
35
+ stroke="currentColor"
36
+ stroke-width="1.5"
37
+ stroke-linecap="round"
38
+ stroke-linejoin="round" />
39
+ </svg>
40
+
41
+ <div class="flex-1">
42
+ <h1 class="text-2xl font-semibold text-red-600">Something went wrong</h1>
43
+ <p class="mt-2 text-sm text-gray-600">
44
+ We're sorry—an unexpected error occurred. Try reloading or return to
45
+ the homepage.
46
+ </p>
47
+ <p class="mt-3 text-sm text-gray-700">{{ error_message }}</p>
48
+
49
+ <div class="mt-4 flex flex-wrap gap-2">
50
+ <a href="/"
51
+ class="inline-block rounded border border-gray-200 bg-gray-100 px-3 py-1.5 text-sm text-gray-800 hover:bg-gray-200">
52
+ Home
53
+ </a>
54
+ <button class="inline-block rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
55
+ onclick="window.location.reload()">
56
+ Reload
57
+ </button>
58
+ {% if error_trace %}
59
+ <button class="inline-block rounded border border-gray-200 bg-transparent px-3 py-1.5 text-sm text-black"
60
+ onclick="setShowTrace(current => !current)">
61
+ {showTrace ? "Hide details" : "Show details"}
62
+ </button>
63
+ {% endif %}
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ {% if error_trace %}
69
+ <section class="mt-4" hidden="{!showTrace}">
70
+ <div class="mb-2 flex items-center justify-between">
71
+ <div class="text-xs text-gray-500">Error details (development only)</div>
72
+ <div class="flex gap-2">
73
+ <button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
74
+ onclick="copyTrace()">
75
+ {traceCopied ? "Copied" : "Copy"}
76
+ </button>
77
+ <button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
78
+ onclick="downloadTrace()">
79
+ Download
80
+ </button>
81
+ </div>
82
+ </div>
83
+ <pre pp-ref="{tracePre}"
84
+ class="overflow-auto rounded bg-gray-100 p-4 text-xs text-black">{{ error_trace }}</pre>
85
+ </section>
86
+ {% endif %}
87
+
88
+ <script>
89
+ const [showTrace, setShowTrace] = pp.state(false);
90
+ const [traceCopied, setTraceCopied] = pp.state(false);
91
+ const tracePre = pp.ref(null);
92
+
93
+ async function copyTrace() {
94
+ if (!tracePre.current) return;
95
+ await navigator.clipboard.writeText(tracePre.current.innerText);
96
+ setTraceCopied(true);
97
+ setTimeout(() => setTraceCopied(false), 2000);
98
+ }
99
+
100
+ function downloadTrace() {
101
+ if (!tracePre.current) return;
102
+ const blob = new Blob([tracePre.current.innerText], { type: "text/plain" });
103
+ const url = URL.createObjectURL(blob);
104
+ const link = document.createElement("a");
105
+ link.href = url;
106
+ link.download = "error_trace.txt";
107
+ link.click();
108
+ URL.revokeObjectURL(url);
109
+ }
110
+ </script>
111
+ </div>
112
+ </main>
113
+ """, error_message=error_message, error_trace=error_trace)
@@ -1,3 +1,8 @@
1
+ from casp.component_decorator import html
2
+
3
+
4
+ def page():
5
+ return html(r"""
1
6
  <div
2
7
  class="min-h-screen bg-background text-foreground selection:bg-primary/30 flex flex-col items-center justify-between py-8 md:py-12 px-6 md:px-24 overflow-x-hidden relative"
3
8
  >
@@ -157,3 +162,4 @@
157
162
  </a>
158
163
  </div>
159
164
  </div>
165
+ """)
@@ -0,0 +1,32 @@
1
+ def layout():
2
+ return r"""
3
+ <!DOCTYPE html>
4
+ <html lang="en">
5
+ <head>
6
+ <meta charset="UTF-8" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>{{ metadata.title | default("Caspian App") }}</title>
9
+ <meta
10
+ name="description"
11
+ content="{{ metadata.description | default('Powered by Caspian Framework') }}"
12
+ />
13
+ {% if metadata.robots %}
14
+ <meta name="robots" content="{{ metadata.robots }}" />
15
+ {% endif %}
16
+ <link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16" />
17
+ <link href="/css/styles.css" rel="stylesheet" />
18
+ <script type="module" src="/js/main.js"></script>
19
+ </head>
20
+
21
+ <body
22
+ style="
23
+ opacity: 0;
24
+ pointer-events: none;
25
+ user-select: none;
26
+ transition: opacity 0.18s ease-out;
27
+ "
28
+ >
29
+ <slot />
30
+ </body>
31
+ </html>
32
+ """
@@ -1,18 +1,32 @@
1
- <div class="bg-gray-50 flex h-screen items-center justify-center px-4">
2
- <div class="text-center">
3
- <h1 class="text-9xl font-black text-gray-200">404</h1>
4
-
5
- <p class="text-2xl font-bold tracking-tight text-gray-900 sm:text-4xl">
6
- Uh-oh!
7
- </p>
8
-
9
- <p class="mt-4 text-gray-500">We can't find that page.</p>
10
-
11
- <a
12
- href="/"
13
- class="mt-6 inline-block rounded bg-indigo-600 px-5 py-3 text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring"
14
- >
15
- Go Back Home
16
- </a>
17
- </div>
18
- </div>
1
+ from casp.component_decorator import html
2
+ from casp.layout import Metadata
3
+
4
+
5
+ metadata = Metadata(
6
+ title="Page Not Found",
7
+ description="The page you are looking for does not exist.",
8
+ extra={"robots": "noindex, nofollow"},
9
+ )
10
+
11
+
12
+ def page():
13
+ return html(r"""
14
+ <main class="flex min-h-screen items-center justify-center bg-gray-50 px-4">
15
+ <div class="text-center">
16
+ <h1 class="text-9xl font-black text-gray-200">404</h1>
17
+
18
+ <p class="text-2xl font-bold tracking-tight text-gray-900 sm:text-4xl">
19
+ Uh-oh!
20
+ </p>
21
+
22
+ <p class="mt-4 text-gray-500">We can't find that page.</p>
23
+
24
+ <a
25
+ href="/"
26
+ class="mt-6 inline-block rounded bg-indigo-600 px-5 py-3 text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring"
27
+ >
28
+ Go Back Home
29
+ </a>
30
+ </div>
31
+ </main>
32
+ """)
@@ -105,7 +105,7 @@ reported `path:line:col`.
105
105
 
106
106
  Removing "unused" imports is the one fix that is dangerous in this app. Caspian
107
107
  single-file components import their children and then use them only as `<x-*>`
108
- tags inside `html(...)`/`render_html(...)` template strings (e.g. `from .Dialog
108
+ tags inside `html(...)` template strings (e.g. `from .Dialog
109
109
  import DialogContent` → `<x-dialog-content>`). Ruff can't parse the template, so
110
110
  it sees the import as unused — but casp resolves the tag from the module's
111
111
  globals at render time, so deleting it breaks the page.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-caspian-app",
3
- "version": "1.0.22",
3
+ "version": "1.2.0",
4
4
  "description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "author": "Jefferson Abraham Omier <thesteelninjacode@gmail.com>",
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
- "chalk": "^5.6.2",
34
+ "chalk": "^6.0.0",
35
35
  "prompts": "^2.4.2"
36
36
  }
37
37
  }
@@ -1,111 +0,0 @@
1
- <div class="container py-10">
2
- <div class="max-w-2xl mx-auto bg-white shadow-sm rounded p-6">
3
- <div class="flex items-start gap-4">
4
- <svg class="w-10 h-10 text-red-600"
5
- viewBox="0 0 24 24"
6
- fill="none"
7
- xmlns="http://www.w3.org/2000/svg"
8
- aria-hidden>
9
- <path d="M11.001 2a1 1 0 0 1 .998 0L21 7v9a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V7l8.001-5z"
10
- stroke="currentColor"
11
- stroke-width="1.5"
12
- stroke-linecap="round"
13
- stroke-linejoin="round" />
14
- <path d="M12 8v4"
15
- stroke="currentColor"
16
- stroke-width="1.5"
17
- stroke-linecap="round"
18
- stroke-linejoin="round" />
19
- <path d="M12 16h.01"
20
- stroke="currentColor"
21
- stroke-width="1.5"
22
- stroke-linecap="round"
23
- stroke-linejoin="round" />
24
- </svg>
25
-
26
- <div class="flex-1">
27
- <h1 class="text-2xl font-semibold text-red-600">
28
- Something went wrong
29
- </h1>
30
- <p class="mt-2 text-sm text-gray-600">
31
- We're sorry — an unexpected error occurred. Try reloading or return to
32
- the homepage.
33
- </p>
34
-
35
- <p class="mt-3 text-sm text-gray-700">{{ error_message }}</p>
36
-
37
- <div class="mt-4 flex flex-wrap gap-2">
38
- <a href="/"
39
- class="inline-block px-3 py-1.5 bg-gray-100 border border-gray-200 rounded text-sm text-gray-800 hover:bg-gray-200">Home</a>
40
- <button class="inline-block px-3 py-1.5 bg-blue-600 text-white rounded text-sm"
41
- onclick="reloadPage()">
42
- Reload
43
- </button>
44
- {% if error_trace %}
45
- <button id="toggleTraceBtn"
46
- class="inline-block px-3 py-1.5 bg-transparent border border-gray-200 rounded text-sm text-black"
47
- onclick="setShowTrace(showTrace => !showTrace)">
48
- {showTrace ? "Hide details" : "Show details"}
49
- </button>
50
- {% endif %}
51
- </div>
52
- </div>
53
- </div>
54
-
55
- {% if error_trace %}
56
- <div id="traceArea"
57
- class="mt-4"
58
- hidden="{!showTrace}">
59
- <div class="flex items-center justify-between mb-2">
60
- <div class="text-xs text-gray-500">Error details (soft copy)</div>
61
- <div class="flex gap-2">
62
- <button class="px-2 py-1 text-xs bg-gray-100 border rounded text-black"
63
- onclick="copyTrace()">
64
- {showCopyTrace ? "Copied" : "Copy"}
65
- </button>
66
- <button class="px-2 py-1 text-xs bg-gray-100 border rounded text-black"
67
- onclick="downloadTrace()">
68
- Download
69
- </button>
70
- </div>
71
- </div>
72
- <pre id="tracePre"
73
- class="p-4 bg-gray-100 rounded overflow-auto text-xs text-black">{{ error_trace }}</pre>
74
- </div>
75
- {% endif %}
76
- </div>
77
-
78
- <script>
79
- const [showTrace, setShowTrace] = pp.state(false);
80
- const [showCopyTrace, setShowCopyTrace] = pp.state(false);
81
-
82
- function reloadPage() {
83
- window.location.reload();
84
- }
85
-
86
- function copyTrace() {
87
- const tracePre = document.getElementById("tracePre");
88
- navigator.clipboard.writeText(tracePre.innerText);
89
-
90
- setShowCopyTrace(true);
91
- setTimeout(() => {
92
- setShowCopyTrace(false);
93
- }, 2000);
94
- }
95
-
96
- function downloadTrace() {
97
- const tracePre = document.getElementById("tracePre");
98
- const blob = new Blob([tracePre.innerText], {
99
- type: "text/plain",
100
- });
101
- const url = URL.createObjectURL(blob);
102
- const a = document.createElement("a");
103
- a.href = url;
104
- a.download = "error_trace.txt";
105
- document.body.appendChild(a);
106
- a.click();
107
- document.body.removeChild(a);
108
- URL.revokeObjectURL(url);
109
- }
110
- </script>
111
- </div>
@@ -1,26 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8" />
6
- <meta name="viewport"
7
- content="width=device-width, initial-scale=1.0" />
8
- <title>{{ metadata.title | default("Caspian App") }}</title>
9
- <meta name="description"
10
- content="{{ metadata.description | default('Powered by Caspian Framework') }}" />
11
- <link rel="icon"
12
- href="/favicon.ico"
13
- type="image/x-icon"
14
- sizes="16x16" />
15
- </head>
16
-
17
- <body style="
18
- opacity: 0;
19
- pointer-events: none;
20
- user-select: none;
21
- transition: opacity 0.18s ease-out;
22
- ">
23
- <slot />
24
- </body>
25
-
26
- </html>